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