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