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