WebViewChromium.java revision fa62be4cdc2630566ee179843393d4a6f9893966
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.res.Configuration;
20import android.graphics.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.Paint;
23import android.graphics.Picture;
24import android.graphics.Rect;
25import android.graphics.drawable.Drawable;
26import android.net.http.SslCertificate;
27import android.os.Build;
28import android.os.Bundle;
29import android.os.CancellationSignal;
30import android.os.Message;
31import android.os.ParcelFileDescriptor;
32import android.print.PrintAttributes;
33import android.text.TextUtils;
34import android.util.Base64;
35import android.util.Log;
36import android.view.HardwareCanvas;
37import android.view.KeyEvent;
38import android.view.MotionEvent;
39import android.view.View;
40import android.view.ViewGroup;
41import android.view.accessibility.AccessibilityEvent;
42import android.view.accessibility.AccessibilityNodeInfo;
43import android.view.accessibility.AccessibilityNodeProvider;
44import android.view.inputmethod.EditorInfo;
45import android.view.inputmethod.InputConnection;
46import android.webkit.DownloadListener;
47import android.webkit.FindActionModeCallback;
48import android.webkit.JavascriptInterface;
49import android.webkit.ValueCallback;
50import android.webkit.WebBackForwardList;
51import android.webkit.WebChromeClient;
52import android.webkit.WebSettings;
53import android.webkit.WebView;
54import android.webkit.WebViewClient;
55import android.webkit.WebViewProvider;
56import android.widget.TextView;
57
58import org.chromium.android_webview.AwBrowserContext;
59import org.chromium.android_webview.AwContents;
60import org.chromium.android_webview.AwLayoutSizer;
61import org.chromium.android_webview.AwPdfExportAttributes;
62import org.chromium.base.ThreadUtils;
63import org.chromium.content.browser.LoadUrlParams;
64import org.chromium.net.NetworkChangeNotifier;
65
66import java.io.BufferedWriter;
67import java.io.File;
68import java.lang.annotation.Annotation;
69import java.util.HashMap;
70import java.util.Map;
71
72/**
73 * This class is the delegate to which WebViewProxy forwards all API calls.
74 *
75 * Most of the actual functionality is implemented by AwContents (or ContentViewCore within
76 * it). This class also contains WebView-specific APIs that require the creation of other
77 * adapters (otherwise org.chromium.content would depend on the webview.chromium package)
78 * and a small set of no-op deprecated APIs.
79 */
80class WebViewChromium implements WebViewProvider,
81          WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate {
82
83    private static final String TAG = WebViewChromium.class.getSimpleName();
84
85    // The WebView that this WebViewChromium is the provider for.
86    WebView mWebView;
87    // Lets us access protected View-derived methods on the WebView instance we're backing.
88    WebView.PrivateAccess mWebViewPrivate;
89    // The client adapter class.
90    private WebViewContentsClientAdapter mContentsClientAdapter;
91
92    // Variables for functionality provided by this adapter ---------------------------------------
93    // WebSettings adapter, lazily initialized in the getter
94    private WebSettings mWebSettings;
95    // The WebView wrapper for ContentViewCore and required browser compontents.
96    private AwContents mAwContents;
97    // Non-null if this webview is using the GL accelerated draw path.
98    private DrawGLFunctor mGLfunctor;
99
100    private AwBrowserContext mBrowserContext;
101
102    private final WebView.HitTestResult mHitTestResult;
103
104    private final int mAppTargetSdkVersion;
105
106    public WebViewChromium(WebView webView, WebView.PrivateAccess webViewPrivate,
107            AwBrowserContext browserContext) {
108        checkThread();
109        mWebView = webView;
110        mWebViewPrivate = webViewPrivate;
111        mHitTestResult = new WebView.HitTestResult();
112        mBrowserContext = browserContext;
113        mAppTargetSdkVersion = mWebView.getContext().getApplicationInfo().targetSdkVersion;
114    }
115
116    static void completeWindowCreation(WebView parent, WebView child) {
117        AwContents parentContents = ((WebViewChromium) parent.getWebViewProvider()).mAwContents;
118        AwContents childContents =
119                child == null ? null : ((WebViewChromium) child.getWebViewProvider()).mAwContents;
120        parentContents.supplyContentsForPopup(childContents);
121    }
122
123    // WebViewProvider methods --------------------------------------------------------------------
124
125    @Override
126    public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
127        // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
128        // so is ignored. TODO: remove it from WebViewProvider.
129        final boolean isAccessFromFileURLsGrantedByDefault =
130                mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
131        final boolean areLegacyQuirksEnabled =
132                mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
133        mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
134        mAwContents = new AwContents(mBrowserContext, mWebView, new InternalAccessAdapter(),
135                mContentsClientAdapter, isAccessFromFileURLsGrantedByDefault,
136                new AwLayoutSizer(), areLegacyQuirksEnabled);
137        mWebSettings = new ContentSettingsAdapter(mAwContents.getSettings());
138
139        if (privateBrowsing) {
140            final String msg = "Private browsing is not supported in WebView.";
141                       if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
142                throw new IllegalArgumentException(msg);
143            } else {
144                Log.w(TAG, msg);
145                // Intentionally irreversibly disable the webview instance, so that private
146                // user data cannot leak through misuse of a non-privateBrowing WebView instance.
147                // Can't just null out mAwContents as we never null-check it before use.
148                mAwContents.destroy();
149                TextView warningLabel = new TextView(mWebView.getContext());
150                warningLabel.setText(mWebView.getContext().getString(
151                        com.android.internal.R.string.webviewchromium_private_browsing_warning));
152                mWebView.addView(warningLabel);
153            }
154        }
155
156    }
157
158    private RuntimeException createThreadException() {
159        return new IllegalStateException("Calling View methods on another thread than the UI " +
160                "thread. PLEASE FILE A BUG! go/klp-webview-bug");
161    }
162
163    //  Intentionally not static, as no need to check thread on static methods
164    private void checkThread() {
165        if (!ThreadUtils.runningOnUiThread()) {
166            final RuntimeException threadViolation = createThreadException();
167            ThreadUtils.postOnUiThread(new Runnable() {
168                @Override
169                public void run() {
170                    throw threadViolation;
171                }
172            });
173            throw createThreadException();
174        }
175    }
176
177    @Override
178    public void setHorizontalScrollbarOverlay(boolean overlay) {
179        checkThread();
180        mAwContents.setHorizontalScrollbarOverlay(overlay);
181    }
182
183    @Override
184    public void setVerticalScrollbarOverlay(boolean overlay) {
185        checkThread();
186        mAwContents.setVerticalScrollbarOverlay(overlay);
187    }
188
189    @Override
190    public boolean overlayHorizontalScrollbar() {
191        checkThread();
192        return mAwContents.overlayHorizontalScrollbar();
193    }
194
195    @Override
196    public boolean overlayVerticalScrollbar() {
197        checkThread();
198        return mAwContents.overlayVerticalScrollbar();
199    }
200
201    @Override
202    public int getVisibleTitleHeight() {
203        // This is deprecated in WebView and should always return 0.
204        return 0;
205    }
206
207    @Override
208    public SslCertificate getCertificate() {
209        checkThread();
210        return mAwContents.getCertificate();
211    }
212
213    @Override
214    public void setCertificate(SslCertificate certificate) {
215        checkThread();
216        UnimplementedWebViewApi.invoke();
217    }
218
219    @Override
220    public void savePassword(String host, String username, String password) {
221        // This is a deprecated API: intentional no-op.
222    }
223
224    @Override
225    public void setHttpAuthUsernamePassword(String host, String realm, String username,
226                                            String password) {
227        checkThread();
228        mAwContents.setHttpAuthUsernamePassword(host, realm, username, password);
229    }
230
231    @Override
232    public String[] getHttpAuthUsernamePassword(String host, String realm) {
233        checkThread();
234        return mAwContents.getHttpAuthUsernamePassword(host, realm);
235    }
236
237    @Override
238    public void destroy() {
239        if (!ThreadUtils.runningOnUiThread()) {
240            ThreadUtils.postOnUiThread(new Runnable() {
241                @Override
242                public void run() {
243                    destroy();
244                }
245            });
246            return;
247        }
248
249        mAwContents.destroy();
250        if (mGLfunctor != null) {
251            mGLfunctor.destroy();
252            mGLfunctor = null;
253        }
254    }
255
256    @Override
257    public void setNetworkAvailable(boolean networkUp) {
258        checkThread();
259        // Note that this purely toggles the JS navigator.online property.
260        // It does not in affect chromium or network stack state in any way.
261        mAwContents.setNetworkAvailable(networkUp);
262    }
263
264    @Override
265    public WebBackForwardList saveState(Bundle outState) {
266        checkThread();
267        if (outState == null) return null;
268        if (!mAwContents.saveState(outState)) return null;
269        return copyBackForwardList();
270    }
271
272    @Override
273    public boolean savePicture(Bundle b, File dest) {
274        // Intentional no-op: hidden method on WebView.
275        return false;
276    }
277
278    @Override
279    public boolean restorePicture(Bundle b, File src) {
280        // Intentional no-op: hidden method on WebView.
281        return false;
282    }
283
284    @Override
285    public WebBackForwardList restoreState(Bundle inState) {
286        checkThread();
287        if (inState == null) return null;
288        if (!mAwContents.restoreState(inState)) return null;
289        return copyBackForwardList();
290    }
291
292    @Override
293    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
294        // TODO: We may actually want to do some sanity checks here (like filter about://chrome).
295
296        // For backwards compatibility, apps targeting less than K will have JS URLs evaluated
297        // directly and any result of the evaluation will not replace the current page content.
298        // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will
299        // have the result of that URL replace the content of the current page.
300        final String JAVASCRIPT_SCHEME = "javascript:";
301        if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
302                url.startsWith(JAVASCRIPT_SCHEME)) {
303            mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
304                    url.substring(JAVASCRIPT_SCHEME.length()));
305            return;
306        }
307
308        LoadUrlParams params = new LoadUrlParams(url);
309        if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders);
310        loadUrlOnUiThread(params);
311    }
312
313    @Override
314    public void loadUrl(String url) {
315        loadUrl(url, null);
316    }
317
318    @Override
319    public void postUrl(String url, byte[] postData) {
320        LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData);
321        Map<String,String> headers = new HashMap<String,String>();
322        headers.put("Content-Type", "application/x-www-form-urlencoded");
323        params.setExtraHeaders(headers);
324        loadUrlOnUiThread(params);
325    }
326
327    private static String fixupMimeType(String mimeType) {
328        return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType;
329    }
330
331    private static String fixupData(String data) {
332        return TextUtils.isEmpty(data) ? "" : data;
333    }
334
335    private static String fixupBase(String url) {
336        return TextUtils.isEmpty(url) ? "about:blank" : url;
337    }
338
339    private static String fixupHistory(String url) {
340        return TextUtils.isEmpty(url) ? "about:blank" : url;
341    }
342
343    private static boolean isBase64Encoded(String encoding) {
344        return "base64".equals(encoding);
345    }
346
347    @Override
348    public void loadData(String data, String mimeType, String encoding) {
349        loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
350                fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
351    }
352
353    @Override
354    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
355            String historyUrl) {
356        data = fixupData(data);
357        mimeType = fixupMimeType(mimeType);
358        LoadUrlParams loadUrlParams;
359        baseUrl = fixupBase(baseUrl);
360        historyUrl = fixupHistory(historyUrl);
361
362        if (baseUrl.startsWith("data:")) {
363            // For backwards compatibility with WebViewClassic, we use the value of |encoding|
364            // as the charset, as long as it's not "base64".
365            boolean isBase64 = isBase64Encoded(encoding);
366            loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
367                    data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
368        } else {
369            // When loading data with a non-data: base URL, the classic WebView would effectively
370            // "dump" that string of data into the WebView without going through regular URL
371            // loading steps such as decoding URL-encoded entities. We achieve this same behavior by
372            // base64 encoding the data that is passed here and then loading that as a data: URL.
373            try {
374                loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
375                        Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
376                        true, baseUrl, historyUrl, "utf-8");
377            } catch (java.io.UnsupportedEncodingException e) {
378                Log.wtf(TAG, "Unable to load data string " + data, e);
379                return;
380            }
381        }
382        loadUrlOnUiThread(loadUrlParams);
383    }
384
385    private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
386        if (ThreadUtils.runningOnUiThread()) {
387            mAwContents.loadUrl(loadUrlParams);
388        } else {
389            // Disallowed in WebView API for apps targetting a new SDK
390            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
391            ThreadUtils.postOnUiThread(new Runnable() {
392                @Override
393                public void run() {
394                    mAwContents.loadUrl(loadUrlParams);
395                }
396            });
397        }
398    }
399
400    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
401        checkThread();
402        mAwContents.evaluateJavaScript(script, resultCallback);
403    }
404
405    @Override
406    public void saveWebArchive(String filename) {
407        checkThread();
408        saveWebArchive(filename, false, null);
409    }
410
411    @Override
412    public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
413        checkThread();
414        mAwContents.saveWebArchive(basename, autoname, callback);
415    }
416
417    @Override
418    public void stopLoading() {
419        checkThread();
420        mAwContents.stopLoading();
421    }
422
423    @Override
424    public void reload() {
425        checkThread();
426        mAwContents.reload();
427    }
428
429    @Override
430    public boolean canGoBack() {
431        checkThread();
432        return mAwContents.canGoBack();
433    }
434
435    @Override
436    public void goBack() {
437        checkThread();
438        mAwContents.goBack();
439    }
440
441    @Override
442    public boolean canGoForward() {
443        checkThread();
444        return mAwContents.canGoForward();
445    }
446
447    @Override
448    public void goForward() {
449        checkThread();
450        mAwContents.goForward();
451    }
452
453    @Override
454    public boolean canGoBackOrForward(int steps) {
455        checkThread();
456        return mAwContents.canGoBackOrForward(steps);
457    }
458
459    @Override
460    public void goBackOrForward(int steps) {
461        checkThread();
462        mAwContents.goBackOrForward(steps);
463    }
464
465    @Override
466    public boolean isPrivateBrowsingEnabled() {
467        // Not supported in this WebView implementation.
468        return false;
469    }
470
471    @Override
472    public boolean pageUp(boolean top) {
473        checkThread();
474        return mAwContents.pageUp(top);
475    }
476
477    @Override
478    public boolean pageDown(boolean bottom) {
479        checkThread();
480        return mAwContents.pageDown(bottom);
481    }
482
483    @Override
484    public void clearView() {
485        checkThread();
486        UnimplementedWebViewApi.invoke();
487    }
488
489    @Override
490    public Picture capturePicture() {
491        checkThread();
492        return mAwContents.capturePicture();
493    }
494
495    @Override
496    public void exportToPdf(ParcelFileDescriptor fd, PrintAttributes attributes,
497            ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal)
498            throws java.io.IOException {
499        checkThread();
500        // We convert frameworks attributes to an android_webview specific print attributes
501        // so we do not tie upstreaming android_webview changes to installation of correct
502        // SDK to try bots.
503        if (attributes == null) {
504            throw new IllegalArgumentException("attributes cannot be null");
505        }
506        if (attributes.getMediaSize() == null) {
507            throw new  IllegalArgumentException("attributes must specify a media size");
508        }
509        if (attributes.getResolution() == null) {
510            throw new IllegalArgumentException("attributes must specify print resolution");
511        }
512        if (attributes.getMargins() == null) {
513            throw new IllegalArgumentException("attributes must specify margins");
514        }
515        AwPdfExportAttributes pdfAttributes = new AwPdfExportAttributes();
516        pdfAttributes.pageWidth = attributes.getMediaSize().getWidthMils();
517        pdfAttributes.pageHeight = attributes.getMediaSize().getHeightMils();
518        pdfAttributes.dpi = getPrintDpi(attributes);
519        pdfAttributes.leftMargin = attributes.getMargins().getLeftMils();
520        pdfAttributes.rightMargin = attributes.getMargins().getRightMils();
521        pdfAttributes.topMargin = attributes.getMargins().getTopMils();
522        pdfAttributes.bottomMargin = attributes.getMargins().getBottomMils();
523
524        mAwContents.getPdfExporter().exportToPdf(fd, pdfAttributes, resultCallback,
525                cancellationSignal);
526    }
527
528    private static int getPrintDpi(PrintAttributes attributes) {
529        // TODO(sgurun) android print attributes support horizontal and
530        // vertical DPI. Chrome has only one DPI. Revisit this.
531        int horizontalDpi = attributes.getResolution().getHorizontalDpi();
532        int verticalDpi = attributes.getResolution().getVerticalDpi();
533        if (horizontalDpi != verticalDpi) {
534            Log.w(TAG, "Horizontal and vertical DPIs differ. Using horizontal DPI " +
535                    " hDpi=" + horizontalDpi + " vDPI=" + verticalDpi);
536        }
537        return horizontalDpi;
538    }
539
540    @Override
541    public float getScale() {
542        // No checkThread() as it is mostly thread safe (workaround for b/10652991).
543        return mAwContents.getScale();
544    }
545
546    @Override
547    public void setInitialScale(int scaleInPercent) {
548        checkThread();
549        mAwContents.getSettings().setInitialPageScale(scaleInPercent);
550    }
551
552    @Override
553    public void invokeZoomPicker() {
554        checkThread();
555        mAwContents.invokeZoomPicker();
556    }
557
558    @Override
559    public WebView.HitTestResult getHitTestResult() {
560        checkThread();
561        AwContents.HitTestData data = mAwContents.getLastHitTestResult();
562        mHitTestResult.setType(data.hitTestResultType);
563        mHitTestResult.setExtra(data.hitTestResultExtraData);
564        return mHitTestResult;
565    }
566
567    @Override
568    public void requestFocusNodeHref(Message hrefMsg) {
569        checkThread();
570        mAwContents.requestFocusNodeHref(hrefMsg);
571    }
572
573    @Override
574    public void requestImageRef(Message msg) {
575        checkThread();
576        mAwContents.requestImageRef(msg);
577    }
578
579    @Override
580    public String getUrl() {
581        checkThread();
582        String url =  mAwContents.getUrl();
583        if (url == null || url.trim().isEmpty()) return null;
584        return url;
585    }
586
587    @Override
588    public String getOriginalUrl() {
589        checkThread();
590        String url =  mAwContents.getOriginalUrl();
591        if (url == null || url.trim().isEmpty()) return null;
592        return url;
593    }
594
595    @Override
596    public String getTitle() {
597        checkThread();
598        return mAwContents.getTitle();
599    }
600
601    @Override
602    public Bitmap getFavicon() {
603        checkThread();
604        return mAwContents.getFavicon();
605    }
606
607    @Override
608    public String getTouchIconUrl() {
609        // Intentional no-op: hidden method on WebView.
610        return null;
611    }
612
613    @Override
614    public int getProgress() {
615        // No checkThread() because the value is cached java side (workaround for b/10533304).
616        return mAwContents.getMostRecentProgress();
617    }
618
619    @Override
620    public int getContentHeight() {
621        // No checkThread() as it is mostly thread safe (workaround for b/10594869).
622        return mAwContents.getContentHeightCss();
623    }
624
625    @Override
626    public int getContentWidth() {
627        checkThread();
628        return mAwContents.getContentWidthCss();
629    }
630
631    @Override
632    public void pauseTimers() {
633        checkThread();
634        mAwContents.pauseTimers();
635    }
636
637    @Override
638    public void resumeTimers() {
639        checkThread();
640        mAwContents.resumeTimers();
641    }
642
643    @Override
644    public void onPause() {
645        checkThread();
646        mAwContents.onPause();
647    }
648
649    @Override
650    public void onResume() {
651        checkThread();
652        mAwContents.onResume();
653    }
654
655    @Override
656    public boolean isPaused() {
657        checkThread();
658        return mAwContents.isPaused();
659    }
660
661    @Override
662    public void freeMemory() {
663        checkThread();
664        // Intentional no-op. Memory is managed automatically by Chromium.
665    }
666
667    @Override
668    public void clearCache(boolean includeDiskFiles) {
669        checkThread();
670        mAwContents.clearCache(includeDiskFiles);
671    }
672
673    /**
674     * This is a poorly named method, but we keep it for historical reasons.
675     */
676    @Override
677    public void clearFormData() {
678        checkThread();
679        mAwContents.hideAutofillPopup();
680    }
681
682    @Override
683    public void clearHistory() {
684        checkThread();
685        mAwContents.clearHistory();
686    }
687
688    @Override
689    public void clearSslPreferences() {
690        checkThread();
691        mAwContents.clearSslPreferences();
692    }
693
694    @Override
695    public WebBackForwardList copyBackForwardList() {
696        checkThread();
697        return new WebBackForwardListChromium(
698                mAwContents.getNavigationHistory());
699    }
700
701    @Override
702    public void setFindListener(WebView.FindListener listener) {
703        checkThread();
704        mContentsClientAdapter.setFindListener(listener);
705    }
706
707    @Override
708    public void findNext(boolean forwards) {
709        checkThread();
710        mAwContents.findNext(forwards);
711    }
712
713    @Override
714    public int findAll(String searchString) {
715        checkThread();
716        mAwContents.findAllAsync(searchString);
717        return 0;
718    }
719
720    @Override
721    public void findAllAsync(String searchString) {
722        checkThread();
723        mAwContents.findAllAsync(searchString);
724    }
725
726    @Override
727    public boolean showFindDialog(String text, boolean showIme) {
728        checkThread();
729        if (mWebView.getParent() == null) {
730            return false;
731        }
732
733        FindActionModeCallback findAction = new FindActionModeCallback(mWebView.getContext());
734        if (findAction == null) {
735            return false;
736        }
737
738        mWebView.startActionMode(findAction);
739        findAction.setWebView(mWebView);
740        if (showIme) {
741            findAction.showSoftInput();
742        }
743
744        if (text != null) {
745            findAction.setText(text);
746            findAction.findAll();
747        }
748
749        return true;
750    }
751
752    @Override
753    public void notifyFindDialogDismissed() {
754        checkThread();
755        clearMatches();
756    }
757
758    @Override
759    public void clearMatches() {
760        checkThread();
761        mAwContents.clearMatches();
762    }
763
764    @Override
765    public void documentHasImages(Message response) {
766        checkThread();
767        mAwContents.documentHasImages(response);
768    }
769
770    @Override
771    public void setWebViewClient(WebViewClient client) {
772        checkThread();
773        mContentsClientAdapter.setWebViewClient(client);
774    }
775
776    @Override
777    public void setDownloadListener(DownloadListener listener) {
778        checkThread();
779        mContentsClientAdapter.setDownloadListener(listener);
780    }
781
782    @Override
783    public void setWebChromeClient(WebChromeClient client) {
784        checkThread();
785        mContentsClientAdapter.setWebChromeClient(client);
786    }
787
788    @Override
789    public void setPictureListener(WebView.PictureListener listener) {
790        checkThread();
791        mContentsClientAdapter.setPictureListener(listener);
792        mAwContents.enableOnNewPicture(listener != null,
793                mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2);
794    }
795
796    @Override
797    public void addJavascriptInterface(Object obj, String interfaceName) {
798        checkThread();
799        Class<? extends Annotation> requiredAnnotation = null;
800        if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
801           requiredAnnotation = JavascriptInterface.class;
802        }
803        mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation);
804    }
805
806    @Override
807    public void removeJavascriptInterface(String interfaceName) {
808        checkThread();
809        mAwContents.removeJavascriptInterface(interfaceName);
810    }
811
812    @Override
813    public WebSettings getSettings() {
814        return mWebSettings;
815    }
816
817    @Override
818    public void setMapTrackballToArrowKeys(boolean setMap) {
819        checkThread();
820        // This is a deprecated API: intentional no-op.
821    }
822
823    @Override
824    public void flingScroll(int vx, int vy) {
825        checkThread();
826        mAwContents.flingScroll(vx, vy);
827    }
828
829    @Override
830    public View getZoomControls() {
831        checkThread();
832        // This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed
833        // to stop very out-dated applications from crashing.
834        Log.w(TAG, "WebView doesn't support getZoomControls");
835        return mAwContents.getSettings().supportZoom() ? new View(mWebView.getContext()) : null;
836    }
837
838    @Override
839    public boolean canZoomIn() {
840        checkThread();
841        return mAwContents.canZoomIn();
842    }
843
844    @Override
845    public boolean canZoomOut() {
846        checkThread();
847        return mAwContents.canZoomOut();
848    }
849
850    @Override
851    public boolean zoomIn() {
852        checkThread();
853        return mAwContents.zoomIn();
854    }
855
856    @Override
857    public boolean zoomOut() {
858        checkThread();
859        return mAwContents.zoomOut();
860    }
861
862    @Override
863    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
864        UnimplementedWebViewApi.invoke();
865    }
866
867    @Override
868    public View findHierarchyView(String className, int hashCode) {
869        UnimplementedWebViewApi.invoke();
870        return null;
871    }
872
873    // WebViewProvider glue methods ---------------------------------------------------------------
874
875    @Override
876    // This needs to be kept thread safe!
877    public WebViewProvider.ViewDelegate getViewDelegate() {
878        return this;
879    }
880
881    @Override
882    public WebViewProvider.ScrollDelegate getScrollDelegate() {
883        checkThread();
884        return this;
885    }
886
887
888    // WebViewProvider.ViewDelegate implementation ------------------------------------------------
889
890    // TODO: remove from WebViewProvider and use default implementation from
891    // ViewGroup.
892    // @Override
893    public boolean shouldDelayChildPressedState() {
894        checkThread();
895        return true;
896    }
897
898//    @Override
899    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
900        checkThread();
901        return mAwContents.getAccessibilityNodeProvider();
902    }
903
904    @Override
905    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
906        checkThread();
907        mAwContents.onInitializeAccessibilityNodeInfo(info);
908    }
909
910    @Override
911    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
912        checkThread();
913        mAwContents.onInitializeAccessibilityEvent(event);
914    }
915
916    @Override
917    public boolean performAccessibilityAction(int action, Bundle arguments) {
918        checkThread();
919        if (mAwContents.supportsAccessibilityAction(action)) {
920            return mAwContents.performAccessibilityAction(action, arguments);
921        }
922        return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
923    }
924
925    @Override
926    public void setOverScrollMode(int mode) {
927        checkThread();
928        // This gets called from the android.view.View c'tor that WebView inherits from. This
929        // causes the method to be called when mAwContents == null.
930        // It's safe to ignore these calls however since AwContents will read the current value of
931        // this setting when it's created.
932        if (mAwContents != null) {
933            mAwContents.setOverScrollMode(mode);
934        }
935    }
936
937    @Override
938    public void setScrollBarStyle(int style) {
939        checkThread();
940        mAwContents.setScrollBarStyle(style);
941    }
942
943    @Override
944    public void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
945                                        int l, int t, int r, int b) {
946        checkThread();
947        // WebViewClassic was overriding this method to handle rubberband over-scroll. Since
948        // WebViewChromium doesn't support that the vanilla implementation of this method can be
949        // used.
950        mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
951    }
952
953    @Override
954    public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
955        checkThread();
956        mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
957    }
958
959    @Override
960    public void onWindowVisibilityChanged(int visibility) {
961        checkThread();
962        mAwContents.onWindowVisibilityChanged(visibility);
963    }
964
965    @Override
966    public void onDraw(Canvas canvas) {
967        checkThread();
968        mAwContents.onDraw(canvas);
969    }
970
971    @Override
972    public void setLayoutParams(ViewGroup.LayoutParams layoutParams) {
973        checkThread();
974        // TODO: This is the minimum implementation for HTMLViewer
975        // bringup. Likely will need to go up to ContentViewCore for
976        // a complete implementation.
977        mWebViewPrivate.super_setLayoutParams(layoutParams);
978    }
979
980    @Override
981    public boolean performLongClick() {
982        checkThread();
983        return mWebViewPrivate.super_performLongClick();
984    }
985
986    @Override
987    public void onConfigurationChanged(Configuration newConfig) {
988        checkThread();
989        mAwContents.onConfigurationChanged(newConfig);
990    }
991
992    @Override
993    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
994        checkThread();
995        return mAwContents.onCreateInputConnection(outAttrs);
996    }
997
998    @Override
999    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
1000        checkThread();
1001        UnimplementedWebViewApi.invoke();
1002        return false;
1003    }
1004
1005    @Override
1006    public boolean onKeyDown(int keyCode, KeyEvent event) {
1007        checkThread();
1008        UnimplementedWebViewApi.invoke();
1009        return false;
1010    }
1011
1012    @Override
1013    public boolean onKeyUp(int keyCode, KeyEvent event) {
1014        checkThread();
1015        return mAwContents.onKeyUp(keyCode, event);
1016    }
1017
1018    @Override
1019    public void onAttachedToWindow() {
1020        checkThread();
1021        mAwContents.onAttachedToWindow();
1022    }
1023
1024    @Override
1025    public void onDetachedFromWindow() {
1026        checkThread();
1027        mAwContents.onDetachedFromWindow();
1028        if (mGLfunctor != null) {
1029            mGLfunctor.detach();
1030        }
1031    }
1032
1033    @Override
1034    public void onVisibilityChanged(View changedView, int visibility) {
1035        checkThread();
1036        // The AwContents will find out the container view visibility before the first draw so we
1037        // can safely ignore onVisibilityChanged callbacks that happen before init().
1038        if (mAwContents != null) {
1039            mAwContents.onVisibilityChanged(changedView, visibility);
1040        }
1041    }
1042
1043    @Override
1044    public void onWindowFocusChanged(boolean hasWindowFocus) {
1045        checkThread();
1046        mAwContents.onWindowFocusChanged(hasWindowFocus);
1047    }
1048
1049    @Override
1050    public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
1051        checkThread();
1052        mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
1053    }
1054
1055    @Override
1056    public boolean setFrame(int left, int top, int right, int bottom) {
1057        // TODO(joth): This is the minimum implementation for initial
1058        // bringup. Likely will need to go up to AwContents for a complete
1059        // implementation, e.g. setting the compositor visible region (to
1060        // avoid painting tiles that are offscreen due to the view's position).
1061        checkThread();
1062        return mWebViewPrivate.super_setFrame(left, top, right, bottom);
1063    }
1064
1065    @Override
1066    public void onSizeChanged(int w, int h, int ow, int oh) {
1067        checkThread();
1068        mAwContents.onSizeChanged(w, h, ow, oh);
1069    }
1070
1071    @Override
1072    public void onScrollChanged(int l, int t, int oldl, int oldt) {
1073        checkThread();
1074    }
1075
1076    @Override
1077    public boolean dispatchKeyEvent(KeyEvent event) {
1078        checkThread();
1079        return mAwContents.dispatchKeyEvent(event);
1080    }
1081
1082    @Override
1083    public boolean onTouchEvent(MotionEvent ev) {
1084        checkThread();
1085        return mAwContents.onTouchEvent(ev);
1086    }
1087
1088    @Override
1089    public boolean onHoverEvent(MotionEvent event) {
1090        checkThread();
1091        return mAwContents.onHoverEvent(event);
1092    }
1093
1094    @Override
1095    public boolean onGenericMotionEvent(MotionEvent event) {
1096        checkThread();
1097        return mAwContents.onGenericMotionEvent(event);
1098    }
1099
1100    @Override
1101    public boolean onTrackballEvent(MotionEvent ev) {
1102        checkThread();
1103        // Trackball event not handled, which eventually gets converted to DPAD keyevents
1104        return false;
1105    }
1106
1107    @Override
1108    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
1109        checkThread();
1110        mAwContents.requestFocus();
1111        return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
1112    }
1113
1114    @Override
1115    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1116        checkThread();
1117        mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
1118    }
1119
1120    @Override
1121    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
1122        checkThread();
1123        UnimplementedWebViewApi.invoke();
1124        return false;
1125    }
1126
1127    @Override
1128    public void setBackgroundColor(final int color) {
1129        if (ThreadUtils.runningOnUiThread()) {
1130            mAwContents.setBackgroundColor(color);
1131        } else {
1132            // Disallowed in WebView API for apps targetting a new SDK
1133            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
1134            ThreadUtils.postOnUiThread(new Runnable() {
1135                @Override
1136                public void run() {
1137                    mAwContents.setBackgroundColor(color);
1138                }
1139            });
1140        }
1141    }
1142
1143    @Override
1144    public void setLayerType(int layerType, Paint paint) {
1145        checkThread();
1146        UnimplementedWebViewApi.invoke();
1147    }
1148
1149    @Override
1150    public void preDispatchDraw(Canvas canvas) {
1151        checkThread();
1152        // TODO(leandrogracia): remove this method from WebViewProvider if we think
1153        // we won't need it again.
1154    }
1155
1156    // WebViewProvider.ScrollDelegate implementation ----------------------------------------------
1157
1158    @Override
1159    public int computeHorizontalScrollRange() {
1160        checkThread();
1161        return mAwContents.computeHorizontalScrollRange();
1162    }
1163
1164    @Override
1165    public int computeHorizontalScrollOffset() {
1166        checkThread();
1167        return mAwContents.computeHorizontalScrollOffset();
1168    }
1169
1170    @Override
1171    public int computeVerticalScrollRange() {
1172        checkThread();
1173        return mAwContents.computeVerticalScrollRange();
1174    }
1175
1176    @Override
1177    public int computeVerticalScrollOffset() {
1178        checkThread();
1179        return mAwContents.computeVerticalScrollOffset();
1180    }
1181
1182    @Override
1183    public int computeVerticalScrollExtent() {
1184        checkThread();
1185        return mAwContents.computeVerticalScrollExtent();
1186    }
1187
1188    @Override
1189    public void computeScroll() {
1190        checkThread();
1191        mAwContents.computeScroll();
1192    }
1193
1194    // AwContents.InternalAccessDelegate implementation --------------------------------------
1195    private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
1196        @Override
1197        public boolean drawChild(Canvas arg0, View arg1, long arg2) {
1198            UnimplementedWebViewApi.invoke();
1199            return false;
1200        }
1201
1202        @Override
1203        public boolean super_onKeyUp(int arg0, KeyEvent arg1) {
1204            UnimplementedWebViewApi.invoke();
1205            return false;
1206        }
1207
1208        @Override
1209        public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) {
1210            UnimplementedWebViewApi.invoke();
1211            return false;
1212        }
1213
1214        @Override
1215        public boolean super_dispatchKeyEvent(KeyEvent event) {
1216            return mWebViewPrivate.super_dispatchKeyEvent(event);
1217        }
1218
1219        @Override
1220        public boolean super_onGenericMotionEvent(MotionEvent arg0) {
1221            UnimplementedWebViewApi.invoke();
1222            return false;
1223        }
1224
1225        @Override
1226        public void super_onConfigurationChanged(Configuration arg0) {
1227            UnimplementedWebViewApi.invoke();
1228        }
1229
1230        @Override
1231        public int super_getScrollBarStyle() {
1232            return mWebViewPrivate.super_getScrollBarStyle();
1233        }
1234
1235        @Override
1236        public boolean awakenScrollBars() {
1237            mWebViewPrivate.awakenScrollBars(0);
1238            // TODO: modify the WebView.PrivateAccess to provide a return value.
1239            return true;
1240        }
1241
1242        @Override
1243        public boolean super_awakenScrollBars(int arg0, boolean arg1) {
1244            // TODO: need method on WebView.PrivateAccess?
1245            UnimplementedWebViewApi.invoke();
1246            return false;
1247        }
1248
1249        @Override
1250        public void onScrollChanged(int l, int t, int oldl, int oldt) {
1251            mWebViewPrivate.setScrollXRaw(l);
1252            mWebViewPrivate.setScrollYRaw(t);
1253            mWebViewPrivate.onScrollChanged(l, t, oldl, oldt);
1254        }
1255
1256        @Override
1257        public void overScrollBy(int deltaX, int deltaY,
1258            int scrollX, int scrollY,
1259            int scrollRangeX, int scrollRangeY,
1260            int maxOverScrollX, int maxOverScrollY,
1261            boolean isTouchEvent) {
1262            mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY,
1263                    scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
1264        }
1265
1266        @Override
1267        public void super_scrollTo(int scrollX, int scrollY) {
1268            mWebViewPrivate.super_scrollTo(scrollX, scrollY);
1269        }
1270
1271        @Override
1272        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
1273            mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
1274        }
1275
1276        @Override
1277        public boolean requestDrawGL(Canvas canvas) {
1278            if (mGLfunctor == null) {
1279                mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext());
1280            }
1281            return mGLfunctor.requestDrawGL((HardwareCanvas)canvas, mWebView.getViewRootImpl());
1282        }
1283    }
1284}
1285