blob: 3ed1004de234a615b6b0cadacaee4738a07d1fcb [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;
Jonathan Dixon4d2fcab2012-02-24 00:13:06 +000030import android.webkit.WebViewClassic;
Michael Kolb8233fac2010-10-26 16:08:53 -070031import android.widget.LinearLayout;
32import android.widget.TextView;
33
Michael Kolb8233fac2010-10-26 16:08:53 -070034/**
35 * Displays page info
36 *
37 */
38public class PageDialogsHandler {
39
40 private Context mContext;
41 private Controller mController;
42 private boolean mPageInfoFromShowSSLCertificateOnError;
Huahui Wuae0c0412011-06-28 10:17:05 -070043 private String mUrlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -070044 private Tab mPageInfoView;
45 private AlertDialog mPageInfoDialog;
46
47 // as SSLCertificateOnError has different style for landscape / portrait,
48 // we have to re-open it when configuration changed
49 private AlertDialog mSSLCertificateOnErrorDialog;
50 private WebView mSSLCertificateOnErrorView;
51 private SslErrorHandler mSSLCertificateOnErrorHandler;
52 private SslError mSSLCertificateOnErrorError;
53
54 // as SSLCertificate has different style for landscape / portrait, we
55 // have to re-open it when configuration changed
56 private AlertDialog mSSLCertificateDialog;
57 private Tab mSSLCertificateView;
58 private HttpAuthenticationDialog mHttpAuthenticationDialog;
59
60 public PageDialogsHandler(Context context, Controller controller) {
61 mContext = context;
62 mController = controller;
63 }
64
65 public void onConfigurationChanged(Configuration config) {
66 if (mPageInfoDialog != null) {
67 mPageInfoDialog.dismiss();
Huahui Wuae0c0412011-06-28 10:17:05 -070068 showPageInfo(mPageInfoView,
69 mPageInfoFromShowSSLCertificateOnError,
70 mUrlCertificateOnError);
Michael Kolb8233fac2010-10-26 16:08:53 -070071 }
72 if (mSSLCertificateDialog != null) {
73 mSSLCertificateDialog.dismiss();
74 showSSLCertificate(mSSLCertificateView);
75 }
76 if (mSSLCertificateOnErrorDialog != null) {
77 mSSLCertificateOnErrorDialog.dismiss();
Huahui Wuae0c0412011-06-28 10:17:05 -070078 showSSLCertificateOnError(mSSLCertificateOnErrorView,
79 mSSLCertificateOnErrorHandler,
80 mSSLCertificateOnErrorError);
Michael Kolb8233fac2010-10-26 16:08:53 -070081 }
82 if (mHttpAuthenticationDialog != null) {
83 mHttpAuthenticationDialog.reshow();
84 }
85 }
86
87 /**
88 * Displays an http-authentication dialog.
89 */
90 void showHttpAuthentication(final Tab tab, final HttpAuthHandler handler, String host, String realm) {
91 mHttpAuthenticationDialog = new HttpAuthenticationDialog(mContext, host, realm);
92 mHttpAuthenticationDialog.setOkListener(new HttpAuthenticationDialog.OkListener() {
93 public void onOk(String host, String realm, String username, String password) {
94 setHttpAuthUsernamePassword(host, realm, username, password);
95 handler.proceed(username, password);
96 mHttpAuthenticationDialog = null;
97 }
98 });
99 mHttpAuthenticationDialog.setCancelListener(new HttpAuthenticationDialog.CancelListener() {
100 public void onCancel() {
101 handler.cancel();
Steve Block2466eff2011-10-03 15:33:09 +0100102 mController.onUpdatedSecurityState(tab);
Michael Kolb8233fac2010-10-26 16:08:53 -0700103 mHttpAuthenticationDialog = null;
104 }
105 });
106 mHttpAuthenticationDialog.show();
107 }
108
109 /**
110 * Set HTTP authentication password.
111 *
112 * @param host The host for the password
113 * @param realm The realm for the password
114 * @param username The username for the password. If it is null, it means
115 * password can't be saved.
116 * @param password The password
117 */
118 public void setHttpAuthUsernamePassword(String host, String realm,
119 String username,
120 String password) {
121 WebView w = mController.getCurrentTopWebView();
122 if (w != null) {
123 w.setHttpAuthUsernamePassword(host, realm, username, password);
124 }
125 }
126
127 /**
128 * Displays a page-info dialog.
129 * @param tab The tab to show info about
130 * @param fromShowSSLCertificateOnError The flag that indicates whether
131 * this dialog was opened from the SSL-certificate-on-error dialog or
132 * not. This is important, since we need to know whether to return to
133 * the parent dialog or simply dismiss.
Huahui Wuae0c0412011-06-28 10:17:05 -0700134 * @param urlCertificateOnError The URL that invokes SSLCertificateError.
135 * Null when fromShowSSLCertificateOnError is false.
Michael Kolb8233fac2010-10-26 16:08:53 -0700136 */
137 void showPageInfo(final Tab tab,
Huahui Wuae0c0412011-06-28 10:17:05 -0700138 final boolean fromShowSSLCertificateOnError,
139 final String urlCertificateOnError) {
Michael Kolbd5cb4fa2012-04-18 08:29:16 -0700140 if (tab == null) return;
Michael Kolb8233fac2010-10-26 16:08:53 -0700141 final LayoutInflater factory = LayoutInflater.from(mContext);
142
143 final View pageInfoView = factory.inflate(R.layout.page_info, null);
144
145 final WebView view = tab.getWebView();
146
Huahui Wuae0c0412011-06-28 10:17:05 -0700147 String url = fromShowSSLCertificateOnError ? urlCertificateOnError : tab.getUrl();
John Reck30c714c2010-12-16 17:30:34 -0800148 String title = tab.getTitle();
Michael Kolb8233fac2010-10-26 16:08:53 -0700149
150 if (url == null) {
151 url = "";
152 }
153 if (title == null) {
154 title = "";
155 }
156
157 ((TextView) pageInfoView.findViewById(R.id.address)).setText(url);
158 ((TextView) pageInfoView.findViewById(R.id.title)).setText(title);
159
160 mPageInfoView = tab;
161 mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError;
Huahui Wuae0c0412011-06-28 10:17:05 -0700162 mUrlCertificateOnError = urlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -0700163
164 AlertDialog.Builder alertDialogBuilder =
165 new AlertDialog.Builder(mContext)
166 .setTitle(R.string.page_info)
167 .setIcon(android.R.drawable.ic_dialog_info)
168 .setView(pageInfoView)
169 .setPositiveButton(
170 R.string.ok,
171 new DialogInterface.OnClickListener() {
172 public void onClick(DialogInterface dialog,
173 int whichButton) {
174 mPageInfoDialog = null;
175 mPageInfoView = null;
176
177 // if we came here from the SSL error dialog
178 if (fromShowSSLCertificateOnError) {
179 // go back to the SSL error dialog
180 showSSLCertificateOnError(
181 mSSLCertificateOnErrorView,
182 mSSLCertificateOnErrorHandler,
183 mSSLCertificateOnErrorError);
184 }
185 }
186 })
187 .setOnCancelListener(
188 new DialogInterface.OnCancelListener() {
189 public void onCancel(DialogInterface dialog) {
190 mPageInfoDialog = null;
191 mPageInfoView = null;
192
193 // if we came here from the SSL error dialog
194 if (fromShowSSLCertificateOnError) {
195 // go back to the SSL error dialog
196 showSSLCertificateOnError(
197 mSSLCertificateOnErrorView,
198 mSSLCertificateOnErrorHandler,
199 mSSLCertificateOnErrorError);
200 }
201 }
202 });
203
204 // if we have a main top-level page SSL certificate set or a certificate
205 // error
206 if (fromShowSSLCertificateOnError ||
207 (view != null && view.getCertificate() != null)) {
208 // add a 'View Certificate' button
209 alertDialogBuilder.setNeutralButton(
210 R.string.view_certificate,
211 new DialogInterface.OnClickListener() {
212 public void onClick(DialogInterface dialog,
213 int whichButton) {
214 mPageInfoDialog = null;
215 mPageInfoView = null;
216
217 // if we came here from the SSL error dialog
218 if (fromShowSSLCertificateOnError) {
219 // go back to the SSL error dialog
220 showSSLCertificateOnError(
221 mSSLCertificateOnErrorView,
222 mSSLCertificateOnErrorHandler,
223 mSSLCertificateOnErrorError);
224 } else {
225 // otherwise, display the top-most certificate from
226 // the chain
Steve Blockcbc67a02011-10-05 17:56:19 +0100227 showSSLCertificate(tab);
Michael Kolb8233fac2010-10-26 16:08:53 -0700228 }
229 }
230 });
231 }
232
233 mPageInfoDialog = alertDialogBuilder.show();
234 }
235
236 /**
237 * Displays the main top-level page SSL certificate dialog
238 * (accessible from the Page-Info dialog).
239 * @param tab The tab to show certificate for.
240 */
241 private void showSSLCertificate(final Tab tab) {
Brian Carlstrom873034c2011-06-26 21:06:43 -0700242
243 SslCertificate cert = tab.getWebView().getCertificate();
244 if (cert == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700245 return;
246 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700247
248 mSSLCertificateView = tab;
Steve Block08a6f0c2011-10-06 12:12:53 +0100249 mSSLCertificateDialog = createSslCertificateDialog(cert, tab.getSslCertificateError())
Michael Kolb8233fac2010-10-26 16:08:53 -0700250 .setPositiveButton(R.string.ok,
251 new DialogInterface.OnClickListener() {
252 public void onClick(DialogInterface dialog,
253 int whichButton) {
254 mSSLCertificateDialog = null;
255 mSSLCertificateView = null;
256
Huahui Wuae0c0412011-06-28 10:17:05 -0700257 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700258 }
259 })
260 .setOnCancelListener(
261 new DialogInterface.OnCancelListener() {
262 public void onCancel(DialogInterface dialog) {
263 mSSLCertificateDialog = null;
264 mSSLCertificateView = null;
265
Huahui Wuae0c0412011-06-28 10:17:05 -0700266 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700267 }
268 })
269 .show();
270 }
271
272 /**
273 * Displays the SSL error certificate dialog.
274 * @param view The target web-view.
275 * @param handler The SSL error handler responsible for cancelling the
276 * connection that resulted in an SSL error or proceeding per user request.
277 * @param error The SSL error object.
278 */
279 void showSSLCertificateOnError(
280 final WebView view, final SslErrorHandler handler,
281 final SslError error) {
282
Brian Carlstrom873034c2011-06-26 21:06:43 -0700283 SslCertificate cert = error.getCertificate();
284 if (cert == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700285 return;
286 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700287
288 mSSLCertificateOnErrorHandler = handler;
289 mSSLCertificateOnErrorView = view;
290 mSSLCertificateOnErrorError = error;
Steve Blockcbc67a02011-10-05 17:56:19 +0100291 mSSLCertificateOnErrorDialog = createSslCertificateDialog(cert, error)
Michael Kolb8233fac2010-10-26 16:08:53 -0700292 .setPositiveButton(R.string.ok,
293 new DialogInterface.OnClickListener() {
294 public void onClick(DialogInterface dialog,
295 int whichButton) {
296 mSSLCertificateOnErrorDialog = null;
297 mSSLCertificateOnErrorView = null;
298 mSSLCertificateOnErrorHandler = null;
299 mSSLCertificateOnErrorError = null;
300
Jonathan Dixon4d2fcab2012-02-24 00:13:06 +0000301 WebViewClassic.fromWebView(view).getWebViewClient().
302 onReceivedSslError(view, handler, error);
Michael Kolb8233fac2010-10-26 16:08:53 -0700303 }
304 })
305 .setNeutralButton(R.string.page_info_view,
306 new DialogInterface.OnClickListener() {
307 public void onClick(DialogInterface dialog,
308 int whichButton) {
309 mSSLCertificateOnErrorDialog = null;
310
311 // do not clear the dialog state: we will
312 // need to show the dialog again once the
313 // user is done exploring the page-info details
314
315 showPageInfo(mController.getTabControl()
316 .getTabFromView(view),
Huahui Wuae0c0412011-06-28 10:17:05 -0700317 true,
318 error.getUrl());
Michael Kolb8233fac2010-10-26 16:08:53 -0700319 }
320 })
321 .setOnCancelListener(
322 new DialogInterface.OnCancelListener() {
323 public void onCancel(DialogInterface dialog) {
324 mSSLCertificateOnErrorDialog = null;
325 mSSLCertificateOnErrorView = null;
326 mSSLCertificateOnErrorHandler = null;
327 mSSLCertificateOnErrorError = null;
328
Jonathan Dixon4d2fcab2012-02-24 00:13:06 +0000329 WebViewClassic.fromWebView(view).getWebViewClient().
330 onReceivedSslError(view, handler, error);
Michael Kolb8233fac2010-10-26 16:08:53 -0700331 }
332 })
333 .show();
334 }
Steve Blockcbc67a02011-10-05 17:56:19 +0100335
336 /*
337 * Creates an AlertDialog to display the given certificate. If error is
338 * null, text is added to state that the certificae is valid and the icon
339 * is set accordingly. If error is non-null, it must relate to the supplied
340 * certificate. In this case, error is used to add text describing the
341 * problems with the certificate and a different icon is used.
342 */
343 private AlertDialog.Builder createSslCertificateDialog(SslCertificate certificate,
344 SslError error) {
345 View certificateView = certificate.inflateCertificateView(mContext);
346 final LinearLayout placeholder =
347 (LinearLayout)certificateView.findViewById(com.android.internal.R.id.placeholder);
348
349 LayoutInflater factory = LayoutInflater.from(mContext);
350 int iconId;
351
352 if (error == null) {
353 iconId = R.drawable.ic_dialog_browser_certificate_secure;
354 LinearLayout table = (LinearLayout)factory.inflate(R.layout.ssl_success, placeholder);
355 TextView successString = (TextView)table.findViewById(R.id.success);
356 successString.setText(com.android.internal.R.string.ssl_certificate_is_valid);
357 } else {
358 iconId = R.drawable.ic_dialog_browser_certificate_partially_secure;
359 if (error.hasError(SslError.SSL_UNTRUSTED)) {
360 addError(factory, placeholder, R.string.ssl_untrusted);
361 }
362 if (error.hasError(SslError.SSL_IDMISMATCH)) {
363 addError(factory, placeholder, R.string.ssl_mismatch);
364 }
365 if (error.hasError(SslError.SSL_EXPIRED)) {
366 addError(factory, placeholder, R.string.ssl_expired);
367 }
368 if (error.hasError(SslError.SSL_NOTYETVALID)) {
369 addError(factory, placeholder, R.string.ssl_not_yet_valid);
370 }
371 if (error.hasError(SslError.SSL_DATE_INVALID)) {
372 addError(factory, placeholder, R.string.ssl_date_invalid);
373 }
374 if (error.hasError(SslError.SSL_INVALID)) {
375 addError(factory, placeholder, R.string.ssl_invalid);
376 }
377 // The SslError should always have at least one type of error and we
378 // should explicitly handle every type of error it supports. We
379 // therefore expect the condition below to never be hit. We use it
380 // as as safety net in case a new error type is added to SslError
381 // without the logic above being updated accordingly.
382 if (placeholder.getChildCount() == 0) {
383 addError(factory, placeholder, R.string.ssl_unknown);
384 }
385 }
386
387 return new AlertDialog.Builder(mContext)
388 .setTitle(com.android.internal.R.string.ssl_certificate)
389 .setIcon(iconId)
390 .setView(certificateView);
391 }
392
393 private void addError(LayoutInflater inflater, LinearLayout parent, int error) {
394 TextView textView = (TextView) inflater.inflate(R.layout.ssl_warning,
395 parent, false);
396 textView.setText(error);
397 parent.addView(textView);
398 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700399}