blob: a38b9040f2a2dea252fabec9b4ee245e7330b323 [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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080018
19import java.lang.reflect.Method;
Michael Kolb8233fac2010-10-26 16:08:53 -070020
21import android.app.AlertDialog;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.res.Configuration;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080025import android.content.res.Resources;
Michael Kolb8233fac2010-10-26 16:08:53 -070026import android.net.http.SslCertificate;
27import android.net.http.SslError;
Michael Kolb8233fac2010-10-26 16:08:53 -070028import android.view.LayoutInflater;
29import android.view.View;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080030import org.codeaurora.swe.HttpAuthHandler;
31import org.codeaurora.swe.SslErrorHandler;
32import org.codeaurora.swe.WebView;
33
Bijan Amirzada41242f22014-03-21 12:12:18 -070034import com.android.browser.R;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080035
Michael Kolb8233fac2010-10-26 16:08:53 -070036import android.widget.LinearLayout;
37import android.widget.TextView;
38
Michael Kolb8233fac2010-10-26 16:08:53 -070039/**
40 * Displays page info
41 *
42 */
43public class PageDialogsHandler {
44
45 private Context mContext;
46 private Controller mController;
47 private boolean mPageInfoFromShowSSLCertificateOnError;
Huahui Wuae0c0412011-06-28 10:17:05 -070048 private String mUrlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -070049 private Tab mPageInfoView;
50 private AlertDialog mPageInfoDialog;
51
52 // as SSLCertificateOnError has different style for landscape / portrait,
53 // we have to re-open it when configuration changed
54 private AlertDialog mSSLCertificateOnErrorDialog;
55 private WebView mSSLCertificateOnErrorView;
56 private SslErrorHandler mSSLCertificateOnErrorHandler;
57 private SslError mSSLCertificateOnErrorError;
58
59 // as SSLCertificate has different style for landscape / portrait, we
60 // have to re-open it when configuration changed
61 private AlertDialog mSSLCertificateDialog;
62 private Tab mSSLCertificateView;
63 private HttpAuthenticationDialog mHttpAuthenticationDialog;
64
65 public PageDialogsHandler(Context context, Controller controller) {
66 mContext = context;
67 mController = controller;
68 }
69
70 public void onConfigurationChanged(Configuration config) {
71 if (mPageInfoDialog != null) {
72 mPageInfoDialog.dismiss();
Huahui Wuae0c0412011-06-28 10:17:05 -070073 showPageInfo(mPageInfoView,
74 mPageInfoFromShowSSLCertificateOnError,
75 mUrlCertificateOnError);
Michael Kolb8233fac2010-10-26 16:08:53 -070076 }
77 if (mSSLCertificateDialog != null) {
78 mSSLCertificateDialog.dismiss();
79 showSSLCertificate(mSSLCertificateView);
80 }
81 if (mSSLCertificateOnErrorDialog != null) {
82 mSSLCertificateOnErrorDialog.dismiss();
Huahui Wuae0c0412011-06-28 10:17:05 -070083 showSSLCertificateOnError(mSSLCertificateOnErrorView,
84 mSSLCertificateOnErrorHandler,
85 mSSLCertificateOnErrorError);
Michael Kolb8233fac2010-10-26 16:08:53 -070086 }
87 if (mHttpAuthenticationDialog != null) {
88 mHttpAuthenticationDialog.reshow();
89 }
90 }
91
92 /**
93 * Displays an http-authentication dialog.
94 */
95 void showHttpAuthentication(final Tab tab, final HttpAuthHandler handler, String host, String realm) {
96 mHttpAuthenticationDialog = new HttpAuthenticationDialog(mContext, host, realm);
97 mHttpAuthenticationDialog.setOkListener(new HttpAuthenticationDialog.OkListener() {
98 public void onOk(String host, String realm, String username, String password) {
99 setHttpAuthUsernamePassword(host, realm, username, password);
100 handler.proceed(username, password);
101 mHttpAuthenticationDialog = null;
102 }
103 });
104 mHttpAuthenticationDialog.setCancelListener(new HttpAuthenticationDialog.CancelListener() {
105 public void onCancel() {
106 handler.cancel();
Steve Block2466eff2011-10-03 15:33:09 +0100107 mController.onUpdatedSecurityState(tab);
Michael Kolb8233fac2010-10-26 16:08:53 -0700108 mHttpAuthenticationDialog = null;
109 }
110 });
111 mHttpAuthenticationDialog.show();
112 }
113
114 /**
115 * Set HTTP authentication password.
116 *
117 * @param host The host for the password
118 * @param realm The realm for the password
119 * @param username The username for the password. If it is null, it means
120 * password can't be saved.
121 * @param password The password
122 */
123 public void setHttpAuthUsernamePassword(String host, String realm,
124 String username,
125 String password) {
126 WebView w = mController.getCurrentTopWebView();
127 if (w != null) {
128 w.setHttpAuthUsernamePassword(host, realm, username, password);
129 }
130 }
131
132 /**
133 * Displays a page-info dialog.
134 * @param tab The tab to show info about
135 * @param fromShowSSLCertificateOnError The flag that indicates whether
136 * this dialog was opened from the SSL-certificate-on-error dialog or
137 * not. This is important, since we need to know whether to return to
138 * the parent dialog or simply dismiss.
Huahui Wuae0c0412011-06-28 10:17:05 -0700139 * @param urlCertificateOnError The URL that invokes SSLCertificateError.
140 * Null when fromShowSSLCertificateOnError is false.
Michael Kolb8233fac2010-10-26 16:08:53 -0700141 */
142 void showPageInfo(final Tab tab,
Huahui Wuae0c0412011-06-28 10:17:05 -0700143 final boolean fromShowSSLCertificateOnError,
144 final String urlCertificateOnError) {
Michael Kolbd5cb4fa2012-04-18 08:29:16 -0700145 if (tab == null) return;
Michael Kolb8233fac2010-10-26 16:08:53 -0700146 final LayoutInflater factory = LayoutInflater.from(mContext);
147
148 final View pageInfoView = factory.inflate(R.layout.page_info, null);
149
150 final WebView view = tab.getWebView();
151
Huahui Wuae0c0412011-06-28 10:17:05 -0700152 String url = fromShowSSLCertificateOnError ? urlCertificateOnError : tab.getUrl();
John Reck30c714c2010-12-16 17:30:34 -0800153 String title = tab.getTitle();
Michael Kolb8233fac2010-10-26 16:08:53 -0700154
155 if (url == null) {
156 url = "";
157 }
158 if (title == null) {
159 title = "";
160 }
161
162 ((TextView) pageInfoView.findViewById(R.id.address)).setText(url);
163 ((TextView) pageInfoView.findViewById(R.id.title)).setText(title);
164
165 mPageInfoView = tab;
166 mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError;
Huahui Wuae0c0412011-06-28 10:17:05 -0700167 mUrlCertificateOnError = urlCertificateOnError;
Michael Kolb8233fac2010-10-26 16:08:53 -0700168
169 AlertDialog.Builder alertDialogBuilder =
170 new AlertDialog.Builder(mContext)
171 .setTitle(R.string.page_info)
172 .setIcon(android.R.drawable.ic_dialog_info)
173 .setView(pageInfoView)
174 .setPositiveButton(
175 R.string.ok,
176 new DialogInterface.OnClickListener() {
177 public void onClick(DialogInterface dialog,
178 int whichButton) {
179 mPageInfoDialog = null;
180 mPageInfoView = null;
181
182 // if we came here from the SSL error dialog
183 if (fromShowSSLCertificateOnError) {
184 // go back to the SSL error dialog
185 showSSLCertificateOnError(
186 mSSLCertificateOnErrorView,
187 mSSLCertificateOnErrorHandler,
188 mSSLCertificateOnErrorError);
189 }
190 }
191 })
192 .setOnCancelListener(
193 new DialogInterface.OnCancelListener() {
194 public void onCancel(DialogInterface dialog) {
195 mPageInfoDialog = null;
196 mPageInfoView = null;
197
198 // if we came here from the SSL error dialog
199 if (fromShowSSLCertificateOnError) {
200 // go back to the SSL error dialog
201 showSSLCertificateOnError(
202 mSSLCertificateOnErrorView,
203 mSSLCertificateOnErrorHandler,
204 mSSLCertificateOnErrorError);
205 }
206 }
207 });
208
209 // if we have a main top-level page SSL certificate set or a certificate
210 // error
211 if (fromShowSSLCertificateOnError ||
212 (view != null && view.getCertificate() != null)) {
213 // add a 'View Certificate' button
214 alertDialogBuilder.setNeutralButton(
215 R.string.view_certificate,
216 new DialogInterface.OnClickListener() {
217 public void onClick(DialogInterface dialog,
218 int whichButton) {
219 mPageInfoDialog = null;
220 mPageInfoView = null;
221
222 // if we came here from the SSL error dialog
223 if (fromShowSSLCertificateOnError) {
224 // go back to the SSL error dialog
225 showSSLCertificateOnError(
226 mSSLCertificateOnErrorView,
227 mSSLCertificateOnErrorHandler,
228 mSSLCertificateOnErrorError);
229 } else {
230 // otherwise, display the top-most certificate from
231 // the chain
Steve Blockcbc67a02011-10-05 17:56:19 +0100232 showSSLCertificate(tab);
Michael Kolb8233fac2010-10-26 16:08:53 -0700233 }
234 }
235 });
236 }
237
238 mPageInfoDialog = alertDialogBuilder.show();
239 }
240
241 /**
242 * Displays the main top-level page SSL certificate dialog
243 * (accessible from the Page-Info dialog).
244 * @param tab The tab to show certificate for.
245 */
246 private void showSSLCertificate(final Tab tab) {
Brian Carlstrom873034c2011-06-26 21:06:43 -0700247
248 SslCertificate cert = tab.getWebView().getCertificate();
249 if (cert == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700250 return;
251 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700252
253 mSSLCertificateView = tab;
Steve Block08a6f0c2011-10-06 12:12:53 +0100254 mSSLCertificateDialog = createSslCertificateDialog(cert, tab.getSslCertificateError())
Michael Kolb8233fac2010-10-26 16:08:53 -0700255 .setPositiveButton(R.string.ok,
256 new DialogInterface.OnClickListener() {
257 public void onClick(DialogInterface dialog,
258 int whichButton) {
259 mSSLCertificateDialog = null;
260 mSSLCertificateView = null;
261
Huahui Wuae0c0412011-06-28 10:17:05 -0700262 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700263 }
264 })
265 .setOnCancelListener(
266 new DialogInterface.OnCancelListener() {
267 public void onCancel(DialogInterface dialog) {
268 mSSLCertificateDialog = null;
269 mSSLCertificateView = null;
270
Huahui Wuae0c0412011-06-28 10:17:05 -0700271 showPageInfo(tab, false, null);
Michael Kolb8233fac2010-10-26 16:08:53 -0700272 }
273 })
274 .show();
275 }
276
277 /**
278 * Displays the SSL error certificate dialog.
279 * @param view The target web-view.
280 * @param handler The SSL error handler responsible for cancelling the
281 * connection that resulted in an SSL error or proceeding per user request.
282 * @param error The SSL error object.
283 */
284 void showSSLCertificateOnError(
285 final WebView view, final SslErrorHandler handler,
286 final SslError error) {
287
Brian Carlstrom873034c2011-06-26 21:06:43 -0700288 SslCertificate cert = error.getCertificate();
289 if (cert == null) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700290 return;
291 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700292
293 mSSLCertificateOnErrorHandler = handler;
294 mSSLCertificateOnErrorView = view;
295 mSSLCertificateOnErrorError = error;
Steve Blockcbc67a02011-10-05 17:56:19 +0100296 mSSLCertificateOnErrorDialog = createSslCertificateDialog(cert, error)
Michael Kolb8233fac2010-10-26 16:08:53 -0700297 .setPositiveButton(R.string.ok,
298 new DialogInterface.OnClickListener() {
299 public void onClick(DialogInterface dialog,
300 int whichButton) {
301 mSSLCertificateOnErrorDialog = null;
302 mSSLCertificateOnErrorView = null;
303 mSSLCertificateOnErrorHandler = null;
304 mSSLCertificateOnErrorError = null;
305
Jonathan Dixone1d6dfc2012-12-17 13:39:17 -0800306 ((BrowserWebView) view).getWebViewClient().
Jonathan Dixon4d2fcab2012-02-24 00:13:06 +0000307 onReceivedSslError(view, handler, error);
Michael Kolb8233fac2010-10-26 16:08:53 -0700308 }
309 })
310 .setNeutralButton(R.string.page_info_view,
311 new DialogInterface.OnClickListener() {
312 public void onClick(DialogInterface dialog,
313 int whichButton) {
314 mSSLCertificateOnErrorDialog = null;
315
316 // do not clear the dialog state: we will
317 // need to show the dialog again once the
318 // user is done exploring the page-info details
319
320 showPageInfo(mController.getTabControl()
321 .getTabFromView(view),
Huahui Wuae0c0412011-06-28 10:17:05 -0700322 true,
323 error.getUrl());
Michael Kolb8233fac2010-10-26 16:08:53 -0700324 }
325 })
326 .setOnCancelListener(
327 new DialogInterface.OnCancelListener() {
328 public void onCancel(DialogInterface dialog) {
329 mSSLCertificateOnErrorDialog = null;
330 mSSLCertificateOnErrorView = null;
331 mSSLCertificateOnErrorHandler = null;
332 mSSLCertificateOnErrorError = null;
333
Jonathan Dixone1d6dfc2012-12-17 13:39:17 -0800334 ((BrowserWebView) view).getWebViewClient().
Jonathan Dixon4d2fcab2012-02-24 00:13:06 +0000335 onReceivedSslError(view, handler, error);
Michael Kolb8233fac2010-10-26 16:08:53 -0700336 }
337 })
338 .show();
339 }
Steve Blockcbc67a02011-10-05 17:56:19 +0100340
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800341 private static View inflateCertificateView(SslCertificate certificate, Context ctx) {
342 Class certClass;
343 try {
344 certClass = Class.forName("android.net.http.SslCertificate");
345
346 Class argTypes[] = new Class[1];
347 argTypes[0] = Context.class;
348
349 Method m = certClass.getDeclaredMethod("inflateCertificateView", argTypes);
350 m.setAccessible(true);
351
352 Object args[] = new Object[1];
353 args[0] = ctx;
354 return (View) m.invoke(certificate, args);
355
356 } catch (Exception e) {
357 e.printStackTrace();
358 }
359
360 return null;
361 }
362
Steve Blockcbc67a02011-10-05 17:56:19 +0100363 /*
364 * Creates an AlertDialog to display the given certificate. If error is
365 * null, text is added to state that the certificae is valid and the icon
366 * is set accordingly. If error is non-null, it must relate to the supplied
367 * certificate. In this case, error is used to add text describing the
368 * problems with the certificate and a different icon is used.
369 */
370 private AlertDialog.Builder createSslCertificateDialog(SslCertificate certificate,
371 SslError error) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800372 View certificateView = inflateCertificateView(certificate, mContext);
373 Resources res = Resources.getSystem();
374 int placeholder_id = res.getIdentifier("placeholder", "id", "android");
Steve Blockcbc67a02011-10-05 17:56:19 +0100375 final LinearLayout placeholder =
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800376 (LinearLayout)certificateView.findViewById(placeholder_id);
Steve Blockcbc67a02011-10-05 17:56:19 +0100377
378 LayoutInflater factory = LayoutInflater.from(mContext);
379 int iconId;
380
381 if (error == null) {
382 iconId = R.drawable.ic_dialog_browser_certificate_secure;
383 LinearLayout table = (LinearLayout)factory.inflate(R.layout.ssl_success, placeholder);
384 TextView successString = (TextView)table.findViewById(R.id.success);
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800385 successString.setText(R.string.ssl_certificate_is_valid);
Steve Blockcbc67a02011-10-05 17:56:19 +0100386 } else {
387 iconId = R.drawable.ic_dialog_browser_certificate_partially_secure;
388 if (error.hasError(SslError.SSL_UNTRUSTED)) {
389 addError(factory, placeholder, R.string.ssl_untrusted);
390 }
391 if (error.hasError(SslError.SSL_IDMISMATCH)) {
392 addError(factory, placeholder, R.string.ssl_mismatch);
393 }
394 if (error.hasError(SslError.SSL_EXPIRED)) {
395 addError(factory, placeholder, R.string.ssl_expired);
396 }
397 if (error.hasError(SslError.SSL_NOTYETVALID)) {
398 addError(factory, placeholder, R.string.ssl_not_yet_valid);
399 }
400 if (error.hasError(SslError.SSL_DATE_INVALID)) {
401 addError(factory, placeholder, R.string.ssl_date_invalid);
402 }
403 if (error.hasError(SslError.SSL_INVALID)) {
404 addError(factory, placeholder, R.string.ssl_invalid);
405 }
406 // The SslError should always have at least one type of error and we
407 // should explicitly handle every type of error it supports. We
408 // therefore expect the condition below to never be hit. We use it
409 // as as safety net in case a new error type is added to SslError
410 // without the logic above being updated accordingly.
411 if (placeholder.getChildCount() == 0) {
412 addError(factory, placeholder, R.string.ssl_unknown);
413 }
414 }
415
416 return new AlertDialog.Builder(mContext)
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800417 .setTitle(R.string.ssl_certificate)
Steve Blockcbc67a02011-10-05 17:56:19 +0100418 .setIcon(iconId)
419 .setView(certificateView);
420 }
421
422 private void addError(LayoutInflater inflater, LinearLayout parent, int error) {
423 TextView textView = (TextView) inflater.inflate(R.layout.ssl_warning,
424 parent, false);
425 textView.setText(error);
426 parent.addView(textView);
427 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700428}