Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2007 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 
     17 package android.webkit;
     18 
     19 import android.app.Activity;
     20 import android.content.ActivityNotFoundException;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.graphics.Bitmap;
     24 import android.net.Uri;
     25 import android.net.http.SslCertificate;
     26 import android.net.http.SslError;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.os.Message;
     30 import android.os.SystemClock;
     31 import android.provider.Browser;
     32 import android.util.Log;
     33 import android.view.KeyEvent;
     34 import com.android.internal.R;
     35 
     36 import java.net.MalformedURLException;
     37 import java.net.URL;
     38 import java.util.HashMap;
     39 import java.util.List;
     40 import java.util.Map;
     41 
     42 /**
     43  * This class is a proxy class for handling WebCore -> UI thread messaging. All
     44  * the callback functions are called from the WebCore thread and messages are
     45  * posted to the UI thread for the actual client callback.
     46  */
     47 /*
     48  * This class is created in the UI thread so its handler and any private classes
     49  * that extend Handler will operate in the UI thread.
     50  */
     51 class CallbackProxy extends Handler {
     52     // Logging tag
     53     private static final String LOGTAG = "CallbackProxy";
     54     // Instance of WebViewClient that is the client callback.
     55     private volatile WebViewClient mWebViewClient;
     56     // Instance of WebChromeClient for handling all chrome functions.
     57     private volatile WebChromeClient mWebChromeClient;
     58     // Instance of WebViewClassic for handling UI requests.
     59     private final WebViewClassic mWebView;
     60     // Client registered callback listener for download events
     61     private volatile DownloadListener mDownloadListener;
     62     // Keep track of multiple progress updates.
     63     private boolean mProgressUpdatePending;
     64     // Keep track of the last progress amount.
     65     // Start with 100 to indicate it is not in load for the empty page.
     66     private volatile int mLatestProgress = 100;
     67     // Back/Forward list
     68     private final WebBackForwardListClassic mBackForwardList;
     69     // Back/Forward list client
     70     private volatile WebBackForwardListClient mWebBackForwardListClient;
     71     // Used to call startActivity during url override.
     72     private final Context mContext;
     73     // block messages flag for destroy
     74     private boolean mBlockMessages;
     75 
     76     // Message IDs
     77     private static final int PAGE_STARTED                         = 100;
     78     private static final int RECEIVED_ICON                        = 101;
     79     private static final int RECEIVED_TITLE                       = 102;
     80     private static final int OVERRIDE_URL                         = 103;
     81     private static final int AUTH_REQUEST                         = 104;
     82     private static final int SSL_ERROR                            = 105;
     83     private static final int PROGRESS                             = 106;
     84     private static final int UPDATE_VISITED                       = 107;
     85     private static final int LOAD_RESOURCE                        = 108;
     86     private static final int CREATE_WINDOW                        = 109;
     87     private static final int CLOSE_WINDOW                         = 110;
     88     private static final int SAVE_PASSWORD                        = 111;
     89     private static final int JS_DIALOG                            = 112;
     90     private static final int ASYNC_KEYEVENTS                      = 116;
     91     private static final int DOWNLOAD_FILE                        = 118;
     92     private static final int REPORT_ERROR                         = 119;
     93     private static final int RESEND_POST_DATA                     = 120;
     94     private static final int PAGE_FINISHED                        = 121;
     95     private static final int REQUEST_FOCUS                        = 122;
     96     private static final int SCALE_CHANGED                        = 123;
     97     private static final int RECEIVED_CERTIFICATE                 = 124;
     98     private static final int SWITCH_OUT_HISTORY                   = 125;
     99     private static final int EXCEEDED_DATABASE_QUOTA              = 126;
    100     private static final int REACHED_APPCACHE_MAXSIZE             = 127;
    101     private static final int JS_TIMEOUT                           = 128;
    102     private static final int ADD_MESSAGE_TO_CONSOLE               = 129;
    103     private static final int GEOLOCATION_PERMISSIONS_SHOW_PROMPT  = 130;
    104     private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT  = 131;
    105     private static final int RECEIVED_TOUCH_ICON_URL              = 132;
    106     private static final int GET_VISITED_HISTORY                  = 133;
    107     private static final int OPEN_FILE_CHOOSER                    = 134;
    108     private static final int ADD_HISTORY_ITEM                     = 135;
    109     private static final int HISTORY_INDEX_CHANGED                = 136;
    110     private static final int AUTH_CREDENTIALS                     = 137;
    111     private static final int AUTO_LOGIN                           = 140;
    112     private static final int CLIENT_CERT_REQUEST                  = 141;
    113     private static final int PROCEEDED_AFTER_SSL_ERROR            = 144;
    114 
    115     // Message triggered by the client to resume execution
    116     private static final int NOTIFY                               = 200;
    117 
    118     // Result transportation object for returning results across thread
    119     // boundaries.
    120     private static class ResultTransport<E> {
    121         // Private result object
    122         private E mResult;
    123 
    124         public ResultTransport(E defaultResult) {
    125             mResult = defaultResult;
    126         }
    127 
    128         public synchronized void setResult(E result) {
    129             mResult = result;
    130         }
    131 
    132         public synchronized E getResult() {
    133             return mResult;
    134         }
    135     }
    136 
    137     private class JsResultReceiver implements JsResult.ResultReceiver {
    138         // This prevents a user from interacting with the result before WebCore is
    139         // ready to handle it.
    140         private boolean mReady;
    141         // Tells us if the user tried to confirm or cancel the result before WebCore
    142         // is ready.
    143         private boolean mTriedToNotifyBeforeReady;
    144 
    145         public JsPromptResult mJsResult = new JsPromptResult(this);
    146 
    147         final void setReady() {
    148             mReady = true;
    149             if (mTriedToNotifyBeforeReady) {
    150                 notifyCallbackProxy();
    151             }
    152         }
    153 
    154         /* Wake up the WebCore thread. */
    155         @Override
    156         public void onJsResultComplete(JsResult result) {
    157             if (mReady) {
    158                 notifyCallbackProxy();
    159             } else {
    160                 mTriedToNotifyBeforeReady = true;
    161             }
    162         }
    163 
    164         private void notifyCallbackProxy() {
    165             synchronized (CallbackProxy.this) {
    166                 CallbackProxy.this.notify();
    167             }
    168         }
    169 }
    170 
    171     /**
    172      * Construct a new CallbackProxy.
    173      */
    174     public CallbackProxy(Context context, WebViewClassic w) {
    175         // Used to start a default activity.
    176         mContext = context;
    177         mWebView = w;
    178         mBackForwardList = new WebBackForwardListClassic(this);
    179     }
    180 
    181     protected synchronized void blockMessages() {
    182         mBlockMessages = true;
    183     }
    184 
    185     protected synchronized boolean messagesBlocked() {
    186         return mBlockMessages;
    187     }
    188 
    189     protected void shutdown() {
    190         removeCallbacksAndMessages(null);
    191         setWebViewClient(null);
    192         setWebChromeClient(null);
    193     }
    194 
    195     /**
    196      * Set the WebViewClient.
    197      * @param client An implementation of WebViewClient.
    198      */
    199     public void setWebViewClient(WebViewClient client) {
    200         mWebViewClient = client;
    201     }
    202 
    203     /**
    204      * Get the WebViewClient.
    205      * @return the current WebViewClient instance.
    206      */
    207     public WebViewClient getWebViewClient() {
    208        return mWebViewClient;
    209     }
    210 
    211     /**
    212      * Set the WebChromeClient.
    213      * @param client An implementation of WebChromeClient.
    214      */
    215     public void setWebChromeClient(WebChromeClient client) {
    216         mWebChromeClient = client;
    217     }
    218 
    219     /**
    220      * Get the WebChromeClient.
    221      * @return the current WebChromeClient instance.
    222      */
    223     public WebChromeClient getWebChromeClient() {
    224        return mWebChromeClient;
    225     }
    226 
    227     /**
    228      * Set the client DownloadListener.
    229      * @param client An implementation of DownloadListener.
    230      */
    231     public void setDownloadListener(DownloadListener client) {
    232         mDownloadListener = client;
    233     }
    234 
    235     /**
    236      * Get the Back/Forward list to return to the user or to update the cached
    237      * history list.
    238      */
    239     public WebBackForwardListClassic getBackForwardList() {
    240         return mBackForwardList;
    241     }
    242 
    243     void setWebBackForwardListClient(WebBackForwardListClient client) {
    244         mWebBackForwardListClient = client;
    245     }
    246 
    247     WebBackForwardListClient getWebBackForwardListClient() {
    248         return mWebBackForwardListClient;
    249     }
    250 
    251     /**
    252      * Called by the UI side.  Calling overrideUrlLoading from the WebCore
    253      * side will post a message to call this method.
    254      */
    255     public boolean uiOverrideUrlLoading(String overrideUrl) {
    256         if (overrideUrl == null || overrideUrl.length() == 0) {
    257             return false;
    258         }
    259         boolean override = false;
    260         if (mWebViewClient != null) {
    261             override = mWebViewClient.shouldOverrideUrlLoading(mWebView.getWebView(),
    262                     overrideUrl);
    263         } else {
    264             Intent intent = new Intent(Intent.ACTION_VIEW,
    265                     Uri.parse(overrideUrl));
    266             intent.addCategory(Intent.CATEGORY_BROWSABLE);
    267             // If another application is running a WebView and launches the
    268             // Browser through this Intent, we want to reuse the same window if
    269             // possible.
    270             intent.putExtra(Browser.EXTRA_APPLICATION_ID,
    271                     mContext.getPackageName());
    272             try {
    273                 mContext.startActivity(intent);
    274                 override = true;
    275             } catch (ActivityNotFoundException ex) {
    276                 // If no application can handle the URL, assume that the
    277                 // browser can handle it.
    278             }
    279         }
    280         return override;
    281     }
    282 
    283     /**
    284      * Called by UI side.
    285      */
    286     public boolean uiOverrideKeyEvent(KeyEvent event) {
    287         if (mWebViewClient != null) {
    288             return mWebViewClient.shouldOverrideKeyEvent(mWebView.getWebView(), event);
    289         }
    290         return false;
    291     }
    292 
    293     @Override
    294     public void handleMessage(Message msg) {
    295         // We don't have to do synchronization because this function operates
    296         // in the UI thread. The WebViewClient and WebChromeClient functions
    297         // that check for a non-null callback are ok because java ensures atomic
    298         // 32-bit reads and writes.
    299         if (messagesBlocked()) return;
    300         switch (msg.what) {
    301             case PAGE_STARTED:
    302                 String startedUrl = msg.getData().getString("url");
    303                 mWebView.onPageStarted(startedUrl);
    304                 if (mWebViewClient != null) {
    305                     mWebViewClient.onPageStarted(mWebView.getWebView(), startedUrl,
    306                             (Bitmap) msg.obj);
    307                 }
    308                 break;
    309 
    310             case PAGE_FINISHED:
    311                 String finishedUrl = (String) msg.obj;
    312                 mWebView.onPageFinished(finishedUrl);
    313                 if (mWebViewClient != null) {
    314                     mWebViewClient.onPageFinished(mWebView.getWebView(), finishedUrl);
    315                 }
    316                 break;
    317 
    318             case RECEIVED_ICON:
    319                 if (mWebChromeClient != null) {
    320                     mWebChromeClient.onReceivedIcon(mWebView.getWebView(), (Bitmap) msg.obj);
    321                 }
    322                 break;
    323 
    324             case RECEIVED_TOUCH_ICON_URL:
    325                 if (mWebChromeClient != null) {
    326                     mWebChromeClient.onReceivedTouchIconUrl(mWebView.getWebView(),
    327                             (String) msg.obj, msg.arg1 == 1);
    328                 }
    329                 break;
    330 
    331             case RECEIVED_TITLE:
    332                 if (mWebChromeClient != null) {
    333                     mWebChromeClient.onReceivedTitle(mWebView.getWebView(),
    334                             (String) msg.obj);
    335                 }
    336                 break;
    337 
    338             case REPORT_ERROR:
    339                 if (mWebViewClient != null) {
    340                     int reasonCode = msg.arg1;
    341                     final String description  = msg.getData().getString("description");
    342                     final String failUrl  = msg.getData().getString("failingUrl");
    343                     mWebViewClient.onReceivedError(mWebView.getWebView(), reasonCode,
    344                             description, failUrl);
    345                 }
    346                 break;
    347 
    348             case RESEND_POST_DATA:
    349                 Message resend =
    350                         (Message) msg.getData().getParcelable("resend");
    351                 Message dontResend =
    352                         (Message) msg.getData().getParcelable("dontResend");
    353                 if (mWebViewClient != null) {
    354                     mWebViewClient.onFormResubmission(mWebView.getWebView(), dontResend,
    355                             resend);
    356                 } else {
    357                     dontResend.sendToTarget();
    358                 }
    359                 break;
    360 
    361             case OVERRIDE_URL:
    362                 String overrideUrl = msg.getData().getString("url");
    363                 boolean override = uiOverrideUrlLoading(overrideUrl);
    364                 ResultTransport<Boolean> result =
    365                         (ResultTransport<Boolean>) msg.obj;
    366                 synchronized (this) {
    367                     result.setResult(override);
    368                     notify();
    369                 }
    370                 break;
    371 
    372             case AUTH_REQUEST:
    373                 if (mWebViewClient != null) {
    374                     HttpAuthHandler handler = (HttpAuthHandler) msg.obj;
    375                     String host = msg.getData().getString("host");
    376                     String realm = msg.getData().getString("realm");
    377                     mWebViewClient.onReceivedHttpAuthRequest(mWebView.getWebView(), handler,
    378                             host, realm);
    379                 }
    380                 break;
    381 
    382             case SSL_ERROR:
    383                 if (mWebViewClient != null) {
    384                     HashMap<String, Object> map =
    385                         (HashMap<String, Object>) msg.obj;
    386                     mWebViewClient.onReceivedSslError(mWebView.getWebView(),
    387                             (SslErrorHandler) map.get("handler"),
    388                             (SslError) map.get("error"));
    389                 }
    390                 break;
    391 
    392             case PROCEEDED_AFTER_SSL_ERROR:
    393                 if (mWebViewClient != null && mWebViewClient instanceof WebViewClientClassicExt) {
    394                     ((WebViewClientClassicExt) mWebViewClient).onProceededAfterSslError(
    395                             mWebView.getWebView(),
    396                             (SslError) msg.obj);
    397                 }
    398                 break;
    399 
    400             case CLIENT_CERT_REQUEST:
    401                 if (mWebViewClient != null  && mWebViewClient instanceof WebViewClientClassicExt) {
    402                     HashMap<String, Object> map = (HashMap<String, Object>) msg.obj;
    403                     ((WebViewClientClassicExt) mWebViewClient).onReceivedClientCertRequest(
    404                             mWebView.getWebView(),
    405                             (ClientCertRequestHandler) map.get("handler"),
    406                             (String) map.get("host_and_port"));
    407                 }
    408                 break;
    409 
    410             case PROGRESS:
    411                 // Synchronize to ensure mLatestProgress is not modified after
    412                 // setProgress is called and before mProgressUpdatePending is
    413                 // changed.
    414                 synchronized (this) {
    415                     if (mWebChromeClient != null) {
    416                         mWebChromeClient.onProgressChanged(mWebView.getWebView(),
    417                                 mLatestProgress);
    418                     }
    419                     mProgressUpdatePending = false;
    420                 }
    421                 break;
    422 
    423             case UPDATE_VISITED:
    424                 if (mWebViewClient != null) {
    425                     mWebViewClient.doUpdateVisitedHistory(mWebView.getWebView(),
    426                             (String) msg.obj, msg.arg1 != 0);
    427                 }
    428                 break;
    429 
    430             case LOAD_RESOURCE:
    431                 if (mWebViewClient != null) {
    432                     mWebViewClient.onLoadResource(mWebView.getWebView(), (String) msg.obj);
    433                 }
    434                 break;
    435 
    436             case DOWNLOAD_FILE:
    437                 if (mDownloadListener != null) {
    438                     String url = msg.getData().getString("url");
    439                     String userAgent = msg.getData().getString("userAgent");
    440                     String contentDisposition =
    441                         msg.getData().getString("contentDisposition");
    442                     String mimetype = msg.getData().getString("mimetype");
    443                     String referer = msg.getData().getString("referer");
    444                     Long contentLength = msg.getData().getLong("contentLength");
    445 
    446                     if (mDownloadListener instanceof BrowserDownloadListener) {
    447                         ((BrowserDownloadListener) mDownloadListener).onDownloadStart(url,
    448                              userAgent, contentDisposition, mimetype, referer, contentLength);
    449                     } else {
    450                         mDownloadListener.onDownloadStart(url, userAgent,
    451                              contentDisposition, mimetype, contentLength);
    452                     }
    453                 }
    454                 break;
    455 
    456             case CREATE_WINDOW:
    457                 if (mWebChromeClient != null) {
    458                     if (!mWebChromeClient.onCreateWindow(mWebView.getWebView(),
    459                                 msg.arg1 == 1, msg.arg2 == 1,
    460                                 (Message) msg.obj)) {
    461                         synchronized (this) {
    462                             notify();
    463                         }
    464                     }
    465                     mWebView.dismissZoomControl();
    466                 }
    467                 break;
    468 
    469             case REQUEST_FOCUS:
    470                 if (mWebChromeClient != null) {
    471                     mWebChromeClient.onRequestFocus(mWebView.getWebView());
    472                 }
    473                 break;
    474 
    475             case CLOSE_WINDOW:
    476                 if (mWebChromeClient != null) {
    477                     mWebChromeClient.onCloseWindow(((WebViewClassic) msg.obj).getWebView());
    478                 }
    479                 break;
    480 
    481             case SAVE_PASSWORD:
    482                 Bundle bundle = msg.getData();
    483                 String schemePlusHost = bundle.getString("host");
    484                 String username = bundle.getString("username");
    485                 String password = bundle.getString("password");
    486                 // If the client returned false it means that the notify message
    487                 // will not be sent and we should notify WebCore ourselves.
    488                 if (!mWebView.onSavePassword(schemePlusHost, username, password,
    489                             (Message) msg.obj)) {
    490                     synchronized (this) {
    491                         notify();
    492                     }
    493                 }
    494                 break;
    495 
    496             case ASYNC_KEYEVENTS:
    497                 if (mWebViewClient != null) {
    498                     mWebViewClient.onUnhandledKeyEvent(mWebView.getWebView(),
    499                             (KeyEvent) msg.obj);
    500                 }
    501                 break;
    502 
    503             case EXCEEDED_DATABASE_QUOTA:
    504                 if (mWebChromeClient != null) {
    505                     HashMap<String, Object> map =
    506                             (HashMap<String, Object>) msg.obj;
    507                     String databaseIdentifier =
    508                             (String) map.get("databaseIdentifier");
    509                     String url = (String) map.get("url");
    510                     long quota =
    511                             ((Long) map.get("quota")).longValue();
    512                     long totalQuota =
    513                             ((Long) map.get("totalQuota")).longValue();
    514                     long estimatedDatabaseSize =
    515                             ((Long) map.get("estimatedDatabaseSize")).longValue();
    516                     WebStorage.QuotaUpdater quotaUpdater =
    517                         (WebStorage.QuotaUpdater) map.get("quotaUpdater");
    518 
    519                     mWebChromeClient.onExceededDatabaseQuota(url,
    520                             databaseIdentifier, quota, estimatedDatabaseSize,
    521                             totalQuota, quotaUpdater);
    522                 }
    523                 break;
    524 
    525             case REACHED_APPCACHE_MAXSIZE:
    526                 if (mWebChromeClient != null) {
    527                     HashMap<String, Object> map =
    528                             (HashMap<String, Object>) msg.obj;
    529                     long requiredStorage =
    530                             ((Long) map.get("requiredStorage")).longValue();
    531                     long quota =
    532                         ((Long) map.get("quota")).longValue();
    533                     WebStorage.QuotaUpdater quotaUpdater =
    534                         (WebStorage.QuotaUpdater) map.get("quotaUpdater");
    535 
    536                     mWebChromeClient.onReachedMaxAppCacheSize(requiredStorage,
    537                             quota, quotaUpdater);
    538                 }
    539                 break;
    540 
    541             case GEOLOCATION_PERMISSIONS_SHOW_PROMPT:
    542                 if (mWebChromeClient != null) {
    543                     HashMap<String, Object> map =
    544                             (HashMap<String, Object>) msg.obj;
    545                     String origin = (String) map.get("origin");
    546                     GeolocationPermissions.Callback callback =
    547                             (GeolocationPermissions.Callback)
    548                             map.get("callback");
    549                     mWebChromeClient.onGeolocationPermissionsShowPrompt(origin,
    550                             callback);
    551                 }
    552                 break;
    553 
    554             case GEOLOCATION_PERMISSIONS_HIDE_PROMPT:
    555                 if (mWebChromeClient != null) {
    556                     mWebChromeClient.onGeolocationPermissionsHidePrompt();
    557                 }
    558                 break;
    559 
    560             case JS_DIALOG:
    561                 if (mWebChromeClient != null) {
    562                     final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
    563                     JsDialogHelper helper = new JsDialogHelper(receiver.mJsResult, msg);
    564                     if (!helper.invokeCallback(mWebChromeClient, mWebView.getWebView())) {
    565                         helper.showDialog(mContext);
    566                     }
    567                     receiver.setReady();
    568                 }
    569                 break;
    570 
    571             case JS_TIMEOUT:
    572                 if(mWebChromeClient != null) {
    573                     final JsResultReceiver receiver = (JsResultReceiver) msg.obj;
    574                     final JsResult res = receiver.mJsResult;
    575                     if (mWebChromeClient.onJsTimeout()) {
    576                         res.confirm();
    577                     } else {
    578                         res.cancel();
    579                     }
    580                     receiver.setReady();
    581                 }
    582                 break;
    583 
    584             case RECEIVED_CERTIFICATE:
    585                 mWebView.setCertificate((SslCertificate) msg.obj);
    586                 break;
    587 
    588             case NOTIFY:
    589                 synchronized (this) {
    590                     notify();
    591                 }
    592                 break;
    593 
    594             case SCALE_CHANGED:
    595                 if (mWebViewClient != null) {
    596                     mWebViewClient.onScaleChanged(mWebView.getWebView(), msg.getData()
    597                             .getFloat("old"), msg.getData().getFloat("new"));
    598                 }
    599                 break;
    600 
    601             case SWITCH_OUT_HISTORY:
    602                 mWebView.switchOutDrawHistory();
    603                 break;
    604 
    605             case ADD_MESSAGE_TO_CONSOLE:
    606                 if (mWebChromeClient == null) {
    607                     break;
    608                 }
    609                 String message = msg.getData().getString("message");
    610                 String sourceID = msg.getData().getString("sourceID");
    611                 int lineNumber = msg.getData().getInt("lineNumber");
    612                 int msgLevel = msg.getData().getInt("msgLevel");
    613                 int numberOfMessageLevels = ConsoleMessage.MessageLevel.values().length;
    614                 // Sanity bounds check as we'll index an array with msgLevel
    615                 if (msgLevel < 0 || msgLevel >= numberOfMessageLevels) {
    616                     msgLevel = 0;
    617                 }
    618 
    619                 ConsoleMessage.MessageLevel messageLevel =
    620                         ConsoleMessage.MessageLevel.values()[msgLevel];
    621 
    622                 if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
    623                         lineNumber, messageLevel))) {
    624                     // If false was returned the user did not provide their own console function so
    625                     //  we should output some default messages to the system log.
    626                     String logTag = "Web Console";
    627                     String logMessage = message + " at " + sourceID + ":" + lineNumber;
    628 
    629                     switch (messageLevel) {
    630                         case TIP:
    631                             Log.v(logTag, logMessage);
    632                             break;
    633                         case LOG:
    634                             Log.i(logTag, logMessage);
    635                             break;
    636                         case WARNING:
    637                             Log.w(logTag, logMessage);
    638                             break;
    639                         case ERROR:
    640                             Log.e(logTag, logMessage);
    641                             break;
    642                         case DEBUG:
    643                             Log.d(logTag, logMessage);
    644                             break;
    645                     }
    646                 }
    647 
    648                 break;
    649 
    650             case GET_VISITED_HISTORY:
    651                 if (mWebChromeClient != null) {
    652                     mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
    653                 }
    654                 break;
    655 
    656             case OPEN_FILE_CHOOSER:
    657                 if (mWebChromeClient != null) {
    658                     UploadFileMessageData data = (UploadFileMessageData)msg.obj;
    659                     mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType(),
    660                             data.getCapture());
    661                 }
    662                 break;
    663 
    664             case ADD_HISTORY_ITEM:
    665                 if (mWebBackForwardListClient != null) {
    666                     mWebBackForwardListClient.onNewHistoryItem(
    667                             (WebHistoryItem) msg.obj);
    668                 }
    669                 break;
    670 
    671             case HISTORY_INDEX_CHANGED:
    672                 if (mWebBackForwardListClient != null) {
    673                     mWebBackForwardListClient.onIndexChanged(
    674                             (WebHistoryItem) msg.obj, msg.arg1);
    675                 }
    676                 break;
    677             case AUTH_CREDENTIALS: {
    678                 String host = msg.getData().getString("host");
    679                 String realm = msg.getData().getString("realm");
    680                 username = msg.getData().getString("username");
    681                 password = msg.getData().getString("password");
    682                 mWebView.setHttpAuthUsernamePassword(
    683                         host, realm, username, password);
    684                 break;
    685             }
    686             case AUTO_LOGIN: {
    687                 if (mWebViewClient != null) {
    688                     String realm = msg.getData().getString("realm");
    689                     String account = msg.getData().getString("account");
    690                     String args = msg.getData().getString("args");
    691                     mWebViewClient.onReceivedLoginRequest(mWebView.getWebView(), realm,
    692                             account, args);
    693                 }
    694                 break;
    695             }
    696         }
    697     }
    698 
    699     /**
    700      * Return the latest progress.
    701      */
    702     public int getProgress() {
    703         return mLatestProgress;
    704     }
    705 
    706     /**
    707      * Called by WebCore side to switch out of history Picture drawing mode
    708      */
    709     void switchOutDrawHistory() {
    710         sendMessage(obtainMessage(SWITCH_OUT_HISTORY));
    711     }
    712 
    713     //--------------------------------------------------------------------------
    714     // WebViewClient functions.
    715     // NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so
    716     // it is not necessary to include it here.
    717     //--------------------------------------------------------------------------
    718 
    719     // Performance probe
    720     private static final boolean PERF_PROBE = false;
    721     private long mWebCoreThreadTime;
    722     private long mWebCoreIdleTime;
    723 
    724     /*
    725      * If PERF_PROBE is true, this block needs to be added to MessageQueue.java.
    726      * startWait() and finishWait() should be called before and after wait().
    727 
    728     private WaitCallback mWaitCallback = null;
    729     public static interface WaitCallback {
    730         void startWait();
    731         void finishWait();
    732     }
    733     public final void setWaitCallback(WaitCallback callback) {
    734         mWaitCallback = callback;
    735     }
    736     */
    737 
    738     // un-comment this block if PERF_PROBE is true
    739     /*
    740     private IdleCallback mIdleCallback = new IdleCallback();
    741 
    742     private final class IdleCallback implements MessageQueue.WaitCallback {
    743         private long mStartTime = 0;
    744 
    745         public void finishWait() {
    746             mWebCoreIdleTime += SystemClock.uptimeMillis() - mStartTime;
    747         }
    748 
    749         public void startWait() {
    750             mStartTime = SystemClock.uptimeMillis();
    751         }
    752     }
    753     */
    754 
    755     public void onPageStarted(String url, Bitmap favicon) {
    756         // We need to send the message even if no WebViewClient is set, because we need to call
    757         // WebView.onPageStarted().
    758 
    759         // Performance probe
    760         if (PERF_PROBE) {
    761             mWebCoreThreadTime = SystemClock.currentThreadTimeMillis();
    762             mWebCoreIdleTime = 0;
    763             // un-comment this if PERF_PROBE is true
    764 //            Looper.myQueue().setWaitCallback(mIdleCallback);
    765         }
    766         Message msg = obtainMessage(PAGE_STARTED);
    767         msg.obj = favicon;
    768         msg.getData().putString("url", url);
    769         sendMessage(msg);
    770     }
    771 
    772     public void onPageFinished(String url) {
    773         // Performance probe
    774         if (PERF_PROBE) {
    775             // un-comment this if PERF_PROBE is true
    776 //            Looper.myQueue().setWaitCallback(null);
    777             Log.d("WebCore", "WebCore thread used " +
    778                     (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime)
    779                     + " ms and idled " + mWebCoreIdleTime + " ms");
    780         }
    781         Message msg = obtainMessage(PAGE_FINISHED, url);
    782         sendMessage(msg);
    783     }
    784 
    785     // Because this method is public and because CallbackProxy is mistakenly
    786     // party of the public classes, we cannot remove this method.
    787     public void onTooManyRedirects(Message cancelMsg, Message continueMsg) {
    788         // deprecated.
    789     }
    790 
    791     public void onReceivedError(int errorCode, String description,
    792             String failingUrl) {
    793         // Do an unsynchronized quick check to avoid posting if no callback has
    794         // been set.
    795         if (mWebViewClient == null) {
    796             return;
    797         }
    798 
    799         Message msg = obtainMessage(REPORT_ERROR);
    800         msg.arg1 = errorCode;
    801         msg.getData().putString("description", description);
    802         msg.getData().putString("failingUrl", failingUrl);
    803         sendMessage(msg);
    804     }
    805 
    806     public void onFormResubmission(Message dontResend,
    807             Message resend) {
    808         // Do an unsynchronized quick check to avoid posting if no callback has
    809         // been set.
    810         if (mWebViewClient == null) {
    811             dontResend.sendToTarget();
    812             return;
    813         }
    814 
    815         Message msg = obtainMessage(RESEND_POST_DATA);
    816         Bundle bundle = msg.getData();
    817         bundle.putParcelable("resend", resend);
    818         bundle.putParcelable("dontResend", dontResend);
    819         sendMessage(msg);
    820     }
    821 
    822     /**
    823      * Called by the WebCore side
    824      */
    825     public boolean shouldOverrideUrlLoading(String url) {
    826         // We have a default behavior if no client exists so always send the
    827         // message.
    828         ResultTransport<Boolean> res = new ResultTransport<Boolean>(false);
    829         Message msg = obtainMessage(OVERRIDE_URL);
    830         msg.getData().putString("url", url);
    831         msg.obj = res;
    832         sendMessageToUiThreadSync(msg);
    833         return res.getResult().booleanValue();
    834     }
    835 
    836     public void onReceivedHttpAuthRequest(HttpAuthHandler handler,
    837             String hostName, String realmName) {
    838         // Do an unsynchronized quick check to avoid posting if no callback has
    839         // been set.
    840         if (mWebViewClient == null) {
    841             handler.cancel();
    842             return;
    843         }
    844         Message msg = obtainMessage(AUTH_REQUEST, handler);
    845         msg.getData().putString("host", hostName);
    846         msg.getData().putString("realm", realmName);
    847         sendMessage(msg);
    848     }
    849 
    850     public void onReceivedSslError(SslErrorHandler handler, SslError error) {
    851         // Do an unsynchronized quick check to avoid posting if no callback has
    852         // been set.
    853         if (mWebViewClient == null) {
    854             handler.cancel();
    855             return;
    856         }
    857         Message msg = obtainMessage(SSL_ERROR);
    858         HashMap<String, Object> map = new HashMap();
    859         map.put("handler", handler);
    860         map.put("error", error);
    861         msg.obj = map;
    862         sendMessage(msg);
    863     }
    864 
    865     public void onProceededAfterSslError(SslError error) {
    866         if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) {
    867             return;
    868         }
    869         Message msg = obtainMessage(PROCEEDED_AFTER_SSL_ERROR);
    870         msg.obj = error;
    871         sendMessage(msg);
    872     }
    873 
    874     public void onReceivedClientCertRequest(ClientCertRequestHandler handler, String host_and_port) {
    875         // Do an unsynchronized quick check to avoid posting if no callback has
    876         // been set.
    877         if (mWebViewClient == null || !(mWebViewClient instanceof WebViewClientClassicExt)) {
    878             handler.cancel();
    879             return;
    880         }
    881         Message msg = obtainMessage(CLIENT_CERT_REQUEST);
    882         HashMap<String, Object> map = new HashMap();
    883         map.put("handler", handler);
    884         map.put("host_and_port", host_and_port);
    885         msg.obj = map;
    886         sendMessage(msg);
    887     }
    888 
    889     public void onReceivedCertificate(SslCertificate certificate) {
    890         // here, certificate can be null (if the site is not secure)
    891         sendMessage(obtainMessage(RECEIVED_CERTIFICATE, certificate));
    892     }
    893 
    894     public void doUpdateVisitedHistory(String url, boolean isReload) {
    895         // Do an unsynchronized quick check to avoid posting if no callback has
    896         // been set.
    897         if (mWebViewClient == null) {
    898             return;
    899         }
    900         sendMessage(obtainMessage(UPDATE_VISITED, isReload ? 1 : 0, 0, url));
    901     }
    902 
    903     WebResourceResponse shouldInterceptRequest(String url) {
    904         if (mWebViewClient == null) {
    905             return null;
    906         }
    907         // Note: This method does _not_ send a message.
    908         WebResourceResponse r =
    909                 mWebViewClient.shouldInterceptRequest(mWebView.getWebView(), url);
    910         if (r == null) {
    911             sendMessage(obtainMessage(LOAD_RESOURCE, url));
    912         }
    913         return r;
    914     }
    915 
    916     public void onUnhandledKeyEvent(KeyEvent event) {
    917         // Do an unsynchronized quick check to avoid posting if no callback has
    918         // been set.
    919         if (mWebViewClient == null) {
    920             return;
    921         }
    922         sendMessage(obtainMessage(ASYNC_KEYEVENTS, event));
    923     }
    924 
    925     public void onScaleChanged(float oldScale, float newScale) {
    926         // Do an unsynchronized quick check to avoid posting if no callback has
    927         // been set.
    928         if (mWebViewClient == null) {
    929             return;
    930         }
    931         Message msg = obtainMessage(SCALE_CHANGED);
    932         Bundle bundle = msg.getData();
    933         bundle.putFloat("old", oldScale);
    934         bundle.putFloat("new", newScale);
    935         sendMessage(msg);
    936     }
    937 
    938     void onReceivedLoginRequest(String realm, String account, String args) {
    939         // Do an unsynchronized quick check to avoid posting if no callback has
    940         // been set.
    941         if (mWebViewClient == null) {
    942             return;
    943         }
    944         Message msg = obtainMessage(AUTO_LOGIN);
    945         Bundle bundle = msg.getData();
    946         bundle.putString("realm", realm);
    947         bundle.putString("account", account);
    948         bundle.putString("args", args);
    949         sendMessage(msg);
    950     }
    951 
    952     //--------------------------------------------------------------------------
    953     // DownloadListener functions.
    954     //--------------------------------------------------------------------------
    955 
    956     /**
    957      * Starts a download if a download listener has been registered, otherwise
    958      * return false.
    959      */
    960     public boolean onDownloadStart(String url, String userAgent,
    961             String contentDisposition, String mimetype, String referer,
    962             long contentLength) {
    963         // Do an unsynchronized quick check to avoid posting if no callback has
    964         // been set.
    965         if (mDownloadListener == null) {
    966             // Cancel the download if there is no browser client.
    967             return false;
    968         }
    969 
    970         Message msg = obtainMessage(DOWNLOAD_FILE);
    971         Bundle bundle = msg.getData();
    972         bundle.putString("url", url);
    973         bundle.putString("userAgent", userAgent);
    974         bundle.putString("mimetype", mimetype);
    975         bundle.putString("referer", referer);
    976         bundle.putLong("contentLength", contentLength);
    977         bundle.putString("contentDisposition", contentDisposition);
    978         sendMessage(msg);
    979         return true;
    980     }
    981 
    982 
    983     //--------------------------------------------------------------------------
    984     // WebView specific functions that do not interact with a client. These
    985     // functions just need to operate within the UI thread.
    986     //--------------------------------------------------------------------------
    987 
    988     public boolean onSavePassword(String schemePlusHost, String username,
    989             String password, Message resumeMsg) {
    990         // resumeMsg should be null at this point because we want to create it
    991         // within the CallbackProxy.
    992         if (DebugFlags.CALLBACK_PROXY) {
    993             junit.framework.Assert.assertNull(resumeMsg);
    994         }
    995         resumeMsg = obtainMessage(NOTIFY);
    996 
    997         Message msg = obtainMessage(SAVE_PASSWORD, resumeMsg);
    998         Bundle bundle = msg.getData();
    999         bundle.putString("host", schemePlusHost);
   1000         bundle.putString("username", username);
   1001         bundle.putString("password", password);
   1002         sendMessageToUiThreadSync(msg);
   1003         // Doesn't matter here
   1004         return false;
   1005     }
   1006 
   1007     public void onReceivedHttpAuthCredentials(String host, String realm,
   1008             String username, String password) {
   1009         Message msg = obtainMessage(AUTH_CREDENTIALS);
   1010         msg.getData().putString("host", host);
   1011         msg.getData().putString("realm", realm);
   1012         msg.getData().putString("username", username);
   1013         msg.getData().putString("password", password);
   1014         sendMessage(msg);
   1015     }
   1016 
   1017     //--------------------------------------------------------------------------
   1018     // WebChromeClient methods
   1019     //--------------------------------------------------------------------------
   1020 
   1021     public void onProgressChanged(int newProgress) {
   1022         // Synchronize so that mLatestProgress is up-to-date.
   1023         synchronized (this) {
   1024             // update mLatestProgress even mWebChromeClient is null as
   1025             // WebView.getProgress() needs it
   1026             if (mLatestProgress == newProgress) {
   1027                 return;
   1028             }
   1029             mLatestProgress = newProgress;
   1030             if (mWebChromeClient == null) {
   1031                 return;
   1032             }
   1033             if (!mProgressUpdatePending) {
   1034                 sendEmptyMessage(PROGRESS);
   1035                 mProgressUpdatePending = true;
   1036             }
   1037         }
   1038     }
   1039 
   1040     public BrowserFrame createWindow(boolean dialog, boolean userGesture) {
   1041         // Do an unsynchronized quick check to avoid posting if no callback has
   1042         // been set.
   1043         if (mWebChromeClient == null) {
   1044             return null;
   1045         }
   1046 
   1047         WebView.WebViewTransport transport =
   1048             mWebView.getWebView().new WebViewTransport();
   1049         final Message msg = obtainMessage(NOTIFY);
   1050         msg.obj = transport;
   1051         sendMessageToUiThreadSync(obtainMessage(CREATE_WINDOW, dialog ? 1 : 0,
   1052                 userGesture ? 1 : 0, msg));
   1053         WebViewClassic w = WebViewClassic.fromWebView(transport.getWebView());
   1054         if (w != null) {
   1055             WebViewCore core = w.getWebViewCore();
   1056             // If WebView.destroy() has been called, core may be null.  Skip
   1057             // initialization in that case and return null.
   1058             if (core != null) {
   1059                 core.initializeSubwindow();
   1060                 return core.getBrowserFrame();
   1061             }
   1062         }
   1063         return null;
   1064     }
   1065 
   1066     public void onRequestFocus() {
   1067         // Do an unsynchronized quick check to avoid posting if no callback has
   1068         // been set.
   1069         if (mWebChromeClient == null) {
   1070             return;
   1071         }
   1072 
   1073         sendEmptyMessage(REQUEST_FOCUS);
   1074     }
   1075 
   1076     public void onCloseWindow(WebViewClassic window) {
   1077         // Do an unsynchronized quick check to avoid posting if no callback has
   1078         // been set.
   1079         if (mWebChromeClient == null) {
   1080             return;
   1081         }
   1082         sendMessage(obtainMessage(CLOSE_WINDOW, window));
   1083     }
   1084 
   1085     public void onReceivedIcon(Bitmap icon) {
   1086         // The current item might be null if the icon was already stored in the
   1087         // database and this is a new WebView.
   1088         WebHistoryItemClassic i = mBackForwardList.getCurrentItem();
   1089         if (i != null) {
   1090             i.setFavicon(icon);
   1091         }
   1092         // Do an unsynchronized quick check to avoid posting if no callback has
   1093         // been set.
   1094         if (mWebChromeClient == null) {
   1095             return;
   1096         }
   1097         sendMessage(obtainMessage(RECEIVED_ICON, icon));
   1098     }
   1099 
   1100     /* package */ void onReceivedTouchIconUrl(String url, boolean precomposed) {
   1101         // We should have a current item but we do not want to crash so check
   1102         // for null.
   1103         WebHistoryItemClassic i = mBackForwardList.getCurrentItem();
   1104         if (i != null) {
   1105             i.setTouchIconUrl(url, precomposed);
   1106         }
   1107         // Do an unsynchronized quick check to avoid posting if no callback has
   1108         // been set.
   1109         if (mWebChromeClient == null) {
   1110             return;
   1111         }
   1112         sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL,
   1113                 precomposed ? 1 : 0, 0, url));
   1114     }
   1115 
   1116     public void onReceivedTitle(String title) {
   1117         // Do an unsynchronized quick check to avoid posting if no callback has
   1118         // been set.
   1119         if (mWebChromeClient == null) {
   1120             return;
   1121         }
   1122         sendMessage(obtainMessage(RECEIVED_TITLE, title));
   1123     }
   1124 
   1125     public void onJsAlert(String url, String message) {
   1126         // Do an unsynchronized quick check to avoid posting if no callback has
   1127         // been set.
   1128         if (mWebChromeClient == null) {
   1129             return;
   1130         }
   1131         JsResultReceiver result = new JsResultReceiver();
   1132         Message alert = obtainMessage(JS_DIALOG, result);
   1133         alert.getData().putString("message", message);
   1134         alert.getData().putString("url", url);
   1135         alert.getData().putInt("type", JsDialogHelper.ALERT);
   1136         sendMessageToUiThreadSync(alert);
   1137     }
   1138 
   1139     public boolean onJsConfirm(String url, String message) {
   1140         // Do an unsynchronized quick check to avoid posting if no callback has
   1141         // been set.
   1142         if (mWebChromeClient == null) {
   1143             return false;
   1144         }
   1145         JsResultReceiver result = new JsResultReceiver();
   1146         Message confirm = obtainMessage(JS_DIALOG, result);
   1147         confirm.getData().putString("message", message);
   1148         confirm.getData().putString("url", url);
   1149         confirm.getData().putInt("type", JsDialogHelper.CONFIRM);
   1150         sendMessageToUiThreadSync(confirm);
   1151         return result.mJsResult.getResult();
   1152     }
   1153 
   1154     public String onJsPrompt(String url, String message, String defaultValue) {
   1155         // Do an unsynchronized quick check to avoid posting if no callback has
   1156         // been set.
   1157         if (mWebChromeClient == null) {
   1158             return null;
   1159         }
   1160         JsResultReceiver result = new JsResultReceiver();
   1161         Message prompt = obtainMessage(JS_DIALOG, result);
   1162         prompt.getData().putString("message", message);
   1163         prompt.getData().putString("default", defaultValue);
   1164         prompt.getData().putString("url", url);
   1165         prompt.getData().putInt("type", JsDialogHelper.PROMPT);
   1166         sendMessageToUiThreadSync(prompt);
   1167         return result.mJsResult.getStringResult();
   1168     }
   1169 
   1170     public boolean onJsBeforeUnload(String url, String message) {
   1171         // Do an unsynchronized quick check to avoid posting if no callback has
   1172         // been set.
   1173         if (mWebChromeClient == null) {
   1174             return true;
   1175         }
   1176         JsResultReceiver result = new JsResultReceiver();
   1177         Message unload = obtainMessage(JS_DIALOG, result);
   1178         unload.getData().putString("message", message);
   1179         unload.getData().putString("url", url);
   1180         unload.getData().putInt("type", JsDialogHelper.UNLOAD);
   1181         sendMessageToUiThreadSync(unload);
   1182         return result.mJsResult.getResult();
   1183     }
   1184 
   1185     /**
   1186      * Called by WebViewCore to inform the Java side that the current origin
   1187      * has overflowed it's database quota. Called in the WebCore thread so
   1188      * posts a message to the UI thread that will prompt the WebChromeClient
   1189      * for what to do. On return back to C++ side, the WebCore thread will
   1190      * sleep pending a new quota value.
   1191      * @param url The URL that caused the quota overflow.
   1192      * @param databaseIdentifier The identifier of the database that the
   1193      *     transaction that caused the overflow was running on.
   1194      * @param quota The current quota the origin is allowed.
   1195      * @param estimatedDatabaseSize The estimated size of the database.
   1196      * @param totalQuota is the sum of all origins' quota.
   1197      * @param quotaUpdater An instance of a class encapsulating a callback
   1198      *     to WebViewCore to run when the decision to allow or deny more
   1199      *     quota has been made.
   1200      */
   1201     public void onExceededDatabaseQuota(
   1202             String url, String databaseIdentifier, long quota,
   1203             long estimatedDatabaseSize, long totalQuota,
   1204             WebStorage.QuotaUpdater quotaUpdater) {
   1205         if (mWebChromeClient == null) {
   1206             // Native-side logic prevents the quota being updated to a smaller
   1207             // value.
   1208             quotaUpdater.updateQuota(quota);
   1209             return;
   1210         }
   1211 
   1212         Message exceededQuota = obtainMessage(EXCEEDED_DATABASE_QUOTA);
   1213         HashMap<String, Object> map = new HashMap();
   1214         map.put("databaseIdentifier", databaseIdentifier);
   1215         map.put("url", url);
   1216         map.put("quota", quota);
   1217         map.put("estimatedDatabaseSize", estimatedDatabaseSize);
   1218         map.put("totalQuota", totalQuota);
   1219         map.put("quotaUpdater", quotaUpdater);
   1220         exceededQuota.obj = map;
   1221         sendMessage(exceededQuota);
   1222     }
   1223 
   1224     /**
   1225      * Called by WebViewCore to inform the Java side that the appcache has
   1226      * exceeded its max size.
   1227      * @param requiredStorage is the amount of storage, in bytes, that would be
   1228      * needed in order for the last appcache operation to succeed.
   1229      * @param quota is the current quota (for all origins).
   1230      * @param quotaUpdater An instance of a class encapsulating a callback
   1231      * to WebViewCore to run when the decision to allow or deny a bigger
   1232      * app cache size has been made.
   1233      */
   1234     public void onReachedMaxAppCacheSize(long requiredStorage,
   1235             long quota, WebStorage.QuotaUpdater quotaUpdater) {
   1236         if (mWebChromeClient == null) {
   1237             // Native-side logic prevents the quota being updated to a smaller
   1238             // value.
   1239             quotaUpdater.updateQuota(quota);
   1240             return;
   1241         }
   1242 
   1243         Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE);
   1244         HashMap<String, Object> map = new HashMap();
   1245         map.put("requiredStorage", requiredStorage);
   1246         map.put("quota", quota);
   1247         map.put("quotaUpdater", quotaUpdater);
   1248         msg.obj = map;
   1249         sendMessage(msg);
   1250     }
   1251 
   1252     /**
   1253      * Called by WebViewCore to instruct the browser to display a prompt to ask
   1254      * the user to set the Geolocation permission state for the given origin.
   1255      * @param origin The origin requesting Geolocation permsissions.
   1256      * @param callback The callback to call once a permission state has been
   1257      *     obtained.
   1258      */
   1259     public void onGeolocationPermissionsShowPrompt(String origin,
   1260             GeolocationPermissions.Callback callback) {
   1261         if (mWebChromeClient == null) {
   1262             return;
   1263         }
   1264 
   1265         Message showMessage =
   1266                 obtainMessage(GEOLOCATION_PERMISSIONS_SHOW_PROMPT);
   1267         HashMap<String, Object> map = new HashMap();
   1268         map.put("origin", origin);
   1269         map.put("callback", callback);
   1270         showMessage.obj = map;
   1271         sendMessage(showMessage);
   1272     }
   1273 
   1274     /**
   1275      * Called by WebViewCore to instruct the browser to hide the Geolocation
   1276      * permissions prompt.
   1277      */
   1278     public void onGeolocationPermissionsHidePrompt() {
   1279         if (mWebChromeClient == null) {
   1280             return;
   1281         }
   1282 
   1283         Message hideMessage = obtainMessage(GEOLOCATION_PERMISSIONS_HIDE_PROMPT);
   1284         sendMessage(hideMessage);
   1285     }
   1286 
   1287     /**
   1288      * Called by WebViewCore when we have a message to be added to the JavaScript
   1289      * error console. Sends a message to the Java side with the details.
   1290      * @param message The message to add to the console.
   1291      * @param lineNumber The lineNumber of the source file on which the error
   1292      *     occurred.
   1293      * @param sourceID The filename of the source file in which the error
   1294      *     occurred.
   1295      * @param msgLevel The message level, corresponding to the MessageLevel enum in
   1296      *     WebCore/page/Console.h
   1297      */
   1298     public void addMessageToConsole(String message, int lineNumber, String sourceID, int msgLevel) {
   1299         if (mWebChromeClient == null) {
   1300             return;
   1301         }
   1302 
   1303         Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE);
   1304         msg.getData().putString("message", message);
   1305         msg.getData().putString("sourceID", sourceID);
   1306         msg.getData().putInt("lineNumber", lineNumber);
   1307         msg.getData().putInt("msgLevel", msgLevel);
   1308         sendMessage(msg);
   1309     }
   1310 
   1311     public boolean onJsTimeout() {
   1312         //always interrupt timedout JS by default
   1313         if (mWebChromeClient == null) {
   1314             return true;
   1315         }
   1316         JsResultReceiver result = new JsResultReceiver();
   1317         Message timeout = obtainMessage(JS_TIMEOUT, result);
   1318         sendMessageToUiThreadSync(timeout);
   1319         return result.mJsResult.getResult();
   1320     }
   1321 
   1322     public void getVisitedHistory(ValueCallback<String[]> callback) {
   1323         if (mWebChromeClient == null) {
   1324             return;
   1325         }
   1326         Message msg = obtainMessage(GET_VISITED_HISTORY);
   1327         msg.obj = callback;
   1328         sendMessage(msg);
   1329     }
   1330 
   1331     private static class UploadFileMessageData {
   1332         private UploadFile mCallback;
   1333         private String mAcceptType;
   1334         private String mCapture;
   1335 
   1336         public UploadFileMessageData(UploadFile uploadFile, String acceptType, String capture) {
   1337             mCallback = uploadFile;
   1338             mAcceptType = acceptType;
   1339             mCapture = capture;
   1340         }
   1341 
   1342         public UploadFile getUploadFile() {
   1343             return mCallback;
   1344         }
   1345 
   1346         public String getAcceptType() {
   1347             return mAcceptType;
   1348         }
   1349 
   1350         public String getCapture() {
   1351             return mCapture;
   1352         }
   1353     }
   1354 
   1355     private class UploadFile implements ValueCallback<Uri> {
   1356         private Uri mValue;
   1357         public void onReceiveValue(Uri value) {
   1358             mValue = value;
   1359             synchronized (CallbackProxy.this) {
   1360                 CallbackProxy.this.notify();
   1361             }
   1362         }
   1363         public Uri getResult() {
   1364             return mValue;
   1365         }
   1366     }
   1367 
   1368     /**
   1369      * Called by WebViewCore to open a file chooser.
   1370      */
   1371     /* package */ Uri openFileChooser(String acceptType, String capture) {
   1372         if (mWebChromeClient == null) {
   1373             return null;
   1374         }
   1375         Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
   1376         UploadFile uploadFile = new UploadFile();
   1377         UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType, capture);
   1378         myMessage.obj = data;
   1379         sendMessageToUiThreadSync(myMessage);
   1380         return uploadFile.getResult();
   1381     }
   1382 
   1383     void onNewHistoryItem(WebHistoryItem item) {
   1384         if (mWebBackForwardListClient == null) {
   1385             return;
   1386         }
   1387         Message msg = obtainMessage(ADD_HISTORY_ITEM, item);
   1388         sendMessage(msg);
   1389     }
   1390 
   1391     void onIndexChanged(WebHistoryItem item, int index) {
   1392         if (mWebBackForwardListClient == null) {
   1393             return;
   1394         }
   1395         Message msg = obtainMessage(HISTORY_INDEX_CHANGED, index, 0, item);
   1396         sendMessage(msg);
   1397     }
   1398 
   1399     private synchronized void sendMessageToUiThreadSync(Message msg) {
   1400         sendMessage(msg);
   1401         WebCoreThreadWatchdog.pause();
   1402         try {
   1403             wait();
   1404         } catch (InterruptedException e) {
   1405             Log.e(LOGTAG, "Caught exception waiting for synchronous UI message to be processed");
   1406             Log.e(LOGTAG, Log.getStackTraceString(e));
   1407         }
   1408         WebCoreThreadWatchdog.resume();
   1409     }
   1410 }
   1411