PageDialogsHandler.java revision db6ff8999159f386ea8a99d980ce533b717fca78
1/* 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 String mUrlCertificateOnError; 46 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(); 70 showPageInfo(mPageInfoView, 71 mPageInfoFromShowSSLCertificateOnError, 72 mUrlCertificateOnError); 73 } 74 if (mSSLCertificateDialog != null) { 75 mSSLCertificateDialog.dismiss(); 76 showSSLCertificate(mSSLCertificateView); 77 } 78 if (mSSLCertificateOnErrorDialog != null) { 79 mSSLCertificateOnErrorDialog.dismiss(); 80 showSSLCertificateOnError(mSSLCertificateOnErrorView, 81 mSSLCertificateOnErrorHandler, 82 mSSLCertificateOnErrorError); 83 } 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(); 104 mController.onUpdatedLockIcon(tab); 105 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. 136 * @param urlCertificateOnError The URL that invokes SSLCertificateError. 137 * Null when fromShowSSLCertificateOnError is false. 138 */ 139 void showPageInfo(final Tab tab, 140 final boolean fromShowSSLCertificateOnError, 141 final String urlCertificateOnError) { 142 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 148 String url = fromShowSSLCertificateOnError ? urlCertificateOnError : tab.getUrl(); 149 String title = tab.getTitle(); 150 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; 163 mUrlCertificateOnError = urlCertificateOnError; 164 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 274 showPageInfo(tab, false, null); 275 } 276 }) 277 .setOnCancelListener( 278 new DialogInterface.OnCancelListener() { 279 public void onCancel(DialogInterface dialog) { 280 mSSLCertificateDialog = null; 281 mSSLCertificateView = null; 282 283 showPageInfo(tab, false, null); 284 } 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), 372 true, 373 error.getUrl()); 374 } 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} 463