WebViewChromium.java revision a622984698ea393f81ce968ec87bab75087af7dc
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.Message;
30import android.print.PrintDocumentAdapter;
31import android.text.TextUtils;
32import android.util.Base64;
33import android.util.Log;
34import android.view.HardwareCanvas;
35import android.view.KeyEvent;
36import android.view.MotionEvent;
37import android.view.View;
38import android.view.ViewGroup;
39import android.view.accessibility.AccessibilityEvent;
40import android.view.accessibility.AccessibilityNodeInfo;
41import android.view.accessibility.AccessibilityNodeProvider;
42import android.view.inputmethod.EditorInfo;
43import android.view.inputmethod.InputConnection;
44import android.webkit.DownloadListener;
45import android.webkit.FindActionModeCallback;
46import android.webkit.JavascriptInterface;
47import android.webkit.ValueCallback;
48import android.webkit.WebBackForwardList;
49import android.webkit.WebChromeClient;
50import android.webkit.WebSettings;
51import android.webkit.WebView;
52import android.webkit.WebViewClient;
53import android.webkit.WebViewProvider;
54import android.widget.TextView;
55
56import org.chromium.android_webview.AwBrowserContext;
57import org.chromium.android_webview.AwContents;
58import org.chromium.android_webview.AwLayoutSizer;
59import org.chromium.android_webview.AwPdfExportAttributes;
60import org.chromium.android_webview.AwPrintDocumentAdapter;
61import org.chromium.base.ThreadUtils;
62import org.chromium.content.browser.LoadUrlParams;
63import org.chromium.net.NetworkChangeNotifier;
64
65import java.io.BufferedWriter;
66import java.io.File;
67import java.lang.annotation.Annotation;
68import java.util.concurrent.Callable;
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    private static <T> T runBlockingFuture(FutureTask<T> task) {
127        if (ThreadUtils.runningOnUiThread()) {
128            throw new IllegalStateException("This method should only be called off the UI thread");
129        }
130        ThreadUtils.postOnUiThread(task);
131        try {
132            return task.get(4, TimeUnit.SECONDS);
133        } catch (Exception e) { // Timeout is one of the possible exceptions here
134            throw new RuntimeException("Probable deadlock detected due to WebView API being called "
135                    + "on incorrect thread while the UI thread is blocked.", e);
136        }
137    }
138
139    // We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin
140    // deadlocks.
141    // Do not call this method while on the UI thread!
142    private void runVoidTaskOnUiThreadBlocking(Runnable r) {
143        FutureTask<Void> task = new FutureTask<Void>(r, null);
144        runBlockingFuture(task);
145    }
146
147    private static <T> T runOnUiThreadBlocking(Callable<T> c) {
148        return runBlockingFuture(new FutureTask<T>(c));
149    }
150
151    // WebViewProvider methods --------------------------------------------------------------------
152
153    @Override
154    public void init(final Map<String, Object> javaScriptInterfaces,
155            final boolean privateBrowsing) {
156        if (!ThreadUtils.runningOnUiThread()) {
157            runVoidTaskOnUiThreadBlocking(new Runnable() {
158                @Override
159                public void run() {
160                    init(javaScriptInterfaces, privateBrowsing);
161                }
162            });
163            return;
164        }
165        // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
166        // so is ignored. TODO: remove it from WebViewProvider.
167        final boolean isAccessFromFileURLsGrantedByDefault =
168                mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
169        final boolean areLegacyQuirksEnabled =
170                mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
171        mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
172        mAwContents = new AwContents(mBrowserContext, mWebView, new InternalAccessAdapter(),
173                mContentsClientAdapter, isAccessFromFileURLsGrantedByDefault,
174                new AwLayoutSizer(), areLegacyQuirksEnabled);
175        mWebSettings = new ContentSettingsAdapter(mAwContents.getSettings());
176
177        if (privateBrowsing) {
178            final String msg = "Private browsing is not supported in WebView.";
179                       if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
180                throw new IllegalArgumentException(msg);
181            } else {
182                Log.w(TAG, msg);
183                // Intentionally irreversibly disable the webview instance, so that private
184                // user data cannot leak through misuse of a non-privateBrowing WebView instance.
185                // Can't just null out mAwContents as we never null-check it before use.
186                mAwContents.destroy();
187                TextView warningLabel = new TextView(mWebView.getContext());
188                warningLabel.setText(mWebView.getContext().getString(
189                        com.android.internal.R.string.webviewchromium_private_browsing_warning));
190                mWebView.addView(warningLabel);
191            }
192        }
193
194    }
195
196    private RuntimeException createThreadException() {
197        return new IllegalStateException(
198                "Calling View methods on another thread than the UI thread.");
199    }
200
201    //  Intentionally not static, as no need to check thread on static methods
202    private void checkThread() {
203        if (!ThreadUtils.runningOnUiThread()) {
204            final RuntimeException threadViolation = createThreadException();
205            ThreadUtils.postOnUiThread(new Runnable() {
206                @Override
207                public void run() {
208                    throw threadViolation;
209                }
210            });
211            throw createThreadException();
212        }
213    }
214
215    @Override
216    public void setHorizontalScrollbarOverlay(final boolean overlay) {
217        if (!ThreadUtils.runningOnUiThread()) {
218            ThreadUtils.postOnUiThread(new Runnable() {
219                @Override
220                public void run() {
221                    setHorizontalScrollbarOverlay(overlay);
222                }
223            });
224            return;
225        }
226        mAwContents.setHorizontalScrollbarOverlay(overlay);
227    }
228
229    @Override
230    public void setVerticalScrollbarOverlay(final boolean overlay) {
231        if (!ThreadUtils.runningOnUiThread()) {
232            ThreadUtils.postOnUiThread(new Runnable() {
233                @Override
234                public void run() {
235                    setVerticalScrollbarOverlay(overlay);
236                }
237            });
238            return;
239        }
240        mAwContents.setVerticalScrollbarOverlay(overlay);
241    }
242
243    @Override
244    public boolean overlayHorizontalScrollbar() {
245        if (!ThreadUtils.runningOnUiThread()) {
246            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
247                @Override
248                public Boolean call() {
249                    return overlayHorizontalScrollbar();
250                }
251            });
252            return ret;
253        }
254        return mAwContents.overlayHorizontalScrollbar();
255    }
256
257    @Override
258    public boolean overlayVerticalScrollbar() {
259        if (!ThreadUtils.runningOnUiThread()) {
260            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
261                @Override
262                public Boolean call() {
263                    return overlayVerticalScrollbar();
264                }
265            });
266            return ret;
267        }
268        return mAwContents.overlayVerticalScrollbar();
269    }
270
271    @Override
272    public int getVisibleTitleHeight() {
273        // This is deprecated in WebView and should always return 0.
274        return 0;
275    }
276
277    @Override
278    public SslCertificate getCertificate() {
279        if (!ThreadUtils.runningOnUiThread()) {
280            SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() {
281                @Override
282                public SslCertificate call() {
283                    return getCertificate();
284                }
285            });
286            return ret;
287        }
288        return mAwContents.getCertificate();
289    }
290
291    @Override
292    public void setCertificate(SslCertificate certificate) {
293        // intentional no-op
294    }
295
296    @Override
297    public void savePassword(String host, String username, String password) {
298        // This is a deprecated API: intentional no-op.
299    }
300
301    @Override
302    public void setHttpAuthUsernamePassword(final String host, final String realm,
303            final String username, final String password) {
304        if (!ThreadUtils.runningOnUiThread()) {
305            ThreadUtils.postOnUiThread(new Runnable() {
306                @Override
307                public void run() {
308                    setHttpAuthUsernamePassword(host, realm, username, password);
309                }
310            });
311            return;
312        }
313        mAwContents.setHttpAuthUsernamePassword(host, realm, username, password);
314    }
315
316    @Override
317    public String[] getHttpAuthUsernamePassword(final String host, final String realm) {
318        if (!ThreadUtils.runningOnUiThread()) {
319            String[] ret = runOnUiThreadBlocking(new Callable<String[]>() {
320                @Override
321                public String[] call() {
322                    return getHttpAuthUsernamePassword(host, realm);
323                }
324            });
325            return ret;
326        }
327        return mAwContents.getHttpAuthUsernamePassword(host, realm);
328    }
329
330    @Override
331    public void destroy() {
332        if (!ThreadUtils.runningOnUiThread()) {
333            ThreadUtils.postOnUiThread(new Runnable() {
334                @Override
335                public void run() {
336                    destroy();
337                }
338            });
339            return;
340        }
341
342        mAwContents.destroy();
343        if (mGLfunctor != null) {
344            mGLfunctor.destroy();
345            mGLfunctor = null;
346        }
347    }
348
349    @Override
350    public void setNetworkAvailable(final boolean networkUp) {
351        // Note that this purely toggles the JS navigator.online property.
352        // It does not in affect chromium or network stack state in any way.
353        if (!ThreadUtils.runningOnUiThread()) {
354            ThreadUtils.postOnUiThread(new Runnable() {
355                @Override
356                public void run() {
357                    setNetworkAvailable(networkUp);
358                }
359            });
360            return;
361        }
362        mAwContents.setNetworkAvailable(networkUp);
363    }
364
365    @Override
366    public WebBackForwardList saveState(final Bundle outState) {
367        if (!ThreadUtils.runningOnUiThread()) {
368            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
369                @Override
370                public WebBackForwardList call() {
371                    return saveState(outState);
372                }
373            });
374            return ret;
375        }
376        if (outState == null) return null;
377        if (!mAwContents.saveState(outState)) return null;
378        return copyBackForwardList();
379    }
380
381    @Override
382    public boolean savePicture(Bundle b, File dest) {
383        // Intentional no-op: hidden method on WebView.
384        return false;
385    }
386
387    @Override
388    public boolean restorePicture(Bundle b, File src) {
389        // Intentional no-op: hidden method on WebView.
390        return false;
391    }
392
393    @Override
394    public WebBackForwardList restoreState(final Bundle inState) {
395        if (!ThreadUtils.runningOnUiThread()) {
396            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
397                @Override
398                public WebBackForwardList call() {
399                    return restoreState(inState);
400                }
401            });
402            return ret;
403        }
404        if (inState == null) return null;
405        if (!mAwContents.restoreState(inState)) return null;
406        return copyBackForwardList();
407    }
408
409    @Override
410    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
411        // TODO: We may actually want to do some sanity checks here (like filter about://chrome).
412
413        // For backwards compatibility, apps targeting less than K will have JS URLs evaluated
414        // directly and any result of the evaluation will not replace the current page content.
415        // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will
416        // have the result of that URL replace the content of the current page.
417        final String JAVASCRIPT_SCHEME = "javascript:";
418        if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
419                url != null && url.startsWith(JAVASCRIPT_SCHEME)) {
420            mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
421                    url.substring(JAVASCRIPT_SCHEME.length()));
422            return;
423        }
424
425        LoadUrlParams params = new LoadUrlParams(url);
426        if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders);
427        loadUrlOnUiThread(params);
428    }
429
430    @Override
431    public void loadUrl(String url) {
432        // Early out to match old WebView implementation
433        if (url == null) {
434            return;
435        }
436        loadUrl(url, null);
437    }
438
439    @Override
440    public void postUrl(String url, byte[] postData) {
441        LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData);
442        Map<String,String> headers = new HashMap<String,String>();
443        headers.put("Content-Type", "application/x-www-form-urlencoded");
444        params.setExtraHeaders(headers);
445        loadUrlOnUiThread(params);
446    }
447
448    private static String fixupMimeType(String mimeType) {
449        return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType;
450    }
451
452    private static String fixupData(String data) {
453        return TextUtils.isEmpty(data) ? "" : data;
454    }
455
456    private static String fixupBase(String url) {
457        return TextUtils.isEmpty(url) ? "about:blank" : url;
458    }
459
460    private static String fixupHistory(String url) {
461        return TextUtils.isEmpty(url) ? "about:blank" : url;
462    }
463
464    private static boolean isBase64Encoded(String encoding) {
465        return "base64".equals(encoding);
466    }
467
468    @Override
469    public void loadData(String data, String mimeType, String encoding) {
470        loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
471                fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
472    }
473
474    @Override
475    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
476            String historyUrl) {
477        data = fixupData(data);
478        mimeType = fixupMimeType(mimeType);
479        LoadUrlParams loadUrlParams;
480        baseUrl = fixupBase(baseUrl);
481        historyUrl = fixupHistory(historyUrl);
482
483        if (baseUrl.startsWith("data:")) {
484            // For backwards compatibility with WebViewClassic, we use the value of |encoding|
485            // as the charset, as long as it's not "base64".
486            boolean isBase64 = isBase64Encoded(encoding);
487            loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
488                    data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
489        } else {
490            // When loading data with a non-data: base URL, the classic WebView would effectively
491            // "dump" that string of data into the WebView without going through regular URL
492            // loading steps such as decoding URL-encoded entities. We achieve this same behavior by
493            // base64 encoding the data that is passed here and then loading that as a data: URL.
494            try {
495                loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
496                        Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
497                        true, baseUrl, historyUrl, "utf-8");
498            } catch (java.io.UnsupportedEncodingException e) {
499                Log.wtf(TAG, "Unable to load data string " + data, e);
500                return;
501            }
502        }
503        loadUrlOnUiThread(loadUrlParams);
504
505        // Data url's with a base url will be resolved in Blink, and not cause an onPageStarted
506        // event to be sent. Sending the callback directly from here.
507        final String finalBaseUrl = loadUrlParams.getBaseUrl();
508        ThreadUtils.postOnUiThread(new Runnable() {
509            @Override
510            public void run() {
511                mContentsClientAdapter.onPageStarted(finalBaseUrl);
512            }
513        });
514    }
515
516    private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
517        if (ThreadUtils.runningOnUiThread()) {
518            mAwContents.loadUrl(loadUrlParams);
519        } else {
520            // Disallowed in WebView API for apps targetting a new SDK
521            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
522            ThreadUtils.postOnUiThread(new Runnable() {
523                @Override
524                public void run() {
525                    mAwContents.loadUrl(loadUrlParams);
526                }
527            });
528        }
529    }
530
531    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
532        checkThread();
533        mAwContents.evaluateJavaScript(script, resultCallback);
534    }
535
536    @Override
537    public void saveWebArchive(String filename) {
538        saveWebArchive(filename, false, null);
539    }
540
541    @Override
542    public void saveWebArchive(final String basename, final boolean autoname,
543            final ValueCallback<String> callback) {
544        if (!ThreadUtils.runningOnUiThread()) {
545            ThreadUtils.postOnUiThread(new Runnable() {
546                @Override
547                public void run() {
548                    saveWebArchive(basename, autoname, callback);
549                }
550            });
551            return;
552        }
553        mAwContents.saveWebArchive(basename, autoname, callback);
554    }
555
556    @Override
557    public void stopLoading() {
558        if (!ThreadUtils.runningOnUiThread()) {
559            ThreadUtils.postOnUiThread(new Runnable() {
560                @Override
561                public void run() {
562                    stopLoading();
563                }
564            });
565            return;
566        }
567
568        mAwContents.stopLoading();
569    }
570
571    @Override
572    public void reload() {
573        if (!ThreadUtils.runningOnUiThread()) {
574            ThreadUtils.postOnUiThread(new Runnable() {
575                @Override
576                public void run() {
577                    reload();
578                }
579            });
580            return;
581        }
582        mAwContents.reload();
583    }
584
585    @Override
586    public boolean canGoBack() {
587        if (!ThreadUtils.runningOnUiThread()) {
588            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
589                @Override
590                public Boolean call() {
591                    return canGoBack();
592                }
593            });
594            return ret;
595        }
596        return mAwContents.canGoBack();
597    }
598
599    @Override
600    public void goBack() {
601        if (!ThreadUtils.runningOnUiThread()) {
602            ThreadUtils.postOnUiThread(new Runnable() {
603                @Override
604                public void run() {
605                    goBack();
606                }
607            });
608            return;
609        }
610        mAwContents.goBack();
611    }
612
613    @Override
614    public boolean canGoForward() {
615        if (!ThreadUtils.runningOnUiThread()) {
616            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
617                @Override
618                public Boolean call() {
619                    return canGoForward();
620                }
621            });
622            return ret;
623        }
624        return mAwContents.canGoForward();
625    }
626
627    @Override
628    public void goForward() {
629        if (!ThreadUtils.runningOnUiThread()) {
630            ThreadUtils.postOnUiThread(new Runnable() {
631                @Override
632                public void run() {
633                    goForward();
634                }
635            });
636            return;
637        }
638        mAwContents.goForward();
639    }
640
641    @Override
642    public boolean canGoBackOrForward(final int steps) {
643        if (!ThreadUtils.runningOnUiThread()) {
644            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
645                @Override
646                public Boolean call() {
647                    return canGoBackOrForward(steps);
648                }
649            });
650            return ret;
651        }
652        return mAwContents.canGoBackOrForward(steps);
653    }
654
655    @Override
656    public void goBackOrForward(final int steps) {
657        if (!ThreadUtils.runningOnUiThread()) {
658            ThreadUtils.postOnUiThread(new Runnable() {
659                @Override
660                public void run() {
661                    goBackOrForward(steps);
662                }
663            });
664            return;
665        }
666        mAwContents.goBackOrForward(steps);
667    }
668
669    @Override
670    public boolean isPrivateBrowsingEnabled() {
671        // Not supported in this WebView implementation.
672        return false;
673    }
674
675    @Override
676    public boolean pageUp(final boolean top) {
677        if (!ThreadUtils.runningOnUiThread()) {
678            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
679                @Override
680                public Boolean call() {
681                    return pageUp(top);
682                }
683            });
684            return ret;
685        }
686        return mAwContents.pageUp(top);
687    }
688
689    @Override
690    public boolean pageDown(final boolean bottom) {
691        if (!ThreadUtils.runningOnUiThread()) {
692            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
693                @Override
694                public Boolean call() {
695                    return pageDown(bottom);
696                }
697            });
698            return ret;
699        }
700        return mAwContents.pageDown(bottom);
701    }
702
703    @Override
704    public void clearView() {
705        if (!ThreadUtils.runningOnUiThread()) {
706            ThreadUtils.postOnUiThread(new Runnable() {
707                @Override
708                public void run() {
709                    clearView();
710                }
711            });
712            return;
713        }
714        UnimplementedWebViewApi.invoke();
715    }
716
717    @Override
718    public Picture capturePicture() {
719        if (!ThreadUtils.runningOnUiThread()) {
720            Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
721                @Override
722                public Picture call() {
723                    return capturePicture();
724                }
725            });
726            return ret;
727        }
728        return mAwContents.capturePicture();
729    }
730
731    @Override
732    public PrintDocumentAdapter createPrintDocumentAdapter() {
733        checkThread();
734        return new AwPrintDocumentAdapter(mAwContents.getPdfExporter());
735    }
736
737    @Override
738    public float getScale() {
739        // No checkThread() as it is mostly thread safe (workaround for b/10652991).
740        return mAwContents.getScale();
741    }
742
743    @Override
744    public void setInitialScale(final int scaleInPercent) {
745        if (!ThreadUtils.runningOnUiThread()) {
746            ThreadUtils.postOnUiThread(new Runnable() {
747                @Override
748                public void run() {
749                    setInitialScale(scaleInPercent);
750                }
751            });
752            return;
753        }
754        mAwContents.getSettings().setInitialPageScale(scaleInPercent);
755    }
756
757    @Override
758    public void invokeZoomPicker() {
759        if (!ThreadUtils.runningOnUiThread()) {
760            ThreadUtils.postOnUiThread(new Runnable() {
761                @Override
762                public void run() {
763                    invokeZoomPicker();
764                }
765            });
766            return;
767        }
768        mAwContents.invokeZoomPicker();
769    }
770
771    @Override
772    public WebView.HitTestResult getHitTestResult() {
773        if (!ThreadUtils.runningOnUiThread()) {
774            WebView.HitTestResult ret = runOnUiThreadBlocking(
775                    new Callable<WebView.HitTestResult>() {
776                @Override
777                public WebView.HitTestResult call() {
778                    return getHitTestResult();
779                }
780            });
781            return ret;
782        }
783        AwContents.HitTestData data = mAwContents.getLastHitTestResult();
784        mHitTestResult.setType(data.hitTestResultType);
785        mHitTestResult.setExtra(data.hitTestResultExtraData);
786        return mHitTestResult;
787    }
788
789    @Override
790    public void requestFocusNodeHref(final Message hrefMsg) {
791        if (!ThreadUtils.runningOnUiThread()) {
792            ThreadUtils.postOnUiThread(new Runnable() {
793                @Override
794                public void run() {
795                    requestFocusNodeHref(hrefMsg);
796                }
797            });
798            return;
799        }
800        mAwContents.requestFocusNodeHref(hrefMsg);
801    }
802
803    @Override
804    public void requestImageRef(final Message msg) {
805        if (!ThreadUtils.runningOnUiThread()) {
806            ThreadUtils.postOnUiThread(new Runnable() {
807                @Override
808                public void run() {
809                    requestImageRef(msg);
810                }
811            });
812            return;
813        }
814        mAwContents.requestImageRef(msg);
815    }
816
817    @Override
818    public String getUrl() {
819        if (!ThreadUtils.runningOnUiThread()) {
820            String ret = runOnUiThreadBlocking(new Callable<String>() {
821                @Override
822                public String call() {
823                    return getUrl();
824                }
825            });
826            return ret;
827        }
828        String url =  mAwContents.getUrl();
829        if (url == null || url.trim().isEmpty()) return null;
830        return url;
831    }
832
833    @Override
834    public String getOriginalUrl() {
835        if (!ThreadUtils.runningOnUiThread()) {
836            String ret = runOnUiThreadBlocking(new Callable<String>() {
837                @Override
838                public String call() {
839                    return getOriginalUrl();
840                }
841            });
842            return ret;
843        }
844        String url =  mAwContents.getOriginalUrl();
845        if (url == null || url.trim().isEmpty()) return null;
846        return url;
847    }
848
849    @Override
850    public String getTitle() {
851        if (!ThreadUtils.runningOnUiThread()) {
852            String ret = runOnUiThreadBlocking(new Callable<String>() {
853                @Override
854                public String call() {
855                    return getTitle();
856                }
857            });
858            return ret;
859        }
860        return mAwContents.getTitle();
861    }
862
863    @Override
864    public Bitmap getFavicon() {
865        if (!ThreadUtils.runningOnUiThread()) {
866            Bitmap ret = runOnUiThreadBlocking(new Callable<Bitmap>() {
867                @Override
868                public Bitmap call() {
869                    return getFavicon();
870                }
871            });
872            return ret;
873        }
874        return mAwContents.getFavicon();
875    }
876
877    @Override
878    public String getTouchIconUrl() {
879        // Intentional no-op: hidden method on WebView.
880        return null;
881    }
882
883    @Override
884    public int getProgress() {
885        // No checkThread() because the value is cached java side (workaround for b/10533304).
886        return mAwContents.getMostRecentProgress();
887    }
888
889    @Override
890    public int getContentHeight() {
891        // No checkThread() as it is mostly thread safe (workaround for b/10594869).
892        return mAwContents.getContentHeightCss();
893    }
894
895    @Override
896    public int getContentWidth() {
897        // No checkThread() as it is mostly thread safe (workaround for b/10594869).
898        return mAwContents.getContentWidthCss();
899    }
900
901    @Override
902    public void pauseTimers() {
903        if (!ThreadUtils.runningOnUiThread()) {
904            ThreadUtils.postOnUiThread(new Runnable() {
905                @Override
906                public void run() {
907                    pauseTimers();
908                }
909            });
910            return;
911        }
912        mAwContents.pauseTimers();
913    }
914
915    @Override
916    public void resumeTimers() {
917        if (!ThreadUtils.runningOnUiThread()) {
918            ThreadUtils.postOnUiThread(new Runnable() {
919                @Override
920                public void run() {
921                    resumeTimers();
922                }
923            });
924            return;
925        }
926        mAwContents.resumeTimers();
927    }
928
929    @Override
930    public void onPause() {
931        if (!ThreadUtils.runningOnUiThread()) {
932            ThreadUtils.postOnUiThread(new Runnable() {
933                @Override
934                public void run() {
935                    onPause();
936                }
937            });
938            return;
939        }
940        mAwContents.onPause();
941    }
942
943    @Override
944    public void onResume() {
945        if (!ThreadUtils.runningOnUiThread()) {
946            ThreadUtils.postOnUiThread(new Runnable() {
947                @Override
948                public void run() {
949                    onResume();
950                }
951            });
952            return;
953        }
954        mAwContents.onResume();
955    }
956
957    @Override
958    public boolean isPaused() {
959        if (!ThreadUtils.runningOnUiThread()) {
960            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
961                @Override
962                public Boolean call() {
963                    return isPaused();
964                }
965            });
966            return ret;
967        }
968        return mAwContents.isPaused();
969    }
970
971    @Override
972    public void freeMemory() {
973        // Intentional no-op. Memory is managed automatically by Chromium.
974    }
975
976    @Override
977    public void clearCache(final boolean includeDiskFiles) {
978        if (!ThreadUtils.runningOnUiThread()) {
979            ThreadUtils.postOnUiThread(new Runnable() {
980                @Override
981                public void run() {
982                    clearCache(includeDiskFiles);
983                }
984            });
985            return;
986        }
987        mAwContents.clearCache(includeDiskFiles);
988    }
989
990    /**
991     * This is a poorly named method, but we keep it for historical reasons.
992     */
993    @Override
994    public void clearFormData() {
995        if (!ThreadUtils.runningOnUiThread()) {
996            ThreadUtils.postOnUiThread(new Runnable() {
997                @Override
998                public void run() {
999                    clearFormData();
1000                }
1001            });
1002            return;
1003        }
1004        mAwContents.hideAutofillPopup();
1005    }
1006
1007    @Override
1008    public void clearHistory() {
1009        if (!ThreadUtils.runningOnUiThread()) {
1010            ThreadUtils.postOnUiThread(new Runnable() {
1011                @Override
1012                public void run() {
1013                    clearHistory();
1014                }
1015            });
1016            return;
1017        }
1018        mAwContents.clearHistory();
1019    }
1020
1021    @Override
1022    public void clearSslPreferences() {
1023        if (!ThreadUtils.runningOnUiThread()) {
1024            ThreadUtils.postOnUiThread(new Runnable() {
1025                @Override
1026                public void run() {
1027                    clearSslPreferences();
1028                }
1029            });
1030            return;
1031        }
1032        mAwContents.clearSslPreferences();
1033    }
1034
1035    @Override
1036    public WebBackForwardList copyBackForwardList() {
1037        if (!ThreadUtils.runningOnUiThread()) {
1038            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
1039                @Override
1040                public WebBackForwardList call() {
1041                    return copyBackForwardList();
1042                }
1043            });
1044            return ret;
1045        }
1046        return new WebBackForwardListChromium(
1047                mAwContents.getNavigationHistory());
1048    }
1049
1050    @Override
1051    public void setFindListener(WebView.FindListener listener) {
1052        mContentsClientAdapter.setFindListener(listener);
1053    }
1054
1055    @Override
1056    public void findNext(final boolean forwards) {
1057        if (!ThreadUtils.runningOnUiThread()) {
1058            ThreadUtils.postOnUiThread(new Runnable() {
1059                @Override
1060                public void run() {
1061                    findNext(forwards);
1062                }
1063            });
1064            return;
1065        }
1066        mAwContents.findNext(forwards);
1067    }
1068
1069    @Override
1070    public int findAll(final String searchString) {
1071        findAllAsync(searchString);
1072        return 0;
1073    }
1074
1075    @Override
1076    public void findAllAsync(final String searchString) {
1077        if (!ThreadUtils.runningOnUiThread()) {
1078            ThreadUtils.postOnUiThread(new Runnable() {
1079                @Override
1080                public void run() {
1081                    findAllAsync(searchString);
1082                }
1083            });
1084            return;
1085        }
1086        mAwContents.findAllAsync(searchString);
1087    }
1088
1089    @Override
1090    public boolean showFindDialog(final String text, final boolean showIme) {
1091        if (!ThreadUtils.runningOnUiThread()) {
1092            return false;
1093        }
1094        if (mWebView.getParent() == null) {
1095            return false;
1096        }
1097
1098        FindActionModeCallback findAction = new FindActionModeCallback(mWebView.getContext());
1099        if (findAction == null) {
1100            return false;
1101        }
1102
1103        mWebView.startActionMode(findAction);
1104        findAction.setWebView(mWebView);
1105        if (showIme) {
1106            findAction.showSoftInput();
1107        }
1108
1109        if (text != null) {
1110            findAction.setText(text);
1111            findAction.findAll();
1112        }
1113
1114        return true;
1115    }
1116
1117    @Override
1118    public void notifyFindDialogDismissed() {
1119        if (!ThreadUtils.runningOnUiThread()) {
1120            ThreadUtils.postOnUiThread(new Runnable() {
1121                @Override
1122                public void run() {
1123                    notifyFindDialogDismissed();
1124                }
1125            });
1126            return;
1127        }
1128        clearMatches();
1129    }
1130
1131    @Override
1132    public void clearMatches() {
1133        if (!ThreadUtils.runningOnUiThread()) {
1134            ThreadUtils.postOnUiThread(new Runnable() {
1135                @Override
1136                public void run() {
1137                    clearMatches();
1138                }
1139            });
1140            return;
1141        }
1142        mAwContents.clearMatches();
1143    }
1144
1145    @Override
1146    public void documentHasImages(final Message response) {
1147        if (!ThreadUtils.runningOnUiThread()) {
1148            ThreadUtils.postOnUiThread(new Runnable() {
1149                @Override
1150                public void run() {
1151                    documentHasImages(response);
1152                }
1153            });
1154            return;
1155        }
1156        mAwContents.documentHasImages(response);
1157    }
1158
1159    @Override
1160    public void setWebViewClient(WebViewClient client) {
1161        mContentsClientAdapter.setWebViewClient(client);
1162    }
1163
1164    @Override
1165    public void setDownloadListener(DownloadListener listener) {
1166        mContentsClientAdapter.setDownloadListener(listener);
1167    }
1168
1169    @Override
1170    public void setWebChromeClient(WebChromeClient client) {
1171        mContentsClientAdapter.setWebChromeClient(client);
1172    }
1173
1174    @Override
1175    public void setPictureListener(final WebView.PictureListener listener) {
1176        if (!ThreadUtils.runningOnUiThread()) {
1177            ThreadUtils.postOnUiThread(new Runnable() {
1178                @Override
1179                public void run() {
1180                    setPictureListener(listener);
1181                }
1182            });
1183            return;
1184        }
1185        mContentsClientAdapter.setPictureListener(listener);
1186        mAwContents.enableOnNewPicture(listener != null,
1187                mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2);
1188    }
1189
1190    @Override
1191    public void addJavascriptInterface(final Object obj, final String interfaceName) {
1192        if (!ThreadUtils.runningOnUiThread()) {
1193            ThreadUtils.postOnUiThread(new Runnable() {
1194                @Override
1195                public void run() {
1196                    addJavascriptInterface(obj, interfaceName);
1197                }
1198            });
1199            return;
1200        }
1201        Class<? extends Annotation> requiredAnnotation = null;
1202        if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
1203           requiredAnnotation = JavascriptInterface.class;
1204        }
1205        mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation);
1206    }
1207
1208    @Override
1209    public void removeJavascriptInterface(final String interfaceName) {
1210        if (!ThreadUtils.runningOnUiThread()) {
1211            ThreadUtils.postOnUiThread(new Runnable() {
1212                @Override
1213                public void run() {
1214                    removeJavascriptInterface(interfaceName);
1215                }
1216            });
1217            return;
1218        }
1219        mAwContents.removeJavascriptInterface(interfaceName);
1220    }
1221
1222    @Override
1223    public WebSettings getSettings() {
1224        return mWebSettings;
1225    }
1226
1227    @Override
1228    public void setMapTrackballToArrowKeys(boolean setMap) {
1229        // This is a deprecated API: intentional no-op.
1230    }
1231
1232    @Override
1233    public void flingScroll(final int vx, final int vy) {
1234        if (!ThreadUtils.runningOnUiThread()) {
1235            ThreadUtils.postOnUiThread(new Runnable() {
1236                @Override
1237                public void run() {
1238                    flingScroll(vx, vy);
1239                }
1240            });
1241            return;
1242        }
1243        mAwContents.flingScroll(vx, vy);
1244    }
1245
1246    @Override
1247    public View getZoomControls() {
1248        if (!ThreadUtils.runningOnUiThread()) {
1249            return null;
1250        }
1251
1252        // This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed
1253        // to stop very out-dated applications from crashing.
1254        Log.w(TAG, "WebView doesn't support getZoomControls");
1255        return mAwContents.getSettings().supportZoom() ? new View(mWebView.getContext()) : null;
1256    }
1257
1258    @Override
1259    public boolean canZoomIn() {
1260        if (!ThreadUtils.runningOnUiThread()) {
1261            return false;
1262        }
1263        return mAwContents.canZoomIn();
1264    }
1265
1266    @Override
1267    public boolean canZoomOut() {
1268        if (!ThreadUtils.runningOnUiThread()) {
1269            return false;
1270        }
1271        return mAwContents.canZoomOut();
1272    }
1273
1274    @Override
1275    public boolean zoomIn() {
1276        if (!ThreadUtils.runningOnUiThread()) {
1277            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1278                @Override
1279                public Boolean call() {
1280                    return zoomIn();
1281                }
1282            });
1283            return ret;
1284        }
1285        return mAwContents.zoomIn();
1286    }
1287
1288    @Override
1289    public boolean zoomOut() {
1290        if (!ThreadUtils.runningOnUiThread()) {
1291            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1292                @Override
1293                public Boolean call() {
1294                    return zoomOut();
1295                }
1296            });
1297            return ret;
1298        }
1299        return mAwContents.zoomOut();
1300    }
1301
1302    @Override
1303    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
1304        // Intentional no-op
1305    }
1306
1307    @Override
1308    public View findHierarchyView(String className, int hashCode) {
1309        // Intentional no-op
1310        return null;
1311    }
1312
1313    // WebViewProvider glue methods ---------------------------------------------------------------
1314
1315    @Override
1316    // This needs to be kept thread safe!
1317    public WebViewProvider.ViewDelegate getViewDelegate() {
1318        return this;
1319    }
1320
1321    @Override
1322    // This needs to be kept thread safe!
1323    public WebViewProvider.ScrollDelegate getScrollDelegate() {
1324        return this;
1325    }
1326
1327
1328    // WebViewProvider.ViewDelegate implementation ------------------------------------------------
1329
1330    // TODO: remove from WebViewProvider and use default implementation from
1331    // ViewGroup.
1332    // @Override
1333    public boolean shouldDelayChildPressedState() {
1334        if (!ThreadUtils.runningOnUiThread()) {
1335            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1336                @Override
1337                public Boolean call() {
1338                    return shouldDelayChildPressedState();
1339                }
1340            });
1341            return ret;
1342        }
1343        return true;
1344    }
1345
1346//    @Override
1347    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1348        if (!ThreadUtils.runningOnUiThread()) {
1349            AccessibilityNodeProvider ret = runOnUiThreadBlocking(
1350                    new Callable<AccessibilityNodeProvider>() {
1351                @Override
1352                public AccessibilityNodeProvider call() {
1353                    return getAccessibilityNodeProvider();
1354                }
1355            });
1356            return ret;
1357        }
1358        return mAwContents.getAccessibilityNodeProvider();
1359    }
1360
1361    @Override
1362        public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) {
1363        if (!ThreadUtils.runningOnUiThread()) {
1364            runVoidTaskOnUiThreadBlocking(new Runnable() {
1365                @Override
1366                public void run() {
1367                    onInitializeAccessibilityNodeInfo(info);
1368                }
1369            });
1370            return;
1371        }
1372        mAwContents.onInitializeAccessibilityNodeInfo(info);
1373    }
1374
1375    @Override
1376    public void onInitializeAccessibilityEvent(final AccessibilityEvent event) {
1377        if (!ThreadUtils.runningOnUiThread()) {
1378            runVoidTaskOnUiThreadBlocking(new Runnable() {
1379                @Override
1380                public void run() {
1381                    onInitializeAccessibilityEvent(event);
1382                }
1383            });
1384            return;
1385        }
1386        mAwContents.onInitializeAccessibilityEvent(event);
1387    }
1388
1389    @Override
1390    public boolean performAccessibilityAction(final int action, final Bundle arguments) {
1391        if (!ThreadUtils.runningOnUiThread()) {
1392            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1393                @Override
1394                public Boolean call() {
1395                    return performAccessibilityAction(action, arguments);
1396                }
1397            });
1398            return ret;
1399        }
1400        if (mAwContents.supportsAccessibilityAction(action)) {
1401            return mAwContents.performAccessibilityAction(action, arguments);
1402        }
1403        return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
1404    }
1405
1406    @Override
1407    public void setOverScrollMode(final int mode) {
1408        if (!ThreadUtils.runningOnUiThread()) {
1409            ThreadUtils.postOnUiThread(new Runnable() {
1410                @Override
1411                public void run() {
1412                    setOverScrollMode(mode);
1413                }
1414            });
1415            return;
1416        }
1417        // This gets called from the android.view.View c'tor that WebView inherits from. This
1418        // causes the method to be called when mAwContents == null.
1419        // It's safe to ignore these calls however since AwContents will read the current value of
1420        // this setting when it's created.
1421        if (mAwContents != null) {
1422            mAwContents.setOverScrollMode(mode);
1423        }
1424    }
1425
1426    @Override
1427    public void setScrollBarStyle(final int style) {
1428        if (!ThreadUtils.runningOnUiThread()) {
1429            ThreadUtils.postOnUiThread(new Runnable() {
1430                @Override
1431                public void run() {
1432                    setScrollBarStyle(style);
1433                }
1434            });
1435            return;
1436        }
1437        mAwContents.setScrollBarStyle(style);
1438    }
1439
1440    @Override
1441    public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l,
1442            final int t, final int r, final int b) {
1443        if (!ThreadUtils.runningOnUiThread()) {
1444            runVoidTaskOnUiThreadBlocking(new Runnable() {
1445                @Override
1446                public void run() {
1447                    onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
1448                }
1449            });
1450            return;
1451        }
1452        // WebViewClassic was overriding this method to handle rubberband over-scroll. Since
1453        // WebViewChromium doesn't support that the vanilla implementation of this method can be
1454        // used.
1455        mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
1456    }
1457
1458    @Override
1459    public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
1460            final boolean clampedY) {
1461        if (!ThreadUtils.runningOnUiThread()) {
1462            ThreadUtils.postOnUiThread(new Runnable() {
1463                @Override
1464                public void run() {
1465                    onOverScrolled(scrollX, scrollY, clampedX, clampedY);
1466                }
1467            });
1468            return;
1469        }
1470        mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
1471    }
1472
1473    @Override
1474    public void onWindowVisibilityChanged(final int visibility) {
1475        if (!ThreadUtils.runningOnUiThread()) {
1476            ThreadUtils.postOnUiThread(new Runnable() {
1477                @Override
1478                public void run() {
1479                    onWindowVisibilityChanged(visibility);
1480                }
1481            });
1482            return;
1483        }
1484        mAwContents.onWindowVisibilityChanged(visibility);
1485    }
1486
1487    @Override
1488    public void onDraw(final Canvas canvas) {
1489        if (!ThreadUtils.runningOnUiThread()) {
1490            runVoidTaskOnUiThreadBlocking(new Runnable() {
1491                @Override
1492                public void run() {
1493                    onDraw(canvas);
1494                }
1495            });
1496            return;
1497        }
1498        mAwContents.onDraw(canvas);
1499    }
1500
1501    @Override
1502    public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) {
1503        if (!ThreadUtils.runningOnUiThread()) {
1504            ThreadUtils.postOnUiThread(new Runnable() {
1505                @Override
1506                public void run() {
1507                    setLayoutParams(layoutParams);
1508                }
1509            });
1510            return;
1511        }
1512        mWebViewPrivate.super_setLayoutParams(layoutParams);
1513    }
1514
1515    @Override
1516    public boolean performLongClick() {
1517        if (!ThreadUtils.runningOnUiThread()) {
1518            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1519                @Override
1520                public Boolean call() {
1521                    return performLongClick();
1522                }
1523            });
1524            return ret;
1525        }
1526        return mWebViewPrivate.super_performLongClick();
1527    }
1528
1529    @Override
1530    public void onConfigurationChanged(final Configuration newConfig) {
1531        if (!ThreadUtils.runningOnUiThread()) {
1532            ThreadUtils.postOnUiThread(new Runnable() {
1533                @Override
1534                public void run() {
1535                    onConfigurationChanged(newConfig);
1536                }
1537            });
1538            return;
1539        }
1540        mAwContents.onConfigurationChanged(newConfig);
1541    }
1542
1543    @Override
1544    public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
1545        if (!ThreadUtils.runningOnUiThread()) {
1546            InputConnection ret = runOnUiThreadBlocking(new Callable<InputConnection>() {
1547                @Override
1548                public InputConnection call() {
1549                    return onCreateInputConnection(outAttrs);
1550                }
1551            });
1552            return ret;
1553        }
1554        return mAwContents.onCreateInputConnection(outAttrs);
1555    }
1556
1557    @Override
1558    public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) {
1559        if (!ThreadUtils.runningOnUiThread()) {
1560            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1561                @Override
1562                public Boolean call() {
1563                    return onKeyMultiple(keyCode, repeatCount, event);
1564                }
1565            });
1566            return ret;
1567        }
1568        UnimplementedWebViewApi.invoke();
1569        return false;
1570    }
1571
1572    @Override
1573    public boolean onKeyDown(final int keyCode, final KeyEvent event) {
1574        if (!ThreadUtils.runningOnUiThread()) {
1575            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1576                @Override
1577                public Boolean call() {
1578                    return onKeyDown(keyCode, event);
1579                }
1580            });
1581            return ret;
1582        }
1583        UnimplementedWebViewApi.invoke();
1584        return false;
1585    }
1586
1587    @Override
1588    public boolean onKeyUp(final int keyCode, final KeyEvent event) {
1589        if (!ThreadUtils.runningOnUiThread()) {
1590            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1591                @Override
1592                public Boolean call() {
1593                    return onKeyUp(keyCode, event);
1594                }
1595            });
1596            return ret;
1597        }
1598        return mAwContents.onKeyUp(keyCode, event);
1599    }
1600
1601    @Override
1602    public void onAttachedToWindow() {
1603        if (!ThreadUtils.runningOnUiThread()) {
1604            ThreadUtils.postOnUiThread(new Runnable() {
1605                @Override
1606                public void run() {
1607                    onAttachedToWindow();
1608                }
1609            });
1610            return;
1611        }
1612        mAwContents.onAttachedToWindow();
1613    }
1614
1615    @Override
1616    public void onDetachedFromWindow() {
1617        if (!ThreadUtils.runningOnUiThread()) {
1618            ThreadUtils.postOnUiThread(new Runnable() {
1619                @Override
1620                public void run() {
1621                    onDetachedFromWindow();
1622                }
1623            });
1624            return;
1625        }
1626        mAwContents.onDetachedFromWindow();
1627        if (mGLfunctor != null) {
1628            mGLfunctor.detach();
1629        }
1630    }
1631
1632    @Override
1633    public void onVisibilityChanged(final View changedView, final int visibility) {
1634        if (!ThreadUtils.runningOnUiThread()) {
1635            ThreadUtils.postOnUiThread(new Runnable() {
1636                @Override
1637                public void run() {
1638                    onVisibilityChanged(changedView, visibility);
1639                }
1640            });
1641            return;
1642        }
1643        // The AwContents will find out the container view visibility before the first draw so we
1644        // can safely ignore onVisibilityChanged callbacks that happen before init().
1645        if (mAwContents != null) {
1646            mAwContents.onVisibilityChanged(changedView, visibility);
1647        }
1648    }
1649
1650    @Override
1651    public void onWindowFocusChanged(final boolean hasWindowFocus) {
1652        if (!ThreadUtils.runningOnUiThread()) {
1653            ThreadUtils.postOnUiThread(new Runnable() {
1654                @Override
1655                public void run() {
1656                    onWindowFocusChanged(hasWindowFocus);
1657                }
1658            });
1659            return;
1660        }
1661        mAwContents.onWindowFocusChanged(hasWindowFocus);
1662    }
1663
1664    @Override
1665    public void onFocusChanged(final boolean focused, final int direction,
1666            final Rect previouslyFocusedRect) {
1667        if (!ThreadUtils.runningOnUiThread()) {
1668            ThreadUtils.postOnUiThread(new Runnable() {
1669                @Override
1670                public void run() {
1671                    onFocusChanged(focused, direction, previouslyFocusedRect);
1672                }
1673            });
1674            return;
1675        }
1676        mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
1677    }
1678
1679    @Override
1680    public boolean setFrame(final int left, final int top, final int right, final int bottom) {
1681        if (!ThreadUtils.runningOnUiThread()) {
1682            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1683                @Override
1684                public Boolean call() {
1685                    return setFrame(left, top, right, bottom);
1686                }
1687            });
1688            return ret;
1689        }
1690        return mWebViewPrivate.super_setFrame(left, top, right, bottom);
1691    }
1692
1693    @Override
1694    public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
1695        if (!ThreadUtils.runningOnUiThread()) {
1696            ThreadUtils.postOnUiThread(new Runnable() {
1697                @Override
1698                public void run() {
1699                    onSizeChanged(w, h, ow, oh);
1700                }
1701            });
1702            return;
1703        }
1704        mAwContents.onSizeChanged(w, h, ow, oh);
1705    }
1706
1707    @Override
1708    public void onScrollChanged(int l, int t, int oldl, int oldt) {
1709    }
1710
1711    @Override
1712    public boolean dispatchKeyEvent(final KeyEvent event) {
1713        if (!ThreadUtils.runningOnUiThread()) {
1714            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1715                @Override
1716                public Boolean call() {
1717                    return dispatchKeyEvent(event);
1718                }
1719            });
1720            return ret;
1721        }
1722        return mAwContents.dispatchKeyEvent(event);
1723    }
1724
1725    @Override
1726    public boolean onTouchEvent(final MotionEvent ev) {
1727        if (!ThreadUtils.runningOnUiThread()) {
1728            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1729                @Override
1730                public Boolean call() {
1731                    return onTouchEvent(ev);
1732                }
1733            });
1734            return ret;
1735        }
1736        return mAwContents.onTouchEvent(ev);
1737    }
1738
1739    @Override
1740    public boolean onHoverEvent(final MotionEvent event) {
1741        if (!ThreadUtils.runningOnUiThread()) {
1742            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1743                @Override
1744                public Boolean call() {
1745                    return onHoverEvent(event);
1746                }
1747            });
1748            return ret;
1749        }
1750        return mAwContents.onHoverEvent(event);
1751    }
1752
1753    @Override
1754    public boolean onGenericMotionEvent(final MotionEvent event) {
1755        if (!ThreadUtils.runningOnUiThread()) {
1756            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1757                @Override
1758                public Boolean call() {
1759                    return onGenericMotionEvent(event);
1760                }
1761            });
1762            return ret;
1763        }
1764        return mAwContents.onGenericMotionEvent(event);
1765    }
1766
1767    @Override
1768    public boolean onTrackballEvent(MotionEvent ev) {
1769        // Trackball event not handled, which eventually gets converted to DPAD keyevents
1770        return false;
1771    }
1772
1773    @Override
1774    public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
1775        if (!ThreadUtils.runningOnUiThread()) {
1776            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1777                @Override
1778                public Boolean call() {
1779                    return requestFocus(direction, previouslyFocusedRect);
1780                }
1781            });
1782            return ret;
1783        }
1784        mAwContents.requestFocus();
1785        return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
1786    }
1787
1788    @Override
1789    public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
1790        if (!ThreadUtils.runningOnUiThread()) {
1791            ThreadUtils.postOnUiThread(new Runnable() {
1792                @Override
1793                public void run() {
1794                    onMeasure(widthMeasureSpec, heightMeasureSpec);
1795                }
1796            });
1797            return;
1798        }
1799        mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
1800    }
1801
1802    @Override
1803    public boolean requestChildRectangleOnScreen(final View child, final Rect rect,
1804            final boolean immediate) {
1805        if (!ThreadUtils.runningOnUiThread()) {
1806            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1807                @Override
1808                public Boolean call() {
1809                    return requestChildRectangleOnScreen(child, rect, immediate);
1810                }
1811            });
1812            return ret;
1813        }
1814        return mAwContents.requestChildRectangleOnScreen(child, rect, immediate);
1815    }
1816
1817    @Override
1818    public void setBackgroundColor(final int color) {
1819        if (!ThreadUtils.runningOnUiThread()) {
1820            ThreadUtils.postOnUiThread(new Runnable() {
1821                @Override
1822                public void run() {
1823                    setBackgroundColor(color);
1824                }
1825            });
1826            return;
1827        }
1828        mAwContents.setBackgroundColor(color);
1829    }
1830
1831    @Override
1832    public void setLayerType(int layerType, Paint paint) {
1833        // Intentional no-op
1834    }
1835
1836    // Remove from superclass
1837    public void preDispatchDraw(Canvas canvas) {
1838        // TODO(leandrogracia): remove this method from WebViewProvider if we think
1839        // we won't need it again.
1840    }
1841
1842    // WebViewProvider.ScrollDelegate implementation ----------------------------------------------
1843
1844    @Override
1845    public int computeHorizontalScrollRange() {
1846        if (!ThreadUtils.runningOnUiThread()) {
1847            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1848                @Override
1849                public Integer call() {
1850                    return computeHorizontalScrollRange();
1851                }
1852            });
1853            return ret;
1854        }
1855        return mAwContents.computeHorizontalScrollRange();
1856    }
1857
1858    @Override
1859    public int computeHorizontalScrollOffset() {
1860        if (!ThreadUtils.runningOnUiThread()) {
1861            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1862                @Override
1863                public Integer call() {
1864                    return computeHorizontalScrollOffset();
1865                }
1866            });
1867            return ret;
1868        }
1869        return mAwContents.computeHorizontalScrollOffset();
1870    }
1871
1872    @Override
1873    public int computeVerticalScrollRange() {
1874        if (!ThreadUtils.runningOnUiThread()) {
1875            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1876                @Override
1877                public Integer call() {
1878                    return computeVerticalScrollRange();
1879                }
1880            });
1881            return ret;
1882        }
1883        return mAwContents.computeVerticalScrollRange();
1884    }
1885
1886    @Override
1887    public int computeVerticalScrollOffset() {
1888        if (!ThreadUtils.runningOnUiThread()) {
1889            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1890                @Override
1891                public Integer call() {
1892                    return computeVerticalScrollOffset();
1893                }
1894            });
1895            return ret;
1896        }
1897        return mAwContents.computeVerticalScrollOffset();
1898    }
1899
1900    @Override
1901    public int computeVerticalScrollExtent() {
1902        if (!ThreadUtils.runningOnUiThread()) {
1903            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1904                @Override
1905                public Integer call() {
1906                    return computeVerticalScrollExtent();
1907                }
1908            });
1909            return ret;
1910        }
1911        return mAwContents.computeVerticalScrollExtent();
1912    }
1913
1914    @Override
1915    public void computeScroll() {
1916        if (!ThreadUtils.runningOnUiThread()) {
1917            ThreadUtils.postOnUiThread(new Runnable() {
1918                @Override
1919                public void run() {
1920                    computeScroll();
1921                }
1922            });
1923            return;
1924        }
1925        mAwContents.computeScroll();
1926    }
1927
1928    // AwContents.InternalAccessDelegate implementation --------------------------------------
1929    private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
1930        @Override
1931        public boolean drawChild(Canvas arg0, View arg1, long arg2) {
1932            UnimplementedWebViewApi.invoke();
1933            return false;
1934        }
1935
1936        @Override
1937        public boolean super_onKeyUp(int arg0, KeyEvent arg1) {
1938            // Intentional no-op
1939            return false;
1940        }
1941
1942        @Override
1943        public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) {
1944            UnimplementedWebViewApi.invoke();
1945            return false;
1946        }
1947
1948        @Override
1949        public boolean super_dispatchKeyEvent(KeyEvent event) {
1950            return mWebViewPrivate.super_dispatchKeyEvent(event);
1951        }
1952
1953        @Override
1954        public boolean super_onGenericMotionEvent(MotionEvent arg0) {
1955            UnimplementedWebViewApi.invoke();
1956            return false;
1957        }
1958
1959        @Override
1960        public void super_onConfigurationChanged(Configuration arg0) {
1961            // Intentional no-op
1962        }
1963
1964        @Override
1965        public int super_getScrollBarStyle() {
1966            return mWebViewPrivate.super_getScrollBarStyle();
1967        }
1968
1969        @Override
1970        public boolean awakenScrollBars() {
1971            mWebViewPrivate.awakenScrollBars(0);
1972            // TODO: modify the WebView.PrivateAccess to provide a return value.
1973            return true;
1974        }
1975
1976        @Override
1977        public boolean super_awakenScrollBars(int arg0, boolean arg1) {
1978            // TODO: need method on WebView.PrivateAccess?
1979            UnimplementedWebViewApi.invoke();
1980            return false;
1981        }
1982
1983        @Override
1984        public void onScrollChanged(int l, int t, int oldl, int oldt) {
1985            mWebViewPrivate.setScrollXRaw(l);
1986            mWebViewPrivate.setScrollYRaw(t);
1987            mWebViewPrivate.onScrollChanged(l, t, oldl, oldt);
1988        }
1989
1990        @Override
1991        public void overScrollBy(int deltaX, int deltaY,
1992            int scrollX, int scrollY,
1993            int scrollRangeX, int scrollRangeY,
1994            int maxOverScrollX, int maxOverScrollY,
1995            boolean isTouchEvent) {
1996            mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY,
1997                    scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
1998        }
1999
2000        @Override
2001        public void super_scrollTo(int scrollX, int scrollY) {
2002            mWebViewPrivate.super_scrollTo(scrollX, scrollY);
2003        }
2004
2005        @Override
2006        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
2007            mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
2008        }
2009
2010        @Override
2011        public boolean requestDrawGL(Canvas canvas) {
2012            if (mGLfunctor == null) {
2013                mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext());
2014            }
2015            return mGLfunctor.requestDrawGL((HardwareCanvas)canvas, mWebView.getViewRootImpl());
2016        }
2017    }
2018}
2019