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