PageDialogsHandler.java revision cbc67a01ccfe9cc5ce441f02a8c0dc475340830e
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.view.LayoutInflater; 26import android.view.View; 27import android.webkit.HttpAuthHandler; 28import android.webkit.SslErrorHandler; 29import android.webkit.WebView; 30import android.widget.LinearLayout; 31import android.widget.TextView; 32 33/** 34 * Displays page info 35 * 36 */ 37public class PageDialogsHandler { 38 39 private Context mContext; 40 private Controller mController; 41 private boolean mPageInfoFromShowSSLCertificateOnError; 42 private String mUrlCertificateOnError; 43 private Tab mPageInfoView; 44 private AlertDialog mPageInfoDialog; 45 46 // as SSLCertificateOnError has different style for landscape / portrait, 47 // we have to re-open it when configuration changed 48 private AlertDialog mSSLCertificateOnErrorDialog; 49 private WebView mSSLCertificateOnErrorView; 50 private SslErrorHandler mSSLCertificateOnErrorHandler; 51 private SslError mSSLCertificateOnErrorError; 52 53 // as SSLCertificate has different style for landscape / portrait, we 54 // have to re-open it when configuration changed 55 private AlertDialog mSSLCertificateDialog; 56 private Tab mSSLCertificateView; 57 private HttpAuthenticationDialog mHttpAuthenticationDialog; 58 59 public PageDialogsHandler(Context context, Controller controller) { 60 mContext = context; 61 mController = controller; 62 } 63 64 public void onConfigurationChanged(Configuration config) { 65 if (mPageInfoDialog != null) { 66 mPageInfoDialog.dismiss(); 67 showPageInfo(mPageInfoView, 68 mPageInfoFromShowSSLCertificateOnError, 69 mUrlCertificateOnError); 70 } 71 if (mSSLCertificateDialog != null) { 72 mSSLCertificateDialog.dismiss(); 73 showSSLCertificate(mSSLCertificateView); 74 } 75 if (mSSLCertificateOnErrorDialog != null) { 76 mSSLCertificateOnErrorDialog.dismiss(); 77 showSSLCertificateOnError(mSSLCertificateOnErrorView, 78 mSSLCertificateOnErrorHandler, 79 mSSLCertificateOnErrorError); 80 } 81 if (mHttpAuthenticationDialog != null) { 82 mHttpAuthenticationDialog.reshow(); 83 } 84 } 85 86 /** 87 * Displays an http-authentication dialog. 88 */ 89 void showHttpAuthentication(final Tab tab, final HttpAuthHandler handler, String host, String realm) { 90 mHttpAuthenticationDialog = new HttpAuthenticationDialog(mContext, host, realm); 91 mHttpAuthenticationDialog.setOkListener(new HttpAuthenticationDialog.OkListener() { 92 public void onOk(String host, String realm, String username, String password) { 93 setHttpAuthUsernamePassword(host, realm, username, password); 94 handler.proceed(username, password); 95 mHttpAuthenticationDialog = null; 96 } 97 }); 98 mHttpAuthenticationDialog.setCancelListener(new HttpAuthenticationDialog.CancelListener() { 99 public void onCancel() { 100 handler.cancel(); 101 mController.onUpdatedSecurityState(tab); 102 mHttpAuthenticationDialog = null; 103 } 104 }); 105 mHttpAuthenticationDialog.show(); 106 } 107 108 /** 109 * Set HTTP authentication password. 110 * 111 * @param host The host for the password 112 * @param realm The realm for the password 113 * @param username The username for the password. If it is null, it means 114 * password can't be saved. 115 * @param password The password 116 */ 117 public void setHttpAuthUsernamePassword(String host, String realm, 118 String username, 119 String password) { 120 WebView w = mController.getCurrentTopWebView(); 121 if (w != null) { 122 w.setHttpAuthUsernamePassword(host, realm, username, password); 123 } 124 } 125 126 /** 127 * Displays a page-info dialog. 128 * @param tab The tab to show info about 129 * @param fromShowSSLCertificateOnError The flag that indicates whether 130 * this dialog was opened from the SSL-certificate-on-error dialog or 131 * not. This is important, since we need to know whether to return to 132 * the parent dialog or simply dismiss. 133 * @param urlCertificateOnError The URL that invokes SSLCertificateError. 134 * Null when fromShowSSLCertificateOnError is false. 135 */ 136 void showPageInfo(final Tab tab, 137 final boolean fromShowSSLCertificateOnError, 138 final String urlCertificateOnError) { 139 final LayoutInflater factory = LayoutInflater.from(mContext); 140 141 final View pageInfoView = factory.inflate(R.layout.page_info, null); 142 143 final WebView view = tab.getWebView(); 144 145 String url = fromShowSSLCertificateOnError ? urlCertificateOnError : tab.getUrl(); 146 String title = tab.getTitle(); 147 148 if (url == null) { 149 url = ""; 150 } 151 if (title == null) { 152 title = ""; 153 } 154 155 ((TextView) pageInfoView.findViewById(R.id.address)).setText(url); 156 ((TextView) pageInfoView.findViewById(R.id.title)).setText(title); 157 158 mPageInfoView = tab; 159 mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError; 160 mUrlCertificateOnError = urlCertificateOnError; 161 162 AlertDialog.Builder alertDialogBuilder = 163 new AlertDialog.Builder(mContext) 164 .setTitle(R.string.page_info) 165 .setIcon(android.R.drawable.ic_dialog_info) 166 .setView(pageInfoView) 167 .setPositiveButton( 168 R.string.ok, 169 new DialogInterface.OnClickListener() { 170 public void onClick(DialogInterface dialog, 171 int whichButton) { 172 mPageInfoDialog = null; 173 mPageInfoView = null; 174 175 // if we came here from the SSL error dialog 176 if (fromShowSSLCertificateOnError) { 177 // go back to the SSL error dialog 178 showSSLCertificateOnError( 179 mSSLCertificateOnErrorView, 180 mSSLCertificateOnErrorHandler, 181 mSSLCertificateOnErrorError); 182 } 183 } 184 }) 185 .setOnCancelListener( 186 new DialogInterface.OnCancelListener() { 187 public void onCancel(DialogInterface dialog) { 188 mPageInfoDialog = null; 189 mPageInfoView = null; 190 191 // if we came here from the SSL error dialog 192 if (fromShowSSLCertificateOnError) { 193 // go back to the SSL error dialog 194 showSSLCertificateOnError( 195 mSSLCertificateOnErrorView, 196 mSSLCertificateOnErrorHandler, 197 mSSLCertificateOnErrorError); 198 } 199 } 200 }); 201 202 // if we have a main top-level page SSL certificate set or a certificate 203 // error 204 if (fromShowSSLCertificateOnError || 205 (view != null && view.getCertificate() != null)) { 206 // add a 'View Certificate' button 207 alertDialogBuilder.setNeutralButton( 208 R.string.view_certificate, 209 new DialogInterface.OnClickListener() { 210 public void onClick(DialogInterface dialog, 211 int whichButton) { 212 mPageInfoDialog = null; 213 mPageInfoView = null; 214 215 // if we came here from the SSL error dialog 216 if (fromShowSSLCertificateOnError) { 217 // go back to the SSL error dialog 218 showSSLCertificateOnError( 219 mSSLCertificateOnErrorView, 220 mSSLCertificateOnErrorHandler, 221 mSSLCertificateOnErrorError); 222 } else { 223 // otherwise, display the top-most certificate from 224 // the chain 225 showSSLCertificate(tab); 226 } 227 } 228 }); 229 } 230 231 mPageInfoDialog = alertDialogBuilder.show(); 232 } 233 234 /** 235 * Displays the main top-level page SSL certificate dialog 236 * (accessible from the Page-Info dialog). 237 * @param tab The tab to show certificate for. 238 */ 239 private void showSSLCertificate(final Tab tab) { 240 241 SslCertificate cert = tab.getWebView().getCertificate(); 242 if (cert == null) { 243 return; 244 } 245 246 mSSLCertificateView = tab; 247 // TODO: We should pass the certificate error for the page's main 248 // resource, if present. See http://b/5248376. 249 mSSLCertificateDialog = createSslCertificateDialog(cert, null) 250 .setPositiveButton(R.string.ok, 251 new DialogInterface.OnClickListener() { 252 public void onClick(DialogInterface dialog, 253 int whichButton) { 254 mSSLCertificateDialog = null; 255 mSSLCertificateView = null; 256 257 showPageInfo(tab, false, null); 258 } 259 }) 260 .setOnCancelListener( 261 new DialogInterface.OnCancelListener() { 262 public void onCancel(DialogInterface dialog) { 263 mSSLCertificateDialog = null; 264 mSSLCertificateView = null; 265 266 showPageInfo(tab, false, null); 267 } 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 283 SslCertificate cert = error.getCertificate(); 284 if (cert == null) { 285 return; 286 } 287 288 mSSLCertificateOnErrorHandler = handler; 289 mSSLCertificateOnErrorView = view; 290 mSSLCertificateOnErrorError = error; 291 mSSLCertificateOnErrorDialog = createSslCertificateDialog(cert, error) 292 .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 301 view.getWebViewClient().onReceivedSslError( 302 view, handler, error); 303 } 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), 317 true, 318 error.getUrl()); 319 } 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 329 view.getWebViewClient().onReceivedSslError( 330 view, handler, error); 331 } 332 }) 333 .show(); 334 } 335 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 } 399} 400