WebViewChromium.java revision c0bac61af98d8597256114c3366cd23051ed2674
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.Uri;
27import android.net.http.SslCertificate;
28import android.os.Build;
29import android.os.Bundle;
30import android.os.Looper;
31import android.os.Message;
32import android.print.PrintDocumentAdapter;
33import android.text.TextUtils;
34import android.util.Base64;
35import android.util.Log;
36import android.view.HardwareCanvas;
37import android.view.KeyEvent;
38import android.view.MotionEvent;
39import android.view.View;
40import android.view.View.MeasureSpec;
41import android.view.ViewGroup;
42import android.view.accessibility.AccessibilityEvent;
43import android.view.accessibility.AccessibilityNodeInfo;
44import android.view.accessibility.AccessibilityNodeProvider;
45import android.view.inputmethod.EditorInfo;
46import android.view.inputmethod.InputConnection;
47import android.webkit.DownloadListener;
48import android.webkit.FindActionModeCallback;
49import android.webkit.JavascriptInterface;
50import android.webkit.ValueCallback;
51import android.webkit.WebBackForwardList;
52import android.webkit.WebChromeClient;
53import android.webkit.WebSettings;
54import android.webkit.WebView;
55import android.webkit.WebViewClient;
56import android.webkit.WebViewProvider;
57import android.widget.TextView;
58
59import org.chromium.android_webview.AwBrowserContext;
60import org.chromium.android_webview.AwContents;
61import org.chromium.android_webview.AwLayoutSizer;
62import org.chromium.android_webview.AwSettings;
63import org.chromium.android_webview.AwPrintDocumentAdapter;
64import org.chromium.base.ThreadUtils;
65import org.chromium.content.browser.LoadUrlParams;
66import org.chromium.net.NetworkChangeNotifier;
67
68import java.io.BufferedWriter;
69import java.io.File;
70import java.lang.annotation.Annotation;
71import java.util.concurrent.Callable;
72import java.util.concurrent.ConcurrentLinkedQueue;
73import java.util.concurrent.FutureTask;
74import java.util.concurrent.TimeUnit;
75import java.util.HashMap;
76import java.util.Map;
77import java.util.Queue;
78
79/**
80 * This class is the delegate to which WebViewProxy forwards all API calls.
81 *
82 * Most of the actual functionality is implemented by AwContents (or ContentViewCore within
83 * it). This class also contains WebView-specific APIs that require the creation of other
84 * adapters (otherwise org.chromium.content would depend on the webview.chromium package)
85 * and a small set of no-op deprecated APIs.
86 */
87class WebViewChromium implements WebViewProvider,
88          WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate {
89
90    private class WebViewChromiumRunQueue {
91        public WebViewChromiumRunQueue() {
92            mQueue = new ConcurrentLinkedQueue<Runnable>();
93        }
94
95        public void addTask(Runnable task) {
96            mQueue.add(task);
97            if (mFactory.hasStarted()) {
98                ThreadUtils.runOnUiThread(new Runnable() {
99                    @Override
100                    public void run() {
101                        drainQueue();
102                    }
103                });
104            }
105        }
106
107        public void drainQueue() {
108            if (mQueue == null || mQueue.isEmpty()) {
109                return;
110            }
111
112            Runnable task = mQueue.poll();
113            while(task != null) {
114                task.run();
115                task = mQueue.poll();
116            }
117        }
118
119        private Queue<Runnable> mQueue;
120    }
121
122    private WebViewChromiumRunQueue mRunQueue;
123
124    private static final String TAG = WebViewChromium.class.getSimpleName();
125
126    // The WebView that this WebViewChromium is the provider for.
127    WebView mWebView;
128    // Lets us access protected View-derived methods on the WebView instance we're backing.
129    WebView.PrivateAccess mWebViewPrivate;
130    // The client adapter class.
131    private WebViewContentsClientAdapter mContentsClientAdapter;
132
133    // Variables for functionality provided by this adapter ---------------------------------------
134    private ContentSettingsAdapter mWebSettings;
135    // The WebView wrapper for ContentViewCore and required browser compontents.
136    private AwContents mAwContents;
137    // Non-null if this webview is using the GL accelerated draw path.
138    private DrawGLFunctor mGLfunctor;
139
140    private final WebView.HitTestResult mHitTestResult;
141
142    private final int mAppTargetSdkVersion;
143
144    private WebViewChromiumFactoryProvider mFactory;
145
146    // This does not touch any global / non-threadsafe state, but note that
147    // init is ofter called right after and is NOT threadsafe.
148    public WebViewChromium(WebViewChromiumFactoryProvider factory, WebView webView,
149            WebView.PrivateAccess webViewPrivate) {
150        mWebView = webView;
151        mWebViewPrivate = webViewPrivate;
152        mHitTestResult = new WebView.HitTestResult();
153        mAppTargetSdkVersion = mWebView.getContext().getApplicationInfo().targetSdkVersion;
154        mFactory = factory;
155        mRunQueue = new WebViewChromiumRunQueue();
156    }
157
158    static void completeWindowCreation(WebView parent, WebView child) {
159        AwContents parentContents = ((WebViewChromium) parent.getWebViewProvider()).mAwContents;
160        AwContents childContents =
161                child == null ? null : ((WebViewChromium) child.getWebViewProvider()).mAwContents;
162        parentContents.supplyContentsForPopup(childContents);
163    }
164
165    private <T> T runBlockingFuture(FutureTask<T> task) {
166        if (!mFactory.hasStarted()) throw new RuntimeException("Must be started before we block!");
167        if (ThreadUtils.runningOnUiThread()) {
168            throw new IllegalStateException("This method should only be called off the UI thread");
169        }
170        mRunQueue.addTask(task);
171        try {
172            return task.get(4, TimeUnit.SECONDS);
173        } catch (java.util.concurrent.TimeoutException e) {
174            throw new RuntimeException("Probable deadlock detected due to WebView API being called "
175                    + "on incorrect thread while the UI thread is blocked.", e);
176        } catch (Exception e) {
177            throw new RuntimeException(e);
178        }
179    }
180
181    // We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin
182    // deadlocks.
183    // Do not call this method while on the UI thread!
184    private void runVoidTaskOnUiThreadBlocking(Runnable r) {
185        FutureTask<Void> task = new FutureTask<Void>(r, null);
186        runBlockingFuture(task);
187    }
188
189    private <T> T runOnUiThreadBlocking(Callable<T> c) {
190        return runBlockingFuture(new FutureTask<T>(c));
191    }
192
193    // WebViewProvider methods --------------------------------------------------------------------
194
195    @Override
196    // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
197    // so is ignored. TODO: remove it from WebViewProvider.
198    public void init(final Map<String, Object> javaScriptInterfaces,
199            final boolean privateBrowsing) {
200        if (privateBrowsing) {
201            mFactory.startYourEngines(true);
202            final String msg = "Private browsing is not supported in WebView.";
203            if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
204                throw new IllegalArgumentException(msg);
205            } else {
206                Log.w(TAG, msg);
207                TextView warningLabel = new TextView(mWebView.getContext());
208                warningLabel.setText(mWebView.getContext().getString(
209                        com.android.internal.R.string.webviewchromium_private_browsing_warning));
210                mWebView.addView(warningLabel);
211            }
212        }
213
214        // We will defer real initialization until we know which thread to do it on, unless:
215        // - we are on the main thread already (common case),
216        // - the app is targeting >= JB MR2, in which case checkThread enforces that all usage
217        //   comes from a single thread. (Note in JB MR2 this exception was in WebView.java).
218        if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
219            mFactory.startYourEngines(false);
220            checkThread();
221        } else if (!mFactory.hasStarted()) {
222            if (Looper.myLooper() == Looper.getMainLooper()) {
223                mFactory.startYourEngines(true);
224            }
225        }
226
227        final boolean isAccessFromFileURLsGrantedByDefault =
228                mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
229        final boolean areLegacyQuirksEnabled =
230                mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
231
232        mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
233        mWebSettings = new ContentSettingsAdapter(new AwSettings(
234                mWebView.getContext(), isAccessFromFileURLsGrantedByDefault,
235                areLegacyQuirksEnabled));
236
237        if (mAppTargetSdkVersion <= Build.VERSION_CODES.KITKAT) {
238            mWebSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
239            // On KK and older versions we always allowed third party cookies.
240            mWebSettings.setAcceptThirdPartyCookies(true);
241        }
242
243        mRunQueue.addTask(new Runnable() {
244                @Override
245                public void run() {
246                    initForReal();
247                    if (privateBrowsing) {
248                        // Intentionally irreversibly disable the webview instance, so that private
249                        // user data cannot leak through misuse of a non-privateBrowing WebView
250                        // instance. Can't just null out mAwContents as we never null-check it
251                        // before use.
252                        destroy();
253                    }
254                }
255        });
256    }
257
258    private void initForReal() {
259        mAwContents = new AwContents(mFactory.getBrowserContext(), mWebView, mWebView.getContext(),
260                new InternalAccessAdapter(), new WebViewNativeGLDelegate(),
261                mContentsClientAdapter, mWebSettings.getAwSettings());
262
263        if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
264            // On KK and above, favicons are automatically downloaded as the method
265            // old apps use to enable that behavior is deprecated.
266            AwContents.setShouldDownloadFavicons();
267        }
268
269        if (mAppTargetSdkVersion <= Build.VERSION_CODES.KITKAT) {
270            // On KK and older versions, JavaScript objects injected via addJavascriptInterface
271            // were not inspectable.
272            mAwContents.disableJavascriptInterfacesInspection();
273        }
274
275        // TODO: This assumes AwContents ignores second Paint param.
276        mAwContents.setLayerType(mWebView.getLayerType(), null);
277    }
278
279    void startYourEngine() {
280        mRunQueue.drainQueue();
281    }
282
283    private RuntimeException createThreadException() {
284        return new IllegalStateException(
285                "Calling View methods on another thread than the UI thread.");
286    }
287
288    private boolean checkNeedsPost() {
289        boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread();
290        if (!needsPost && mAwContents == null) {
291            throw new IllegalStateException(
292                    "AwContents must be created if we are not posting!");
293        }
294        return needsPost;
295    }
296
297    //  Intentionally not static, as no need to check thread on static methods
298    private void checkThread() {
299        if (!ThreadUtils.runningOnUiThread()) {
300            final RuntimeException threadViolation = createThreadException();
301            ThreadUtils.postOnUiThread(new Runnable() {
302                @Override
303                public void run() {
304                    throw threadViolation;
305                }
306            });
307            throw createThreadException();
308        }
309    }
310
311    @Override
312    public void setHorizontalScrollbarOverlay(final boolean overlay) {
313        if (checkNeedsPost()) {
314            mRunQueue.addTask(new Runnable() {
315                @Override
316                public void run() {
317                    setHorizontalScrollbarOverlay(overlay);
318                }
319            });
320            return;
321        }
322        mAwContents.setHorizontalScrollbarOverlay(overlay);
323    }
324
325    @Override
326    public void setVerticalScrollbarOverlay(final boolean overlay) {
327        if (checkNeedsPost()) {
328            mRunQueue.addTask(new Runnable() {
329                @Override
330                public void run() {
331                    setVerticalScrollbarOverlay(overlay);
332                }
333            });
334            return;
335        }
336        mAwContents.setVerticalScrollbarOverlay(overlay);
337    }
338
339    @Override
340    public boolean overlayHorizontalScrollbar() {
341        mFactory.startYourEngines(false);
342        if (checkNeedsPost()) {
343            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
344                @Override
345                public Boolean call() {
346                    return overlayHorizontalScrollbar();
347                }
348            });
349            return ret;
350        }
351        return mAwContents.overlayHorizontalScrollbar();
352    }
353
354    @Override
355    public boolean overlayVerticalScrollbar() {
356        mFactory.startYourEngines(false);
357        if (checkNeedsPost()) {
358            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
359                @Override
360                public Boolean call() {
361                    return overlayVerticalScrollbar();
362                }
363            });
364            return ret;
365        }
366        return mAwContents.overlayVerticalScrollbar();
367    }
368
369    @Override
370    public int getVisibleTitleHeight() {
371        // This is deprecated in WebView and should always return 0.
372        return 0;
373    }
374
375    @Override
376    public SslCertificate getCertificate() {
377        mFactory.startYourEngines(true);
378        if (checkNeedsPost()) {
379            SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() {
380                @Override
381                public SslCertificate call() {
382                    return getCertificate();
383                }
384            });
385            return ret;
386        }
387        return mAwContents.getCertificate();
388    }
389
390    @Override
391    public void setCertificate(SslCertificate certificate) {
392        // intentional no-op
393    }
394
395    @Override
396    public void savePassword(String host, String username, String password) {
397        // This is a deprecated API: intentional no-op.
398    }
399
400    @Override
401    public void setHttpAuthUsernamePassword(final String host, final String realm,
402            final String username, final String password) {
403        if (checkNeedsPost()) {
404            mRunQueue.addTask(new Runnable() {
405                @Override
406                public void run() {
407                    setHttpAuthUsernamePassword(host, realm, username, password);
408                }
409            });
410            return;
411        }
412        mAwContents.setHttpAuthUsernamePassword(host, realm, username, password);
413    }
414
415    @Override
416    public String[] getHttpAuthUsernamePassword(final String host, final String realm) {
417        mFactory.startYourEngines(true);
418        if (checkNeedsPost()) {
419            String[] ret = runOnUiThreadBlocking(new Callable<String[]>() {
420                @Override
421                public String[] call() {
422                    return getHttpAuthUsernamePassword(host, realm);
423                }
424            });
425            return ret;
426        }
427        return mAwContents.getHttpAuthUsernamePassword(host, realm);
428    }
429
430    @Override
431    public void destroy() {
432        if (checkNeedsPost()) {
433            mRunQueue.addTask(new Runnable() {
434                @Override
435                public void run() {
436                    destroy();
437                }
438            });
439            return;
440        }
441
442        mAwContents.destroy();
443        if (mGLfunctor != null) {
444            mGLfunctor.destroy();
445            mGLfunctor = null;
446        }
447    }
448
449    @Override
450    public void setNetworkAvailable(final boolean networkUp) {
451        // Note that this purely toggles the JS navigator.online property.
452        // It does not in affect chromium or network stack state in any way.
453        if (checkNeedsPost()) {
454            mRunQueue.addTask(new Runnable() {
455                @Override
456                public void run() {
457                    setNetworkAvailable(networkUp);
458                }
459            });
460            return;
461        }
462        mAwContents.setNetworkAvailable(networkUp);
463    }
464
465    @Override
466    public WebBackForwardList saveState(final Bundle outState) {
467        mFactory.startYourEngines(true);
468        if (checkNeedsPost()) {
469            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
470                @Override
471                public WebBackForwardList call() {
472                    return saveState(outState);
473                }
474            });
475            return ret;
476        }
477        if (outState == null) return null;
478        if (!mAwContents.saveState(outState)) return null;
479        return copyBackForwardList();
480    }
481
482    @Override
483    public boolean savePicture(Bundle b, File dest) {
484        // Intentional no-op: hidden method on WebView.
485        return false;
486    }
487
488    @Override
489    public boolean restorePicture(Bundle b, File src) {
490        // Intentional no-op: hidden method on WebView.
491        return false;
492    }
493
494    @Override
495    public WebBackForwardList restoreState(final Bundle inState) {
496        mFactory.startYourEngines(true);
497        if (checkNeedsPost()) {
498            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
499                @Override
500                public WebBackForwardList call() {
501                    return restoreState(inState);
502                }
503            });
504            return ret;
505        }
506        if (inState == null) return null;
507        if (!mAwContents.restoreState(inState)) return null;
508        return copyBackForwardList();
509    }
510
511    @Override
512    public void loadUrl(final String url, Map<String, String> additionalHttpHeaders) {
513        // TODO: We may actually want to do some sanity checks here (like filter about://chrome).
514
515        // For backwards compatibility, apps targeting less than K will have JS URLs evaluated
516        // directly and any result of the evaluation will not replace the current page content.
517        // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will
518        // have the result of that URL replace the content of the current page.
519        final String JAVASCRIPT_SCHEME = "javascript:";
520        if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
521                url != null && url.startsWith(JAVASCRIPT_SCHEME)) {
522            mFactory.startYourEngines(true);
523            if (checkNeedsPost()) {
524                mRunQueue.addTask(new Runnable() {
525                    @Override
526                    public void run() {
527                        mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
528                                url.substring(JAVASCRIPT_SCHEME.length()));
529                    }
530                });
531            } else {
532                mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
533                        url.substring(JAVASCRIPT_SCHEME.length()));
534            }
535            return;
536        }
537
538        LoadUrlParams params = new LoadUrlParams(url);
539        if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders);
540        loadUrlOnUiThread(params);
541    }
542
543    @Override
544    public void loadUrl(String url) {
545        // Early out to match old WebView implementation
546        if (url == null) {
547            return;
548        }
549        loadUrl(url, null);
550    }
551
552    @Override
553    public void postUrl(String url, byte[] postData) {
554        LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData);
555        Map<String,String> headers = new HashMap<String,String>();
556        headers.put("Content-Type", "application/x-www-form-urlencoded");
557        params.setExtraHeaders(headers);
558        loadUrlOnUiThread(params);
559    }
560
561    private static String fixupMimeType(String mimeType) {
562        return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType;
563    }
564
565    private static String fixupData(String data) {
566        return TextUtils.isEmpty(data) ? "" : data;
567    }
568
569    private static String fixupBase(String url) {
570        return TextUtils.isEmpty(url) ? "about:blank" : url;
571    }
572
573    private static String fixupHistory(String url) {
574        return TextUtils.isEmpty(url) ? "about:blank" : url;
575    }
576
577    private static boolean isBase64Encoded(String encoding) {
578        return "base64".equals(encoding);
579    }
580
581    @Override
582    public void loadData(String data, String mimeType, String encoding) {
583        loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
584                fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
585    }
586
587    @Override
588    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
589            String historyUrl) {
590        data = fixupData(data);
591        mimeType = fixupMimeType(mimeType);
592        LoadUrlParams loadUrlParams;
593        baseUrl = fixupBase(baseUrl);
594        historyUrl = fixupHistory(historyUrl);
595
596        if (baseUrl.startsWith("data:")) {
597            // For backwards compatibility with WebViewClassic, we use the value of |encoding|
598            // as the charset, as long as it's not "base64".
599            boolean isBase64 = isBase64Encoded(encoding);
600            loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
601                    data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
602        } else {
603            // When loading data with a non-data: base URL, the classic WebView would effectively
604            // "dump" that string of data into the WebView without going through regular URL
605            // loading steps such as decoding URL-encoded entities. We achieve this same behavior by
606            // base64 encoding the data that is passed here and then loading that as a data: URL.
607            try {
608                loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
609                        Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
610                        true, baseUrl, historyUrl, "utf-8");
611            } catch (java.io.UnsupportedEncodingException e) {
612                Log.wtf(TAG, "Unable to load data string " + data, e);
613                return;
614            }
615        }
616        loadUrlOnUiThread(loadUrlParams);
617    }
618
619    private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
620        // This is the last point that we can delay starting the Chromium backend up
621        // and if the app has not caused us to bind the Chromium UI thread to a background thread
622        // we now bind Chromium's notion of the UI thread to the app main thread.
623        mFactory.startYourEngines(true);
624        if (checkNeedsPost()) {
625            // Disallowed in WebView API for apps targetting a new SDK
626            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
627            mRunQueue.addTask(new Runnable() {
628                @Override
629                public void run() {
630                    mAwContents.loadUrl(loadUrlParams);
631                }
632            });
633            return;
634        }
635        mAwContents.loadUrl(loadUrlParams);
636    }
637
638    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
639        checkThread();
640        mAwContents.evaluateJavaScript(script, resultCallback);
641    }
642
643    @Override
644    public void saveWebArchive(String filename) {
645        saveWebArchive(filename, false, null);
646    }
647
648    @Override
649    public void saveWebArchive(final String basename, final boolean autoname,
650            final ValueCallback<String> callback) {
651        if (checkNeedsPost()) {
652            mRunQueue.addTask(new Runnable() {
653                @Override
654                public void run() {
655                    saveWebArchive(basename, autoname, callback);
656                }
657            });
658            return;
659        }
660        mAwContents.saveWebArchive(basename, autoname, callback);
661    }
662
663    @Override
664    public void stopLoading() {
665        if (checkNeedsPost()) {
666            mRunQueue.addTask(new Runnable() {
667                @Override
668                public void run() {
669                    stopLoading();
670                }
671            });
672            return;
673        }
674
675        mAwContents.stopLoading();
676    }
677
678    @Override
679    public void reload() {
680        if (checkNeedsPost()) {
681            mRunQueue.addTask(new Runnable() {
682                @Override
683                public void run() {
684                    reload();
685                }
686            });
687            return;
688        }
689        mAwContents.reload();
690    }
691
692    @Override
693    public boolean canGoBack() {
694        mFactory.startYourEngines(true);
695        if (checkNeedsPost()) {
696            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
697                @Override
698                public Boolean call() {
699                    return canGoBack();
700                }
701            });
702            return ret;
703        }
704        return mAwContents.canGoBack();
705    }
706
707    @Override
708    public void goBack() {
709        if (checkNeedsPost()) {
710            mRunQueue.addTask(new Runnable() {
711                @Override
712                public void run() {
713                    goBack();
714                }
715            });
716            return;
717        }
718        mAwContents.goBack();
719    }
720
721    @Override
722    public boolean canGoForward() {
723        mFactory.startYourEngines(true);
724        if (checkNeedsPost()) {
725            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
726                @Override
727                public Boolean call() {
728                    return canGoForward();
729                }
730            });
731            return ret;
732        }
733        return mAwContents.canGoForward();
734    }
735
736    @Override
737    public void goForward() {
738        if (checkNeedsPost()) {
739            mRunQueue.addTask(new Runnable() {
740                @Override
741                public void run() {
742                    goForward();
743                }
744            });
745            return;
746        }
747        mAwContents.goForward();
748    }
749
750    @Override
751    public boolean canGoBackOrForward(final int steps) {
752        mFactory.startYourEngines(true);
753        if (checkNeedsPost()) {
754            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
755                @Override
756                public Boolean call() {
757                    return canGoBackOrForward(steps);
758                }
759            });
760            return ret;
761        }
762        return mAwContents.canGoBackOrForward(steps);
763    }
764
765    @Override
766    public void goBackOrForward(final int steps) {
767        if (checkNeedsPost()) {
768            mRunQueue.addTask(new Runnable() {
769                @Override
770                public void run() {
771                    goBackOrForward(steps);
772                }
773            });
774            return;
775        }
776        mAwContents.goBackOrForward(steps);
777    }
778
779    @Override
780    public boolean isPrivateBrowsingEnabled() {
781        // Not supported in this WebView implementation.
782        return false;
783    }
784
785    @Override
786    public boolean pageUp(final boolean top) {
787        mFactory.startYourEngines(true);
788        if (checkNeedsPost()) {
789            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
790                @Override
791                public Boolean call() {
792                    return pageUp(top);
793                }
794            });
795            return ret;
796        }
797        return mAwContents.pageUp(top);
798    }
799
800    @Override
801    public boolean pageDown(final boolean bottom) {
802        mFactory.startYourEngines(true);
803        if (checkNeedsPost()) {
804            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
805                @Override
806                public Boolean call() {
807                    return pageDown(bottom);
808                }
809            });
810            return ret;
811        }
812        return mAwContents.pageDown(bottom);
813    }
814
815    @Override
816    public void clearView() {
817        if (checkNeedsPost()) {
818            mRunQueue.addTask(new Runnable() {
819                @Override
820                public void run() {
821                    clearView();
822                }
823            });
824            return;
825        }
826        mAwContents.clearView();
827    }
828
829    @Override
830    public Picture capturePicture() {
831        mFactory.startYourEngines(true);
832        if (checkNeedsPost()) {
833            Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
834                @Override
835                public Picture call() {
836                    return capturePicture();
837                }
838            });
839            return ret;
840        }
841        return mAwContents.capturePicture();
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    //TODO: remove once frameworks/base is updated
1417    //@Override
1418    public boolean zoomBy(float factor) {
1419        mFactory.startYourEngines(true);
1420        // This is an L API and therefore we can enforce stricter threading constraints.
1421        checkThread();
1422        //TODO: remove once Chromium-side CL is merged.
1423        //return mAwContents.zoomBy(factor);
1424        return false;
1425    }
1426
1427    @Override
1428    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
1429        // Intentional no-op
1430    }
1431
1432    @Override
1433    public View findHierarchyView(String className, int hashCode) {
1434        // Intentional no-op
1435        return null;
1436    }
1437
1438    // WebViewProvider glue methods ---------------------------------------------------------------
1439
1440    @Override
1441    // This needs to be kept thread safe!
1442    public WebViewProvider.ViewDelegate getViewDelegate() {
1443        return this;
1444    }
1445
1446    @Override
1447    // This needs to be kept thread safe!
1448    public WebViewProvider.ScrollDelegate getScrollDelegate() {
1449        return this;
1450    }
1451
1452
1453    // WebViewProvider.ViewDelegate implementation ------------------------------------------------
1454
1455    // TODO: remove from WebViewProvider and use default implementation from
1456    // ViewGroup.
1457    // @Override
1458    public boolean shouldDelayChildPressedState() {
1459        mFactory.startYourEngines(false);
1460        if (checkNeedsPost()) {
1461            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1462                @Override
1463                public Boolean call() {
1464                    return shouldDelayChildPressedState();
1465                }
1466            });
1467            return ret;
1468        }
1469        return true;
1470    }
1471
1472//    @Override
1473    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
1474        mFactory.startYourEngines(false);
1475        if (checkNeedsPost()) {
1476            AccessibilityNodeProvider ret = runOnUiThreadBlocking(
1477                    new Callable<AccessibilityNodeProvider>() {
1478                @Override
1479                public AccessibilityNodeProvider call() {
1480                    return getAccessibilityNodeProvider();
1481                }
1482            });
1483            return ret;
1484        }
1485        return mAwContents.getAccessibilityNodeProvider();
1486    }
1487
1488    @Override
1489    public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) {
1490        mFactory.startYourEngines(false);
1491        if (checkNeedsPost()) {
1492            runVoidTaskOnUiThreadBlocking(new Runnable() {
1493                @Override
1494                public void run() {
1495                    onInitializeAccessibilityNodeInfo(info);
1496                }
1497            });
1498            return;
1499        }
1500        mAwContents.onInitializeAccessibilityNodeInfo(info);
1501    }
1502
1503    @Override
1504    public void onInitializeAccessibilityEvent(final AccessibilityEvent event) {
1505        mFactory.startYourEngines(false);
1506        if (checkNeedsPost()) {
1507            runVoidTaskOnUiThreadBlocking(new Runnable() {
1508                @Override
1509                public void run() {
1510                    onInitializeAccessibilityEvent(event);
1511                }
1512            });
1513            return;
1514        }
1515        mAwContents.onInitializeAccessibilityEvent(event);
1516    }
1517
1518    @Override
1519    public boolean performAccessibilityAction(final int action, final Bundle arguments) {
1520        mFactory.startYourEngines(false);
1521        if (checkNeedsPost()) {
1522            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1523                @Override
1524                public Boolean call() {
1525                    return performAccessibilityAction(action, arguments);
1526                }
1527            });
1528            return ret;
1529        }
1530        if (mAwContents.supportsAccessibilityAction(action)) {
1531            return mAwContents.performAccessibilityAction(action, arguments);
1532        }
1533        return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
1534    }
1535
1536    @Override
1537    public void setOverScrollMode(final int mode) {
1538        // This gets called from the android.view.View c'tor that WebView inherits from. This
1539        // causes the method to be called when mAwContents == null.
1540        // It's safe to ignore these calls however since AwContents will read the current value of
1541        // this setting when it's created.
1542        if (mAwContents == null) return;
1543
1544        if (checkNeedsPost()) {
1545            mRunQueue.addTask(new Runnable() {
1546                @Override
1547                public void run() {
1548                    setOverScrollMode(mode);
1549                }
1550            });
1551            return;
1552        }
1553        mAwContents.setOverScrollMode(mode);
1554    }
1555
1556    @Override
1557    public void setScrollBarStyle(final int style) {
1558        if (checkNeedsPost()) {
1559            mRunQueue.addTask(new Runnable() {
1560                @Override
1561                public void run() {
1562                    setScrollBarStyle(style);
1563                }
1564            });
1565            return;
1566        }
1567        mAwContents.setScrollBarStyle(style);
1568    }
1569
1570    @Override
1571    public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l,
1572            final int t, final int r, final int b) {
1573        // WebViewClassic was overriding this method to handle rubberband over-scroll. Since
1574        // WebViewChromium doesn't support that the vanilla implementation of this method can be
1575        // used.
1576        mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
1577    }
1578
1579    @Override
1580    public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
1581            final boolean clampedY) {
1582        if (checkNeedsPost()) {
1583            mRunQueue.addTask(new Runnable() {
1584                @Override
1585                public void run() {
1586                    onOverScrolled(scrollX, scrollY, clampedX, clampedY);
1587                }
1588            });
1589            return;
1590        }
1591        mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY);
1592    }
1593
1594    @Override
1595    public void onWindowVisibilityChanged(final int visibility) {
1596        if (checkNeedsPost()) {
1597            mRunQueue.addTask(new Runnable() {
1598                @Override
1599                public void run() {
1600                    onWindowVisibilityChanged(visibility);
1601                }
1602            });
1603            return;
1604        }
1605        mAwContents.onWindowVisibilityChanged(visibility);
1606    }
1607
1608    @Override
1609    public void onDraw(final Canvas canvas) {
1610        mFactory.startYourEngines(true);
1611        if (checkNeedsPost()) {
1612            runVoidTaskOnUiThreadBlocking(new Runnable() {
1613                @Override
1614                public void run() {
1615                    onDraw(canvas);
1616                }
1617            });
1618            return;
1619        }
1620        mAwContents.onDraw(canvas);
1621    }
1622
1623    @Override
1624    public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) {
1625        // This API is our strongest signal from the View system that this
1626        // WebView is going to be bound to a View hierarchy and so at this
1627        // point we must bind Chromium's UI thread to the current thread.
1628        mFactory.startYourEngines(false);
1629        checkThread();
1630        mWebViewPrivate.super_setLayoutParams(layoutParams);
1631    }
1632
1633    @Override
1634    public boolean performLongClick() {
1635        // Return false unless the WebView is attached to a View with a parent
1636        return mWebView.getParent() != null ? mWebViewPrivate.super_performLongClick() : false;
1637    }
1638
1639    @Override
1640    public void onConfigurationChanged(final Configuration newConfig) {
1641        if (checkNeedsPost()) {
1642            mRunQueue.addTask(new Runnable() {
1643                @Override
1644                public void run() {
1645                    onConfigurationChanged(newConfig);
1646                }
1647            });
1648            return;
1649        }
1650        mAwContents.onConfigurationChanged(newConfig);
1651    }
1652
1653    @Override
1654    public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
1655        mFactory.startYourEngines(false);
1656        if (checkNeedsPost()) {
1657           return null;
1658        }
1659        return mAwContents.onCreateInputConnection(outAttrs);
1660    }
1661
1662    @Override
1663    public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) {
1664        mFactory.startYourEngines(false);
1665        if (checkNeedsPost()) {
1666            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1667                @Override
1668                public Boolean call() {
1669                    return onKeyMultiple(keyCode, repeatCount, event);
1670                }
1671            });
1672            return ret;
1673        }
1674        UnimplementedWebViewApi.invoke();
1675        return false;
1676    }
1677
1678    @Override
1679    public boolean onKeyDown(final int keyCode, final KeyEvent event) {
1680        mFactory.startYourEngines(false);
1681        if (checkNeedsPost()) {
1682            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1683                @Override
1684                public Boolean call() {
1685                    return onKeyDown(keyCode, event);
1686                }
1687            });
1688            return ret;
1689        }
1690        UnimplementedWebViewApi.invoke();
1691        return false;
1692    }
1693
1694    @Override
1695    public boolean onKeyUp(final int keyCode, final KeyEvent event) {
1696        mFactory.startYourEngines(false);
1697        if (checkNeedsPost()) {
1698            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1699                @Override
1700                public Boolean call() {
1701                    return onKeyUp(keyCode, event);
1702                }
1703            });
1704            return ret;
1705        }
1706        return mAwContents.onKeyUp(keyCode, event);
1707    }
1708
1709    @Override
1710    public void onAttachedToWindow() {
1711        // This API is our strongest signal from the View system that this
1712        // WebView is going to be bound to a View hierarchy and so at this
1713        // point we must bind Chromium's UI thread to the current thread.
1714        mFactory.startYourEngines(false);
1715        checkThread();
1716        mAwContents.onAttachedToWindow();
1717    }
1718
1719    @Override
1720    public void onDetachedFromWindow() {
1721        if (checkNeedsPost()) {
1722            mRunQueue.addTask(new Runnable() {
1723                @Override
1724                public void run() {
1725                    onDetachedFromWindow();
1726                }
1727            });
1728            return;
1729        }
1730
1731        mAwContents.onDetachedFromWindow();
1732    }
1733
1734    @Override
1735    public void onVisibilityChanged(final View changedView, final int visibility) {
1736        // The AwContents will find out the container view visibility before the first draw so we
1737        // can safely ignore onVisibilityChanged callbacks that happen before init().
1738        if (mAwContents == null) return;
1739
1740        if (checkNeedsPost()) {
1741            mRunQueue.addTask(new Runnable() {
1742                @Override
1743                public void run() {
1744                    onVisibilityChanged(changedView, visibility);
1745                }
1746            });
1747            return;
1748        }
1749        mAwContents.onVisibilityChanged(changedView, visibility);
1750    }
1751
1752    @Override
1753    public void onWindowFocusChanged(final boolean hasWindowFocus) {
1754        if (checkNeedsPost()) {
1755            mRunQueue.addTask(new Runnable() {
1756                @Override
1757                public void run() {
1758                    onWindowFocusChanged(hasWindowFocus);
1759                }
1760            });
1761            return;
1762        }
1763        mAwContents.onWindowFocusChanged(hasWindowFocus);
1764    }
1765
1766    @Override
1767    public void onFocusChanged(final boolean focused, final int direction,
1768            final Rect previouslyFocusedRect) {
1769        if (checkNeedsPost()) {
1770            mRunQueue.addTask(new Runnable() {
1771                @Override
1772                public void run() {
1773                    onFocusChanged(focused, direction, previouslyFocusedRect);
1774                }
1775            });
1776            return;
1777        }
1778        mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect);
1779    }
1780
1781    @Override
1782    public boolean setFrame(final int left, final int top, final int right, final int bottom) {
1783        return mWebViewPrivate.super_setFrame(left, top, right, bottom);
1784    }
1785
1786    @Override
1787    public void onSizeChanged(final int w, final int h, final int ow, final int oh) {
1788        if (checkNeedsPost()) {
1789            mRunQueue.addTask(new Runnable() {
1790                @Override
1791                public void run() {
1792                    onSizeChanged(w, h, ow, oh);
1793                }
1794            });
1795            return;
1796        }
1797        mAwContents.onSizeChanged(w, h, ow, oh);
1798    }
1799
1800    @Override
1801    public void onScrollChanged(int l, int t, int oldl, int oldt) {
1802    }
1803
1804    @Override
1805    public boolean dispatchKeyEvent(final KeyEvent event) {
1806        mFactory.startYourEngines(false);
1807        if (checkNeedsPost()) {
1808            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1809                @Override
1810                public Boolean call() {
1811                    return dispatchKeyEvent(event);
1812                }
1813            });
1814            return ret;
1815        }
1816        return mAwContents.dispatchKeyEvent(event);
1817    }
1818
1819    @Override
1820    public boolean onTouchEvent(final MotionEvent ev) {
1821        mFactory.startYourEngines(false);
1822        if (checkNeedsPost()) {
1823            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1824                @Override
1825                public Boolean call() {
1826                    return onTouchEvent(ev);
1827                }
1828            });
1829            return ret;
1830        }
1831        return mAwContents.onTouchEvent(ev);
1832    }
1833
1834    @Override
1835    public boolean onHoverEvent(final MotionEvent event) {
1836        mFactory.startYourEngines(false);
1837        if (checkNeedsPost()) {
1838            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1839                @Override
1840                public Boolean call() {
1841                    return onHoverEvent(event);
1842                }
1843            });
1844            return ret;
1845        }
1846        return mAwContents.onHoverEvent(event);
1847    }
1848
1849    @Override
1850    public boolean onGenericMotionEvent(final MotionEvent event) {
1851        mFactory.startYourEngines(false);
1852        if (checkNeedsPost()) {
1853            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1854                @Override
1855                public Boolean call() {
1856                    return onGenericMotionEvent(event);
1857                }
1858            });
1859            return ret;
1860        }
1861        return mAwContents.onGenericMotionEvent(event);
1862    }
1863
1864    @Override
1865    public boolean onTrackballEvent(MotionEvent ev) {
1866        // Trackball event not handled, which eventually gets converted to DPAD keyevents
1867        return false;
1868    }
1869
1870    @Override
1871    public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) {
1872        mFactory.startYourEngines(false);
1873        if (checkNeedsPost()) {
1874            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1875                @Override
1876                public Boolean call() {
1877                    return requestFocus(direction, previouslyFocusedRect);
1878                }
1879            });
1880            return ret;
1881        }
1882        mAwContents.requestFocus();
1883        return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
1884    }
1885
1886    @Override
1887    public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
1888        mFactory.startYourEngines(false);
1889        if (checkNeedsPost()) {
1890            runVoidTaskOnUiThreadBlocking(new Runnable() {
1891                @Override
1892                public void run() {
1893                    onMeasure(widthMeasureSpec, heightMeasureSpec);
1894                }
1895            });
1896            return;
1897        }
1898        mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec);
1899    }
1900
1901    @Override
1902    public boolean requestChildRectangleOnScreen(final View child, final Rect rect,
1903            final boolean immediate) {
1904        mFactory.startYourEngines(false);
1905        if (checkNeedsPost()) {
1906            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
1907                @Override
1908                public Boolean call() {
1909                    return requestChildRectangleOnScreen(child, rect, immediate);
1910                }
1911            });
1912            return ret;
1913        }
1914        return mAwContents.requestChildRectangleOnScreen(child, rect, immediate);
1915    }
1916
1917    @Override
1918    public void setBackgroundColor(final int color) {
1919        mFactory.startYourEngines(false);
1920        if (checkNeedsPost()) {
1921            ThreadUtils.postOnUiThread(new Runnable() {
1922                @Override
1923                public void run() {
1924                    setBackgroundColor(color);
1925                }
1926            });
1927            return;
1928        }
1929        mAwContents.setBackgroundColor(color);
1930    }
1931
1932    @Override
1933    public void setLayerType(final int layerType, final Paint paint) {
1934        // This can be called from WebView constructor in which case mAwContents
1935        // is still null. We set the layer type in initForReal in that case.
1936        if (mAwContents == null) return;
1937        if (checkNeedsPost()) {
1938            ThreadUtils.postOnUiThread(new Runnable() {
1939                @Override
1940                public void run() {
1941                    setLayerType(layerType, paint);
1942                }
1943            });
1944            return;
1945        }
1946        mAwContents.setLayerType(layerType, paint);
1947    }
1948
1949    // Remove from superclass
1950    public void preDispatchDraw(Canvas canvas) {
1951        // TODO(leandrogracia): remove this method from WebViewProvider if we think
1952        // we won't need it again.
1953    }
1954
1955    public void onStartTemporaryDetach() {
1956        mAwContents.onStartTemporaryDetach();
1957    }
1958
1959    public void onFinishTemporaryDetach() {
1960        mAwContents.onFinishTemporaryDetach();
1961    }
1962
1963    // WebViewProvider.ScrollDelegate implementation ----------------------------------------------
1964
1965    @Override
1966    public int computeHorizontalScrollRange() {
1967        mFactory.startYourEngines(false);
1968        if (checkNeedsPost()) {
1969            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1970                @Override
1971                public Integer call() {
1972                    return computeHorizontalScrollRange();
1973                }
1974            });
1975            return ret;
1976        }
1977        return mAwContents.computeHorizontalScrollRange();
1978    }
1979
1980    @Override
1981    public int computeHorizontalScrollOffset() {
1982        mFactory.startYourEngines(false);
1983        if (checkNeedsPost()) {
1984            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
1985                @Override
1986                public Integer call() {
1987                    return computeHorizontalScrollOffset();
1988                }
1989            });
1990            return ret;
1991        }
1992        return mAwContents.computeHorizontalScrollOffset();
1993    }
1994
1995    @Override
1996    public int computeVerticalScrollRange() {
1997        mFactory.startYourEngines(false);
1998        if (checkNeedsPost()) {
1999            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
2000                @Override
2001                public Integer call() {
2002                    return computeVerticalScrollRange();
2003                }
2004            });
2005            return ret;
2006        }
2007        return mAwContents.computeVerticalScrollRange();
2008    }
2009
2010    @Override
2011    public int computeVerticalScrollOffset() {
2012        mFactory.startYourEngines(false);
2013        if (checkNeedsPost()) {
2014            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
2015                @Override
2016                public Integer call() {
2017                    return computeVerticalScrollOffset();
2018                }
2019            });
2020            return ret;
2021        }
2022        return mAwContents.computeVerticalScrollOffset();
2023    }
2024
2025    @Override
2026    public int computeVerticalScrollExtent() {
2027        mFactory.startYourEngines(false);
2028        if (checkNeedsPost()) {
2029            int ret = runOnUiThreadBlocking(new Callable<Integer>() {
2030                @Override
2031                public Integer call() {
2032                    return computeVerticalScrollExtent();
2033                }
2034            });
2035            return ret;
2036        }
2037        return mAwContents.computeVerticalScrollExtent();
2038    }
2039
2040    @Override
2041    public void computeScroll() {
2042        mFactory.startYourEngines(false);
2043        if (checkNeedsPost()) {
2044            runVoidTaskOnUiThreadBlocking(new Runnable() {
2045                @Override
2046                public void run() {
2047                    computeScroll();
2048                }
2049            });
2050            return;
2051        }
2052        mAwContents.computeScroll();
2053    }
2054
2055    @Override
2056    public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
2057        checkThread();
2058        return new AwPrintDocumentAdapter(mAwContents.getPdfExporter(), documentName);
2059    }
2060
2061    // AwContents.NativeGLDelegate implementation --------------------------------------
2062    private class WebViewNativeGLDelegate implements AwContents.NativeGLDelegate {
2063        @Override
2064        public boolean requestDrawGL(Canvas canvas, boolean waitForCompletion,
2065                View containerView) {
2066            if (mGLfunctor == null) {
2067                mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext());
2068            }
2069            return mGLfunctor.requestDrawGL(
2070                    (HardwareCanvas) canvas, containerView.getViewRootImpl(), waitForCompletion);
2071        }
2072
2073        @Override
2074        public void detachGLFunctor() {
2075            if (mGLfunctor != null) {
2076                mGLfunctor.detach();
2077            }
2078        }
2079    }
2080
2081    // AwContents.InternalAccessDelegate implementation --------------------------------------
2082    private class InternalAccessAdapter implements AwContents.InternalAccessDelegate {
2083        @Override
2084        public boolean drawChild(Canvas arg0, View arg1, long arg2) {
2085            UnimplementedWebViewApi.invoke();
2086            return false;
2087        }
2088
2089        @Override
2090        public boolean super_onKeyUp(int arg0, KeyEvent arg1) {
2091            // Intentional no-op
2092            return false;
2093        }
2094
2095        @Override
2096        public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) {
2097            UnimplementedWebViewApi.invoke();
2098            return false;
2099        }
2100
2101        @Override
2102        public boolean super_dispatchKeyEvent(KeyEvent event) {
2103            return mWebViewPrivate.super_dispatchKeyEvent(event);
2104        }
2105
2106        @Override
2107        public boolean super_onGenericMotionEvent(MotionEvent arg0) {
2108            return mWebViewPrivate.super_onGenericMotionEvent(arg0);
2109        }
2110
2111        @Override
2112        public void super_onConfigurationChanged(Configuration arg0) {
2113            // Intentional no-op
2114        }
2115
2116        @Override
2117        public int super_getScrollBarStyle() {
2118            return mWebViewPrivate.super_getScrollBarStyle();
2119        }
2120
2121        @Override
2122        public boolean awakenScrollBars() {
2123            mWebViewPrivate.awakenScrollBars(0);
2124            // TODO: modify the WebView.PrivateAccess to provide a return value.
2125            return true;
2126        }
2127
2128        @Override
2129        public boolean super_awakenScrollBars(int arg0, boolean arg1) {
2130            // TODO: need method on WebView.PrivateAccess?
2131            UnimplementedWebViewApi.invoke();
2132            return false;
2133        }
2134
2135        @Override
2136        public void onScrollChanged(int l, int t, int oldl, int oldt) {
2137            // Intentional no-op.
2138            // Chromium calls this directly to trigger accessibility events. That isn't needed
2139            // for WebView since super_scrollTo invokes onScrollChanged for us.
2140        }
2141
2142        @Override
2143        public void overScrollBy(int deltaX, int deltaY,
2144            int scrollX, int scrollY,
2145            int scrollRangeX, int scrollRangeY,
2146            int maxOverScrollX, int maxOverScrollY,
2147            boolean isTouchEvent) {
2148            mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY,
2149                    scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
2150        }
2151
2152        @Override
2153        public void super_scrollTo(int scrollX, int scrollY) {
2154            mWebViewPrivate.super_scrollTo(scrollX, scrollY);
2155        }
2156
2157        @Override
2158        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
2159            mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
2160        }
2161
2162        // @Override
2163        public boolean super_onHoverEvent(MotionEvent event) {
2164            return mWebViewPrivate.super_onHoverEvent(event);
2165        }
2166    }
2167}
2168