WebViewContentsClientAdapter.java revision 33e0aee95766f7091948865f4753eae3ed6f6306
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        }
572        TraceEvent.end();
573    }
574
575    @Override
576    public void handleJsBeforeUnload(String url, String message, JsResultReceiver receiver) {
577        TraceEvent.begin();
578        if (mWebChromeClient != null) {
579            final JsPromptResult res =
580                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
581            if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, message, res)) {
582                new JsDialogHelper(res, JsDialogHelper.UNLOAD, null, message, url)
583                        .showDialog(mWebView.getContext());
584            }
585        }
586        TraceEvent.end();
587    }
588
589    @Override
590    public void handleJsConfirm(String url, String message, JsResultReceiver receiver) {
591        TraceEvent.begin();
592        if (mWebChromeClient != null) {
593            final JsPromptResult res =
594                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
595            if (!mWebChromeClient.onJsConfirm(mWebView, url, message, res)) {
596                new JsDialogHelper(res, JsDialogHelper.CONFIRM, null, message, url)
597                        .showDialog(mWebView.getContext());
598            }
599        }
600        TraceEvent.end();
601    }
602
603    @Override
604    public void handleJsPrompt(String url, String message, String defaultValue,
605            JsPromptResultReceiver receiver) {
606        TraceEvent.begin();
607        if (mWebChromeClient != null) {
608            final JsPromptResult res =
609                    new JsPromptResultReceiverAdapter(receiver).getPromptResult();
610            if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultValue, res)) {
611                new JsDialogHelper(res, JsDialogHelper.PROMPT, defaultValue, message, url)
612                        .showDialog(mWebView.getContext());
613            }
614        }
615        TraceEvent.end();
616    }
617
618    @Override
619    public void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) {
620        TraceEvent.begin();
621        mWebViewClient.onReceivedHttpAuthRequest(mWebView,
622                new AwHttpAuthHandlerAdapter(handler), host, realm);
623        TraceEvent.end();
624    }
625
626    @Override
627    public void onReceivedSslError(final ValueCallback<Boolean> callback, SslError error) {
628        SslErrorHandler handler = new SslErrorHandler() {
629            @Override
630            public void proceed() {
631                postProceed(true);
632            }
633            @Override
634            public void cancel() {
635                postProceed(false);
636            }
637            private void postProceed(final boolean proceed) {
638                post(new Runnable() {
639                        @Override
640                        public void run() {
641                            callback.onReceiveValue(proceed);
642                        }
643                    });
644            }
645        };
646        TraceEvent.begin();
647        mWebViewClient.onReceivedSslError(mWebView, handler, error);
648        TraceEvent.end();
649    }
650
651    @Override
652    public void onReceivedLoginRequest(String realm, String account, String args) {
653        TraceEvent.begin();
654        mWebViewClient.onReceivedLoginRequest(mWebView, realm, account, args);
655        TraceEvent.end();
656    }
657
658    @Override
659    public void onFormResubmission(Message dontResend, Message resend) {
660        TraceEvent.begin();
661        mWebViewClient.onFormResubmission(mWebView, dontResend, resend);
662        TraceEvent.end();
663    }
664
665    @Override
666    public void onDownloadStart(String url,
667                                String userAgent,
668                                String contentDisposition,
669                                String mimeType,
670                                long contentLength) {
671        if (mDownloadListener != null) {
672            TraceEvent.begin();
673            mDownloadListener.onDownloadStart(url,
674                                              userAgent,
675                                              contentDisposition,
676                                              mimeType,
677                                              contentLength);
678            TraceEvent.end();
679        }
680    }
681
682    @Override
683    public void onScaleChangedScaled(float oldScale, float newScale) {
684        TraceEvent.begin();
685        mWebViewClient.onScaleChanged(mWebView, oldScale, newScale);
686        TraceEvent.end();
687    }
688
689    @Override
690    public void onShowCustomView(View view, CustomViewCallback cb) {
691        TraceEvent.begin();
692        if (mWebChromeClient != null) {
693            mWebChromeClient.onShowCustomView(view, cb);
694        }
695        TraceEvent.end();
696    }
697
698    @Override
699    public void onHideCustomView() {
700        TraceEvent.begin();
701        if (mWebChromeClient != null) {
702            mWebChromeClient.onHideCustomView();
703        }
704        TraceEvent.end();
705    }
706
707    @Override
708    protected View getVideoLoadingProgressView() {
709        TraceEvent.begin();
710        View result;
711        if (mWebChromeClient != null) {
712            result = mWebChromeClient.getVideoLoadingProgressView();
713        } else {
714            result = null;
715        }
716        TraceEvent.end();
717        return result;
718    }
719
720    @Override
721    public Bitmap getDefaultVideoPoster() {
722        TraceEvent.begin();
723        Bitmap result;
724        if (mWebChromeClient != null) {
725            result = mWebChromeClient.getDefaultVideoPoster();
726        } else {
727            result = null;
728        }
729        TraceEvent.end();
730        return result;
731    }
732
733    private static class AwHttpAuthHandlerAdapter extends android.webkit.HttpAuthHandler {
734        private AwHttpAuthHandler mAwHandler;
735
736        public AwHttpAuthHandlerAdapter(AwHttpAuthHandler awHandler) {
737            mAwHandler = awHandler;
738        }
739
740        @Override
741        public void proceed(String username, String password) {
742            if (username == null) {
743                username = "";
744            }
745
746            if (password == null) {
747                password = "";
748            }
749            mAwHandler.proceed(username, password);
750        }
751
752        @Override
753        public void cancel() {
754            mAwHandler.cancel();
755        }
756
757        @Override
758        public boolean useHttpAuthUsernamePassword() {
759            return mAwHandler.isFirstAttempt();
760        }
761    }
762}
763