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