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