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