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