WebViewChromium.java revision e80eedd97cf65f058960f6d07cdc7c1bc457035f
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        if (!ThreadUtils.runningOnUiThread()) {
420            ThreadUtils.postOnUiThread(new Runnable() {
421                @Override
422                public void run() {
423                    stopLoading();
424                }
425            });
426            return;
427        }
428
429        mAwContents.stopLoading();
430    }
431
432    @Override
433    public void reload() {
434        checkThread();
435        mAwContents.reload();
436    }
437
438    @Override
439    public boolean canGoBack() {
440        checkThread();
441        return mAwContents.canGoBack();
442    }
443
444    @Override
445    public void goBack() {
446        checkThread();
447        mAwContents.goBack();
448    }
449
450    @Override
451    public boolean canGoForward() {
452        checkThread();
453        return mAwContents.canGoForward();
454    }
455
456    @Override
457    public void goForward() {
458        checkThread();
459        mAwContents.goForward();
460    }
461
462    @Override
463    public boolean canGoBackOrForward(int steps) {
464        checkThread();
465        return mAwContents.canGoBackOrForward(steps);
466    }
467
468    @Override
469    public void goBackOrForward(int steps) {
470        checkThread();
471        mAwContents.goBackOrForward(steps);
472    }
473
474    @Override
475    public boolean isPrivateBrowsingEnabled() {
476        // Not supported in this WebView implementation.
477        return false;
478    }
479
480    @Override
481    public boolean pageUp(boolean top) {
482        checkThread();
483        return mAwContents.pageUp(top);
484    }
485
486    @Override
487    public boolean pageDown(boolean bottom) {
488        checkThread();
489        return mAwContents.pageDown(bottom);
490    }
491
492    @Override
493    public void clearView() {
494        checkThread();
495        UnimplementedWebViewApi.invoke();
496    }
497
498    @Override
499    public Picture capturePicture() {
500        checkThread();
501        return mAwContents.capturePicture();
502    }
503
504    @Override
505    public void exportToPdf(ParcelFileDescriptor fd, PrintAttributes attributes,
506            ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal)
507            throws java.io.IOException {
508        checkThread();
509        // We convert frameworks attributes to an android_webview specific print attributes
510        // so we do not tie upstreaming android_webview changes to installation of correct
511        // SDK to try bots.
512        if (attributes == null) {
513            throw new IllegalArgumentException("attributes cannot be null");
514        }
515        if (attributes.getMediaSize() == null) {
516            throw new  IllegalArgumentException("attributes must specify a media size");
517        }
518        if (attributes.getResolution() == null) {
519            throw new IllegalArgumentException("attributes must specify print resolution");
520        }
521        if (attributes.getMargins() == null) {
522            throw new IllegalArgumentException("attributes must specify margins");
523        }
524        AwPdfExportAttributes pdfAttributes = new AwPdfExportAttributes();
525        pdfAttributes.pageWidth = attributes.getMediaSize().getWidthMils();
526        pdfAttributes.pageHeight = attributes.getMediaSize().getHeightMils();
527        pdfAttributes.dpi = getPrintDpi(attributes);
528        pdfAttributes.leftMargin = attributes.getMargins().getLeftMils();
529        pdfAttributes.rightMargin = attributes.getMargins().getRightMils();
530        pdfAttributes.topMargin = attributes.getMargins().getTopMils();
531        pdfAttributes.bottomMargin = attributes.getMargins().getBottomMils();
532
533        mAwContents.getPdfExporter().exportToPdf(fd, pdfAttributes, resultCallback,
534                cancellationSignal);
535    }
536
537    private static int getPrintDpi(PrintAttributes attributes) {
538        // TODO(sgurun) android print attributes support horizontal and
539        // vertical DPI. Chrome has only one DPI. Revisit this.
540        int horizontalDpi = attributes.getResolution().getHorizontalDpi();
541        int verticalDpi = attributes.getResolution().getVerticalDpi();
542        if (horizontalDpi != verticalDpi) {
543            Log.w(TAG, "Horizontal and vertical DPIs differ. Using horizontal DPI " +
544                    " hDpi=" + horizontalDpi + " vDPI=" + verticalDpi);
545        }
546        return horizontalDpi;
547    }
548
549    @Override
550    public float getScale() {
551        // No checkThread() as it is mostly thread safe (workaround for b/10652991).
552        return mAwContents.getScale();
553    }
554
555    @Override
556    public void setInitialScale(int scaleInPercent) {
557        checkThread();
558        mAwContents.getSettings().setInitialPageScale(scaleInPercent);
559    }
560
561    @Override
562    public void invokeZoomPicker() {
563        checkThread();
564        mAwContents.invokeZoomPicker();
565    }
566
567    @Override
568    public WebView.HitTestResult getHitTestResult() {
569        checkThread();
570        AwContents.HitTestData data = mAwContents.getLastHitTestResult();
571        mHitTestResult.setType(data.hitTestResultType);
572        mHitTestResult.setExtra(data.hitTestResultExtraData);
573        return mHitTestResult;
574    }
575
576    @Override
577    public void requestFocusNodeHref(Message hrefMsg) {
578        checkThread();
579        mAwContents.requestFocusNodeHref(hrefMsg);
580    }
581
582    @Override
583    public void requestImageRef(Message msg) {
584        checkThread();
585        mAwContents.requestImageRef(msg);
586    }
587
588    @Override
589    public String getUrl() {
590        checkThread();
591        String url =  mAwContents.getUrl();
592        if (url == null || url.trim().isEmpty()) return null;
593        return url;
594    }
595
596    @Override
597    public String getOriginalUrl() {
598        checkThread();
599        String url =  mAwContents.getOriginalUrl();
600        if (url == null || url.trim().isEmpty()) return null;
601        return url;
602    }
603
604    @Override
605    public String getTitle() {
606        checkThread();
607        return mAwContents.getTitle();
608    }
609
610    @Override
611    public Bitmap getFavicon() {
612        checkThread();
613        return mAwContents.getFavicon();
614    }
615
616    @Override
617    public String getTouchIconUrl() {
618        // Intentional no-op: hidden method on WebView.
619        return null;
620    }
621
622    @Override
623    public int getProgress() {
624        // No checkThread() because the value is cached java side (workaround for b/10533304).
625        return mAwContents.getMostRecentProgress();
626    }
627
628    @Override
629    public int getContentHeight() {
630        // No checkThread() as it is mostly thread safe (workaround for b/10594869).
631        return mAwContents.getContentHeightCss();
632    }
633
634    @Override
635    public int getContentWidth() {
636        checkThread();
637        return mAwContents.getContentWidthCss();
638    }
639
640    @Override
641    public void pauseTimers() {
642        checkThread();
643        mAwContents.pauseTimers();
644    }
645
646    @Override
647    public void resumeTimers() {
648        checkThread();
649        mAwContents.resumeTimers();
650    }
651
652    @Override
653    public void onPause() {
654        checkThread();
655        mAwContents.onPause();
656    }
657
658    @Override
659    public void onResume() {
660        checkThread();
661        mAwContents.onResume();
662    }
663
664    @Override
665    public boolean isPaused() {
666        checkThread();
667        return mAwContents.isPaused();
668    }
669
670    @Override
671    public void freeMemory() {
672        checkThread();
673        // Intentional no-op. Memory is managed automatically by Chromium.
674    }
675
676    @Override
677    public void clearCache(boolean includeDiskFiles) {
678        checkThread();
679        mAwContents.clearCache(includeDiskFiles);
680    }
681
682    /**
683     * This is a poorly named method, but we keep it for historical reasons.
684     */
685    @Override
686    public void clearFormData() {
687        checkThread();
688        mAwContents.hideAutofillPopup();
689    }
690
691    @Override
692    public void clearHistory() {
693        checkThread();
694        mAwContents.clearHistory();
695    }
696
697    @Override
698    public void clearSslPreferences() {
699        checkThread();
700        mAwContents.clearSslPreferences();
701    }
702
703    @Override
704    public WebBackForwardList copyBackForwardList() {
705        checkThread();
706        return new WebBackForwardListChromium(
707                mAwContents.getNavigationHistory());
708    }
709
710    @Override
711    public void setFindListener(WebView.FindListener listener) {
712        checkThread();
713        mContentsClientAdapter.setFindListener(listener);
714    }
715
716    @Override
717    public void findNext(boolean forwards) {
718        checkThread();
719        mAwContents.findNext(forwards);
720    }
721
722    @Override
723    public int findAll(String searchString) {
724        checkThread();
725        mAwContents.findAllAsync(searchString);
726        return 0;
727    }
728
729    @Override
730    public void findAllAsync(String searchString) {
731        checkThread();
732        mAwContents.findAllAsync(searchString);
733    }
734
735    @Override
736    public boolean showFindDialog(String text, boolean showIme) {
737        checkThread();
738        if (mWebView.getParent() == null) {
739            return false;
740        }
741
742        FindActionModeCallback findAction = new FindActionModeCallback(mWebView.getContext());
743        if (findAction == null) {
744            return false;
745        }
746
747        mWebView.startActionMode(findAction);
748        findAction.setWebView(mWebView);
749        if (showIme) {
750            findAction.showSoftInput();
751        }
752
753        if (text != null) {
754            findAction.setText(text);
755            findAction.findAll();
756        }
757
758        return true;
759    }
760
761    @Override
762    public void notifyFindDialogDismissed() {
763        checkThread();
764        clearMatches();
765    }
766
767    @Override
768    public void clearMatches() {
769        checkThread();
770        mAwContents.clearMatches();
771    }
772
773    @Override
774    public void documentHasImages(Message response) {
775        checkThread();
776        mAwContents.documentHasImages(response);
777    }
778
779    @Override
780    public void setWebViewClient(WebViewClient client) {
781        checkThread();
782        mContentsClientAdapter.setWebViewClient(client);
783    }
784
785    @Override
786    public void setDownloadListener(DownloadListener listener) {
787        checkThread();
788        mContentsClientAdapter.setDownloadListener(listener);
789    }
790
791    @Override
792    public void setWebChromeClient(WebChromeClient client) {
793        checkThread();
794        mContentsClientAdapter.setWebChromeClient(client);
795    }
796
797    @Override
798    public void setPictureListener(WebView.PictureListener listener) {
799        checkThread();
800        mContentsClientAdapter.setPictureListener(listener);
801        mAwContents.enableOnNewPicture(listener != null,
802                mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2);
803    }
804
805    @Override
806    public void addJavascriptInterface(Object obj, String interfaceName) {
807        checkThread();
808        Class<? extends Annotation> requiredAnnotation = null;
809        if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
810           requiredAnnotation = JavascriptInterface.class;
811        }
812        mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation);
813    }
814
815    @Override
816    public void removeJavascriptInterface(String interfaceName) {
817        checkThread();
818        mAwContents.removeJavascriptInterface(interfaceName);
819    }
820
821    @Override
822    public WebSettings getSettings() {
823        return mWebSettings;
824    }
825
826    @Override
827    public void setMapTrackballToArrowKeys(boolean setMap) {
828        checkThread();
829        // This is a deprecated API: intentional no-op.
830    }
831
832    @Override
833    public void flingScroll(int vx, int vy) {
834        checkThread();
835        mAwContents.flingScroll(vx, vy);
836    }
837
838    @Override
839    public View getZoomControls() {
840        checkThread();
841        // This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed
842        // to stop very out-dated applications from crashing.
843        Log.w(TAG, "WebView doesn't support getZoomControls");
844        return mAwContents.getSettings().supportZoom() ? new View(mWebView.getContext()) : null;
845    }
846
847    @Override
848    public boolean canZoomIn() {
849        checkThread();
850        return mAwContents.canZoomIn();
851    }
852
853    @Override
854    public boolean canZoomOut() {
855        checkThread();
856        return mAwContents.canZoomOut();
857    }
858
859    @Override
860    public boolean zoomIn() {
861        checkThread();
862        return mAwContents.zoomIn();
863    }
864
865    @Override
866    public boolean zoomOut() {
867        checkThread();
868        return mAwContents.zoomOut();
869    }
870
871    @Override
872    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
873        UnimplementedWebViewApi.invoke();
874    }
875
876    @Override
877    public View findHierarchyView(String className, int hashCode) {
878        UnimplementedWebViewApi.invoke();
879        return null;
880    }
881
882    // WebViewProvider glue methods ---------------------------------------------------------------
883
884    @Override
885    // This needs to be kept thread safe!
886    public WebViewProvider.ViewDelegate getViewDelegate() {
887        return this;
888    }
889
890    @Override
891    public WebViewProvider.ScrollDelegate getScrollDelegate() {
892        checkThread();
893        return this;
894    }
895
896
897    // WebViewProvider.ViewDelegate implementation ------------------------------------------------
898
899    // TODO: remove from WebViewProvider and use default implementation from
900    // ViewGroup.
901    // @Override
902    public boolean shouldDelayChildPressedState() {
903        checkThread();
904        return true;
905    }
906
907//    @Override
908    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
909        checkThread();
910        return mAwContents.getAccessibilityNodeProvider();
911    }
912
913    @Override
914    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
915        checkThread();
916        mAwContents.onInitializeAccessibilityNodeInfo(info);
917    }
918
919    @Override
920    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
921        checkThread();
922        mAwContents.onInitializeAccessibilityEvent(event);
923    }
924
925    @Override
926    public boolean performAccessibilityAction(int action, Bundle arguments) {
927        checkThread();
928        if (mAwContents.supportsAccessibilityAction(action)) {
929            return mAwContents.performAccessibilityAction(action, arguments);
930        }
931        return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
932    }
933
934    @Override
935    public void setOverScrollMode(int mode) {
936        checkThread();
937        // This gets called from the android.view.View c'tor that WebView inherits from. This
938        // causes the method to be called when mAwContents == null.
939        // It's safe to ignore these calls however since AwContents will read the current value of
940        // this setting when it's created.
941        if (mAwContents != null) {
942            mAwContents.setOverScrollMode(mode);
943        }
944    }
945
946    @Override
947    public void setScrollBarStyle(int style) {
948        checkThread();
949        mAwContents.setScrollBarStyle(style);
950    }
951
952    @Override
953    public void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
954                                        int l, int t, int r, int b) {
955        checkThread();
956        // WebViewClassic was overriding this method to handle rubberband over-scroll. Since
957        // WebViewChromium doesn't support that the vanilla implementation of this method can be
958        // used.
959        mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
960    }
961
962    @Override
963    public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
964        checkThread();
965        mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
966    }
967
968    @Override
969    public void onWindowVisibilityChanged(int visibility) {
970        checkThread();
971        mAwContents.onWindowVisibilityChanged(visibility);
972    }
973
974    @Override
975    public void onDraw(Canvas canvas) {
976        checkThread();
977        mAwContents.onDraw(canvas);
978    }
979
980    @Override
981    public void setLayoutParams(ViewGroup.LayoutParams layoutParams) {
982        checkThread();
983        // TODO: This is the minimum implementation for HTMLViewer
984        // bringup. Likely will need to go up to ContentViewCore for
985        // a complete implementation.
986        mWebViewPrivate.super_setLayoutParams(layoutParams);
987    }
988
989    @Override
990    public boolean performLongClick() {
991        checkThread();
992        return mWebViewPrivate.super_performLongClick();
993    }
994
995    @Override
996    public void onConfigurationChanged(Configuration newConfig) {
997        checkThread();
998        mAwContents.onConfigurationChanged(newConfig);
999    }
1000
1001    @Override
1002    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
1003        checkThread();
1004        return mAwContents.onCreateInputConnection(outAttrs);
1005    }
1006
1007    @Override
1008    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
1009        checkThread();
1010        UnimplementedWebViewApi.invoke();
1011        return false;
1012    }
1013
1014    @Override
1015    public boolean onKeyDown(int keyCode, KeyEvent event) {
1016        checkThread();
1017        UnimplementedWebViewApi.invoke();
1018        return false;
1019    }
1020
1021    @Override
1022    public boolean onKeyUp(int keyCode, KeyEvent event) {
1023        checkThread();
1024        return mAwContents.onKeyUp(keyCode, event);
1025    }
1026
1027    @Override
1028    public void onAttachedToWindow() {
1029        checkThread();
1030        mAwContents.onAttachedToWindow();
1031    }
1032
1033    @Override
1034    public void onDetachedFromWindow() {
1035        checkThread();
1036        mAwContents.onDetachedFromWindow();
1037        if (mGLfunctor != null) {
1038            mGLfunctor.detach();
1039        }
1040    }
1041
1042    @Override
1043    public void onVisibilityChanged(View changedView, int visibility) {
1044        checkThread();
1045        // The AwContents will find out the container view visibility before the first draw so we
1046        // can safely ignore onVisibilityChanged callbacks that happen before init().
1047        if (mAwContents != null) {
1048            mAwContents.onVisibilityChanged(changedView, visibility);
1049        }
1050    }
1051
1052    @Override
1053    public void onWindowFocusChanged(boolean hasWindowFocus) {
1054        checkThread();
1055        mAwContents.onWindowFocusChanged(hasWindowFocus);
1056    }
1057
1058    @Override
1059    public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
1060        checkThread();
1061        mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
1062    }
1063
1064    @Override
1065    public boolean setFrame(int left, int top, int right, int bottom) {
1066        // TODO(joth): This is the minimum implementation for initial
1067        // bringup. Likely will need to go up to AwContents for a complete
1068        // implementation, e.g. setting the compositor visible region (to
1069        // avoid painting tiles that are offscreen due to the view's position).
1070        checkThread();
1071        return mWebViewPrivate.super_setFrame(left, top, right, bottom);
1072    }
1073
1074    @Override
1075    public void onSizeChanged(int w, int h, int ow, int oh) {
1076        checkThread();
1077        mAwContents.onSizeChanged(w, h, ow, oh);
1078    }
1079
1080    @Override
1081    public void onScrollChanged(int l, int t, int oldl, int oldt) {
1082        checkThread();
1083    }
1084
1085    @Override
1086    public boolean dispatchKeyEvent(KeyEvent event) {
1087        checkThread();
1088        return mAwContents.dispatchKeyEvent(event);
1089    }
1090
1091    @Override
1092    public boolean onTouchEvent(MotionEvent ev) {
1093        checkThread();
1094        return mAwContents.onTouchEvent(ev);
1095    }
1096
1097    @Override
1098    public boolean onHoverEvent(MotionEvent event) {
1099        checkThread();
1100        return mAwContents.onHoverEvent(event);
1101    }
1102
1103    @Override
1104    public boolean onGenericMotionEvent(MotionEvent event) {
1105        checkThread();
1106        return mAwContents.onGenericMotionEvent(event);
1107    }
1108
1109    @Override
1110    public boolean onTrackballEvent(MotionEvent ev) {
1111        checkThread();
1112        // Trackball event not handled, which eventually gets converted to DPAD keyevents
1113        return false;
1114    }
1115
1116    @Override
1117    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
1118        checkThread();
1119        mAwContents.requestFocus();
1120        return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
1121    }
1122
1123    @Override
1124    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1125        checkThread();
1126        mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
1127    }
1128
1129    @Override
1130    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
1131        checkThread();
1132        return mAwContents.requestChildRectangleOnScreen(child, rect, immediate);
1133    }
1134
1135    @Override
1136    public void setBackgroundColor(final int color) {
1137        if (ThreadUtils.runningOnUiThread()) {
1138            mAwContents.setBackgroundColor(color);
1139        } else {
1140            // Disallowed in WebView API for apps targetting a new SDK
1141            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
1142            ThreadUtils.postOnUiThread(new Runnable() {
1143                @Override
1144                public void run() {
1145                    mAwContents.setBackgroundColor(color);
1146                }
1147            });
1148        }
1149    }
1150
1151    @Override
1152    public void setLayerType(int layerType, Paint paint) {
1153        checkThread();
1154        UnimplementedWebViewApi.invoke();
1155    }
1156
1157    @Override
1158    public void preDispatchDraw(Canvas canvas) {
1159        checkThread();
1160        // TODO(leandrogracia): remove this method from WebViewProvider if we think
1161        // we won't need it again.
1162    }
1163
1164    // WebViewProvider.ScrollDelegate implementation ----------------------------------------------
1165
1166    @Override
1167    public int computeHorizontalScrollRange() {
1168        checkThread();
1169        return mAwContents.computeHorizontalScrollRange();
1170    }
1171
1172    @Override
1173    public int computeHorizontalScrollOffset() {
1174        checkThread();
1175        return mAwContents.computeHorizontalScrollOffset();
1176    }
1177
1178    @Override
1179    public int computeVerticalScrollRange() {
1180        checkThread();
1181        return mAwContents.computeVerticalScrollRange();
1182    }
1183
1184    @Override
1185    public int computeVerticalScrollOffset() {
1186        checkThread();
1187        return mAwContents.computeVerticalScrollOffset();
1188    }
1189
1190    @Override
1191    public int computeVerticalScrollExtent() {
1192        checkThread();
1193        return mAwContents.computeVerticalScrollExtent();
1194    }
1195
1196    @Override
1197    public void computeScroll() {
1198        checkThread();
1199        mAwContents.computeScroll();
1200    }
1201
1202    // AwContents.InternalAccessDelegate implementation --------------------------------------
1203    private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
1204        @Override
1205        public boolean drawChild(Canvas arg0, View arg1, long arg2) {
1206            UnimplementedWebViewApi.invoke();
1207            return false;
1208        }
1209
1210        @Override
1211        public boolean super_onKeyUp(int arg0, KeyEvent arg1) {
1212            UnimplementedWebViewApi.invoke();
1213            return false;
1214        }
1215
1216        @Override
1217        public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) {
1218            UnimplementedWebViewApi.invoke();
1219            return false;
1220        }
1221
1222        @Override
1223        public boolean super_dispatchKeyEvent(KeyEvent event) {
1224            return mWebViewPrivate.super_dispatchKeyEvent(event);
1225        }
1226
1227        @Override
1228        public boolean super_onGenericMotionEvent(MotionEvent arg0) {
1229            UnimplementedWebViewApi.invoke();
1230            return false;
1231        }
1232
1233        @Override
1234        public void super_onConfigurationChanged(Configuration arg0) {
1235            UnimplementedWebViewApi.invoke();
1236        }
1237
1238        @Override
1239        public int super_getScrollBarStyle() {
1240            return mWebViewPrivate.super_getScrollBarStyle();
1241        }
1242
1243        @Override
1244        public boolean awakenScrollBars() {
1245            mWebViewPrivate.awakenScrollBars(0);
1246            // TODO: modify the WebView.PrivateAccess to provide a return value.
1247            return true;
1248        }
1249
1250        @Override
1251        public boolean super_awakenScrollBars(int arg0, boolean arg1) {
1252            // TODO: need method on WebView.PrivateAccess?
1253            UnimplementedWebViewApi.invoke();
1254            return false;
1255        }
1256
1257        @Override
1258        public void onScrollChanged(int l, int t, int oldl, int oldt) {
1259            mWebViewPrivate.setScrollXRaw(l);
1260            mWebViewPrivate.setScrollYRaw(t);
1261            mWebViewPrivate.onScrollChanged(l, t, oldl, oldt);
1262        }
1263
1264        @Override
1265        public void overScrollBy(int deltaX, int deltaY,
1266            int scrollX, int scrollY,
1267            int scrollRangeX, int scrollRangeY,
1268            int maxOverScrollX, int maxOverScrollY,
1269            boolean isTouchEvent) {
1270            mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY,
1271                    scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
1272        }
1273
1274        @Override
1275        public void super_scrollTo(int scrollX, int scrollY) {
1276            mWebViewPrivate.super_scrollTo(scrollX, scrollY);
1277        }
1278
1279        @Override
1280        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
1281            mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
1282        }
1283
1284        @Override
1285        public boolean requestDrawGL(Canvas canvas) {
1286            if (mGLfunctor == null) {
1287                mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext());
1288            }
1289            return mGLfunctor.requestDrawGL((HardwareCanvas)canvas, mWebView.getViewRootImpl());
1290        }
1291    }
1292}
1293