PageDialogsHandler.java revision ab448ac18b10a76cea78b023af3c85ae903df557
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                            if (view.getCertificate() != null) {
226                                showSSLCertificate(tab);
227                            }
228                        }
229                    }
230                });
231        }
232
233        mPageInfoDialog = alertDialogBuilder.show();
234    }
235
236    /**
237     * Displays the main top-level page SSL certificate dialog
238     * (accessible from the Page-Info dialog).
239     * @param tab The tab to show certificate for.
240     */
241    private void showSSLCertificate(final Tab tab) {
242
243        SslCertificate cert = tab.getWebView().getCertificate();
244        if (cert == null) {
245            return;
246        }
247        final View certificateView = cert.inflateCertificateView(mContext);
248
249        LayoutInflater factory = LayoutInflater.from(mContext);
250
251        final LinearLayout placeholder =
252                (LinearLayout)certificateView.findViewById(com.android.internal.R.id.placeholder);
253
254        LinearLayout ll = (LinearLayout) factory.inflate(
255            R.layout.ssl_success, placeholder);
256        ((TextView)ll.findViewById(R.id.success))
257            .setText(com.android.internal.R.string.ssl_certificate_is_valid);
258
259        mSSLCertificateView = tab;
260        mSSLCertificateDialog =
261            new AlertDialog.Builder(mContext)
262                .setTitle(com.android.internal.R.string.ssl_certificate).setIcon(
263                    R.drawable.ic_dialog_browser_certificate_secure)
264                .setView(certificateView)
265                .setPositiveButton(R.string.ok,
266                        new DialogInterface.OnClickListener() {
267                            public void onClick(DialogInterface dialog,
268                                    int whichButton) {
269                                mSSLCertificateDialog = null;
270                                mSSLCertificateView = null;
271
272                                showPageInfo(tab, false, null);
273                            }
274                        })
275                .setOnCancelListener(
276                        new DialogInterface.OnCancelListener() {
277                            public void onCancel(DialogInterface dialog) {
278                                mSSLCertificateDialog = null;
279                                mSSLCertificateView = null;
280
281                                showPageInfo(tab, false, null);
282                            }
283                        })
284                .show();
285    }
286
287    private void addError(LayoutInflater inflater, LinearLayout parent, int error) {
288        TextView textView = (TextView) inflater.inflate(R.layout.ssl_warning,
289                parent, false);
290        textView.setText(error);
291        parent.addView(textView);
292    }
293
294    /**
295     * Displays the SSL error certificate dialog.
296     * @param view The target web-view.
297     * @param handler The SSL error handler responsible for cancelling the
298     * connection that resulted in an SSL error or proceeding per user request.
299     * @param error The SSL error object.
300     */
301    void showSSLCertificateOnError(
302            final WebView view, final SslErrorHandler handler,
303            final SslError error) {
304
305        SslCertificate cert = error.getCertificate();
306        if (cert == null) {
307            return;
308        }
309        final View certificateView = cert.inflateCertificateView(mContext);
310
311        LayoutInflater factory = LayoutInflater.from(mContext);
312
313        final LinearLayout placeholder =
314                (LinearLayout)certificateView.findViewById(com.android.internal.R.id.placeholder);
315
316        if (error.hasError(SslError.SSL_UNTRUSTED)) {
317            addError(factory, placeholder, R.string.ssl_untrusted);
318        }
319        if (error.hasError(SslError.SSL_IDMISMATCH)) {
320            addError(factory, placeholder, R.string.ssl_mismatch);
321        }
322        if (error.hasError(SslError.SSL_EXPIRED)) {
323            addError(factory, placeholder, R.string.ssl_expired);
324        }
325        if (error.hasError(SslError.SSL_NOTYETVALID)) {
326            addError(factory, placeholder, R.string.ssl_not_yet_valid);
327        }
328        if (error.hasError(SslError.SSL_DATE_INVALID)) {
329            addError(factory, placeholder, R.string.ssl_date_invalid);
330        }
331        if (error.hasError(SslError.SSL_INVALID)) {
332            addError(factory, placeholder, R.string.ssl_invalid);
333        }
334        if (placeholder.getChildCount() == 0) {
335            addError(factory, placeholder, R.string.ssl_unknown);
336        }
337
338        mSSLCertificateOnErrorHandler = handler;
339        mSSLCertificateOnErrorView = view;
340        mSSLCertificateOnErrorError = error;
341        mSSLCertificateOnErrorDialog =
342            new AlertDialog.Builder(mContext)
343                .setTitle(com.android.internal.R.string.ssl_certificate).setIcon(
344                    R.drawable.ic_dialog_browser_certificate_partially_secure)
345                .setView(certificateView)
346                .setPositiveButton(R.string.ok,
347                        new DialogInterface.OnClickListener() {
348                            public void onClick(DialogInterface dialog,
349                                    int whichButton) {
350                                mSSLCertificateOnErrorDialog = null;
351                                mSSLCertificateOnErrorView = null;
352                                mSSLCertificateOnErrorHandler = null;
353                                mSSLCertificateOnErrorError = null;
354
355                                view.getWebViewClient().onReceivedSslError(
356                                                view, handler, error);
357                            }
358                        })
359                 .setNeutralButton(R.string.page_info_view,
360                        new DialogInterface.OnClickListener() {
361                            public void onClick(DialogInterface dialog,
362                                    int whichButton) {
363                                mSSLCertificateOnErrorDialog = null;
364
365                                // do not clear the dialog state: we will
366                                // need to show the dialog again once the
367                                // user is done exploring the page-info details
368
369                                showPageInfo(mController.getTabControl()
370                                        .getTabFromView(view),
371                                        true,
372                                        error.getUrl());
373                            }
374                        })
375                .setOnCancelListener(
376                        new DialogInterface.OnCancelListener() {
377                            public void onCancel(DialogInterface dialog) {
378                                mSSLCertificateOnErrorDialog = null;
379                                mSSLCertificateOnErrorView = null;
380                                mSSLCertificateOnErrorHandler = null;
381                                mSSLCertificateOnErrorError = null;
382
383                                view.getWebViewClient().onReceivedSslError(
384                                                view, handler, error);
385                            }
386                        })
387                .show();
388    }
389}
390