PageDialogsHandler.java revision 16b6806562476950f5c26878ebf1d6075f1a16d8
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 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(); 100 mController.onUpdatedLockIcon(tab); 101 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 141 String url = tab.getUrl(); 142 String title = tab.getTitle(); 143 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} 454