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