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