WebViewChromium.java revision 446527cc7e4f0a5df72ca9c4d68753a698e2895b
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("Calling View methods on another thread than the UI " +
197                "thread. PLEASE FILE A BUG! go/klp-webview-bug");
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
505    private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
506        if (ThreadUtils.runningOnUiThread()) {
507            mAwContents.loadUrl(loadUrlParams);
508        } else {
509            // Disallowed in WebView API for apps targetting a new SDK
510            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
511            ThreadUtils.postOnUiThread(new Runnable() {
512                @Override
513                public void run() {
514                    mAwContents.loadUrl(loadUrlParams);
515                }
516            });
517        }
518    }
519
520    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
521        checkThread();
522        mAwContents.evaluateJavaScript(script, resultCallback);
523    }
524
525    @Override
526    public void saveWebArchive(String filename) {
527        saveWebArchive(filename, false, null);
528    }
529
530    @Override
531    public void saveWebArchive(final String basename, final boolean autoname,
532            final ValueCallback<String> callback) {
533        if (!ThreadUtils.runningOnUiThread()) {
534            ThreadUtils.postOnUiThread(new Runnable() {
535                @Override
536                public void run() {
537                    saveWebArchive(basename, autoname, callback);
538                }
539            });
540            return;
541        }
542        mAwContents.saveWebArchive(basename, autoname, callback);
543    }
544
545    @Override
546    public void stopLoading() {
547        if (!ThreadUtils.runningOnUiThread()) {
548            ThreadUtils.postOnUiThread(new Runnable() {
549                @Override
550                public void run() {
551                    stopLoading();
552                }
553            });
554            return;
555        }
556
557        mAwContents.stopLoading();
558    }
559
560    @Override
561    public void reload() {
562        if (!ThreadUtils.runningOnUiThread()) {
563            ThreadUtils.postOnUiThread(new Runnable() {
564                @Override
565                public void run() {
566                    reload();
567                }
568            });
569            return;
570        }
571        mAwContents.reload();
572    }
573
574    @Override
575    public boolean canGoBack() {
576        if (!ThreadUtils.runningOnUiThread()) {
577            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
578                @Override
579                public Boolean call() {
580                    return canGoBack();
581                }
582            });
583            return ret;
584        }
585        return mAwContents.canGoBack();
586    }
587
588    @Override
589    public void goBack() {
590        if (!ThreadUtils.runningOnUiThread()) {
591            ThreadUtils.postOnUiThread(new Runnable() {
592                @Override
593                public void run() {
594                    goBack();
595                }
596            });
597            return;
598        }
599        mAwContents.goBack();
600    }
601
602    @Override
603    public boolean canGoForward() {
604        if (!ThreadUtils.runningOnUiThread()) {
605            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
606                @Override
607                public Boolean call() {
608                    return canGoForward();
609                }
610            });
611            return ret;
612        }
613        return mAwContents.canGoForward();
614    }
615
616    @Override
617    public void goForward() {
618        if (!ThreadUtils.runningOnUiThread()) {
619            ThreadUtils.postOnUiThread(new Runnable() {
620                @Override
621                public void run() {
622                    goForward();
623                }
624            });
625            return;
626        }
627        mAwContents.goForward();
628    }
629
630    @Override
631    public boolean canGoBackOrForward(final int steps) {
632        if (!ThreadUtils.runningOnUiThread()) {
633            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
634                @Override
635                public Boolean call() {
636                    return canGoBackOrForward(steps);
637                }
638            });
639            return ret;
640        }
641        return mAwContents.canGoBackOrForward(steps);
642    }
643
644    @Override
645    public void goBackOrForward(final int steps) {
646        if (!ThreadUtils.runningOnUiThread()) {
647            ThreadUtils.postOnUiThread(new Runnable() {
648                @Override
649                public void run() {
650                    goBackOrForward(steps);
651                }
652            });
653            return;
654        }
655        mAwContents.goBackOrForward(steps);
656    }
657
658    @Override
659    public boolean isPrivateBrowsingEnabled() {
660        // Not supported in this WebView implementation.
661        return false;
662    }
663
664    @Override
665    public boolean pageUp(final boolean top) {
666        if (!ThreadUtils.runningOnUiThread()) {
667            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
668                @Override
669                public Boolean call() {
670                    return pageUp(top);
671                }
672            });
673            return ret;
674        }
675        return mAwContents.pageUp(top);
676    }
677
678    @Override
679    public boolean pageDown(final boolean bottom) {
680        if (!ThreadUtils.runningOnUiThread()) {
681            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
682                @Override
683                public Boolean call() {
684                    return pageDown(bottom);
685                }
686            });
687            return ret;
688        }
689        return mAwContents.pageDown(bottom);
690    }
691
692    @Override
693    public void clearView() {
694        if (!ThreadUtils.runningOnUiThread()) {
695            ThreadUtils.postOnUiThread(new Runnable() {
696                @Override
697                public void run() {
698                    clearView();
699                }
700            });
701            return;
702        }
703        UnimplementedWebViewApi.invoke();
704    }
705
706    @Override
707    public Picture capturePicture() {
708        if (!ThreadUtils.runningOnUiThread()) {
709            Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
710                @Override
711                public Picture call() {
712                    return capturePicture();
713                }
714            });
715            return ret;
716        }
717        return mAwContents.capturePicture();
718    }
719
720    @Override
721    public PrintDocumentAdapter createPrintDocumentAdapter() {
722        checkThread();
723        // TODO(sgurun) fix this after upstream part lands
724        return null;
725    }
726
727    @Override
728    public float getScale() {
729        // No checkThread() as it is mostly thread safe (workaround for b/10652991).
730        return mAwContents.getScale();
731    }
732
733    @Override
734    public void setInitialScale(final int scaleInPercent) {
735        if (!ThreadUtils.runningOnUiThread()) {
736            ThreadUtils.postOnUiThread(new Runnable() {
737                @Override
738                public void run() {
739                    setInitialScale(scaleInPercent);
740                }
741            });
742            return;
743        }
744        mAwContents.getSettings().setInitialPageScale(scaleInPercent);
745    }
746
747    @Override
748    public void invokeZoomPicker() {
749        if (!ThreadUtils.runningOnUiThread()) {
750            ThreadUtils.postOnUiThread(new Runnable() {
751                @Override
752                public void run() {
753                    invokeZoomPicker();
754                }
755            });
756            return;
757        }
758        mAwContents.invokeZoomPicker();
759    }
760
761    @Override
762    public WebView.HitTestResult getHitTestResult() {
763        if (!ThreadUtils.runningOnUiThread()) {
764            WebView.HitTestResult ret = runOnUiThreadBlocking(
765                    new Callable<WebView.HitTestResult>() {
766                @Override
767                public WebView.HitTestResult call() {
768                    return getHitTestResult();
769                }
770            });
771            return ret;
772        }
773        AwContents.HitTestData data = mAwContents.getLastHitTestResult();
774        mHitTestResult.setType(data.hitTestResultType);
775        mHitTestResult.setExtra(data.hitTestResultExtraData);
776        return mHitTestResult;
777    }
778
779    @Override
780    public void requestFocusNodeHref(final Message hrefMsg) {
781        if (!ThreadUtils.runningOnUiThread()) {
782            ThreadUtils.postOnUiThread(new Runnable() {
783                @Override
784                public void run() {
785                    requestFocusNodeHref(hrefMsg);
786                }
787            });
788            return;
789        }
790        mAwContents.requestFocusNodeHref(hrefMsg);
791    }
792
793    @Override
794    public void requestImageRef(final Message msg) {
795        if (!ThreadUtils.runningOnUiThread()) {
796            ThreadUtils.postOnUiThread(new Runnable() {
797                @Override
798                public void run() {
799                    requestImageRef(msg);
800                }
801            });
802            return;
803        }
804        mAwContents.requestImageRef(msg);
805    }
806
807    @Override
808    public String getUrl() {
809        if (!ThreadUtils.runningOnUiThread()) {
810            String ret = runOnUiThreadBlocking(new Callable<String>() {
811                @Override
812                public String call() {
813                    return getUrl();
814                }
815            });
816            return ret;
817        }
818        String url =  mAwContents.getUrl();
819        if (url == null || url.trim().isEmpty()) return null;
820        return url;
821    }
822
823    @Override
824    public String getOriginalUrl() {
825        if (!ThreadUtils.runningOnUiThread()) {
826            String ret = runOnUiThreadBlocking(new Callable<String>() {
827                @Override
828                public String call() {
829                    return getOriginalUrl();
830                }
831            });
832            return ret;
833        }
834        String url =  mAwContents.getOriginalUrl();
835        if (url == null || url.trim().isEmpty()) return null;
836        return url;
837    }
838
839    @Override
840    public String getTitle() {
841        if (!ThreadUtils.runningOnUiThread()) {
842            String ret = runOnUiThreadBlocking(new Callable<String>() {
843                @Override
844                public String call() {
845                    return getTitle();
846                }
847            });
848            return ret;
849        }
850        return mAwContents.getTitle();
851    }
852
853    @Override
854    public Bitmap getFavicon() {
855        if (!ThreadUtils.runningOnUiThread()) {
856            Bitmap ret = runOnUiThreadBlocking(new Callable<Bitmap>() {
857                @Override
858                public Bitmap call() {
859                    return getFavicon();
860                }
861            });
862            return ret;
863        }
864        return mAwContents.getFavicon();
865    }
866
867    @Override
868    public String getTouchIconUrl() {
869        // Intentional no-op: hidden method on WebView.
870        return null;
871    }
872
873    @Override
874    public int getProgress() {
875        // No checkThread() because the value is cached java side (workaround for b/10533304).
876        return mAwContents.getMostRecentProgress();
877    }
878
879    @Override
880    public int getContentHeight() {
881        // No checkThread() as it is mostly thread safe (workaround for b/10594869).
882        return mAwContents.getContentHeightCss();
883    }
884
885    @Override
886    public int getContentWidth() {
887        // No checkThread() as it is mostly thread safe (workaround for b/10594869).
888        return mAwContents.getContentWidthCss();
889    }
890
891    @Override
892    public void pauseTimers() {
893        if (!ThreadUtils.runningOnUiThread()) {
894            ThreadUtils.postOnUiThread(new Runnable() {
895                @Override
896                public void run() {
897                    pauseTimers();
898                }
899            });
900            return;
901        }
902        mAwContents.pauseTimers();
903    }
904
905    @Override
906    public void resumeTimers() {
907        if (!ThreadUtils.runningOnUiThread()) {
908            ThreadUtils.postOnUiThread(new Runnable() {
909                @Override
910                public void run() {
911                    resumeTimers();
912                }
913            });
914            return;
915        }
916        mAwContents.resumeTimers();
917    }
918
919    @Override
920    public void onPause() {
921        if (!ThreadUtils.runningOnUiThread()) {
922            ThreadUtils.postOnUiThread(new Runnable() {
923                @Override
924                public void run() {
925                    onPause();
926                }
927            });
928            return;
929        }
930        mAwContents.onPause();
931    }
932
933    @Override
934    public void onResume() {
935        if (!ThreadUtils.runningOnUiThread()) {
936            ThreadUtils.postOnUiThread(new Runnable() {
937                @Override
938                public void run() {
939                    onResume();
940                }
941            });
942            return;
943        }
944        mAwContents.onResume();
945    }
946
947    @Override
948    public boolean isPaused() {
949        if (!ThreadUtils.runningOnUiThread()) {
950            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
951                @Override
952                public Boolean call() {
953                    return isPaused();
954                }
955            });
956            return ret;
957        }
958        return mAwContents.isPaused();
959    }
960
961    @Override
962    public void freeMemory() {
963        // Intentional no-op. Memory is managed automatically by Chromium.
964    }
965
966    @Override
967    public void clearCache(final boolean includeDiskFiles) {
968        if (!ThreadUtils.runningOnUiThread()) {
969            ThreadUtils.postOnUiThread(new Runnable() {
970                @Override
971                public void run() {
972                    clearCache(includeDiskFiles);
973                }
974            });
975            return;
976        }
977        mAwContents.clearCache(includeDiskFiles);
978    }
979
980    /**
981     * This is a poorly named method, but we keep it for historical reasons.
982     */
983    @Override
984    public void clearFormData() {
985        if (!ThreadUtils.runningOnUiThread()) {
986            ThreadUtils.postOnUiThread(new Runnable() {
987                @Override
988                public void run() {
989                    clearFormData();
990                }
991            });
992            return;
993        }
994        mAwContents.hideAutofillPopup();
995    }
996
997    @Override
998    public void clearHistory() {
999        if (!ThreadUtils.runningOnUiThread()) {
1000            ThreadUtils.postOnUiThread(new Runnable() {
1001                @Override
1002                public void run() {
1003                    clearHistory();
1004                }
1005            });
1006            return;
1007        }
1008        mAwContents.clearHistory();
1009    }
1010
1011    @Override
1012    public void clearSslPreferences() {
1013        if (!ThreadUtils.runningOnUiThread()) {
1014            ThreadUtils.postOnUiThread(new Runnable() {
1015                @Override
1016                public void run() {
1017                    clearSslPreferences();
1018                }
1019            });
1020            return;
1021        }
1022        mAwContents.clearSslPreferences();
1023    }
1024
1025    @Override
1026    public WebBackForwardList copyBackForwardList() {
1027        if (!ThreadUtils.runningOnUiThread()) {
1028            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
1029                @Override
1030                public WebBackForwardList call() {
1031                    return copyBackForwardList();
1032                }
1033            });
1034            return ret;
1035        }
1036        return new WebBackForwardListChromium(
1037                mAwContents.getNavigationHistory());
1038    }
1039
1040    @Override
1041    public void setFindListener(WebView.FindListener listener) {
1042        mContentsClientAdapter.setFindListener(listener);
1043    }
1044
1045    @Override
1046    public void findNext(final boolean forwards) {
1047        if (!ThreadUtils.runningOnUiThread()) {
1048            ThreadUtils.postOnUiThread(new Runnable() {
1049                @Override
1050                public void run() {
1051                    findNext(forwards);
1052                }
1053            });
1054            return;
1055        }
1056        mAwContents.findNext(forwards);
1057    }
1058
1059    @Override
1060    public int findAll(final String searchString) {
1061        findAllAsync(searchString);
1062        return 0;
1063    }
1064
1065    @Override
1066    public void findAllAsync(final String searchString) {
1067        if (!ThreadUtils.runningOnUiThread()) {
1068            ThreadUtils.postOnUiThread(new Runnable() {
1069                @Override
1070                public void run() {
1071                    findAllAsync(searchString);
1072                }
1073            });
1074            return;
1075        }
1076        mAwContents.findAllAsync(searchString);
1077    }
1078
1079    @Override
1080    public boolean showFindDialog(final String text, final boolean showIme) {
1081        if (!ThreadUtils.runningOnUiThread()) {
1082            return false;
1083        }
1084        if (mWebView.getParent() == null) {
1085            return false;
1086        }
1087
1088        FindActionModeCallback findAction = new FindActionModeCallback(mWebView.getContext());
1089        if (findAction == null) {
1090            return false;
1091        }
1092
1093        mWebView.startActionMode(findAction);
1094        findAction.setWebView(mWebView);
1095        if (showIme) {
1096            findAction.showSoftInput();
1097        }
1098
1099        if (text != null) {
1100            findAction.setText(text);
1101            findAction.findAll();
1102        }
1103
1104        return true;
1105    }
1106
1107    @Override
1108    public void notifyFindDialogDismissed() {
1109        if (!ThreadUtils.runningOnUiThread()) {
1110            ThreadUtils.postOnUiThread(new Runnable() {
1111                @Override
1112                public void run() {
1113                    notifyFindDialogDismissed();
1114                }
1115            });
1116            return;
1117        }
1118        clearMatches();
1119    }
1120
1121    @Override
1122    public void clearMatches() {
1123        if (!ThreadUtils.runningOnUiThread()) {
1124            ThreadUtils.postOnUiThread(new Runnable() {
1125                @Override
1126                public void run() {
1127                    clearMatches();
1128                }
1129            });
1130            return;
1131        }
1132        mAwContents.clearMatches();
1133    }
1134
1135    @Override
1136    public void documentHasImages(final Message response) {
1137        if (!ThreadUtils.runningOnUiThread()) {
1138            ThreadUtils.postOnUiThread(new Runnable() {
1139                @Override
1140                public void run() {
1141                    documentHasImages(response);
1142                }
1143            });
1144            return;
1145        }
1146        mAwContents.documentHasImages(response);
1147    }
1148
1149    @Override
1150    public void setWebViewClient(WebViewClient client) {
1151        mContentsClientAdapter.setWebViewClient(client);
1152    }
1153
1154    @Override
1155    public void setDownloadListener(DownloadListener listener) {
1156        mContentsClientAdapter.setDownloadListener(listener);
1157    }
1158
1159    @Override
1160    public void setWebChromeClient(WebChromeClient client) {
1161        mContentsClientAdapter.setWebChromeClient(client);
1162    }
1163
1164    @Override
1165    public void setPictureListener(final WebView.PictureListener listener) {
1166        if (!ThreadUtils.runningOnUiThread()) {
1167            ThreadUtils.postOnUiThread(new Runnable() {
1168                @Override
1169                public void run() {
1170                    setPictureListener(listener);
1171                }
1172            });
1173            return;
1174        }
1175        mContentsClientAdapter.setPictureListener(listener);
1176        mAwContents.enableOnNewPicture(listener != null,
1177                mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2);
1178    }
1179
1180    @Override
1181    public void addJavascriptInterface(final Object obj, final String interfaceName) {
1182        if (!ThreadUtils.runningOnUiThread()) {
1183            ThreadUtils.postOnUiThread(new Runnable() {
1184                @Override
1185                public void run() {
1186                    addJavascriptInterface(obj, interfaceName);
1187                }
1188            });
1189            return;
1190        }
1191        Class<? extends Annotation> requiredAnnotation = null;
1192        if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
1193           requiredAnnotation = JavascriptInterface.class;
1194        }
1195        mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation);
1196    }
1197
1198    @Override
1199    public void removeJavascriptInterface(final String interfaceName) {
1200        if (!ThreadUtils.runningOnUiThread()) {
1201            ThreadUtils.postOnUiThread(new Runnable() {
1202                @Override
1203                public void run() {
1204                    removeJavascriptInterface(interfaceName);
1205                }
1206            });
1207            return;
1208        }
1209        mAwContents.removeJavascriptInterface(interfaceName);
1210    }
1211
1212    @Override
1213    public WebSettings getSettings() {
1214        return mWebSettings;
1215    }
1216
1217    @Override
1218    public void setMapTrackballToArrowKeys(boolean setMap) {
1219        // This is a deprecated API: intentional no-op.
1220    }
1221
1222    @Override
1223    public void flingScroll(final int vx, final int vy) {
1224        if (!ThreadUtils.runningOnUiThread()) {
1225            ThreadUtils.postOnUiThread(new Runnable() {
1226                @Override
1227                public void run() {
1228                    flingScroll(vx, vy);
1229                }
1230            });
1231            return;
1232        }
1233        mAwContents.flingScroll(vx, vy);
1234    }
1235
1236    @Override
1237    public View getZoomControls() {
1238        if (!ThreadUtils.runningOnUiThread()) {
1239            return null;
1240        }
1241
1242        // This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed
1243        // to stop very out-dated applications from crashing.
1244        Log.w(TAG, "WebView doesn't support getZoomControls");
1245        return mAwContents.getSettings().supportZoom() ? new View(mWebView.getContext()) : null;
1246    }
1247
1248    @Override
1249    public boolean canZoomIn() {
1250        if (!ThreadUtils.runningOnUiThread()) {
1251            return false;
1252        }
1253        return mAwContents.canZoomIn();
1254    }
1255
1256    @Override
1257    public boolean canZoomOut() {
1258        if (!ThreadUtils.runningOnUiThread()) {
1259            return false;
1260        }
1261        return mAwContents.canZoomOut();
1262    }
1263
1264    @Override
1265    public boolean zoomIn() {
1266        if (!ThreadUtils.runningOnUiThread()) {
1267            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1268                @Override
1269                public Boolean call() {
1270                    return zoomIn();
1271                }
1272            });
1273            return ret;
1274        }
1275        return mAwContents.zoomIn();
1276    }
1277
1278    @Override
1279    public boolean zoomOut() {
1280        if (!ThreadUtils.runningOnUiThread()) {
1281            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1282                @Override
1283                public Boolean call() {
1284                    return zoomOut();
1285                }
1286            });
1287            return ret;
1288        }
1289        return mAwContents.zoomOut();
1290    }
1291
1292    @Override
1293    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
1294        UnimplementedWebViewApi.invoke();
1295    }
1296
1297    @Override
1298    public View findHierarchyView(String className, int hashCode) {
1299        UnimplementedWebViewApi.invoke();
1300        return null;
1301    }
1302
1303    // WebViewProvider glue methods ---------------------------------------------------------------
1304
1305    @Override
1306    // This needs to be kept thread safe!
1307    public WebViewProvider.ViewDelegate getViewDelegate() {
1308        return this;
1309    }
1310
1311    @Override
1312    // This needs to be kept thread safe!
1313    public WebViewProvider.ScrollDelegate getScrollDelegate() {
1314        return this;
1315    }
1316
1317
1318    // WebViewProvider.ViewDelegate implementation ------------------------------------------------
1319
1320    // TODO: remove from WebViewProvider and use default implementation from
1321    // ViewGroup.
1322    // @Override
1323    public boolean shouldDelayChildPressedState() {
1324        if (!ThreadUtils.runningOnUiThread()) {
1325            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1326                @Override
1327                public Boolean call() {
1328                    return shouldDelayChildPressedState();
1329                }
1330            });
1331            return ret;
1332        }
1333        return true;
1334    }
1335
1336//    @Override
1337    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1338        if (!ThreadUtils.runningOnUiThread()) {
1339            AccessibilityNodeProvider ret = runOnUiThreadBlocking(
1340                    new Callable<AccessibilityNodeProvider>() {
1341                @Override
1342                public AccessibilityNodeProvider call() {
1343                    return getAccessibilityNodeProvider();
1344                }
1345            });
1346            return ret;
1347        }
1348        return mAwContents.getAccessibilityNodeProvider();
1349    }
1350
1351    @Override
1352        public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) {
1353        if (!ThreadUtils.runningOnUiThread()) {
1354            runVoidTaskOnUiThreadBlocking(new Runnable() {
1355                @Override
1356                public void run() {
1357                    onInitializeAccessibilityNodeInfo(info);
1358                }
1359            });
1360            return;
1361        }
1362        mAwContents.onInitializeAccessibilityNodeInfo(info);
1363    }
1364
1365    @Override
1366    public void onInitializeAccessibilityEvent(final AccessibilityEvent event) {
1367        if (!ThreadUtils.runningOnUiThread()) {
1368            runVoidTaskOnUiThreadBlocking(new Runnable() {
1369                @Override
1370                public void run() {
1371                    onInitializeAccessibilityEvent(event);
1372                }
1373            });
1374            return;
1375        }
1376        mAwContents.onInitializeAccessibilityEvent(event);
1377    }
1378
1379    @Override
1380    public boolean performAccessibilityAction(final int action, final Bundle arguments) {
1381        if (!ThreadUtils.runningOnUiThread()) {
1382            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1383                @Override
1384                public Boolean call() {
1385                    return performAccessibilityAction(action, arguments);
1386                }
1387            });
1388            return ret;
1389        }
1390        if (mAwContents.supportsAccessibilityAction(action)) {
1391            return mAwContents.performAccessibilityAction(action, arguments);
1392        }
1393        return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
1394    }
1395
1396    @Override
1397    public void setOverScrollMode(final int mode) {
1398        if (!ThreadUtils.runningOnUiThread()) {
1399            ThreadUtils.postOnUiThread(new Runnable() {
1400                @Override
1401                public void run() {
1402                    setOverScrollMode(mode);
1403                }
1404            });
1405            return;
1406        }
1407        // This gets called from the android.view.View c'tor that WebView inherits from. This
1408        // causes the method to be called when mAwContents == null.
1409        // It's safe to ignore these calls however since AwContents will read the current value of
1410        // this setting when it's created.
1411        if (mAwContents != null) {
1412            mAwContents.setOverScrollMode(mode);
1413        }
1414    }
1415
1416    @Override
1417    public void setScrollBarStyle(final int style) {
1418        if (!ThreadUtils.runningOnUiThread()) {
1419            ThreadUtils.postOnUiThread(new Runnable() {
1420                @Override
1421                public void run() {
1422                    setScrollBarStyle(style);
1423                }
1424            });
1425            return;
1426        }
1427        mAwContents.setScrollBarStyle(style);
1428    }
1429
1430    @Override
1431    public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l,
1432            final int t, final int r, final int b) {
1433        if (!ThreadUtils.runningOnUiThread()) {
1434            runVoidTaskOnUiThreadBlocking(new Runnable() {
1435                @Override
1436                public void run() {
1437                    onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
1438                }
1439            });
1440            return;
1441        }
1442        // WebViewClassic was overriding this method to handle rubberband over-scroll. Since
1443        // WebViewChromium doesn't support that the vanilla implementation of this method can be
1444        // used.
1445        mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
1446    }
1447
1448    @Override
1449    public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
1450            final boolean clampedY) {
1451        if (!ThreadUtils.runningOnUiThread()) {
1452            ThreadUtils.postOnUiThread(new Runnable() {
1453                @Override
1454                public void run() {
1455                    onOverScrolled(scrollX, scrollY, clampedX, clampedY);
1456                }
1457            });
1458            return;
1459        }
1460        mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
1461    }
1462
1463    @Override
1464    public void onWindowVisibilityChanged(final int visibility) {
1465        if (!ThreadUtils.runningOnUiThread()) {
1466            ThreadUtils.postOnUiThread(new Runnable() {
1467                @Override
1468                public void run() {
1469                    onWindowVisibilityChanged(visibility);
1470                }
1471            });
1472            return;
1473        }
1474        mAwContents.onWindowVisibilityChanged(visibility);
1475    }
1476
1477    @Override
1478    public void onDraw(final Canvas canvas) {
1479        if (!ThreadUtils.runningOnUiThread()) {
1480            runVoidTaskOnUiThreadBlocking(new Runnable() {
1481                @Override
1482                public void run() {
1483                    onDraw(canvas);
1484                }
1485            });
1486            return;
1487        }
1488        mAwContents.onDraw(canvas);
1489    }
1490
1491    @Override
1492    public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) {
1493        if (!ThreadUtils.runningOnUiThread()) {
1494            ThreadUtils.postOnUiThread(new Runnable() {
1495                @Override
1496                public void run() {
1497                    setLayoutParams(layoutParams);
1498                }
1499            });
1500            return;
1501        }
1502        mWebViewPrivate.super_setLayoutParams(layoutParams);
1503    }
1504
1505    @Override
1506    public boolean performLongClick() {
1507        if (!ThreadUtils.runningOnUiThread()) {
1508            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1509                @Override
1510                public Boolean call() {
1511                    return performLongClick();
1512                }
1513            });
1514            return ret;
1515        }
1516        return mWebViewPrivate.super_performLongClick();
1517    }
1518
1519    @Override
1520    public void onConfigurationChanged(final Configuration newConfig) {
1521        if (!ThreadUtils.runningOnUiThread()) {
1522            ThreadUtils.postOnUiThread(new Runnable() {
1523                @Override
1524                public void run() {
1525                    onConfigurationChanged(newConfig);
1526                }
1527            });
1528            return;
1529        }
1530        mAwContents.onConfigurationChanged(newConfig);
1531    }
1532
1533    @Override
1534    public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
1535        if (!ThreadUtils.runningOnUiThread()) {
1536            InputConnection ret = runOnUiThreadBlocking(new Callable<InputConnection>() {
1537                @Override
1538                public InputConnection call() {
1539                    return onCreateInputConnection(outAttrs);
1540                }
1541            });
1542            return ret;
1543        }
1544        return mAwContents.onCreateInputConnection(outAttrs);
1545    }
1546
1547    @Override
1548    public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) {
1549        if (!ThreadUtils.runningOnUiThread()) {
1550            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1551                @Override
1552                public Boolean call() {
1553                    return onKeyMultiple(keyCode, repeatCount, event);
1554                }
1555            });
1556            return ret;
1557        }
1558        UnimplementedWebViewApi.invoke();
1559        return false;
1560    }
1561
1562    @Override
1563    public boolean onKeyDown(final int keyCode, final KeyEvent event) {
1564        if (!ThreadUtils.runningOnUiThread()) {
1565            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1566                @Override
1567                public Boolean call() {
1568                    return onKeyDown(keyCode, event);
1569                }
1570            });
1571            return ret;
1572        }
1573        UnimplementedWebViewApi.invoke();
1574        return false;
1575    }
1576
1577    @Override
1578    public boolean onKeyUp(final int keyCode, final KeyEvent event) {
1579        if (!ThreadUtils.runningOnUiThread()) {
1580            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1581                @Override
1582                public Boolean call() {
1583                    return onKeyUp(keyCode, event);
1584                }
1585            });
1586            return ret;
1587        }
1588        return mAwContents.onKeyUp(keyCode, event);
1589    }
1590
1591    @Override
1592    public void onAttachedToWindow() {
1593        if (!ThreadUtils.runningOnUiThread()) {
1594            ThreadUtils.postOnUiThread(new Runnable() {
1595                @Override
1596                public void run() {
1597                    onAttachedToWindow();
1598                }
1599            });
1600            return;
1601        }
1602        mAwContents.onAttachedToWindow();
1603    }
1604
1605    @Override
1606    public void onDetachedFromWindow() {
1607        if (!ThreadUtils.runningOnUiThread()) {
1608            ThreadUtils.postOnUiThread(new Runnable() {
1609                @Override
1610                public void run() {
1611                    onDetachedFromWindow();
1612                }
1613            });
1614            return;
1615        }
1616        mAwContents.onDetachedFromWindow();
1617        if (mGLfunctor != null) {
1618            mGLfunctor.detach();
1619        }
1620    }
1621
1622    @Override
1623    public void onVisibilityChanged(final View changedView, final int visibility) {
1624        if (!ThreadUtils.runningOnUiThread()) {
1625            ThreadUtils.postOnUiThread(new Runnable() {
1626                @Override
1627                public void run() {
1628                    onVisibilityChanged(changedView, visibility);
1629                }
1630            });
1631            return;
1632        }
1633        // The AwContents will find out the container view visibility before the first draw so we
1634        // can safely ignore onVisibilityChanged callbacks that happen before init().
1635        if (mAwContents != null) {
1636            mAwContents.onVisibilityChanged(changedView, visibility);
1637        }
1638    }
1639
1640    @Override
1641    public void onWindowFocusChanged(final boolean hasWindowFocus) {
1642        if (!ThreadUtils.runningOnUiThread()) {
1643            ThreadUtils.postOnUiThread(new Runnable() {
1644                @Override
1645                public void run() {
1646                    onWindowFocusChanged(hasWindowFocus);
1647                }
1648            });
1649            return;
1650        }
1651        mAwContents.onWindowFocusChanged(hasWindowFocus);
1652    }
1653
1654    @Override
1655    public void onFocusChanged(final boolean focused, final int direction,
1656            final Rect previouslyFocusedRect) {
1657        if (!ThreadUtils.runningOnUiThread()) {
1658            ThreadUtils.postOnUiThread(new Runnable() {
1659                @Override
1660                public void run() {
1661                    onFocusChanged(focused, direction, previouslyFocusedRect);
1662                }
1663            });
1664            return;
1665        }
1666        mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
1667    }
1668
1669    @Override
1670    public boolean setFrame(final int left, final int top, final int right, final int bottom) {
1671        if (!ThreadUtils.runningOnUiThread()) {
1672            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1673                @Override
1674                public Boolean call() {
1675                    return setFrame(left, top, right, bottom);
1676                }
1677            });
1678            return ret;
1679        }
1680        return mWebViewPrivate.super_setFrame(left, top, right, bottom);
1681    }
1682
1683    @Override
1684    public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
1685        if (!ThreadUtils.runningOnUiThread()) {
1686            ThreadUtils.postOnUiThread(new Runnable() {
1687                @Override
1688                public void run() {
1689                    onSizeChanged(w, h, ow, oh);
1690                }
1691            });
1692            return;
1693        }
1694        mAwContents.onSizeChanged(w, h, ow, oh);
1695    }
1696
1697    @Override
1698    public void onScrollChanged(int l, int t, int oldl, int oldt) {
1699    }
1700
1701    @Override
1702    public boolean dispatchKeyEvent(final KeyEvent event) {
1703        if (!ThreadUtils.runningOnUiThread()) {
1704            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1705                @Override
1706                public Boolean call() {
1707                    return dispatchKeyEvent(event);
1708                }
1709            });
1710            return ret;
1711        }
1712        return mAwContents.dispatchKeyEvent(event);
1713    }
1714
1715    @Override
1716    public boolean onTouchEvent(final MotionEvent ev) {
1717        if (!ThreadUtils.runningOnUiThread()) {
1718            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1719                @Override
1720                public Boolean call() {
1721                    return onTouchEvent(ev);
1722                }
1723            });
1724            return ret;
1725        }
1726        return mAwContents.onTouchEvent(ev);
1727    }
1728
1729    @Override
1730    public boolean onHoverEvent(final MotionEvent event) {
1731        if (!ThreadUtils.runningOnUiThread()) {
1732            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1733                @Override
1734                public Boolean call() {
1735                    return onHoverEvent(event);
1736                }
1737            });
1738            return ret;
1739        }
1740        return mAwContents.onHoverEvent(event);
1741    }
1742
1743    @Override
1744    public boolean onGenericMotionEvent(final MotionEvent event) {
1745        if (!ThreadUtils.runningOnUiThread()) {
1746            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1747                @Override
1748                public Boolean call() {
1749                    return onGenericMotionEvent(event);
1750                }
1751            });
1752            return ret;
1753        }
1754        return mAwContents.onGenericMotionEvent(event);
1755    }
1756
1757    @Override
1758    public boolean onTrackballEvent(MotionEvent ev) {
1759        // Trackball event not handled, which eventually gets converted to DPAD keyevents
1760        return false;
1761    }
1762
1763    @Override
1764    public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
1765        if (!ThreadUtils.runningOnUiThread()) {
1766            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1767                @Override
1768                public Boolean call() {
1769                    return requestFocus(direction, previouslyFocusedRect);
1770                }
1771            });
1772            return ret;
1773        }
1774        mAwContents.requestFocus();
1775        return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
1776    }
1777
1778    @Override
1779    public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
1780        if (!ThreadUtils.runningOnUiThread()) {
1781            ThreadUtils.postOnUiThread(new Runnable() {
1782                @Override
1783                public void run() {
1784                    onMeasure(widthMeasureSpec, heightMeasureSpec);
1785                }
1786            });
1787            return;
1788        }
1789        mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
1790    }
1791
1792    @Override
1793    public boolean requestChildRectangleOnScreen(final View child, final Rect rect,
1794            final boolean immediate) {
1795        if (!ThreadUtils.runningOnUiThread()) {
1796            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1797                @Override
1798                public Boolean call() {
1799                    return requestChildRectangleOnScreen(child, rect, immediate);
1800                }
1801            });
1802            return ret;
1803        }
1804        return mAwContents.requestChildRectangleOnScreen(child, rect, immediate);
1805    }
1806
1807    @Override
1808    public void setBackgroundColor(final int color) {
1809        if (!ThreadUtils.runningOnUiThread()) {
1810            ThreadUtils.postOnUiThread(new Runnable() {
1811                @Override
1812                public void run() {
1813                    setBackgroundColor(color);
1814                }
1815            });
1816            return;
1817        }
1818        mAwContents.setBackgroundColor(color);
1819    }
1820
1821    @Override
1822    public void setLayerType(int layerType, Paint paint) {
1823        UnimplementedWebViewApi.invoke();
1824    }
1825
1826    // Remove from superclass
1827    public void preDispatchDraw(Canvas canvas) {
1828        // TODO(leandrogracia): remove this method from WebViewProvider if we think
1829        // we won't need it again.
1830    }
1831
1832    // WebViewProvider.ScrollDelegate implementation ----------------------------------------------
1833
1834    @Override
1835    public int computeHorizontalScrollRange() {
1836        if (!ThreadUtils.runningOnUiThread()) {
1837            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1838                @Override
1839                public Integer call() {
1840                    return computeHorizontalScrollRange();
1841                }
1842            });
1843            return ret;
1844        }
1845        return mAwContents.computeHorizontalScrollRange();
1846    }
1847
1848    @Override
1849    public int computeHorizontalScrollOffset() {
1850        if (!ThreadUtils.runningOnUiThread()) {
1851            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1852                @Override
1853                public Integer call() {
1854                    return computeHorizontalScrollOffset();
1855                }
1856            });
1857            return ret;
1858        }
1859        return mAwContents.computeHorizontalScrollOffset();
1860    }
1861
1862    @Override
1863    public int computeVerticalScrollRange() {
1864        if (!ThreadUtils.runningOnUiThread()) {
1865            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1866                @Override
1867                public Integer call() {
1868                    return computeVerticalScrollRange();
1869                }
1870            });
1871            return ret;
1872        }
1873        return mAwContents.computeVerticalScrollRange();
1874    }
1875
1876    @Override
1877    public int computeVerticalScrollOffset() {
1878        if (!ThreadUtils.runningOnUiThread()) {
1879            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1880                @Override
1881                public Integer call() {
1882                    return computeVerticalScrollOffset();
1883                }
1884            });
1885            return ret;
1886        }
1887        return mAwContents.computeVerticalScrollOffset();
1888    }
1889
1890    @Override
1891    public int computeVerticalScrollExtent() {
1892        if (!ThreadUtils.runningOnUiThread()) {
1893            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1894                @Override
1895                public Integer call() {
1896                    return computeVerticalScrollExtent();
1897                }
1898            });
1899            return ret;
1900        }
1901        return mAwContents.computeVerticalScrollExtent();
1902    }
1903
1904    @Override
1905    public void computeScroll() {
1906        if (!ThreadUtils.runningOnUiThread()) {
1907            ThreadUtils.postOnUiThread(new Runnable() {
1908                @Override
1909                public void run() {
1910                    computeScroll();
1911                }
1912            });
1913            return;
1914        }
1915        mAwContents.computeScroll();
1916    }
1917
1918    // AwContents.InternalAccessDelegate implementation --------------------------------------
1919    private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
1920        @Override
1921        public boolean drawChild(Canvas arg0, View arg1, long arg2) {
1922            UnimplementedWebViewApi.invoke();
1923            return false;
1924        }
1925
1926        @Override
1927        public boolean super_onKeyUp(int arg0, KeyEvent arg1) {
1928            UnimplementedWebViewApi.invoke();
1929            return false;
1930        }
1931
1932        @Override
1933        public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) {
1934            UnimplementedWebViewApi.invoke();
1935            return false;
1936        }
1937
1938        @Override
1939        public boolean super_dispatchKeyEvent(KeyEvent event) {
1940            return mWebViewPrivate.super_dispatchKeyEvent(event);
1941        }
1942
1943        @Override
1944        public boolean super_onGenericMotionEvent(MotionEvent arg0) {
1945            UnimplementedWebViewApi.invoke();
1946            return false;
1947        }
1948
1949        @Override
1950        public void super_onConfigurationChanged(Configuration arg0) {
1951            UnimplementedWebViewApi.invoke();
1952        }
1953
1954        @Override
1955        public int super_getScrollBarStyle() {
1956            return mWebViewPrivate.super_getScrollBarStyle();
1957        }
1958
1959        @Override
1960        public boolean awakenScrollBars() {
1961            mWebViewPrivate.awakenScrollBars(0);
1962            // TODO: modify the WebView.PrivateAccess to provide a return value.
1963            return true;
1964        }
1965
1966        @Override
1967        public boolean super_awakenScrollBars(int arg0, boolean arg1) {
1968            // TODO: need method on WebView.PrivateAccess?
1969            UnimplementedWebViewApi.invoke();
1970            return false;
1971        }
1972
1973        @Override
1974        public void onScrollChanged(int l, int t, int oldl, int oldt) {
1975            mWebViewPrivate.setScrollXRaw(l);
1976            mWebViewPrivate.setScrollYRaw(t);
1977            mWebViewPrivate.onScrollChanged(l, t, oldl, oldt);
1978        }
1979
1980        @Override
1981        public void overScrollBy(int deltaX, int deltaY,
1982            int scrollX, int scrollY,
1983            int scrollRangeX, int scrollRangeY,
1984            int maxOverScrollX, int maxOverScrollY,
1985            boolean isTouchEvent) {
1986            mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY,
1987                    scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
1988        }
1989
1990        @Override
1991        public void super_scrollTo(int scrollX, int scrollY) {
1992            mWebViewPrivate.super_scrollTo(scrollX, scrollY);
1993        }
1994
1995        @Override
1996        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
1997            mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
1998        }
1999
2000        @Override
2001        public boolean requestDrawGL(Canvas canvas) {
2002            if (mGLfunctor == null) {
2003                mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext());
2004            }
2005            return mGLfunctor.requestDrawGL((HardwareCanvas)canvas, mWebView.getViewRootImpl());
2006        }
2007    }
2008}
2009