WebViewContentsClientAdapter.java revision 1a0ca658d1d188ed9dc9b2163be0f2ee89e06a1e
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            String message = consoleMessage.message();
313            if (result && message != null && message.startsWith("[blocked]")) {
314                Log.e(TAG, "Blocked URL: " + message);
315            }
316        } else {
317            result = false;
318        }
319        TraceEvent.end();
320        return result;
321    }
322
323    /**
324     * @see AwContentsClient#onFindResultReceived(int,int,boolean)
325     */
326    @Override
327    public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
328            boolean isDoneCounting) {
329        if (mFindListener == null) return;
330        TraceEvent.begin();
331        if (TRACE) Log.d(TAG, "onFindResultReceived");
332        mFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
333        TraceEvent.end();
334    }
335
336    /**
337     * @See AwContentsClient#onNewPicture(Picture)
338     */
339    @Override
340    public void onNewPicture(Picture picture) {
341        if (mPictureListener == null) return;
342        TraceEvent.begin();
343        if (TRACE) Log.d(TAG, "onNewPicture");
344        mPictureListener.onNewPicture(mWebView, picture);
345        TraceEvent.end();
346    }
347
348    @Override
349    public void onLoadResource(String url) {
350        TraceEvent.begin();
351        if (TRACE) Log.d(TAG, "onLoadResource=" + url);
352        mWebViewClient.onLoadResource(mWebView, url);
353        TraceEvent.end();
354    }
355
356    @Override
357    public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) {
358        Message m = mUiThreadHandler.obtainMessage(
359                NEW_WEBVIEW_CREATED, mWebView.new WebViewTransport());
360        TraceEvent.begin();
361        boolean result;
362        if (mWebChromeClient != null) {
363            if (TRACE) Log.d(TAG, "onCreateWindow");
364            result = mWebChromeClient.onCreateWindow(mWebView, isDialog, isUserGesture, m);
365        } else {
366            result = false;
367        }
368        TraceEvent.end();
369        return result;
370    }
371
372    /**
373     * @see AwContentsClient#onCloseWindow()
374     */
375    @Override
376    public void onCloseWindow() {
377        TraceEvent.begin();
378        if (mWebChromeClient != null) {
379            if (TRACE) Log.d(TAG, "onCloseWindow");
380            mWebChromeClient.onCloseWindow(mWebView);
381        }
382        TraceEvent.end();
383    }
384
385    /**
386     * @see AwContentsClient#onRequestFocus()
387     */
388    @Override
389    public void onRequestFocus() {
390        TraceEvent.begin();
391        if (mWebChromeClient != null) {
392            if (TRACE) Log.d(TAG, "onRequestFocus");
393            mWebChromeClient.onRequestFocus(mWebView);
394        }
395        TraceEvent.end();
396    }
397
398    /**
399     * @see AwContentsClient#onReceivedTouchIconUrl(String url, boolean precomposed)
400     */
401    @Override
402    public void onReceivedTouchIconUrl(String url, boolean precomposed) {
403        TraceEvent.begin();
404        if (mWebChromeClient != null) {
405            if (TRACE) Log.d(TAG, "onReceivedTouchIconUrl=" + url);
406            mWebChromeClient.onReceivedTouchIconUrl(mWebView, url, precomposed);
407        }
408        TraceEvent.end();
409    }
410
411    /**
412     * @see AwContentsClient#onReceivedIcon(Bitmap bitmap)
413     */
414    @Override
415    public void onReceivedIcon(Bitmap bitmap) {
416        TraceEvent.begin();
417        if (mWebChromeClient != null) {
418            if (TRACE) Log.d(TAG, "onReceivedIcon");
419            mWebChromeClient.onReceivedIcon(mWebView, bitmap);
420        }
421        TraceEvent.end();
422    }
423
424    /**
425     * @see ContentViewClient#onPageStarted(String)
426     */
427    @Override
428    public void onPageStarted(String url) {
429        TraceEvent.begin();
430        if (TRACE) Log.d(TAG, "onPageStarted=" + url);
431        mWebViewClient.onPageStarted(mWebView, url, mWebView.getFavicon());
432        TraceEvent.end();
433    }
434
435    /**
436     * @see ContentViewClient#onPageFinished(String)
437     */
438    @Override
439    public void onPageFinished(String url) {
440        TraceEvent.begin();
441        if (TRACE) Log.d(TAG, "onPageFinished=" + url);
442        mWebViewClient.onPageFinished(mWebView, url);
443        TraceEvent.end();
444
445        // See b/8208948
446        // This fakes an onNewPicture callback after onPageFinished to allow
447        // CTS tests to run in an un-flaky manner. This is required as the
448        // path for sending Picture updates in Chromium are decoupled from the
449        // page loading callbacks, i.e. the Chrome compositor may draw our
450        // content and send the Picture before onPageStarted or onPageFinished
451        // are invoked. The CTS harness discards any pictures it receives before
452        // onPageStarted is invoked, so in the case we get the Picture before that and
453        // no further updates after onPageStarted, we'll fail the test by timing
454        // out waiting for a Picture.
455        // To ensure backwards compatibility, we need to defer sending Picture updates
456        // until onPageFinished has been invoked. This work is being done
457        // upstream, and we can revert this hack when it lands.
458        if (mPictureListener != null) {
459            new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
460                @Override
461                public void run() {
462                    UnimplementedWebViewApi.invoke();
463                    if (mPictureListener != null) {
464                        if (TRACE) Log.d(TAG, "onPageFinished-fake");
465                        mPictureListener.onNewPicture(mWebView, new Picture());
466                    }
467                }
468            }, 100);
469        }
470    }
471
472    /**
473     * @see ContentViewClient#onReceivedError(int,String,String)
474     */
475    @Override
476    public void onReceivedError(int errorCode, String description, String failingUrl) {
477        if (description == null || description.isEmpty()) {
478            // ErrorStrings is @hidden, so we can't do this in AwContents.
479            // Normally the net/ layer will set a valid description, but for synthesized callbacks
480            // (like in the case for intercepted requests) AwContents will pass in null.
481            description = ErrorStrings.getString(errorCode, mWebView.getContext());
482        }
483        TraceEvent.begin();
484        if (TRACE) Log.d(TAG, "onReceivedError=" + failingUrl);
485        mWebViewClient.onReceivedError(mWebView, errorCode, description, failingUrl);
486        TraceEvent.end();
487    }
488
489    /**
490     * @see ContentViewClient#onReceivedTitle(String)
491     */
492    @Override
493    public void onReceivedTitle(String title) {
494        TraceEvent.begin();
495        if (mWebChromeClient != null) {
496            if (TRACE) Log.d(TAG, "onReceivedTitle");
497            mWebChromeClient.onReceivedTitle(mWebView, title);
498        }
499        TraceEvent.end();
500    }
501
502
503    /**
504     * @see ContentViewClient#shouldOverrideKeyEvent(KeyEvent)
505     */
506    @Override
507    public boolean shouldOverrideKeyEvent(KeyEvent event) {
508        // TODO(joth): The expression here is a workaround for http://b/7697782 :-
509        // 1. The check for system key should be made in AwContents or ContentViewCore,
510        //    before shouldOverrideKeyEvent() is called at all.
511        // 2. shouldOverrideKeyEvent() should be called in onKeyDown/onKeyUp, not from
512        //    dispatchKeyEvent().
513        if (event.isSystem()) return true;
514        TraceEvent.begin();
515        if (TRACE) Log.d(TAG, "shouldOverrideKeyEvent");
516        boolean result = mWebViewClient.shouldOverrideKeyEvent(mWebView, event);
517        TraceEvent.end();
518        return result;
519    }
520
521
522    /**
523     * @see ContentViewClient#onStartContentIntent(Context, String)
524     * Callback when detecting a click on a content link.
525     */
526    // TODO: Delete this method when removed from base class.
527    public void onStartContentIntent(Context context, String contentUrl) {
528        TraceEvent.begin();
529        if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + contentUrl);
530        mWebViewClient.shouldOverrideUrlLoading(mWebView, contentUrl);
531        TraceEvent.end();
532    }
533
534    @Override
535    public void onGeolocationPermissionsShowPrompt(String origin,
536            GeolocationPermissions.Callback callback) {
537        TraceEvent.begin();
538        if (mWebChromeClient != null) {
539            if (TRACE) Log.d(TAG, "onGeolocationPermissionsShowPrompt");
540            mWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
541        }
542        TraceEvent.end();
543    }
544
545    @Override
546    public void onGeolocationPermissionsHidePrompt() {
547        TraceEvent.begin();
548        if (mWebChromeClient != null) {
549            if (TRACE) Log.d(TAG, "onGeolocationPermissionsHidePrompt");
550            mWebChromeClient.onGeolocationPermissionsHidePrompt();
551        }
552        TraceEvent.end();
553    }
554
555    private static class JsPromptResultReceiverAdapter implements JsResult.ResultReceiver {
556        private JsPromptResultReceiver mChromePromptResultReceiver;
557        private JsResultReceiver mChromeResultReceiver;
558        // We hold onto the JsPromptResult here, just to avoid the need to downcast
559        // in onJsResultComplete.
560        private final JsPromptResult mPromptResult = new JsPromptResult(this);
561
562        public JsPromptResultReceiverAdapter(JsPromptResultReceiver receiver) {
563            mChromePromptResultReceiver = receiver;
564        }
565
566        public JsPromptResultReceiverAdapter(JsResultReceiver receiver) {
567            mChromeResultReceiver = receiver;
568        }
569
570        public JsPromptResult getPromptResult() {
571            return mPromptResult;
572        }
573
574        @Override
575        public void onJsResultComplete(JsResult result) {
576            if (mChromePromptResultReceiver != null) {
577                if (mPromptResult.getResult()) {
578                    mChromePromptResultReceiver.confirm(mPromptResult.getStringResult());
579                } else {
580                    mChromePromptResultReceiver.cancel();
581                }
582            } else {
583                if (mPromptResult.getResult()) {
584                    mChromeResultReceiver.confirm();
585                } else {
586                    mChromeResultReceiver.cancel();
587                }
588            }
589        }
590    }
591
592    @Override
593    public void handleJsAlert(String url, String message, JsResultReceiver receiver) {
594        TraceEvent.begin();
595        if (mWebChromeClient != null) {
596            final JsPromptResult res =
597                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
598            if (TRACE) Log.d(TAG, "onJsAlert");
599            if (!mWebChromeClient.onJsAlert(mWebView, url, message, res)) {
600                new JsDialogHelper(res, JsDialogHelper.ALERT, null, message, url)
601                        .showDialog(mWebView.getContext());
602            }
603        } else {
604            receiver.cancel();
605        }
606        TraceEvent.end();
607    }
608
609    @Override
610    public void handleJsBeforeUnload(String url, String message, JsResultReceiver receiver) {
611        TraceEvent.begin();
612        if (mWebChromeClient != null) {
613            final JsPromptResult res =
614                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
615            if (TRACE) Log.d(TAG, "onJsBeforeUnload");
616            if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, message, res)) {
617                new JsDialogHelper(res, JsDialogHelper.UNLOAD, null, message, url)
618                        .showDialog(mWebView.getContext());
619            }
620        } else {
621            receiver.cancel();
622        }
623        TraceEvent.end();
624    }
625
626    @Override
627    public void handleJsConfirm(String url, String message, JsResultReceiver receiver) {
628        TraceEvent.begin();
629        if (mWebChromeClient != null) {
630            final JsPromptResult res =
631                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
632            if (TRACE) Log.d(TAG, "onJsConfirm");
633            if (!mWebChromeClient.onJsConfirm(mWebView, url, message, res)) {
634                new JsDialogHelper(res, JsDialogHelper.CONFIRM, null, message, url)
635                        .showDialog(mWebView.getContext());
636            }
637        } else {
638            receiver.cancel();
639        }
640        TraceEvent.end();
641    }
642
643    @Override
644    public void handleJsPrompt(String url, String message, String defaultValue,
645            JsPromptResultReceiver receiver) {
646        TraceEvent.begin();
647        if (mWebChromeClient != null) {
648            final JsPromptResult res =
649                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
650            if (TRACE) Log.d(TAG, "onJsPrompt");
651            if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultValue, res)) {
652                new JsDialogHelper(res, JsDialogHelper.PROMPT, defaultValue, message, url)
653                        .showDialog(mWebView.getContext());
654            }
655        } else {
656            receiver.cancel();
657        }
658        TraceEvent.end();
659    }
660
661    @Override
662    public void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
663        TraceEvent.begin();
664        if (TRACE) Log.d(TAG, "onReceivedHttpAuthRequest=" + host);
665        mWebViewClient.onReceivedHttpAuthRequest(mWebView,
666                new AwHttpAuthHandlerAdapter(handler), host, realm);
667        TraceEvent.end();
668    }
669
670    @Override
671    public void onReceivedSslError(final ValueCallback<Boolean> callback, SslError error) {
672        SslErrorHandler handler = new SslErrorHandler() {
673            @Override
674            public void proceed() {
675                postProceed(true);
676            }
677            @Override
678            public void cancel() {
679                postProceed(false);
680            }
681            private void postProceed(final boolean proceed) {
682                post(new Runnable() {
683                        @Override
684                        public void run() {
685                            callback.onReceiveValue(proceed);
686                        }
687                    });
688            }
689        };
690        TraceEvent.begin();
691        if (TRACE) Log.d(TAG, "onReceivedSslError");
692        mWebViewClient.onReceivedSslError(mWebView, handler, error);
693        TraceEvent.end();
694    }
695
696    @Override
697    public void onReceivedLoginRequest(String realm, String account, String args) {
698        TraceEvent.begin();
699        if (TRACE) Log.d(TAG, "onReceivedLoginRequest=" + realm);
700        mWebViewClient.onReceivedLoginRequest(mWebView, realm, account, args);
701        TraceEvent.end();
702    }
703
704    @Override
705    public void onFormResubmission(Message dontResend, Message resend) {
706        TraceEvent.begin();
707        if (TRACE) Log.d(TAG, "onFormResubmission");
708        mWebViewClient.onFormResubmission(mWebView, dontResend, resend);
709        TraceEvent.end();
710    }
711
712    @Override
713    public void onDownloadStart(String url,
714                                String userAgent,
715                                String contentDisposition,
716                                String mimeType,
717                                long contentLength) {
718        if (mDownloadListener != null) {
719            TraceEvent.begin();
720            if (TRACE) Log.d(TAG, "onDownloadStart");
721            mDownloadListener.onDownloadStart(url,
722                                              userAgent,
723                                              contentDisposition,
724                                              mimeType,
725                                              contentLength);
726            TraceEvent.end();
727        }
728    }
729
730    @Override
731    public void onScaleChangedScaled(float oldScale, float newScale) {
732        TraceEvent.begin();
733        if (TRACE) Log.d(TAG, " onScaleChangedScaled");
734        mWebViewClient.onScaleChanged(mWebView, oldScale, newScale);
735        TraceEvent.end();
736    }
737
738    @Override
739    public void onShowCustomView(View view, CustomViewCallback cb) {
740        TraceEvent.begin();
741        if (mWebChromeClient != null) {
742            if (TRACE) Log.d(TAG, "onShowCustomView");
743            mWebChromeClient.onShowCustomView(view, cb);
744        }
745        TraceEvent.end();
746    }
747
748    @Override
749    public void onHideCustomView() {
750        TraceEvent.begin();
751        if (mWebChromeClient != null) {
752            if (TRACE) Log.d(TAG, "onHideCustomView");
753            mWebChromeClient.onHideCustomView();
754        }
755        TraceEvent.end();
756    }
757
758    @Override
759    protected View getVideoLoadingProgressView() {
760        TraceEvent.begin();
761        View result;
762        if (mWebChromeClient != null) {
763            if (TRACE) Log.d(TAG, "getVideoLoadingProgressView");
764            result = mWebChromeClient.getVideoLoadingProgressView();
765        } else {
766            result = null;
767        }
768        TraceEvent.end();
769        return result;
770    }
771
772    @Override
773    public Bitmap getDefaultVideoPoster() {
774        TraceEvent.begin();
775        Bitmap result;
776        if (mWebChromeClient != null) {
777            if (TRACE) Log.d(TAG, "getDefaultVideoPoster");
778            result = mWebChromeClient.getDefaultVideoPoster();
779        } else {
780            result = null;
781        }
782        TraceEvent.end();
783        return result;
784    }
785
786    private static class AwHttpAuthHandlerAdapter extends android.webkit.HttpAuthHandler {
787        private AwHttpAuthHandler mAwHandler;
788
789        public AwHttpAuthHandlerAdapter(AwHttpAuthHandler awHandler) {
790            mAwHandler = awHandler;
791        }
792
793        @Override
794        public void proceed(String username, String password) {
795            if (username == null) {
796                username = "";
797            }
798
799            if (password == null) {
800                password = "";
801            }
802            mAwHandler.proceed(username, password);
803        }
804
805        @Override
806        public void cancel() {
807            mAwHandler.cancel();
808        }
809
810        @Override
811        public boolean useHttpAuthUsernamePassword() {
812            return mAwHandler.isFirstAttempt();
813        }
814    }
815}
816