blob: b9e08594a70400a85f3331f1758d238a1fdca223 [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;
25import android.text.format.DateFormat;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.webkit.HttpAuthHandler;
29import android.webkit.SslErrorHandler;
30import android.webkit.WebView;
31import android.widget.LinearLayout;
32import android.widget.TextView;
33
34import java.util.Date;
35
36/**
37 * Displays page info
38 *
39 */
40public class PageDialogsHandler {
41
42 private Context mContext;
43 private Controller mController;
44 private boolean mPageInfoFromShowSSLCertificateOnError;
Huahui Wudb6ff892011-06-27 11:16:07 -070045 private String mUrlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -070046 private Tab mPageInfoView;
47 private AlertDialog mPageInfoDialog;
48
49 // as SSLCertificateOnError has different style for landscape / portrait,
50 // we have to re-open it when configuration changed
51 private AlertDialog mSSLCertificateOnErrorDialog;
52 private WebView mSSLCertificateOnErrorView;
53 private SslErrorHandler mSSLCertificateOnErrorHandler;
54 private SslError mSSLCertificateOnErrorError;
55
56 // as SSLCertificate has different style for landscape / portrait, we
57 // have to re-open it when configuration changed
58 private AlertDialog mSSLCertificateDialog;
59 private Tab mSSLCertificateView;
60 private HttpAuthenticationDialog mHttpAuthenticationDialog;
61
62 public PageDialogsHandler(Context context, Controller controller) {
63 mContext = context;
64 mController = controller;
65 }
66
67 public void onConfigurationChanged(Configuration config) {
68 if (mPageInfoDialog != null) {
69 mPageInfoDialog.dismiss();
Huahui Wudb6ff892011-06-27 11:16:07 -070070 showPageInfo(mPageInfoView,
71 mPageInfoFromShowSSLCertificateOnError,
72 mUrlCertificateOnError);
Michael Kolb8233fac2010-10-26 16:08:53 -070073 }
74 if (mSSLCertificateDialog != null) {
75 mSSLCertificateDialog.dismiss();
76 showSSLCertificate(mSSLCertificateView);
77 }
78 if (mSSLCertificateOnErrorDialog != null) {
79 mSSLCertificateOnErrorDialog.dismiss();
Huahui Wudb6ff892011-06-27 11:16:07 -070080 showSSLCertificateOnError(mSSLCertificateOnErrorView,
81 mSSLCertificateOnErrorHandler,
82 mSSLCertificateOnErrorError);
Michael Kolb8233fac2010-10-26 16:08:53 -070083 }
84 if (mHttpAuthenticationDialog != null) {
85 mHttpAuthenticationDialog.reshow();
86 }
87 }
88
89 /**
90 * Displays an http-authentication dialog.
91 */
92 void showHttpAuthentication(final Tab tab, final HttpAuthHandler handler, String host, String realm) {
93 mHttpAuthenticationDialog = new HttpAuthenticationDialog(mContext, host, realm);
94 mHttpAuthenticationDialog.setOkListener(new HttpAuthenticationDialog.OkListener() {
95 public void onOk(String host, String realm, String username, String password) {
96 setHttpAuthUsernamePassword(host, realm, username, password);
97 handler.proceed(username, password);
98 mHttpAuthenticationDialog = null;
99 }
100 });
101 mHttpAuthenticationDialog.setCancelListener(new HttpAuthenticationDialog.CancelListener() {
102 public void onCancel() {
103 handler.cancel();
John Reck30c714c2010-12-16 17:30:34 -0800104 mController.onUpdatedLockIcon(tab);
Michael Kolb8233fac2010-10-26 16:08:53 -0700105 mHttpAuthenticationDialog = null;
106 }
107 });
108 mHttpAuthenticationDialog.show();
109 }
110
111 /**
112 * Set HTTP authentication password.
113 *
114 * @param host The host for the password
115 * @param realm The realm for the password
116 * @param username The username for the password. If it is null, it means
117 * password can't be saved.
118 * @param password The password
119 */
120 public void setHttpAuthUsernamePassword(String host, String realm,
121 String username,
122 String password) {
123 WebView w = mController.getCurrentTopWebView();
124 if (w != null) {
125 w.setHttpAuthUsernamePassword(host, realm, username, password);
126 }
127 }
128
129 /**
130 * Displays a page-info dialog.
131 * @param tab The tab to show info about
132 * @param fromShowSSLCertificateOnError The flag that indicates whether
133 * this dialog was opened from the SSL-certificate-on-error dialog or
134 * not. This is important, since we need to know whether to return to
135 * the parent dialog or simply dismiss.
Huahui Wudb6ff892011-06-27 11:16:07 -0700136 * @param urlCertificateOnError The URL that invokes SSLCertificateError.
137 * Null when fromShowSSLCertificateOnError is false.
Michael Kolb8233fac2010-10-26 16:08:53 -0700138 */
139 void showPageInfo(final Tab tab,
Huahui Wudb6ff892011-06-27 11:16:07 -0700140 final boolean fromShowSSLCertificateOnError,
141 final String urlCertificateOnError) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700142 final LayoutInflater factory = LayoutInflater.from(mContext);
143
144 final View pageInfoView = factory.inflate(R.layout.page_info, null);
145
146 final WebView view = tab.getWebView();
147
Huahui Wudb6ff892011-06-27 11:16:07 -0700148 String url = fromShowSSLCertificateOnError ? urlCertificateOnError : tab.getUrl();
John Reck30c714c2010-12-16 17:30:34 -0800149 String title = tab.getTitle();
Michael Kolb8233fac2010-10-26 16:08:53 -0700150
151 if (url == null) {
152 url = "";
153 }
154 if (title == null) {
155 title = "";
156 }
157
158 ((TextView) pageInfoView.findViewById(R.id.address)).setText(url);
159 ((TextView) pageInfoView.findViewById(R.id.title)).setText(title);
160
161 mPageInfoView = tab;
162 mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError;
Huahui Wudb6ff892011-06-27 11:16:07 -0700163 mUrlCertificateOnError = urlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -0700164
165 AlertDialog.Builder alertDialogBuilder =
166 new AlertDialog.Builder(mContext)
167 .setTitle(R.string.page_info)
168 .setIcon(android.R.drawable.ic_dialog_info)
169 .setView(pageInfoView)
170 .setPositiveButton(
171 R.string.ok,
172 new DialogInterface.OnClickListener() {
173 public void onClick(DialogInterface dialog,
174 int whichButton) {
175 mPageInfoDialog = null;
176 mPageInfoView = null;
177
178 // if we came here from the SSL error dialog
179 if (fromShowSSLCertificateOnError) {
180 // go back to the SSL error dialog
181 showSSLCertificateOnError(
182 mSSLCertificateOnErrorView,
183 mSSLCertificateOnErrorHandler,
184 mSSLCertificateOnErrorError);
185 }
186 }
187 })
188 .setOnCancelListener(
189 new DialogInterface.OnCancelListener() {
190 public void onCancel(DialogInterface dialog) {
191 mPageInfoDialog = null;
192 mPageInfoView = null;
193
194 // if we came here from the SSL error dialog
195 if (fromShowSSLCertificateOnError) {
196 // go back to the SSL error dialog
197 showSSLCertificateOnError(
198 mSSLCertificateOnErrorView,
199 mSSLCertificateOnErrorHandler,
200 mSSLCertificateOnErrorError);
201 }
202 }
203 });
204
205 // if we have a main top-level page SSL certificate set or a certificate
206 // error
207 if (fromShowSSLCertificateOnError ||
208 (view != null && view.getCertificate() != null)) {
209 // add a 'View Certificate' button
210 alertDialogBuilder.setNeutralButton(
211 R.string.view_certificate,
212 new DialogInterface.OnClickListener() {
213 public void onClick(DialogInterface dialog,
214 int whichButton) {
215 mPageInfoDialog = null;
216 mPageInfoView = null;
217
218 // if we came here from the SSL error dialog
219 if (fromShowSSLCertificateOnError) {
220 // go back to the SSL error dialog
221 showSSLCertificateOnError(
222 mSSLCertificateOnErrorView,
223 mSSLCertificateOnErrorHandler,
224 mSSLCertificateOnErrorError);
225 } else {
226 // otherwise, display the top-most certificate from
227 // the chain
228 if (view.getCertificate() != null) {
229 showSSLCertificate(tab);
230 }
231 }
232 }
233 });
234 }
235
236 mPageInfoDialog = alertDialogBuilder.show();
237 }
238
239 /**
240 * Displays the main top-level page SSL certificate dialog
241 * (accessible from the Page-Info dialog).
242 * @param tab The tab to show certificate for.
243 */
244 private void showSSLCertificate(final Tab tab) {
245 final View certificateView =
246 inflateCertificateView(tab.getWebView().getCertificate());
247 if (certificateView == null) {
248 return;
249 }
250
251 LayoutInflater factory = LayoutInflater.from(mContext);
252
253 final LinearLayout placeholder =
254 (LinearLayout)certificateView.findViewById(R.id.placeholder);
255
256 LinearLayout ll = (LinearLayout) factory.inflate(
257 R.layout.ssl_success, placeholder);
258 ((TextView)ll.findViewById(R.id.success))
259 .setText(R.string.ssl_certificate_is_valid);
260
261 mSSLCertificateView = tab;
262 mSSLCertificateDialog =
263 new AlertDialog.Builder(mContext)
264 .setTitle(R.string.ssl_certificate).setIcon(
265 R.drawable.ic_dialog_browser_certificate_secure)
266 .setView(certificateView)
267 .setPositiveButton(R.string.ok,
268 new DialogInterface.OnClickListener() {
269 public void onClick(DialogInterface dialog,
270 int whichButton) {
271 mSSLCertificateDialog = null;
272 mSSLCertificateView = null;
273
Huahui Wudb6ff892011-06-27 11:16:07 -0700274 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700275 }
276 })
277 .setOnCancelListener(
278 new DialogInterface.OnCancelListener() {
279 public void onCancel(DialogInterface dialog) {
280 mSSLCertificateDialog = null;
281 mSSLCertificateView = null;
282
Huahui Wudb6ff892011-06-27 11:16:07 -0700283 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700284 }
285 })
286 .show();
287 }
288
289 /**
290 * Displays the SSL error certificate dialog.
291 * @param view The target web-view.
292 * @param handler The SSL error handler responsible for cancelling the
293 * connection that resulted in an SSL error or proceeding per user request.
294 * @param error The SSL error object.
295 */
296 void showSSLCertificateOnError(
297 final WebView view, final SslErrorHandler handler,
298 final SslError error) {
299
300 final View certificateView =
301 inflateCertificateView(error.getCertificate());
302 if (certificateView == null) {
303 return;
304 }
305
306 LayoutInflater factory = LayoutInflater.from(mContext);
307
308 final LinearLayout placeholder =
309 (LinearLayout)certificateView.findViewById(R.id.placeholder);
310
311 if (error.hasError(SslError.SSL_UNTRUSTED)) {
312 LinearLayout ll = (LinearLayout)factory
313 .inflate(R.layout.ssl_warning, placeholder);
314 ((TextView)ll.findViewById(R.id.warning))
315 .setText(R.string.ssl_untrusted);
316 }
317
318 if (error.hasError(SslError.SSL_IDMISMATCH)) {
319 LinearLayout ll = (LinearLayout)factory
320 .inflate(R.layout.ssl_warning, placeholder);
321 ((TextView)ll.findViewById(R.id.warning))
322 .setText(R.string.ssl_mismatch);
323 }
324
325 if (error.hasError(SslError.SSL_EXPIRED)) {
326 LinearLayout ll = (LinearLayout)factory
327 .inflate(R.layout.ssl_warning, placeholder);
328 ((TextView)ll.findViewById(R.id.warning))
329 .setText(R.string.ssl_expired);
330 }
331
332 if (error.hasError(SslError.SSL_NOTYETVALID)) {
333 LinearLayout ll = (LinearLayout)factory
334 .inflate(R.layout.ssl_warning, placeholder);
335 ((TextView)ll.findViewById(R.id.warning))
336 .setText(R.string.ssl_not_yet_valid);
337 }
338
339 mSSLCertificateOnErrorHandler = handler;
340 mSSLCertificateOnErrorView = view;
341 mSSLCertificateOnErrorError = error;
342 mSSLCertificateOnErrorDialog =
343 new AlertDialog.Builder(mContext)
344 .setTitle(R.string.ssl_certificate).setIcon(
345 R.drawable.ic_dialog_browser_certificate_partially_secure)
346 .setView(certificateView)
347 .setPositiveButton(R.string.ok,
348 new DialogInterface.OnClickListener() {
349 public void onClick(DialogInterface dialog,
350 int whichButton) {
351 mSSLCertificateOnErrorDialog = null;
352 mSSLCertificateOnErrorView = null;
353 mSSLCertificateOnErrorHandler = null;
354 mSSLCertificateOnErrorError = null;
355
356 view.getWebViewClient().onReceivedSslError(
357 view, handler, error);
358 }
359 })
360 .setNeutralButton(R.string.page_info_view,
361 new DialogInterface.OnClickListener() {
362 public void onClick(DialogInterface dialog,
363 int whichButton) {
364 mSSLCertificateOnErrorDialog = null;
365
366 // do not clear the dialog state: we will
367 // need to show the dialog again once the
368 // user is done exploring the page-info details
369
370 showPageInfo(mController.getTabControl()
371 .getTabFromView(view),
Huahui Wudb6ff892011-06-27 11:16:07 -0700372 true,
373 error.getUrl());
Michael Kolb8233fac2010-10-26 16:08:53 -0700374 }
375 })
376 .setOnCancelListener(
377 new DialogInterface.OnCancelListener() {
378 public void onCancel(DialogInterface dialog) {
379 mSSLCertificateOnErrorDialog = null;
380 mSSLCertificateOnErrorView = null;
381 mSSLCertificateOnErrorHandler = null;
382 mSSLCertificateOnErrorError = null;
383
384 view.getWebViewClient().onReceivedSslError(
385 view, handler, error);
386 }
387 })
388 .show();
389 }
390
391 /**
392 * Inflates the SSL certificate view (helper method).
393 * @param certificate The SSL certificate.
394 * @return The resultant certificate view with issued-to, issued-by,
395 * issued-on, expires-on, and possibly other fields set.
396 * If the input certificate is null, returns null.
397 */
398 private View inflateCertificateView(SslCertificate certificate) {
399 if (certificate == null) {
400 return null;
401 }
402
403 LayoutInflater factory = LayoutInflater.from(mContext);
404
405 View certificateView = factory.inflate(
406 R.layout.ssl_certificate, null);
407
408 // issued to:
409 SslCertificate.DName issuedTo = certificate.getIssuedTo();
410 if (issuedTo != null) {
411 ((TextView) certificateView.findViewById(R.id.to_common))
412 .setText(issuedTo.getCName());
413 ((TextView) certificateView.findViewById(R.id.to_org))
414 .setText(issuedTo.getOName());
415 ((TextView) certificateView.findViewById(R.id.to_org_unit))
416 .setText(issuedTo.getUName());
417 }
418
419 // issued by:
420 SslCertificate.DName issuedBy = certificate.getIssuedBy();
421 if (issuedBy != null) {
422 ((TextView) certificateView.findViewById(R.id.by_common))
423 .setText(issuedBy.getCName());
424 ((TextView) certificateView.findViewById(R.id.by_org))
425 .setText(issuedBy.getOName());
426 ((TextView) certificateView.findViewById(R.id.by_org_unit))
427 .setText(issuedBy.getUName());
428 }
429
430 // issued on:
431 String issuedOn = formatCertificateDate(
432 certificate.getValidNotBeforeDate());
433 ((TextView) certificateView.findViewById(R.id.issued_on))
434 .setText(issuedOn);
435
436 // expires on:
437 String expiresOn = formatCertificateDate(
438 certificate.getValidNotAfterDate());
439 ((TextView) certificateView.findViewById(R.id.expires_on))
440 .setText(expiresOn);
441
442 return certificateView;
443 }
444
445 /**
446 * Formats the certificate date to a properly localized date string.
447 * @return Properly localized version of the certificate date string and
448 * the "" if it fails to localize.
449 */
450 private String formatCertificateDate(Date certificateDate) {
451 if (certificateDate == null) {
452 return "";
453 }
454 String formattedDate = DateFormat.getDateFormat(mContext)
455 .format(certificateDate);
456 if (formattedDate == null) {
457 return "";
458 }
459 return formattedDate;
460 }
461
462}