blob: dba52893a769a58fd023aadaf159e715e8a971e6 [file] [log] [blame]
Michael Kolb8233fac2010-10-26 16:08:53 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser;
18
19import android.app.AlertDialog;
20import android.content.Context;
21import android.content.DialogInterface;
22import android.content.res.Configuration;
23import android.net.http.SslCertificate;
24import android.net.http.SslError;
Michael Kolb8233fac2010-10-26 16:08:53 -070025import android.view.LayoutInflater;
26import android.view.View;
27import android.webkit.HttpAuthHandler;
28import android.webkit.SslErrorHandler;
29import android.webkit.WebView;
30import android.widget.LinearLayout;
31import android.widget.TextView;
32
Michael Kolb8233fac2010-10-26 16:08:53 -070033/**
34 * Displays page info
35 *
36 */
37public class PageDialogsHandler {
38
39 private Context mContext;
40 private Controller mController;
41 private boolean mPageInfoFromShowSSLCertificateOnError;
Huahui Wuae0c0412011-06-28 10:17:05 -070042 private String mUrlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -070043 private Tab mPageInfoView;
44 private AlertDialog mPageInfoDialog;
45
46 // as SSLCertificateOnError has different style for landscape / portrait,
47 // we have to re-open it when configuration changed
48 private AlertDialog mSSLCertificateOnErrorDialog;
49 private WebView mSSLCertificateOnErrorView;
50 private SslErrorHandler mSSLCertificateOnErrorHandler;
51 private SslError mSSLCertificateOnErrorError;
52
53 // as SSLCertificate has different style for landscape / portrait, we
54 // have to re-open it when configuration changed
55 private AlertDialog mSSLCertificateDialog;
56 private Tab mSSLCertificateView;
57 private HttpAuthenticationDialog mHttpAuthenticationDialog;
58
59 public PageDialogsHandler(Context context, Controller controller) {
60 mContext = context;
61 mController = controller;
62 }
63
64 public void onConfigurationChanged(Configuration config) {
65 if (mPageInfoDialog != null) {
66 mPageInfoDialog.dismiss();
Huahui Wuae0c0412011-06-28 10:17:05 -070067 showPageInfo(mPageInfoView,
68 mPageInfoFromShowSSLCertificateOnError,
69 mUrlCertificateOnError);
Michael Kolb8233fac2010-10-26 16:08:53 -070070 }
71 if (mSSLCertificateDialog != null) {
72 mSSLCertificateDialog.dismiss();
73 showSSLCertificate(mSSLCertificateView);
74 }
75 if (mSSLCertificateOnErrorDialog != null) {
76 mSSLCertificateOnErrorDialog.dismiss();
Huahui Wuae0c0412011-06-28 10:17:05 -070077 showSSLCertificateOnError(mSSLCertificateOnErrorView,
78 mSSLCertificateOnErrorHandler,
79 mSSLCertificateOnErrorError);
Michael Kolb8233fac2010-10-26 16:08:53 -070080 }
81 if (mHttpAuthenticationDialog != null) {
82 mHttpAuthenticationDialog.reshow();
83 }
84 }
85
86 /**
87 * Displays an http-authentication dialog.
88 */
89 void showHttpAuthentication(final Tab tab, final HttpAuthHandler handler, String host, String realm) {
90 mHttpAuthenticationDialog = new HttpAuthenticationDialog(mContext, host, realm);
91 mHttpAuthenticationDialog.setOkListener(new HttpAuthenticationDialog.OkListener() {
92 public void onOk(String host, String realm, String username, String password) {
93 setHttpAuthUsernamePassword(host, realm, username, password);
94 handler.proceed(username, password);
95 mHttpAuthenticationDialog = null;
96 }
97 });
98 mHttpAuthenticationDialog.setCancelListener(new HttpAuthenticationDialog.CancelListener() {
99 public void onCancel() {
100 handler.cancel();
Steve Block2466eff2011-10-03 15:33:09 +0100101 mController.onUpdatedSecurityState(tab);
Michael Kolb8233fac2010-10-26 16:08:53 -0700102 mHttpAuthenticationDialog = null;
103 }
104 });
105 mHttpAuthenticationDialog.show();
106 }
107
108 /**
109 * Set HTTP authentication password.
110 *
111 * @param host The host for the password
112 * @param realm The realm for the password
113 * @param username The username for the password. If it is null, it means
114 * password can't be saved.
115 * @param password The password
116 */
117 public void setHttpAuthUsernamePassword(String host, String realm,
118 String username,
119 String password) {
120 WebView w = mController.getCurrentTopWebView();
121 if (w != null) {
122 w.setHttpAuthUsernamePassword(host, realm, username, password);
123 }
124 }
125
126 /**
127 * Displays a page-info dialog.
128 * @param tab The tab to show info about
129 * @param fromShowSSLCertificateOnError The flag that indicates whether
130 * this dialog was opened from the SSL-certificate-on-error dialog or
131 * not. This is important, since we need to know whether to return to
132 * the parent dialog or simply dismiss.
Huahui Wuae0c0412011-06-28 10:17:05 -0700133 * @param urlCertificateOnError The URL that invokes SSLCertificateError.
134 * Null when fromShowSSLCertificateOnError is false.
Michael Kolb8233fac2010-10-26 16:08:53 -0700135 */
136 void showPageInfo(final Tab tab,
Huahui Wuae0c0412011-06-28 10:17:05 -0700137 final boolean fromShowSSLCertificateOnError,
138 final String urlCertificateOnError) {
Michael Kolbd5cb4fa2012-04-18 08:29:16 -0700139 if (tab == null) return;
Michael Kolb8233fac2010-10-26 16:08:53 -0700140 final LayoutInflater factory = LayoutInflater.from(mContext);
141
142 final View pageInfoView = factory.inflate(R.layout.page_info, null);
143
144 final WebView view = tab.getWebView();
145
Huahui Wuae0c0412011-06-28 10:17:05 -0700146 String url = fromShowSSLCertificateOnError ? urlCertificateOnError : tab.getUrl();
John Reck30c714c2010-12-16 17:30:34 -0800147 String title = tab.getTitle();
Michael Kolb8233fac2010-10-26 16:08:53 -0700148
149 if (url == null) {
150 url = "";
151 }
152 if (title == null) {
153 title = "";
154 }
155
156 ((TextView) pageInfoView.findViewById(R.id.address)).setText(url);
157 ((TextView) pageInfoView.findViewById(R.id.title)).setText(title);
158
159 mPageInfoView = tab;
160 mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError;
Huahui Wuae0c0412011-06-28 10:17:05 -0700161 mUrlCertificateOnError = urlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -0700162
163 AlertDialog.Builder alertDialogBuilder =
164 new AlertDialog.Builder(mContext)
165 .setTitle(R.string.page_info)
166 .setIcon(android.R.drawable.ic_dialog_info)
167 .setView(pageInfoView)
168 .setPositiveButton(
169 R.string.ok,
170 new DialogInterface.OnClickListener() {
171 public void onClick(DialogInterface dialog,
172 int whichButton) {
173 mPageInfoDialog = null;
174 mPageInfoView = null;
175
176 // if we came here from the SSL error dialog
177 if (fromShowSSLCertificateOnError) {
178 // go back to the SSL error dialog
179 showSSLCertificateOnError(
180 mSSLCertificateOnErrorView,
181 mSSLCertificateOnErrorHandler,
182 mSSLCertificateOnErrorError);
183 }
184 }
185 })
186 .setOnCancelListener(
187 new DialogInterface.OnCancelListener() {
188 public void onCancel(DialogInterface dialog) {
189 mPageInfoDialog = null;
190 mPageInfoView = null;
191
192 // if we came here from the SSL error dialog
193 if (fromShowSSLCertificateOnError) {
194 // go back to the SSL error dialog
195 showSSLCertificateOnError(
196 mSSLCertificateOnErrorView,
197 mSSLCertificateOnErrorHandler,
198 mSSLCertificateOnErrorError);
199 }
200 }
201 });
202
203 // if we have a main top-level page SSL certificate set or a certificate
204 // error
205 if (fromShowSSLCertificateOnError ||
206 (view != null && view.getCertificate() != null)) {
207 // add a 'View Certificate' button
208 alertDialogBuilder.setNeutralButton(
209 R.string.view_certificate,
210 new DialogInterface.OnClickListener() {
211 public void onClick(DialogInterface dialog,
212 int whichButton) {
213 mPageInfoDialog = null;
214 mPageInfoView = null;
215
216 // if we came here from the SSL error dialog
217 if (fromShowSSLCertificateOnError) {
218 // go back to the SSL error dialog
219 showSSLCertificateOnError(
220 mSSLCertificateOnErrorView,
221 mSSLCertificateOnErrorHandler,
222 mSSLCertificateOnErrorError);
223 } else {
224 // otherwise, display the top-most certificate from
225 // the chain
Steve Blockcbc67a02011-10-05 17:56:19 +0100226 showSSLCertificate(tab);
Michael Kolb8233fac2010-10-26 16:08:53 -0700227 }
228 }
229 });
230 }
231
232 mPageInfoDialog = alertDialogBuilder.show();
233 }
234
235 /**
236 * Displays the main top-level page SSL certificate dialog
237 * (accessible from the Page-Info dialog).
238 * @param tab The tab to show certificate for.
239 */
240 private void showSSLCertificate(final Tab tab) {
Brian Carlstrom873034c2011-06-26 21:06:43 -0700241
242 SslCertificate cert = tab.getWebView().getCertificate();
243 if (cert == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700244 return;
245 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700246
247 mSSLCertificateView = tab;
Steve Block08a6f0c2011-10-06 12:12:53 +0100248 mSSLCertificateDialog = createSslCertificateDialog(cert, tab.getSslCertificateError())
Michael Kolb8233fac2010-10-26 16:08:53 -0700249 .setPositiveButton(R.string.ok,
250 new DialogInterface.OnClickListener() {
251 public void onClick(DialogInterface dialog,
252 int whichButton) {
253 mSSLCertificateDialog = null;
254 mSSLCertificateView = null;
255
Huahui Wuae0c0412011-06-28 10:17:05 -0700256 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700257 }
258 })
259 .setOnCancelListener(
260 new DialogInterface.OnCancelListener() {
261 public void onCancel(DialogInterface dialog) {
262 mSSLCertificateDialog = null;
263 mSSLCertificateView = null;
264
Huahui Wuae0c0412011-06-28 10:17:05 -0700265 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700266 }
267 })
268 .show();
269 }
270
271 /**
272 * Displays the SSL error certificate dialog.
273 * @param view The target web-view.
274 * @param handler The SSL error handler responsible for cancelling the
275 * connection that resulted in an SSL error or proceeding per user request.
276 * @param error The SSL error object.
277 */
278 void showSSLCertificateOnError(
279 final WebView view, final SslErrorHandler handler,
280 final SslError error) {
281
Brian Carlstrom873034c2011-06-26 21:06:43 -0700282 SslCertificate cert = error.getCertificate();
283 if (cert == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700284 return;
285 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700286
287 mSSLCertificateOnErrorHandler = handler;
288 mSSLCertificateOnErrorView = view;
289 mSSLCertificateOnErrorError = error;
Steve Blockcbc67a02011-10-05 17:56:19 +0100290 mSSLCertificateOnErrorDialog = createSslCertificateDialog(cert, error)
Michael Kolb8233fac2010-10-26 16:08:53 -0700291 .setPositiveButton(R.string.ok,
292 new DialogInterface.OnClickListener() {
293 public void onClick(DialogInterface dialog,
294 int whichButton) {
295 mSSLCertificateOnErrorDialog = null;
296 mSSLCertificateOnErrorView = null;
297 mSSLCertificateOnErrorHandler = null;
298 mSSLCertificateOnErrorError = null;
299
Jonathan Dixone1d6dfc2012-12-17 13:39:17 -0800300 ((BrowserWebView) view).getWebViewClient().
Jonathan Dixon4d2fcab2012-02-24 00:13:06 +0000301 onReceivedSslError(view, handler, error);
Michael Kolb8233fac2010-10-26 16:08:53 -0700302 }
303 })
304 .setNeutralButton(R.string.page_info_view,
305 new DialogInterface.OnClickListener() {
306 public void onClick(DialogInterface dialog,
307 int whichButton) {
308 mSSLCertificateOnErrorDialog = null;
309
310 // do not clear the dialog state: we will
311 // need to show the dialog again once the
312 // user is done exploring the page-info details
313
314 showPageInfo(mController.getTabControl()
315 .getTabFromView(view),
Huahui Wuae0c0412011-06-28 10:17:05 -0700316 true,
317 error.getUrl());
Michael Kolb8233fac2010-10-26 16:08:53 -0700318 }
319 })
320 .setOnCancelListener(
321 new DialogInterface.OnCancelListener() {
322 public void onCancel(DialogInterface dialog) {
323 mSSLCertificateOnErrorDialog = null;
324 mSSLCertificateOnErrorView = null;
325 mSSLCertificateOnErrorHandler = null;
326 mSSLCertificateOnErrorError = null;
327
Jonathan Dixone1d6dfc2012-12-17 13:39:17 -0800328 ((BrowserWebView) view).getWebViewClient().
Jonathan Dixon4d2fcab2012-02-24 00:13:06 +0000329 onReceivedSslError(view, handler, error);
Michael Kolb8233fac2010-10-26 16:08:53 -0700330 }
331 })
332 .show();
333 }
Steve Blockcbc67a02011-10-05 17:56:19 +0100334
335 /*
336 * Creates an AlertDialog to display the given certificate. If error is
337 * null, text is added to state that the certificae is valid and the icon
338 * is set accordingly. If error is non-null, it must relate to the supplied
339 * certificate. In this case, error is used to add text describing the
340 * problems with the certificate and a different icon is used.
341 */
342 private AlertDialog.Builder createSslCertificateDialog(SslCertificate certificate,
343 SslError error) {
344 View certificateView = certificate.inflateCertificateView(mContext);
345 final LinearLayout placeholder =
346 (LinearLayout)certificateView.findViewById(com.android.internal.R.id.placeholder);
347
348 LayoutInflater factory = LayoutInflater.from(mContext);
349 int iconId;
350
351 if (error == null) {
352 iconId = R.drawable.ic_dialog_browser_certificate_secure;
353 LinearLayout table = (LinearLayout)factory.inflate(R.layout.ssl_success, placeholder);
354 TextView successString = (TextView)table.findViewById(R.id.success);
355 successString.setText(com.android.internal.R.string.ssl_certificate_is_valid);
356 } else {
357 iconId = R.drawable.ic_dialog_browser_certificate_partially_secure;
358 if (error.hasError(SslError.SSL_UNTRUSTED)) {
359 addError(factory, placeholder, R.string.ssl_untrusted);
360 }
361 if (error.hasError(SslError.SSL_IDMISMATCH)) {
362 addError(factory, placeholder, R.string.ssl_mismatch);
363 }
364 if (error.hasError(SslError.SSL_EXPIRED)) {
365 addError(factory, placeholder, R.string.ssl_expired);
366 }
367 if (error.hasError(SslError.SSL_NOTYETVALID)) {
368 addError(factory, placeholder, R.string.ssl_not_yet_valid);
369 }
370 if (error.hasError(SslError.SSL_DATE_INVALID)) {
371 addError(factory, placeholder, R.string.ssl_date_invalid);
372 }
373 if (error.hasError(SslError.SSL_INVALID)) {
374 addError(factory, placeholder, R.string.ssl_invalid);
375 }
376 // The SslError should always have at least one type of error and we
377 // should explicitly handle every type of error it supports. We
378 // therefore expect the condition below to never be hit. We use it
379 // as as safety net in case a new error type is added to SslError
380 // without the logic above being updated accordingly.
381 if (placeholder.getChildCount() == 0) {
382 addError(factory, placeholder, R.string.ssl_unknown);
383 }
384 }
385
386 return new AlertDialog.Builder(mContext)
387 .setTitle(com.android.internal.R.string.ssl_certificate)
388 .setIcon(iconId)
389 .setView(certificateView);
390 }
391
392 private void addError(LayoutInflater inflater, LinearLayout parent, int error) {
393 TextView textView = (TextView) inflater.inflate(R.layout.ssl_warning,
394 parent, false);
395 textView.setText(error);
396 parent.addView(textView);
397 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700398}