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