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