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