WebViewContentsClientAdapter.java revision a66d54c9f62ace5182635c4bf74449fb62f9d464
1/*
2 * Copyright (C) 2012 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.webview.chromium;
18
19import android.content.ActivityNotFoundException;
20import android.content.Context;
21import android.content.Intent;
22import android.graphics.Bitmap;
23import android.graphics.Picture;
24import android.net.http.ErrorStrings;
25import android.net.http.SslError;
26import android.os.Handler;
27import android.os.Looper;
28import android.os.Message;
29import android.provider.Browser;
30import android.util.Log;
31import android.view.KeyEvent;
32import android.view.View;
33import android.webkit.ConsoleMessage;
34import android.webkit.DownloadListener;
35import android.webkit.GeolocationPermissions;
36import android.webkit.JsDialogHelper;
37import android.webkit.JsPromptResult;
38import android.webkit.JsResult;
39import android.webkit.SslErrorHandler;
40import android.webkit.ValueCallback;
41import android.webkit.WebChromeClient;
42import android.webkit.WebChromeClient.CustomViewCallback;
43import android.webkit.WebResourceResponse;
44import android.webkit.WebView;
45import android.webkit.WebViewClient;
46
47import org.chromium.android_webview.AwContentsClient;
48import org.chromium.android_webview.AwHttpAuthHandler;
49import org.chromium.android_webview.InterceptedRequestData;
50import org.chromium.android_webview.JsPromptResultReceiver;
51import org.chromium.android_webview.JsResultReceiver;
52import org.chromium.content.browser.ContentView;
53import org.chromium.content.browser.ContentViewClient;
54import org.chromium.content.common.TraceEvent;
55
56import java.net.URISyntaxException;
57
58/**
59 * An adapter class that forwards the callbacks from {@link ContentViewClient}
60 * to the appropriate {@link WebViewClient} or {@link WebChromeClient}.
61 *
62 * An instance of this class is associated with one {@link WebViewChromium}
63 * instance. A WebViewChromium is a WebView implementation provider (that is
64 * android.webkit.WebView delegates all functionality to it) and has exactly
65 * one corresponding {@link ContentView} instance.
66 *
67 * A {@link ContentViewClient} may be shared between multiple {@link ContentView}s,
68 * and hence multiple WebViews. Many WebViewClient methods pass the source
69 * WebView as an argument. This means that we either need to pass the
70 * corresponding ContentView to the corresponding ContentViewClient methods,
71 * or use an instance of ContentViewClientAdapter per WebViewChromium, to
72 * allow the source WebView to be injected by ContentViewClientAdapter. We
73 * choose the latter, because it makes for a cleaner design.
74 */
75public class WebViewContentsClientAdapter extends AwContentsClient {
76    // TAG is chosen for consistency with classic webview tracing.
77    private static final String TAG = "WebViewCallback";
78    // Enables API callback tracing
79    private static final boolean TRACE = android.webkit.DebugFlags.TRACE_CALLBACK;
80    // The WebView instance that this adapter is serving.
81    private final WebView mWebView;
82    // The WebViewClient instance that was passed to WebView.setWebViewClient().
83    private WebViewClient mWebViewClient;
84    // The WebChromeClient instance that was passed to WebView.setContentViewClient().
85    private WebChromeClient mWebChromeClient;
86    // The listener receiving find-in-page API results.
87    private WebView.FindListener mFindListener;
88    // The listener receiving notifications of screen updates.
89    private WebView.PictureListener mPictureListener;
90
91    private DownloadListener mDownloadListener;
92
93    private Handler mUiThreadHandler;
94
95    private static final int NEW_WEBVIEW_CREATED = 100;
96
97    /**
98     * Adapter constructor.
99     *
100     * @param webView the {@link WebView} instance that this adapter is serving.
101     */
102    WebViewContentsClientAdapter(WebView webView) {
103        if (webView == null) {
104            throw new IllegalArgumentException("webView can't be null");
105        }
106
107        mWebView = webView;
108        setWebViewClient(null);
109
110        mUiThreadHandler = new Handler() {
111
112            @Override
113            public void handleMessage(Message msg) {
114                switch(msg.what) {
115                    case NEW_WEBVIEW_CREATED:
116                        WebView.WebViewTransport t = (WebView.WebViewTransport) msg.obj;
117                        WebView newWebView = t.getWebView();
118                        if (newWebView == mWebView) {
119                            throw new IllegalArgumentException(
120                                    "Parent WebView cannot host it's own popup window. Please " +
121                                    "use WebSettings.setSupportMultipleWindows(false)");
122                        }
123
124                        if (newWebView != null && newWebView.copyBackForwardList().getSize() != 0) {
125                            throw new IllegalArgumentException(
126                                    "New WebView for popup window must not have been previously " +
127                                    "navigated.");
128                        }
129
130                        WebViewChromium.completeWindowCreation(mWebView, newWebView);
131                        break;
132                    default:
133                        throw new IllegalStateException();
134                }
135            }
136        };
137
138    }
139
140    // WebViewClassic is coded in such a way that even if a null WebViewClient is set,
141    // certain actions take place.
142    // We choose to replicate this behavior by using a NullWebViewClient implementation (also known
143    // as the Null Object pattern) rather than duplicating the WebViewClassic approach in
144    // ContentView.
145    static class NullWebViewClient extends WebViewClient {
146        @Override
147        public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
148            // TODO: Investigate more and add a test case.
149            // This is a copy of what Clank does. The WebViewCore key handling code and Clank key
150            // handling code differ enough that it's not trivial to figure out how keycodes are
151            // being filtered.
152            int keyCode = event.getKeyCode();
153            if (keyCode == KeyEvent.KEYCODE_MENU ||
154                keyCode == KeyEvent.KEYCODE_HOME ||
155                keyCode == KeyEvent.KEYCODE_BACK ||
156                keyCode == KeyEvent.KEYCODE_CALL ||
157                keyCode == KeyEvent.KEYCODE_ENDCALL ||
158                keyCode == KeyEvent.KEYCODE_POWER ||
159                keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
160                keyCode == KeyEvent.KEYCODE_CAMERA ||
161                keyCode == KeyEvent.KEYCODE_FOCUS ||
162                keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
163                keyCode == KeyEvent.KEYCODE_VOLUME_MUTE ||
164                keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
165                return true;
166            }
167            return false;
168        }
169
170        @Override
171        public boolean shouldOverrideUrlLoading(WebView view, String url) {
172            Intent intent;
173            // Perform generic parsing of the URI to turn it into an Intent.
174            try {
175                intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
176            } catch (URISyntaxException ex) {
177                Log.w(TAG, "Bad URI " + url + ": " + ex.getMessage());
178                return false;
179            }
180            // Sanitize the Intent, ensuring web pages can not bypass browser
181            // security (only access to BROWSABLE activities).
182            intent.addCategory(Intent.CATEGORY_BROWSABLE);
183            intent.setComponent(null);
184            // Pass the package name as application ID so that the intent from the
185            // same application can be opened in the same tab.
186            intent.putExtra(Browser.EXTRA_APPLICATION_ID,
187                    view.getContext().getPackageName());
188            try {
189                view.getContext().startActivity(intent);
190            } catch (ActivityNotFoundException ex) {
191                Log.w(TAG, "No application can handle " + url);
192                return false;
193            }
194            return true;
195        }
196    }
197
198    void setWebViewClient(WebViewClient client) {
199        if (client != null) {
200            mWebViewClient = client;
201        } else {
202            mWebViewClient = new NullWebViewClient();
203        }
204    }
205
206    void setWebChromeClient(WebChromeClient client) {
207        mWebChromeClient = client;
208    }
209
210    void setDownloadListener(DownloadListener listener) {
211        mDownloadListener = listener;
212    }
213
214    void setFindListener(WebView.FindListener listener) {
215        mFindListener = listener;
216    }
217
218    void setPictureListener(WebView.PictureListener listener) {
219        mPictureListener = listener;
220    }
221
222    //--------------------------------------------------------------------------------------------
223    //                        Adapter for all the methods.
224    //--------------------------------------------------------------------------------------------
225
226    /**
227     * @see AwContentsClient#getVisitedHistory
228     */
229    @Override
230    public void getVisitedHistory(ValueCallback<String[]> callback) {
231        TraceEvent.begin();
232        if (mWebChromeClient != null) {
233            if (TRACE) Log.d(TAG, "getVisitedHistory");
234            mWebChromeClient.getVisitedHistory(callback);
235        }
236        TraceEvent.end();
237    }
238
239    /**
240     * @see AwContentsClient#doUpdateVisiteHistory(String, boolean)
241     */
242    @Override
243    public void doUpdateVisitedHistory(String url, boolean isReload) {
244        TraceEvent.begin();
245        if (TRACE) Log.d(TAG, "doUpdateVisitedHistory=" + url + " reload=" + isReload);
246        mWebViewClient.doUpdateVisitedHistory(mWebView, url, isReload);
247        TraceEvent.end();
248    }
249
250    /**
251     * @see AwContentsClient#onProgressChanged(int)
252     */
253    @Override
254    public void onProgressChanged(int progress) {
255        TraceEvent.begin();
256        if (mWebChromeClient != null) {
257            if (TRACE) Log.d(TAG, "onProgressChanged=" + progress);
258            mWebChromeClient.onProgressChanged(mWebView, progress);
259        }
260        TraceEvent.end();
261    }
262
263    /**
264     * @see AwContentsClient#shouldInterceptRequest(java.lang.String)
265     */
266    @Override
267    public InterceptedRequestData shouldInterceptRequest(String url) {
268        TraceEvent.begin();
269        if (TRACE) Log.d(TAG, "shouldInterceptRequest=" + url);
270        WebResourceResponse response = mWebViewClient.shouldInterceptRequest(mWebView, url);
271        TraceEvent.end();
272        if (response == null) return null;
273        return new InterceptedRequestData(
274                response.getMimeType(),
275                response.getEncoding(),
276                response.getData());
277    }
278
279    /**
280     * @see AwContentsClient#shouldOverrideUrlLoading(java.lang.String)
281     */
282    @Override
283    public boolean shouldOverrideUrlLoading(String url) {
284        TraceEvent.begin();
285        if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + url);
286        boolean result = mWebViewClient.shouldOverrideUrlLoading(mWebView, url);
287        TraceEvent.end();
288        return result;
289    }
290
291    /**
292     * @see AwContentsClient#onUnhandledKeyEvent(android.view.KeyEvent)
293     */
294    @Override
295    public void onUnhandledKeyEvent(KeyEvent event) {
296        TraceEvent.begin();
297        if (TRACE) Log.d(TAG, "onUnhandledKeyEvent");
298        mWebViewClient.onUnhandledKeyEvent(mWebView, event);
299        TraceEvent.end();
300    }
301
302    /**
303     * @see AwContentsClient#onConsoleMessage(android.webkit.ConsoleMessage)
304     */
305    @Override
306    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
307        TraceEvent.begin();
308        boolean result;
309        if (mWebChromeClient != null) {
310            if (TRACE) Log.d(TAG, "onConsoleMessage");
311            result = mWebChromeClient.onConsoleMessage(consoleMessage);
312        } else {
313            result = false;
314        }
315        TraceEvent.end();
316        return result;
317    }
318
319    /**
320     * @see AwContentsClient#onFindResultReceived(int,int,boolean)
321     */
322    @Override
323    public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
324            boolean isDoneCounting) {
325        if (mFindListener == null) return;
326        TraceEvent.begin();
327        if (TRACE) Log.d(TAG, "onFindResultReceived");
328        mFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
329        TraceEvent.end();
330    }
331
332    /**
333     * @See AwContentsClient#onNewPicture(Picture)
334     */
335    @Override
336    public void onNewPicture(Picture picture) {
337        if (mPictureListener == null) return;
338        TraceEvent.begin();
339        if (TRACE) Log.d(TAG, "onNewPicture");
340        mPictureListener.onNewPicture(mWebView, picture);
341        TraceEvent.end();
342    }
343
344    @Override
345    public void onLoadResource(String url) {
346        TraceEvent.begin();
347        if (TRACE) Log.d(TAG, "onLoadResource=" + url);
348        mWebViewClient.onLoadResource(mWebView, url);
349        TraceEvent.end();
350    }
351
352    @Override
353    public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) {
354        Message m = mUiThreadHandler.obtainMessage(
355                NEW_WEBVIEW_CREATED, mWebView.new WebViewTransport());
356        TraceEvent.begin();
357        boolean result;
358        if (mWebChromeClient != null) {
359            if (TRACE) Log.d(TAG, "onCreateWindow");
360            result = mWebChromeClient.onCreateWindow(mWebView, isDialog, isUserGesture, m);
361        } else {
362            result = false;
363        }
364        TraceEvent.end();
365        return result;
366    }
367
368    /**
369     * @see AwContentsClient#onCloseWindow()
370     */
371    @Override
372    public void onCloseWindow() {
373        TraceEvent.begin();
374        if (mWebChromeClient != null) {
375            if (TRACE) Log.d(TAG, "onCloseWindow");
376            mWebChromeClient.onCloseWindow(mWebView);
377        }
378        TraceEvent.end();
379    }
380
381    /**
382     * @see AwContentsClient#onRequestFocus()
383     */
384    @Override
385    public void onRequestFocus() {
386        TraceEvent.begin();
387        if (mWebChromeClient != null) {
388            if (TRACE) Log.d(TAG, "onRequestFocus");
389            mWebChromeClient.onRequestFocus(mWebView);
390        }
391        TraceEvent.end();
392    }
393
394    /**
395     * @see AwContentsClient#onReceivedTouchIconUrl(String url, boolean precomposed)
396     */
397    @Override
398    public void onReceivedTouchIconUrl(String url, boolean precomposed) {
399        TraceEvent.begin();
400        if (mWebChromeClient != null) {
401            if (TRACE) Log.d(TAG, "onReceivedTouchIconUrl=" + url);
402            mWebChromeClient.onReceivedTouchIconUrl(mWebView, url, precomposed);
403        }
404        TraceEvent.end();
405    }
406
407    /**
408     * @see AwContentsClient#onReceivedIcon(Bitmap bitmap)
409     */
410    @Override
411    public void onReceivedIcon(Bitmap bitmap) {
412        TraceEvent.begin();
413        if (mWebChromeClient != null) {
414            if (TRACE) Log.d(TAG, "onReceivedIcon");
415            mWebChromeClient.onReceivedIcon(mWebView, bitmap);
416        }
417        TraceEvent.end();
418    }
419
420    /**
421     * @see ContentViewClient#onPageStarted(String)
422     */
423    @Override
424    public void onPageStarted(String url) {
425        TraceEvent.begin();
426        if (TRACE) Log.d(TAG, "onPageStarted=" + url);
427        mWebViewClient.onPageStarted(mWebView, url, mWebView.getFavicon());
428        TraceEvent.end();
429    }
430
431    /**
432     * @see ContentViewClient#onPageFinished(String)
433     */
434    @Override
435    public void onPageFinished(String url) {
436        TraceEvent.begin();
437        if (TRACE) Log.d(TAG, "onPageFinished=" + url);
438        mWebViewClient.onPageFinished(mWebView, url);
439        TraceEvent.end();
440
441        // See b/8208948
442        // This fakes an onNewPicture callback after onPageFinished to allow
443        // CTS tests to run in an un-flaky manner. This is required as the
444        // path for sending Picture updates in Chromium are decoupled from the
445        // page loading callbacks, i.e. the Chrome compositor may draw our
446        // content and send the Picture before onPageStarted or onPageFinished
447        // are invoked. The CTS harness discards any pictures it receives before
448        // onPageStarted is invoked, so in the case we get the Picture before that and
449        // no further updates after onPageStarted, we'll fail the test by timing
450        // out waiting for a Picture.
451        // To ensure backwards compatibility, we need to defer sending Picture updates
452        // until onPageFinished has been invoked. This work is being done
453        // upstream, and we can revert this hack when it lands.
454        if (mPictureListener != null) {
455            new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
456                @Override
457                public void run() {
458                    UnimplementedWebViewApi.invoke();
459                    if (mPictureListener != null) {
460                        if (TRACE) Log.d(TAG, "onPageFinished-fake");
461                        mPictureListener.onNewPicture(mWebView, new Picture());
462                    }
463                }
464            }, 100);
465        }
466    }
467
468    /**
469     * @see ContentViewClient#onReceivedError(int,String,String)
470     */
471    @Override
472    public void onReceivedError(int errorCode, String description, String failingUrl) {
473        if (description == null || description.isEmpty()) {
474            // ErrorStrings is @hidden, so we can't do this in AwContents.
475            // Normally the net/ layer will set a valid description, but for synthesized callbacks
476            // (like in the case for intercepted requests) AwContents will pass in null.
477            description = ErrorStrings.getString(errorCode, mWebView.getContext());
478        }
479        TraceEvent.begin();
480        if (TRACE) Log.d(TAG, "onReceivedError=" + failingUrl);
481        mWebViewClient.onReceivedError(mWebView, errorCode, description, failingUrl);
482        TraceEvent.end();
483    }
484
485    /**
486     * @see ContentViewClient#onReceivedTitle(String)
487     */
488    @Override
489    public void onReceivedTitle(String title) {
490        TraceEvent.begin();
491        if (mWebChromeClient != null) {
492            if (TRACE) Log.d(TAG, "onReceivedTitle");
493            mWebChromeClient.onReceivedTitle(mWebView, title);
494        }
495        TraceEvent.end();
496    }
497
498
499    /**
500     * @see ContentViewClient#shouldOverrideKeyEvent(KeyEvent)
501     */
502    @Override
503    public boolean shouldOverrideKeyEvent(KeyEvent event) {
504        // TODO(joth): The expression here is a workaround for http://b/7697782 :-
505        // 1. The check for system key should be made in AwContents or ContentViewCore,
506        //    before shouldOverrideKeyEvent() is called at all.
507        // 2. shouldOverrideKeyEvent() should be called in onKeyDown/onKeyUp, not from
508        //    dispatchKeyEvent().
509        if (event.isSystem()) return true;
510        TraceEvent.begin();
511        if (TRACE) Log.d(TAG, "shouldOverrideKeyEvent");
512        boolean result = mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
513        TraceEvent.end();
514        return result;
515    }
516
517
518    /**
519     * @see ContentViewClient#onStartContentIntent(Context, String)
520     * Callback when detecting a click on a content link.
521     */
522    // TODO: Delete this method when removed from base class.
523    public void onStartContentIntent(Context context, String contentUrl) {
524        TraceEvent.begin();
525        if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + contentUrl);
526        mWebViewClient.shouldOverrideUrlLoading(mWebView, contentUrl);
527        TraceEvent.end();
528    }
529
530    @Override
531    public void onGeolocationPermissionsShowPrompt(String origin,
532            GeolocationPermissions.Callback callback) {
533        TraceEvent.begin();
534        if (mWebChromeClient != null) {
535            if (TRACE) Log.d(TAG, "onGeolocationPermissionsShowPrompt");
536            mWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
537        }
538        TraceEvent.end();
539    }
540
541    @Override
542    public void onGeolocationPermissionsHidePrompt() {
543        TraceEvent.begin();
544        if (mWebChromeClient != null) {
545            if (TRACE) Log.d(TAG, "onGeolocationPermissionsHidePrompt");
546            mWebChromeClient.onGeolocationPermissionsHidePrompt();
547        }
548        TraceEvent.end();
549    }
550
551    private static class JsPromptResultReceiverAdapter implements JsResult.ResultReceiver {
552        private JsPromptResultReceiver mChromePromptResultReceiver;
553        private JsResultReceiver mChromeResultReceiver;
554        // We hold onto the JsPromptResult here, just to avoid the need to downcast
555        // in onJsResultComplete.
556        private final JsPromptResult mPromptResult = new JsPromptResult(this);
557
558        public JsPromptResultReceiverAdapter(JsPromptResultReceiver receiver) {
559            mChromePromptResultReceiver = receiver;
560        }
561
562        public JsPromptResultReceiverAdapter(JsResultReceiver receiver) {
563            mChromeResultReceiver = receiver;
564        }
565
566        public JsPromptResult getPromptResult() {
567            return mPromptResult;
568        }
569
570        @Override
571        public void onJsResultComplete(JsResult result) {
572            if (mChromePromptResultReceiver != null) {
573                if (mPromptResult.getResult()) {
574                    mChromePromptResultReceiver.confirm(mPromptResult.getStringResult());
575                } else {
576                    mChromePromptResultReceiver.cancel();
577                }
578            } else {
579                if (mPromptResult.getResult()) {
580                    mChromeResultReceiver.confirm();
581                } else {
582                    mChromeResultReceiver.cancel();
583                }
584            }
585        }
586    }
587
588    @Override
589    public void handleJsAlert(String url, String message, JsResultReceiver receiver) {
590        TraceEvent.begin();
591        if (mWebChromeClient != null) {
592            final JsPromptResult res =
593                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
594            if (TRACE) Log.d(TAG, "onJsAlert");
595            if (!mWebChromeClient.onJsAlert(mWebView, url, message, res)) {
596                new JsDialogHelper(res, JsDialogHelper.ALERT, null, message, url)
597                        .showDialog(mWebView.getContext());
598            }
599        } else {
600            receiver.cancel();
601        }
602        TraceEvent.end();
603    }
604
605    @Override
606    public void handleJsBeforeUnload(String url, String message, JsResultReceiver receiver) {
607        TraceEvent.begin();
608        if (mWebChromeClient != null) {
609            final JsPromptResult res =
610                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
611            if (TRACE) Log.d(TAG, "onJsBeforeUnload");
612            if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, message, res)) {
613                new JsDialogHelper(res, JsDialogHelper.UNLOAD, null, message, url)
614                        .showDialog(mWebView.getContext());
615            }
616        } else {
617            receiver.cancel();
618        }
619        TraceEvent.end();
620    }
621
622    @Override
623    public void handleJsConfirm(String url, String message, JsResultReceiver receiver) {
624        TraceEvent.begin();
625        if (mWebChromeClient != null) {
626            final JsPromptResult res =
627                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
628            if (TRACE) Log.d(TAG, "onJsConfirm");
629            if (!mWebChromeClient.onJsConfirm(mWebView, url, message, res)) {
630                new JsDialogHelper(res, JsDialogHelper.CONFIRM, null, message, url)
631                        .showDialog(mWebView.getContext());
632            }
633        } else {
634            receiver.cancel();
635        }
636        TraceEvent.end();
637    }
638
639    @Override
640    public void handleJsPrompt(String url, String message, String defaultValue,
641            JsPromptResultReceiver receiver) {
642        TraceEvent.begin();
643        if (mWebChromeClient != null) {
644            final JsPromptResult res =
645                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
646            if (TRACE) Log.d(TAG, "onJsPrompt");
647            if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultValue, res)) {
648                new JsDialogHelper(res, JsDialogHelper.PROMPT, defaultValue, message, url)
649                        .showDialog(mWebView.getContext());
650            }
651        } else {
652            receiver.cancel();
653        }
654        TraceEvent.end();
655    }
656
657    @Override
658    public void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
659        TraceEvent.begin();
660        if (TRACE) Log.d(TAG, "onReceivedHttpAuthRequest=" + host);
661        mWebViewClient.onReceivedHttpAuthRequest(mWebView,
662                new AwHttpAuthHandlerAdapter(handler), host, realm);
663        TraceEvent.end();
664    }
665
666    @Override
667    public void onReceivedSslError(final ValueCallback<Boolean> callback, SslError error) {
668        SslErrorHandler handler = new SslErrorHandler() {
669            @Override
670            public void proceed() {
671                postProceed(true);
672            }
673            @Override
674            public void cancel() {
675                postProceed(false);
676            }
677            private void postProceed(final boolean proceed) {
678                post(new Runnable() {
679                        @Override
680                        public void run() {
681                            callback.onReceiveValue(proceed);
682                        }
683                    });
684            }
685        };
686        TraceEvent.begin();
687        if (TRACE) Log.d(TAG, "onReceivedSslError");
688        mWebViewClient.onReceivedSslError(mWebView, handler, error);
689        TraceEvent.end();
690    }
691
692    @Override
693    public void onReceivedLoginRequest(String realm, String account, String args) {
694        TraceEvent.begin();
695        if (TRACE) Log.d(TAG, "onReceivedLoginRequest=" + realm);
696        mWebViewClient.onReceivedLoginRequest(mWebView, realm, account, args);
697        TraceEvent.end();
698    }
699
700    @Override
701    public void onFormResubmission(Message dontResend, Message resend) {
702        TraceEvent.begin();
703        if (TRACE) Log.d(TAG, "onFormResubmission");
704        mWebViewClient.onFormResubmission(mWebView, dontResend, resend);
705        TraceEvent.end();
706    }
707
708    @Override
709    public void onDownloadStart(String url,
710                                String userAgent,
711                                String contentDisposition,
712                                String mimeType,
713                                long contentLength) {
714        if (mDownloadListener != null) {
715            TraceEvent.begin();
716            if (TRACE) Log.d(TAG, "onDownloadStart");
717            mDownloadListener.onDownloadStart(url,
718                                              userAgent,
719                                              contentDisposition,
720                                              mimeType,
721                                              contentLength);
722            TraceEvent.end();
723        }
724    }
725
726    @Override
727    public void onScaleChangedScaled(float oldScale, float newScale) {
728        TraceEvent.begin();
729        if (TRACE) Log.d(TAG, " onScaleChangedScaled");
730        mWebViewClient.onScaleChanged(mWebView, oldScale, newScale);
731        TraceEvent.end();
732    }
733
734    @Override
735    public void onShowCustomView(View view, CustomViewCallback cb) {
736        TraceEvent.begin();
737        if (mWebChromeClient != null) {
738            if (TRACE) Log.d(TAG, "onShowCustomView");
739            mWebChromeClient.onShowCustomView(view, cb);
740        }
741        TraceEvent.end();
742    }
743
744    @Override
745    public void onHideCustomView() {
746        TraceEvent.begin();
747        if (mWebChromeClient != null) {
748            if (TRACE) Log.d(TAG, "onHideCustomView");
749            mWebChromeClient.onHideCustomView();
750        }
751        TraceEvent.end();
752    }
753
754    @Override
755    protected View getVideoLoadingProgressView() {
756        TraceEvent.begin();
757        View result;
758        if (mWebChromeClient != null) {
759            if (TRACE) Log.d(TAG, "getVideoLoadingProgressView");
760            result = mWebChromeClient.getVideoLoadingProgressView();
761        } else {
762            result = null;
763        }
764        TraceEvent.end();
765        return result;
766    }
767
768    @Override
769    public Bitmap getDefaultVideoPoster() {
770        TraceEvent.begin();
771        Bitmap result;
772        if (mWebChromeClient != null) {
773            if (TRACE) Log.d(TAG, "getDefaultVideoPoster");
774            result = mWebChromeClient.getDefaultVideoPoster();
775        } else {
776            result = null;
777        }
778        TraceEvent.end();
779        return result;
780    }
781
782    private static class AwHttpAuthHandlerAdapter extends android.webkit.HttpAuthHandler {
783        private AwHttpAuthHandler mAwHandler;
784
785        public AwHttpAuthHandlerAdapter(AwHttpAuthHandler awHandler) {
786            mAwHandler = awHandler;
787        }
788
789        @Override
790        public void proceed(String username, String password) {
791            if (username == null) {
792                username = "";
793            }
794
795            if (password == null) {
796                password = "";
797            }
798            mAwHandler.proceed(username, password);
799        }
800
801        @Override
802        public void cancel() {
803            mAwHandler.cancel();
804        }
805
806        @Override
807        public boolean useHttpAuthUsernamePassword() {
808            return mAwHandler.isFirstAttempt();
809        }
810    }
811}
812