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