WebViewChromium.java revision 53fc207e8b5079af5edfef849996a7e5dda01364
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.View.MeasureSpec;
39import android.view.ViewGroup;
40import android.view.accessibility.AccessibilityEvent;
41import android.view.accessibility.AccessibilityNodeInfo;
42import android.view.accessibility.AccessibilityNodeProvider;
43import android.view.inputmethod.EditorInfo;
44import android.view.inputmethod.InputConnection;
45import android.webkit.DownloadListener;
46import android.webkit.FindActionModeCallback;
47import android.webkit.JavascriptInterface;
48import android.webkit.ValueCallback;
49import android.webkit.WebBackForwardList;
50import android.webkit.WebChromeClient;
51import android.webkit.WebSettings;
52import android.webkit.WebView;
53import android.webkit.WebViewClient;
54import android.webkit.WebViewProvider;
55import android.widget.TextView;
56
57import org.chromium.android_webview.AwBrowserContext;
58import org.chromium.android_webview.AwContents;
59import org.chromium.android_webview.AwLayoutSizer;
60import org.chromium.base.ThreadUtils;
61import org.chromium.content.browser.LoadUrlParams;
62import org.chromium.net.NetworkChangeNotifier;
63
64import java.io.BufferedWriter;
65import java.io.File;
66import java.lang.annotation.Annotation;
67import java.util.concurrent.Callable;
68import java.util.concurrent.FutureTask;
69import java.util.concurrent.TimeUnit;
70import java.util.HashMap;
71import java.util.Map;
72
73/**
74 * This class is the delegate to which WebViewProxy forwards all API calls.
75 *
76 * Most of the actual functionality is implemented by AwContents (or ContentViewCore within
77 * it). This class also contains WebView-specific APIs that require the creation of other
78 * adapters (otherwise org.chromium.content would depend on the webview.chromium package)
79 * and a small set of no-op deprecated APIs.
80 */
81class WebViewChromium implements WebViewProvider,
82          WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate {
83
84    private static final String TAG = WebViewChromium.class.getSimpleName();
85
86    // The WebView that this WebViewChromium is the provider for.
87    WebView mWebView;
88    // Lets us access protected View-derived methods on the WebView instance we're backing.
89    WebView.PrivateAccess mWebViewPrivate;
90    // The client adapter class.
91    private WebViewContentsClientAdapter mContentsClientAdapter;
92
93    // Variables for functionality provided by this adapter ---------------------------------------
94    // WebSettings adapter, lazily initialized in the getter
95    private WebSettings mWebSettings;
96    // The WebView wrapper for ContentViewCore and required browser compontents.
97    private AwContents mAwContents;
98    // Non-null if this webview is using the GL accelerated draw path.
99    private DrawGLFunctor mGLfunctor;
100
101    private AwBrowserContext mBrowserContext;
102
103    private final WebView.HitTestResult mHitTestResult;
104
105    private final int mAppTargetSdkVersion;
106
107    // This does not touch any global / non-threadsafe state, but note that
108    // init is ofter called right after and is NOT threadsafe.
109    public WebViewChromium(WebView webView, WebView.PrivateAccess webViewPrivate,
110            AwBrowserContext browserContext) {
111        mWebView = webView;
112        mWebViewPrivate = webViewPrivate;
113        mHitTestResult = new WebView.HitTestResult();
114        mBrowserContext = browserContext;
115        mAppTargetSdkVersion = mWebView.getContext().getApplicationInfo().targetSdkVersion;
116    }
117
118    static void completeWindowCreation(WebView parent, WebView child) {
119        AwContents parentContents = ((WebViewChromium) parent.getWebViewProvider()).mAwContents;
120        AwContents childContents =
121                child == null ? null : ((WebViewChromium) child.getWebViewProvider()).mAwContents;
122        parentContents.supplyContentsForPopup(childContents);
123    }
124
125    private static <T> T runBlockingFuture(FutureTask<T> task) {
126        if (ThreadUtils.runningOnUiThread()) {
127            throw new IllegalStateException("This method should only be called off the UI thread");
128        }
129        ThreadUtils.postOnUiThread(task);
130        try {
131            return task.get(4, TimeUnit.SECONDS);
132        } catch (Exception e) { // Timeout is one of the possible exceptions here
133            throw new RuntimeException("Probable deadlock detected due to WebView API being called "
134                    + "on incorrect thread while the UI thread is blocked.", e);
135        }
136    }
137
138    // We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin
139    // deadlocks.
140    // Do not call this method while on the UI thread!
141    private void runVoidTaskOnUiThreadBlocking(Runnable r) {
142        FutureTask<Void> task = new FutureTask<Void>(r, null);
143        runBlockingFuture(task);
144    }
145
146    private static <T> T runOnUiThreadBlocking(Callable<T> c) {
147        return runBlockingFuture(new FutureTask<T>(c));
148    }
149
150    // WebViewProvider methods --------------------------------------------------------------------
151
152    @Override
153    public void init(final Map<String, Object> javaScriptInterfaces,
154            final boolean privateBrowsing) {
155        if (!ThreadUtils.runningOnUiThread()) {
156            runVoidTaskOnUiThreadBlocking(new Runnable() {
157                @Override
158                public void run() {
159                    init(javaScriptInterfaces, privateBrowsing);
160                }
161            });
162            return;
163        }
164        // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
165        // so is ignored. TODO: remove it from WebViewProvider.
166        final boolean isAccessFromFileURLsGrantedByDefault =
167                mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
168        final boolean areLegacyQuirksEnabled =
169                mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
170        mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
171        mAwContents = new AwContents(mBrowserContext, mWebView, new InternalAccessAdapter(),
172                mContentsClientAdapter, isAccessFromFileURLsGrantedByDefault,
173                new AwLayoutSizer(), areLegacyQuirksEnabled);
174        mWebSettings = new ContentSettingsAdapter(mAwContents.getSettings());
175
176        if (privateBrowsing) {
177            final String msg = "Private browsing is not supported in WebView.";
178            if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
179                throw new IllegalArgumentException(msg);
180            } else {
181                Log.w(TAG, msg);
182                // Intentionally irreversibly disable the webview instance, so that private
183                // user data cannot leak through misuse of a non-privateBrowing WebView instance.
184                // Can't just null out mAwContents as we never null-check it before use.
185                mAwContents.destroy();
186                TextView warningLabel = new TextView(mWebView.getContext());
187                warningLabel.setText(mWebView.getContext().getString(
188                        com.android.internal.R.string.webviewchromium_private_browsing_warning));
189                mWebView.addView(warningLabel);
190            }
191        }
192
193    }
194
195    private RuntimeException createThreadException() {
196        return new IllegalStateException(
197                "Calling View methods on another thread than the UI thread.");
198    }
199
200    //  Intentionally not static, as no need to check thread on static methods
201    private void checkThread() {
202        if (!ThreadUtils.runningOnUiThread()) {
203            final RuntimeException threadViolation = createThreadException();
204            ThreadUtils.postOnUiThread(new Runnable() {
205                @Override
206                public void run() {
207                    throw threadViolation;
208                }
209            });
210            throw createThreadException();
211        }
212    }
213
214    @Override
215    public void setHorizontalScrollbarOverlay(final boolean overlay) {
216        if (!ThreadUtils.runningOnUiThread()) {
217            ThreadUtils.postOnUiThread(new Runnable() {
218                @Override
219                public void run() {
220                    setHorizontalScrollbarOverlay(overlay);
221                }
222            });
223            return;
224        }
225        mAwContents.setHorizontalScrollbarOverlay(overlay);
226    }
227
228    @Override
229    public void setVerticalScrollbarOverlay(final boolean overlay) {
230        if (!ThreadUtils.runningOnUiThread()) {
231            ThreadUtils.postOnUiThread(new Runnable() {
232                @Override
233                public void run() {
234                    setVerticalScrollbarOverlay(overlay);
235                }
236            });
237            return;
238        }
239        mAwContents.setVerticalScrollbarOverlay(overlay);
240    }
241
242    @Override
243    public boolean overlayHorizontalScrollbar() {
244        if (!ThreadUtils.runningOnUiThread()) {
245            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
246                @Override
247                public Boolean call() {
248                    return overlayHorizontalScrollbar();
249                }
250            });
251            return ret;
252        }
253        return mAwContents.overlayHorizontalScrollbar();
254    }
255
256    @Override
257    public boolean overlayVerticalScrollbar() {
258        if (!ThreadUtils.runningOnUiThread()) {
259            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
260                @Override
261                public Boolean call() {
262                    return overlayVerticalScrollbar();
263                }
264            });
265            return ret;
266        }
267        return mAwContents.overlayVerticalScrollbar();
268    }
269
270    @Override
271    public int getVisibleTitleHeight() {
272        // This is deprecated in WebView and should always return 0.
273        return 0;
274    }
275
276    @Override
277    public SslCertificate getCertificate() {
278        if (!ThreadUtils.runningOnUiThread()) {
279            SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() {
280                @Override
281                public SslCertificate call() {
282                    return getCertificate();
283                }
284            });
285            return ret;
286        }
287        return mAwContents.getCertificate();
288    }
289
290    @Override
291    public void setCertificate(SslCertificate certificate) {
292        // intentional no-op
293    }
294
295    @Override
296    public void savePassword(String host, String username, String password) {
297        // This is a deprecated API: intentional no-op.
298    }
299
300    @Override
301    public void setHttpAuthUsernamePassword(final String host, final String realm,
302            final String username, final String password) {
303        if (!ThreadUtils.runningOnUiThread()) {
304            ThreadUtils.postOnUiThread(new Runnable() {
305                @Override
306                public void run() {
307                    setHttpAuthUsernamePassword(host, realm, username, password);
308                }
309            });
310            return;
311        }
312        mAwContents.setHttpAuthUsernamePassword(host, realm, username, password);
313    }
314
315    @Override
316    public String[] getHttpAuthUsernamePassword(final String host, final String realm) {
317        if (!ThreadUtils.runningOnUiThread()) {
318            String[] ret = runOnUiThreadBlocking(new Callable<String[]>() {
319                @Override
320                public String[] call() {
321                    return getHttpAuthUsernamePassword(host, realm);
322                }
323            });
324            return ret;
325        }
326        return mAwContents.getHttpAuthUsernamePassword(host, realm);
327    }
328
329    @Override
330    public void destroy() {
331        if (!ThreadUtils.runningOnUiThread()) {
332            ThreadUtils.postOnUiThread(new Runnable() {
333                @Override
334                public void run() {
335                    destroy();
336                }
337            });
338            return;
339        }
340
341        mAwContents.destroy();
342        if (mGLfunctor != null) {
343            mGLfunctor.destroy();
344            mGLfunctor = null;
345        }
346    }
347
348    @Override
349    public void setNetworkAvailable(final boolean networkUp) {
350        // Note that this purely toggles the JS navigator.online property.
351        // It does not in affect chromium or network stack state in any way.
352        if (!ThreadUtils.runningOnUiThread()) {
353            ThreadUtils.postOnUiThread(new Runnable() {
354                @Override
355                public void run() {
356                    setNetworkAvailable(networkUp);
357                }
358            });
359            return;
360        }
361        mAwContents.setNetworkAvailable(networkUp);
362    }
363
364    @Override
365    public WebBackForwardList saveState(final Bundle outState) {
366        if (!ThreadUtils.runningOnUiThread()) {
367            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
368                @Override
369                public WebBackForwardList call() {
370                    return saveState(outState);
371                }
372            });
373            return ret;
374        }
375        if (outState == null) return null;
376        if (!mAwContents.saveState(outState)) return null;
377        return copyBackForwardList();
378    }
379
380    @Override
381    public boolean savePicture(Bundle b, File dest) {
382        // Intentional no-op: hidden method on WebView.
383        return false;
384    }
385
386    @Override
387    public boolean restorePicture(Bundle b, File src) {
388        // Intentional no-op: hidden method on WebView.
389        return false;
390    }
391
392    @Override
393    public WebBackForwardList restoreState(final Bundle inState) {
394        if (!ThreadUtils.runningOnUiThread()) {
395            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
396                @Override
397                public WebBackForwardList call() {
398                    return restoreState(inState);
399                }
400            });
401            return ret;
402        }
403        if (inState == null) return null;
404        if (!mAwContents.restoreState(inState)) return null;
405        return copyBackForwardList();
406    }
407
408    @Override
409    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
410        // TODO: We may actually want to do some sanity checks here (like filter about://chrome).
411
412        // For backwards compatibility, apps targeting less than K will have JS URLs evaluated
413        // directly and any result of the evaluation will not replace the current page content.
414        // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will
415        // have the result of that URL replace the content of the current page.
416        final String JAVASCRIPT_SCHEME = "javascript:";
417        if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
418                url != null && url.startsWith(JAVASCRIPT_SCHEME)) {
419            mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
420                    url.substring(JAVASCRIPT_SCHEME.length()));
421            return;
422        }
423
424        LoadUrlParams params = new LoadUrlParams(url);
425        if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders);
426        loadUrlOnUiThread(params);
427    }
428
429    @Override
430    public void loadUrl(String url) {
431        // Early out to match old WebView implementation
432        if (url == null) {
433            return;
434        }
435        loadUrl(url, null);
436    }
437
438    @Override
439    public void postUrl(String url, byte[] postData) {
440        LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData);
441        Map<String,String> headers = new HashMap<String,String>();
442        headers.put("Content-Type", "application/x-www-form-urlencoded");
443        params.setExtraHeaders(headers);
444        loadUrlOnUiThread(params);
445    }
446
447    private static String fixupMimeType(String mimeType) {
448        return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType;
449    }
450
451    private static String fixupData(String data) {
452        return TextUtils.isEmpty(data) ? "" : data;
453    }
454
455    private static String fixupBase(String url) {
456        return TextUtils.isEmpty(url) ? "about:blank" : url;
457    }
458
459    private static String fixupHistory(String url) {
460        return TextUtils.isEmpty(url) ? "about:blank" : url;
461    }
462
463    private static boolean isBase64Encoded(String encoding) {
464        return "base64".equals(encoding);
465    }
466
467    @Override
468    public void loadData(String data, String mimeType, String encoding) {
469        loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
470                fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
471    }
472
473    @Override
474    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
475            String historyUrl) {
476        data = fixupData(data);
477        mimeType = fixupMimeType(mimeType);
478        LoadUrlParams loadUrlParams;
479        baseUrl = fixupBase(baseUrl);
480        historyUrl = fixupHistory(historyUrl);
481
482        if (baseUrl.startsWith("data:")) {
483            // For backwards compatibility with WebViewClassic, we use the value of |encoding|
484            // as the charset, as long as it's not "base64".
485            boolean isBase64 = isBase64Encoded(encoding);
486            loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
487                    data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
488        } else {
489            // When loading data with a non-data: base URL, the classic WebView would effectively
490            // "dump" that string of data into the WebView without going through regular URL
491            // loading steps such as decoding URL-encoded entities. We achieve this same behavior by
492            // base64 encoding the data that is passed here and then loading that as a data: URL.
493            try {
494                loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
495                        Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
496                        true, baseUrl, historyUrl, "utf-8");
497            } catch (java.io.UnsupportedEncodingException e) {
498                Log.wtf(TAG, "Unable to load data string " + data, e);
499                return;
500            }
501        }
502        loadUrlOnUiThread(loadUrlParams);
503
504        // Data url's with a base url will be resolved in Blink, and not cause an onPageStarted
505        // event to be sent. Sending the callback directly from here.
506        final String finalBaseUrl = loadUrlParams.getBaseUrl();
507        ThreadUtils.postOnUiThread(new Runnable() {
508            @Override
509            public void run() {
510                mContentsClientAdapter.onPageStarted(finalBaseUrl);
511            }
512        });
513    }
514
515    private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
516        if (ThreadUtils.runningOnUiThread()) {
517            mAwContents.loadUrl(loadUrlParams);
518        } else {
519            // Disallowed in WebView API for apps targetting a new SDK
520            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
521            ThreadUtils.postOnUiThread(new Runnable() {
522                @Override
523                public void run() {
524                    mAwContents.loadUrl(loadUrlParams);
525                }
526            });
527        }
528    }
529
530    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
531        checkThread();
532        mAwContents.evaluateJavaScript(script, resultCallback);
533    }
534
535    @Override
536    public void saveWebArchive(String filename) {
537        saveWebArchive(filename, false, null);
538    }
539
540    @Override
541    public void saveWebArchive(final String basename, final boolean autoname,
542            final ValueCallback<String> callback) {
543        if (!ThreadUtils.runningOnUiThread()) {
544            ThreadUtils.postOnUiThread(new Runnable() {
545                @Override
546                public void run() {
547                    saveWebArchive(basename, autoname, callback);
548                }
549            });
550            return;
551        }
552        mAwContents.saveWebArchive(basename, autoname, callback);
553    }
554
555    @Override
556    public void stopLoading() {
557        if (!ThreadUtils.runningOnUiThread()) {
558            ThreadUtils.postOnUiThread(new Runnable() {
559                @Override
560                public void run() {
561                    stopLoading();
562                }
563            });
564            return;
565        }
566
567        mAwContents.stopLoading();
568    }
569
570    @Override
571    public void reload() {
572        if (!ThreadUtils.runningOnUiThread()) {
573            ThreadUtils.postOnUiThread(new Runnable() {
574                @Override
575                public void run() {
576                    reload();
577                }
578            });
579            return;
580        }
581        mAwContents.reload();
582    }
583
584    @Override
585    public boolean canGoBack() {
586        if (!ThreadUtils.runningOnUiThread()) {
587            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
588                @Override
589                public Boolean call() {
590                    return canGoBack();
591                }
592            });
593            return ret;
594        }
595        return mAwContents.canGoBack();
596    }
597
598    @Override
599    public void goBack() {
600        if (!ThreadUtils.runningOnUiThread()) {
601            ThreadUtils.postOnUiThread(new Runnable() {
602                @Override
603                public void run() {
604                    goBack();
605                }
606            });
607            return;
608        }
609        mAwContents.goBack();
610    }
611
612    @Override
613    public boolean canGoForward() {
614        if (!ThreadUtils.runningOnUiThread()) {
615            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
616                @Override
617                public Boolean call() {
618                    return canGoForward();
619                }
620            });
621            return ret;
622        }
623        return mAwContents.canGoForward();
624    }
625
626    @Override
627    public void goForward() {
628        if (!ThreadUtils.runningOnUiThread()) {
629            ThreadUtils.postOnUiThread(new Runnable() {
630                @Override
631                public void run() {
632                    goForward();
633                }
634            });
635            return;
636        }
637        mAwContents.goForward();
638    }
639
640    @Override
641    public boolean canGoBackOrForward(final int steps) {
642        if (!ThreadUtils.runningOnUiThread()) {
643            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
644                @Override
645                public Boolean call() {
646                    return canGoBackOrForward(steps);
647                }
648            });
649            return ret;
650        }
651        return mAwContents.canGoBackOrForward(steps);
652    }
653
654    @Override
655    public void goBackOrForward(final int steps) {
656        if (!ThreadUtils.runningOnUiThread()) {
657            ThreadUtils.postOnUiThread(new Runnable() {
658                @Override
659                public void run() {
660                    goBackOrForward(steps);
661                }
662            });
663            return;
664        }
665        mAwContents.goBackOrForward(steps);
666    }
667
668    @Override
669    public boolean isPrivateBrowsingEnabled() {
670        // Not supported in this WebView implementation.
671        return false;
672    }
673
674    @Override
675    public boolean pageUp(final boolean top) {
676        if (!ThreadUtils.runningOnUiThread()) {
677            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
678                @Override
679                public Boolean call() {
680                    return pageUp(top);
681                }
682            });
683            return ret;
684        }
685        return mAwContents.pageUp(top);
686    }
687
688    @Override
689    public boolean pageDown(final boolean bottom) {
690        if (!ThreadUtils.runningOnUiThread()) {
691            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
692                @Override
693                public Boolean call() {
694                    return pageDown(bottom);
695                }
696            });
697            return ret;
698        }
699        return mAwContents.pageDown(bottom);
700    }
701
702    @Override
703    public void clearView() {
704        if (!ThreadUtils.runningOnUiThread()) {
705            ThreadUtils.postOnUiThread(new Runnable() {
706                @Override
707                public void run() {
708                    clearView();
709                }
710            });
711            return;
712        }
713        UnimplementedWebViewApi.invoke();
714    }
715
716    @Override
717    public Picture capturePicture() {
718        if (!ThreadUtils.runningOnUiThread()) {
719            Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
720                @Override
721                public Picture call() {
722                    return capturePicture();
723                }
724            });
725            return ret;
726        }
727        return mAwContents.capturePicture();
728    }
729
730    @Override
731    public PrintDocumentAdapter createPrintDocumentAdapter() {
732        checkThread();
733        // TODO(sgurun) fix this after upstream part lands
734        return null;
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