WebViewChromium.java revision 91472035f666e633d4c299617ae1b20ccd3b6e0c
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.ViewGroup;
40import android.view.accessibility.AccessibilityEvent;
41import android.view.accessibility.AccessibilityNodeInfo;
42import android.view.accessibility.AccessibilityNodeProvider;
43import android.view.inputmethod.EditorInfo;
44import android.view.inputmethod.InputConnection;
45import android.webkit.DownloadListener;
46import android.webkit.FindActionModeCallback;
47import android.webkit.JavascriptInterface;
48import android.webkit.ValueCallback;
49import android.webkit.WebBackForwardList;
50import android.webkit.WebChromeClient;
51import android.webkit.WebSettings;
52import android.webkit.WebView;
53import android.webkit.WebViewClient;
54import android.webkit.WebViewProvider;
55import android.widget.TextView;
56
57import org.chromium.android_webview.AwBrowserContext;
58import org.chromium.android_webview.AwContents;
59import org.chromium.android_webview.AwLayoutSizer;
60import org.chromium.android_webview.AwPdfExportAttributes;
61import org.chromium.android_webview.AwPrintDocumentAdapter;
62import org.chromium.android_webview.AwSettings;
63import org.chromium.base.ThreadUtils;
64import org.chromium.content.browser.LoadUrlParams;
65import org.chromium.net.NetworkChangeNotifier;
66
67import java.io.BufferedWriter;
68import java.io.File;
69import java.lang.annotation.Annotation;
70import java.util.concurrent.Callable;
71import java.util.concurrent.ConcurrentLinkedQueue;
72import java.util.concurrent.FutureTask;
73import java.util.concurrent.TimeUnit;
74import java.util.HashMap;
75import java.util.Map;
76import java.util.Queue;
77
78/**
79 * This class is the delegate to which WebViewProxy forwards all API calls.
80 *
81 * Most of the actual functionality is implemented by AwContents (or ContentViewCore within
82 * it). This class also contains WebView-specific APIs that require the creation of other
83 * adapters (otherwise org.chromium.content would depend on the webview.chromium package)
84 * and a small set of no-op deprecated APIs.
85 */
86class WebViewChromium implements WebViewProvider,
87          WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate {
88
89    private class WebViewChromiumRunQueue {
90        public WebViewChromiumRunQueue() {
91            mQueue = new ConcurrentLinkedQueue<Runnable>();
92        }
93
94        public void addTask(Runnable task) {
95            mQueue.add(task);
96            if (mFactory.hasStarted()) {
97                ThreadUtils.runOnUiThread(new Runnable() {
98                    @Override
99                    public void run() {
100                        drainQueue();
101                    }
102                });
103            }
104        }
105
106        public void drainQueue() {
107            if (mQueue == null || mQueue.isEmpty()) {
108                return;
109            }
110
111            Runnable task = mQueue.poll();
112            while(task != null) {
113                task.run();
114                task = mQueue.poll();
115            }
116        }
117
118        private Queue<Runnable> mQueue;
119    }
120
121    private WebViewChromiumRunQueue mRunQueue;
122
123    private static final String TAG = WebViewChromium.class.getSimpleName();
124
125    // The WebView that this WebViewChromium is the provider for.
126    WebView mWebView;
127    // Lets us access protected View-derived methods on the WebView instance we're backing.
128    WebView.PrivateAccess mWebViewPrivate;
129    // The client adapter class.
130    private WebViewContentsClientAdapter mContentsClientAdapter;
131
132    // Variables for functionality provided by this adapter ---------------------------------------
133    private ContentSettingsAdapter mWebSettings;
134    // The WebView wrapper for ContentViewCore and required browser compontents.
135    private AwContents mAwContents;
136    // Non-null if this webview is using the GL accelerated draw path.
137    private DrawGLFunctor mGLfunctor;
138
139    private final WebView.HitTestResult mHitTestResult;
140
141    private final int mAppTargetSdkVersion;
142
143    private WebViewChromiumFactoryProvider mFactory;
144
145    // This does not touch any global / non-threadsafe state, but note that
146    // init is ofter called right after and is NOT threadsafe.
147    public WebViewChromium(WebViewChromiumFactoryProvider factory, WebView webView,
148            WebView.PrivateAccess webViewPrivate) {
149        mWebView = webView;
150        mWebViewPrivate = webViewPrivate;
151        mHitTestResult = new WebView.HitTestResult();
152        mAppTargetSdkVersion = mWebView.getContext().getApplicationInfo().targetSdkVersion;
153        mFactory = factory;
154        mRunQueue = new WebViewChromiumRunQueue();
155    }
156
157    static void completeWindowCreation(WebView parent, WebView child) {
158        AwContents parentContents = ((WebViewChromium) parent.getWebViewProvider()).mAwContents;
159        AwContents childContents =
160                child == null ? null : ((WebViewChromium) child.getWebViewProvider()).mAwContents;
161        parentContents.supplyContentsForPopup(childContents);
162    }
163
164    private <T> T runBlockingFuture(FutureTask<T> task) {
165        if (!mFactory.hasStarted()) throw new RuntimeException("Must be started before we block!");
166        if (ThreadUtils.runningOnUiThread()) {
167            throw new IllegalStateException("This method should only be called off the UI thread");
168        }
169        mRunQueue.addTask(task);
170        try {
171            return task.get(4, TimeUnit.SECONDS);
172        } catch (java.util.concurrent.TimeoutException e) {
173            throw new RuntimeException("Probable deadlock detected due to WebView API being called "
174                    + "on incorrect thread while the UI thread is blocked.", e);
175        } catch (Exception e) {
176            throw new RuntimeException(e);
177        }
178    }
179
180    // We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin
181    // deadlocks.
182    // Do not call this method while on the UI thread!
183    private void runVoidTaskOnUiThreadBlocking(Runnable r) {
184        FutureTask<Void> task = new FutureTask<Void>(r, null);
185        runBlockingFuture(task);
186    }
187
188    private <T> T runOnUiThreadBlocking(Callable<T> c) {
189        return runBlockingFuture(new FutureTask<T>(c));
190    }
191
192    // WebViewProvider methods --------------------------------------------------------------------
193
194    @Override
195    // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
196    // so is ignored. TODO: remove it from WebViewProvider.
197    public void init(final Map<String, Object> javaScriptInterfaces,
198            final boolean privateBrowsing) {
199        if (privateBrowsing) {
200            mFactory.startYourEngines(true);
201            final String msg = "Private browsing is not supported in WebView.";
202            if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
203                throw new IllegalArgumentException(msg);
204            } else {
205                Log.w(TAG, msg);
206                TextView warningLabel = new TextView(mWebView.getContext());
207                warningLabel.setText(mWebView.getContext().getString(
208                        com.android.internal.R.string.webviewchromium_private_browsing_warning));
209                mWebView.addView(warningLabel);
210            }
211        }
212
213        if (!mFactory.hasStarted()) {
214            // We will defer real initialization until we know which thread to do it on, unless we
215            // are on the main thread already.
216            if (Looper.myLooper() == Looper.getMainLooper()) {
217                mFactory.startYourEngines(true);
218            }
219        }
220
221        final boolean isAccessFromFileURLsGrantedByDefault =
222                mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
223        final boolean areLegacyQuirksEnabled =
224                mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
225
226        mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
227        mWebSettings = new ContentSettingsAdapter(new AwSettings(
228                mWebView.getContext(), isAccessFromFileURLsGrantedByDefault,
229                areLegacyQuirksEnabled));
230
231        mRunQueue.addTask(new Runnable() {
232                @Override
233                public void run() {
234                    initForReal();
235                    if (privateBrowsing) {
236                        // Intentionally irreversibly disable the webview instance, so that private
237                        // user data cannot leak through misuse of a non-privateBrowing WebView
238                        // instance. Can't just null out mAwContents as we never null-check it
239                        // before use.
240                        destroy();
241                    }
242                }
243        });
244    }
245
246    private void initForReal() {
247        mAwContents = new AwContents(mFactory.getBrowserContext(), mWebView,
248                new InternalAccessAdapter(), mContentsClientAdapter, new AwLayoutSizer(),
249                mWebSettings.getAwSettings());
250
251        if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
252            // On KK and above, favicons are automatically downloaded as the method
253            // old apps use to enable that behavior is deprecated.
254            AwContents.setShouldDownloadFavicons();
255        }
256    }
257
258    void startYourEngine() {
259        mRunQueue.drainQueue();
260    }
261
262    private RuntimeException createThreadException() {
263        return new IllegalStateException(
264                "Calling View methods on another thread than the UI thread.");
265    }
266
267    private boolean checkNeedsPost() {
268        boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread();
269        if (!needsPost && mAwContents == null) {
270            throw new IllegalStateException(
271                    "AwContents must be created if we are not posting!");
272        }
273        return needsPost;
274    }
275
276    //  Intentionally not static, as no need to check thread on static methods
277    private void checkThread() {
278        if (!ThreadUtils.runningOnUiThread()) {
279            final RuntimeException threadViolation = createThreadException();
280            ThreadUtils.postOnUiThread(new Runnable() {
281                @Override
282                public void run() {
283                    throw threadViolation;
284                }
285            });
286            throw createThreadException();
287        }
288    }
289
290    @Override
291    public void setHorizontalScrollbarOverlay(final boolean overlay) {
292        if (checkNeedsPost()) {
293            mRunQueue.addTask(new Runnable() {
294                @Override
295                public void run() {
296                    setHorizontalScrollbarOverlay(overlay);
297                }
298            });
299            return;
300        }
301        mAwContents.setHorizontalScrollbarOverlay(overlay);
302    }
303
304    @Override
305    public void setVerticalScrollbarOverlay(final boolean overlay) {
306        if (checkNeedsPost()) {
307            mRunQueue.addTask(new Runnable() {
308                @Override
309                public void run() {
310                    setVerticalScrollbarOverlay(overlay);
311                }
312            });
313            return;
314        }
315        mAwContents.setVerticalScrollbarOverlay(overlay);
316    }
317
318    @Override
319    public boolean overlayHorizontalScrollbar() {
320        mFactory.startYourEngines(false);
321        if (checkNeedsPost()) {
322            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
323                @Override
324                public Boolean call() {
325                    return overlayHorizontalScrollbar();
326                }
327            });
328            return ret;
329        }
330        return mAwContents.overlayHorizontalScrollbar();
331    }
332
333    @Override
334    public boolean overlayVerticalScrollbar() {
335        mFactory.startYourEngines(false);
336        if (checkNeedsPost()) {
337            boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
338                @Override
339                public Boolean call() {
340                    return overlayVerticalScrollbar();
341                }
342            });
343            return ret;
344        }
345        return mAwContents.overlayVerticalScrollbar();
346    }
347
348    @Override
349    public int getVisibleTitleHeight() {
350        // This is deprecated in WebView and should always return 0.
351        return 0;
352    }
353
354    @Override
355    public SslCertificate getCertificate() {
356        mFactory.startYourEngines(true);
357        if (checkNeedsPost()) {
358            SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() {
359                @Override
360                public SslCertificate call() {
361                    return getCertificate();
362                }
363            });
364            return ret;
365        }
366        return mAwContents.getCertificate();
367    }
368
369    @Override
370    public void setCertificate(SslCertificate certificate) {
371        // intentional no-op
372    }
373
374    @Override
375    public void savePassword(String host, String username, String password) {
376        // This is a deprecated API: intentional no-op.
377    }
378
379    @Override
380    public void setHttpAuthUsernamePassword(final String host, final String realm,
381            final String username, final String password) {
382        if (checkNeedsPost()) {
383            mRunQueue.addTask(new Runnable() {
384                @Override
385                public void run() {
386                    setHttpAuthUsernamePassword(host, realm, username, password);
387                }
388            });
389            return;
390        }
391        mAwContents.setHttpAuthUsernamePassword(host, realm, username, password);
392    }
393
394    @Override
395    public String[] getHttpAuthUsernamePassword(final String host, final String realm) {
396        mFactory.startYourEngines(true);
397        if (checkNeedsPost()) {
398            String[] ret = runOnUiThreadBlocking(new Callable<String[]>() {
399                @Override
400                public String[] call() {
401                    return getHttpAuthUsernamePassword(host, realm);
402                }
403            });
404            return ret;
405        }
406        return mAwContents.getHttpAuthUsernamePassword(host, realm);
407    }
408
409    @Override
410    public void destroy() {
411        if (checkNeedsPost()) {
412            mRunQueue.addTask(new Runnable() {
413                @Override
414                public void run() {
415                    destroy();
416                }
417            });
418            return;
419        }
420
421        mAwContents.destroy();
422        if (mGLfunctor != null) {
423            mGLfunctor.destroy();
424            mGLfunctor = null;
425        }
426    }
427
428    @Override
429    public void setNetworkAvailable(final boolean networkUp) {
430        // Note that this purely toggles the JS navigator.online property.
431        // It does not in affect chromium or network stack state in any way.
432        if (checkNeedsPost()) {
433            mRunQueue.addTask(new Runnable() {
434                @Override
435                public void run() {
436                    setNetworkAvailable(networkUp);
437                }
438            });
439            return;
440        }
441        mAwContents.setNetworkAvailable(networkUp);
442    }
443
444    @Override
445    public WebBackForwardList saveState(final Bundle outState) {
446        mFactory.startYourEngines(true);
447        if (checkNeedsPost()) {
448            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
449                @Override
450                public WebBackForwardList call() {
451                    return saveState(outState);
452                }
453            });
454            return ret;
455        }
456        if (outState == null) return null;
457        if (!mAwContents.saveState(outState)) return null;
458        return copyBackForwardList();
459    }
460
461    @Override
462    public boolean savePicture(Bundle b, File dest) {
463        // Intentional no-op: hidden method on WebView.
464        return false;
465    }
466
467    @Override
468    public boolean restorePicture(Bundle b, File src) {
469        // Intentional no-op: hidden method on WebView.
470        return false;
471    }
472
473    @Override
474    public WebBackForwardList restoreState(final Bundle inState) {
475        mFactory.startYourEngines(true);
476        if (checkNeedsPost()) {
477            WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() {
478                @Override
479                public WebBackForwardList call() {
480                    return restoreState(inState);
481                }
482            });
483            return ret;
484        }
485        if (inState == null) return null;
486        if (!mAwContents.restoreState(inState)) return null;
487        return copyBackForwardList();
488    }
489
490    @Override
491    public void loadUrl(final String url, Map<String, String> additionalHttpHeaders) {
492        // TODO: We may actually want to do some sanity checks here (like filter about://chrome).
493
494        // For backwards compatibility, apps targeting less than K will have JS URLs evaluated
495        // directly and any result of the evaluation will not replace the current page content.
496        // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will
497        // have the result of that URL replace the content of the current page.
498        final String JAVASCRIPT_SCHEME = "javascript:";
499        if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT &&
500                url != null && url.startsWith(JAVASCRIPT_SCHEME)) {
501            mFactory.startYourEngines(true);
502            if (checkNeedsPost()) {
503                mRunQueue.addTask(new Runnable() {
504                    @Override
505                    public void run() {
506                        mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
507                                url.substring(JAVASCRIPT_SCHEME.length()));
508                    }
509                });
510            } else {
511                mAwContents.evaluateJavaScriptEvenIfNotYetNavigated(
512                        url.substring(JAVASCRIPT_SCHEME.length()));
513            }
514            return;
515        }
516
517        LoadUrlParams params = new LoadUrlParams(url);
518        if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders);
519        loadUrlOnUiThread(params);
520    }
521
522    @Override
523    public void loadUrl(String url) {
524        // Early out to match old WebView implementation
525        if (url == null) {
526            return;
527        }
528        loadUrl(url, null);
529    }
530
531    @Override
532    public void postUrl(String url, byte[] postData) {
533        LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData);
534        Map<String,String> headers = new HashMap<String,String>();
535        headers.put("Content-Type", "application/x-www-form-urlencoded");
536        params.setExtraHeaders(headers);
537        loadUrlOnUiThread(params);
538    }
539
540    private static String fixupMimeType(String mimeType) {
541        return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType;
542    }
543
544    private static String fixupData(String data) {
545        return TextUtils.isEmpty(data) ? "" : data;
546    }
547
548    private static String fixupBase(String url) {
549        return TextUtils.isEmpty(url) ? "about:blank" : url;
550    }
551
552    private static String fixupHistory(String url) {
553        return TextUtils.isEmpty(url) ? "about:blank" : url;
554    }
555
556    private static boolean isBase64Encoded(String encoding) {
557        return "base64".equals(encoding);
558    }
559
560    @Override
561    public void loadData(String data, String mimeType, String encoding) {
562        loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
563                fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
564    }
565
566    @Override
567    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
568            String historyUrl) {
569        data = fixupData(data);
570        mimeType = fixupMimeType(mimeType);
571        LoadUrlParams loadUrlParams;
572        baseUrl = fixupBase(baseUrl);
573        historyUrl = fixupHistory(historyUrl);
574
575        if (baseUrl.startsWith("data:")) {
576            // For backwards compatibility with WebViewClassic, we use the value of |encoding|
577            // as the charset, as long as it's not "base64".
578            boolean isBase64 = isBase64Encoded(encoding);
579            loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
580                    data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
581        } else {
582            // When loading data with a non-data: base URL, the classic WebView would effectively
583            // "dump" that string of data into the WebView without going through regular URL
584            // loading steps such as decoding URL-encoded entities. We achieve this same behavior by
585            // base64 encoding the data that is passed here and then loading that as a data: URL.
586            try {
587                loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
588                        Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
589                        true, baseUrl, historyUrl, "utf-8");
590            } catch (java.io.UnsupportedEncodingException e) {
591                Log.wtf(TAG, "Unable to load data string " + data, e);
592                return;
593            }
594        }
595        loadUrlOnUiThread(loadUrlParams);
596
597        // Data url's with a base url will be resolved in Blink, and not cause an onPageStarted
598        // event to be sent. Sending the callback directly from here.
599        final String finalBaseUrl = loadUrlParams.getBaseUrl();
600        ThreadUtils.postOnUiThread(new Runnable() {
601            @Override
602            public void run() {
603                mContentsClientAdapter.onPageStarted(finalBaseUrl);
604            }
605        });
606    }
607
608    private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) {
609        // This is the last point that we can delay starting the Chromium backend up
610        // and if the app has not caused us to bind the Chromium UI thread to a background thread
611        // we now bind Chromium's notion of the UI thread to the app main thread.
612        mFactory.startYourEngines(true);
613        if (checkNeedsPost()) {
614            // Disallowed in WebView API for apps targetting a new SDK
615            assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2;
616            mRunQueue.addTask(new Runnable() {
617                @Override
618                public void run() {
619                    mAwContents.loadUrl(loadUrlParams);
620                }
621            });
622            return;
623        }
624        mAwContents.loadUrl(loadUrlParams);
625    }
626
627    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
628        checkThread();
629        mAwContents.evaluateJavaScript(script, resultCallback);
630    }
631
632    @Override
633    public void saveWebArchive(String filename) {
634        saveWebArchive(filename, false, null);
635    }
636
637    @Override
638    public void saveWebArchive(final String basename, final boolean autoname,
639            final ValueCallback<String> callback) {
640        if (checkNeedsPost()) {
641            mRunQueue.addTask(new Runnable() {
642                @Override
643                public void run() {
644                    saveWebArchive(basename, autoname, callback);
645                }
646            });
647            return;
648        }
649        mAwContents.saveWebArchive(basename, autoname, callback);
650    }
651
652    @Override
653    public void stopLoading() {
654        if (checkNeedsPost()) {
655            mRunQueue.addTask(new Runnable() {
656                @Override
657                public void run() {
658                    stopLoading();
659                }
660            });
661            return;
662        }
663
664        mAwContents.stopLoading();
665    }
666
667    @Override
668    public void reload() {
669        if (checkNeedsPost()) {
670            mRunQueue.addTask(new Runnable() {
671                @Override
672                public void run() {
673                    reload();
674                }
675            });
676            return;
677        }
678        mAwContents.reload();
679    }
680
681    @Override
682    public boolean canGoBack() {
683        mFactory.startYourEngines(true);
684        if (checkNeedsPost()) {
685            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
686                @Override
687                public Boolean call() {
688                    return canGoBack();
689                }
690            });
691            return ret;
692        }
693        return mAwContents.canGoBack();
694    }
695
696    @Override
697    public void goBack() {
698        if (checkNeedsPost()) {
699            mRunQueue.addTask(new Runnable() {
700                @Override
701                public void run() {
702                    goBack();
703                }
704            });
705            return;
706        }
707        mAwContents.goBack();
708    }
709
710    @Override
711    public boolean canGoForward() {
712        mFactory.startYourEngines(true);
713        if (checkNeedsPost()) {
714            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
715                @Override
716                public Boolean call() {
717                    return canGoForward();
718                }
719            });
720            return ret;
721        }
722        return mAwContents.canGoForward();
723    }
724
725    @Override
726    public void goForward() {
727        if (checkNeedsPost()) {
728            mRunQueue.addTask(new Runnable() {
729                @Override
730                public void run() {
731                    goForward();
732                }
733            });
734            return;
735        }
736        mAwContents.goForward();
737    }
738
739    @Override
740    public boolean canGoBackOrForward(final int steps) {
741        mFactory.startYourEngines(true);
742        if (checkNeedsPost()) {
743            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
744                @Override
745                public Boolean call() {
746                    return canGoBackOrForward(steps);
747                }
748            });
749            return ret;
750        }
751        return mAwContents.canGoBackOrForward(steps);
752    }
753
754    @Override
755    public void goBackOrForward(final int steps) {
756        if (checkNeedsPost()) {
757            mRunQueue.addTask(new Runnable() {
758                @Override
759                public void run() {
760                    goBackOrForward(steps);
761                }
762            });
763            return;
764        }
765        mAwContents.goBackOrForward(steps);
766    }
767
768    @Override
769    public boolean isPrivateBrowsingEnabled() {
770        // Not supported in this WebView implementation.
771        return false;
772    }
773
774    @Override
775    public boolean pageUp(final boolean top) {
776        mFactory.startYourEngines(true);
777        if (checkNeedsPost()) {
778            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
779                @Override
780                public Boolean call() {
781                    return pageUp(top);
782                }
783            });
784            return ret;
785        }
786        return mAwContents.pageUp(top);
787    }
788
789    @Override
790    public boolean pageDown(final boolean bottom) {
791        mFactory.startYourEngines(true);
792        if (checkNeedsPost()) {
793            Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() {
794                @Override
795                public Boolean call() {
796                    return pageDown(bottom);
797                }
798            });
799            return ret;
800        }
801        return mAwContents.pageDown(bottom);
802    }
803
804    @Override
805    public void clearView() {
806        if (checkNeedsPost()) {
807            mRunQueue.addTask(new Runnable() {
808                @Override
809                public void run() {
810                    clearView();
811                }
812            });
813            return;
814        }
815        mAwContents.clearView();
816    }
817
818    @Override
819    public Picture capturePicture() {
820        mFactory.startYourEngines(true);
821        if (checkNeedsPost()) {
822            Picture ret = runOnUiThreadBlocking(new Callable<Picture>() {
823                @Override
824                public Picture call() {
825                    return capturePicture();
826                }
827            });
828            return ret;
829        }
830        return mAwContents.capturePicture();
831    }
832
833    @Override
834    public PrintDocumentAdapter createPrintDocumentAdapter() {
835        checkThread();
836        return new AwPrintDocumentAdapter(mAwContents.getPdfExporter());
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