CallbackProxy.java revision 6262ae5c9df44c0673cebaeaf7f655094f5b5485
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.AlertDialog; 20import android.content.ActivityNotFoundException; 21import android.content.Context; 22import android.content.DialogInterface; 23import android.content.Intent; 24import android.graphics.Bitmap; 25import android.net.Uri; 26import android.net.http.SslCertificate; 27import android.net.http.SslError; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.Message; 31import android.os.SystemClock; 32import android.provider.Browser; 33import android.util.Log; 34import android.view.KeyEvent; 35import android.view.LayoutInflater; 36import android.view.View; 37import android.widget.EditText; 38import android.widget.TextView; 39import com.android.internal.R; 40 41import java.net.MalformedURLException; 42import java.net.URL; 43import java.util.HashMap; 44 45/** 46 * This class is a proxy class for handling WebCore -> UI thread messaging. All 47 * the callback functions are called from the WebCore thread and messages are 48 * posted to the UI thread for the actual client callback. 49 */ 50/* 51 * This class is created in the UI thread so its handler and any private classes 52 * that extend Handler will operate in the UI thread. 53 */ 54class CallbackProxy extends Handler { 55 // Logging tag 56 private static final String LOGTAG = "CallbackProxy"; 57 // Instance of WebViewClient that is the client callback. 58 private volatile WebViewClient mWebViewClient; 59 // Instance of WebChromeClient for handling all chrome functions. 60 private volatile WebChromeClient mWebChromeClient; 61 // Instance of WebView for handling UI requests. 62 private final WebView mWebView; 63 // Client registered callback listener for download events 64 private volatile DownloadListener mDownloadListener; 65 // Keep track of multiple progress updates. 66 private boolean mProgressUpdatePending; 67 // Keep track of the last progress amount. 68 private volatile int mLatestProgress; 69 // Back/Forward list 70 private final WebBackForwardList mBackForwardList; 71 // Used to call startActivity during url override. 72 private final Context mContext; 73 74 // Message Ids 75 private static final int PAGE_STARTED = 100; 76 private static final int RECEIVED_ICON = 101; 77 private static final int RECEIVED_TITLE = 102; 78 private static final int OVERRIDE_URL = 103; 79 private static final int AUTH_REQUEST = 104; 80 private static final int SSL_ERROR = 105; 81 private static final int PROGRESS = 106; 82 private static final int UPDATE_VISITED = 107; 83 private static final int LOAD_RESOURCE = 108; 84 private static final int CREATE_WINDOW = 109; 85 private static final int CLOSE_WINDOW = 110; 86 private static final int SAVE_PASSWORD = 111; 87 private static final int JS_ALERT = 112; 88 private static final int JS_CONFIRM = 113; 89 private static final int JS_PROMPT = 114; 90 private static final int JS_UNLOAD = 115; 91 private static final int ASYNC_KEYEVENTS = 116; 92 private static final int TOO_MANY_REDIRECTS = 117; 93 private static final int DOWNLOAD_FILE = 118; 94 private static final int REPORT_ERROR = 119; 95 private static final int RESEND_POST_DATA = 120; 96 private static final int PAGE_FINISHED = 121; 97 private static final int REQUEST_FOCUS = 122; 98 private static final int SCALE_CHANGED = 123; 99 private static final int RECEIVED_CERTIFICATE = 124; 100 private static final int SWITCH_OUT_HISTORY = 125; 101 private static final int EXCEEDED_DATABASE_QUOTA = 126; 102 private static final int JS_TIMEOUT = 127; 103 private static final int ADD_MESSAGE_TO_CONSOLE = 128; 104 105 // Message triggered by the client to resume execution 106 private static final int NOTIFY = 200; 107 108 // Result transportation object for returning results across thread 109 // boundaries. 110 private class ResultTransport<E> { 111 // Private result object 112 private E mResult; 113 114 public synchronized void setResult(E result) { 115 mResult = result; 116 } 117 118 public synchronized E getResult() { 119 return mResult; 120 } 121 } 122 123 /** 124 * Construct a new CallbackProxy. 125 */ 126 public CallbackProxy(Context context, WebView w) { 127 // Used to start a default activity. 128 mContext = context; 129 mWebView = w; 130 mBackForwardList = new WebBackForwardList(); 131 } 132 133 /** 134 * Set the WebViewClient. 135 * @param client An implementation of WebViewClient. 136 */ 137 public void setWebViewClient(WebViewClient client) { 138 mWebViewClient = client; 139 } 140 141 /** 142 * Set the WebChromeClient. 143 * @param client An implementation of WebChromeClient. 144 */ 145 public void setWebChromeClient(WebChromeClient client) { 146 mWebChromeClient = client; 147 } 148 149 /** 150 * Set the client DownloadListener. 151 * @param client An implementation of DownloadListener. 152 */ 153 public void setDownloadListener(DownloadListener client) { 154 mDownloadListener = client; 155 } 156 157 /** 158 * Get the Back/Forward list to return to the user or to update the cached 159 * history list. 160 */ 161 public WebBackForwardList getBackForwardList() { 162 return mBackForwardList; 163 } 164 165 /** 166 * Called by the UI side. Calling overrideUrlLoading from the WebCore 167 * side will post a message to call this method. 168 */ 169 public boolean uiOverrideUrlLoading(String overrideUrl) { 170 if (overrideUrl == null || overrideUrl.length() == 0) { 171 return false; 172 } 173 boolean override = false; 174 if (mWebViewClient != null) { 175 override = mWebViewClient.shouldOverrideUrlLoading(mWebView, 176 overrideUrl); 177 } else { 178 Intent intent = new Intent(Intent.ACTION_VIEW, 179 Uri.parse(overrideUrl)); 180 intent.addCategory(Intent.CATEGORY_BROWSABLE); 181 // If another application is running a WebView and launches the 182 // Browser through this Intent, we want to reuse the same window if 183 // possible. 184 intent.putExtra(Browser.EXTRA_APPLICATION_ID, 185 mContext.getPackageName()); 186 try { 187 mContext.startActivity(intent); 188 override = true; 189 } catch (ActivityNotFoundException ex) { 190 // If no application can handle the URL, assume that the 191 // browser can handle it. 192 } 193 } 194 return override; 195 } 196 197 /** 198 * Called by UI side. 199 */ 200 public boolean uiOverrideKeyEvent(KeyEvent event) { 201 if (mWebViewClient != null) { 202 return mWebViewClient.shouldOverrideKeyEvent(mWebView, event); 203 } 204 return false; 205 } 206 207 @Override 208 public void handleMessage(Message msg) { 209 // We don't have to do synchronization because this function operates 210 // in the UI thread. The WebViewClient and WebChromeClient functions 211 // that check for a non-null callback are ok because java ensures atomic 212 // 32-bit reads and writes. 213 switch (msg.what) { 214 case PAGE_STARTED: 215 if (mWebViewClient != null) { 216 mWebViewClient.onPageStarted(mWebView, 217 msg.getData().getString("url"), 218 (Bitmap) msg.obj); 219 } 220 break; 221 222 case PAGE_FINISHED: 223 if (mWebViewClient != null) { 224 mWebViewClient.onPageFinished(mWebView, (String) msg.obj); 225 } 226 break; 227 228 case RECEIVED_ICON: 229 if (mWebChromeClient != null) { 230 mWebChromeClient.onReceivedIcon(mWebView, (Bitmap) msg.obj); 231 } 232 break; 233 234 case RECEIVED_TITLE: 235 if (mWebChromeClient != null) { 236 mWebChromeClient.onReceivedTitle(mWebView, 237 (String) msg.obj); 238 } 239 break; 240 241 case TOO_MANY_REDIRECTS: 242 Message cancelMsg = 243 (Message) msg.getData().getParcelable("cancelMsg"); 244 Message continueMsg = 245 (Message) msg.getData().getParcelable("continueMsg"); 246 if (mWebViewClient != null) { 247 mWebViewClient.onTooManyRedirects(mWebView, cancelMsg, 248 continueMsg); 249 } else { 250 cancelMsg.sendToTarget(); 251 } 252 break; 253 254 case REPORT_ERROR: 255 if (mWebViewClient != null) { 256 int reasonCode = msg.arg1; 257 final String description = msg.getData().getString("description"); 258 final String failUrl = msg.getData().getString("failingUrl"); 259 mWebViewClient.onReceivedError(mWebView, reasonCode, 260 description, failUrl); 261 } 262 break; 263 264 case RESEND_POST_DATA: 265 Message resend = 266 (Message) msg.getData().getParcelable("resend"); 267 Message dontResend = 268 (Message) msg.getData().getParcelable("dontResend"); 269 if (mWebViewClient != null) { 270 mWebViewClient.onFormResubmission(mWebView, dontResend, 271 resend); 272 } else { 273 dontResend.sendToTarget(); 274 } 275 break; 276 277 case OVERRIDE_URL: 278 String overrideUrl = msg.getData().getString("url"); 279 boolean override = uiOverrideUrlLoading(overrideUrl); 280 ResultTransport<Boolean> result = 281 (ResultTransport<Boolean>) msg.obj; 282 synchronized (this) { 283 result.setResult(override); 284 notify(); 285 } 286 break; 287 288 case AUTH_REQUEST: 289 if (mWebViewClient != null) { 290 HttpAuthHandler handler = (HttpAuthHandler) msg.obj; 291 String host = msg.getData().getString("host"); 292 String realm = msg.getData().getString("realm"); 293 mWebViewClient.onReceivedHttpAuthRequest(mWebView, handler, 294 host, realm); 295 } 296 break; 297 298 case SSL_ERROR: 299 if (mWebViewClient != null) { 300 HashMap<String, Object> map = 301 (HashMap<String, Object>) msg.obj; 302 mWebViewClient.onReceivedSslError(mWebView, 303 (SslErrorHandler) map.get("handler"), 304 (SslError) map.get("error")); 305 } 306 break; 307 308 case PROGRESS: 309 // Synchronize to ensure mLatestProgress is not modified after 310 // setProgress is called and before mProgressUpdatePending is 311 // changed. 312 synchronized (this) { 313 if (mWebChromeClient != null) { 314 mWebChromeClient.onProgressChanged(mWebView, 315 mLatestProgress); 316 } 317 mProgressUpdatePending = false; 318 } 319 break; 320 321 case UPDATE_VISITED: 322 if (mWebViewClient != null) { 323 mWebViewClient.doUpdateVisitedHistory(mWebView, 324 (String) msg.obj, msg.arg1 != 0); 325 } 326 break; 327 328 case LOAD_RESOURCE: 329 if (mWebViewClient != null) { 330 mWebViewClient.onLoadResource(mWebView, (String) msg.obj); 331 } 332 break; 333 334 case DOWNLOAD_FILE: 335 if (mDownloadListener != null) { 336 String url = msg.getData().getString("url"); 337 String userAgent = msg.getData().getString("userAgent"); 338 String contentDisposition = 339 msg.getData().getString("contentDisposition"); 340 String mimetype = msg.getData().getString("mimetype"); 341 Long contentLength = msg.getData().getLong("contentLength"); 342 343 mDownloadListener.onDownloadStart(url, userAgent, 344 contentDisposition, mimetype, contentLength); 345 } 346 break; 347 348 case CREATE_WINDOW: 349 if (mWebChromeClient != null) { 350 if (!mWebChromeClient.onCreateWindow(mWebView, 351 msg.arg1 == 1, msg.arg2 == 1, 352 (Message) msg.obj)) { 353 synchronized (this) { 354 notify(); 355 } 356 } 357 } 358 break; 359 360 case REQUEST_FOCUS: 361 if (mWebChromeClient != null) { 362 mWebChromeClient.onRequestFocus(mWebView); 363 } 364 break; 365 366 case CLOSE_WINDOW: 367 if (mWebChromeClient != null) { 368 mWebChromeClient.onCloseWindow((WebView) msg.obj); 369 } 370 break; 371 372 case SAVE_PASSWORD: 373 Bundle bundle = msg.getData(); 374 String schemePlusHost = bundle.getString("host"); 375 String username = bundle.getString("username"); 376 String password = bundle.getString("password"); 377 // If the client returned false it means that the notify message 378 // will not be sent and we should notify WebCore ourselves. 379 if (!mWebView.onSavePassword(schemePlusHost, username, password, 380 (Message) msg.obj)) { 381 synchronized (this) { 382 notify(); 383 } 384 } 385 break; 386 387 case ASYNC_KEYEVENTS: 388 if (mWebViewClient != null) { 389 mWebViewClient.onUnhandledKeyEvent(mWebView, 390 (KeyEvent) msg.obj); 391 } 392 break; 393 394 case EXCEEDED_DATABASE_QUOTA: 395 if (mWebChromeClient != null) { 396 HashMap<String, Object> map = 397 (HashMap<String, Object>) msg.obj; 398 String databaseIdentifier = 399 (String) map.get("databaseIdentifier"); 400 String url = (String) map.get("url"); 401 long currentQuota = 402 ((Long) map.get("currentQuota")).longValue(); 403 WebStorage.QuotaUpdater quotaUpdater = 404 (WebStorage.QuotaUpdater) map.get("quotaUpdater"); 405 406 mWebChromeClient.onExceededDatabaseQuota(url, 407 databaseIdentifier, currentQuota, quotaUpdater); 408 } 409 break; 410 411 case JS_ALERT: 412 if (mWebChromeClient != null) { 413 final JsResult res = (JsResult) msg.obj; 414 String message = msg.getData().getString("message"); 415 String url = msg.getData().getString("url"); 416 if (!mWebChromeClient.onJsAlert(mWebView, url, message, 417 res)) { 418 new AlertDialog.Builder(mContext) 419 .setTitle(getJsDialogTitle(url)) 420 .setMessage(message) 421 .setPositiveButton(R.string.ok, 422 new AlertDialog.OnClickListener() { 423 public void onClick( 424 DialogInterface dialog, 425 int which) { 426 res.confirm(); 427 } 428 }) 429 .setCancelable(false) 430 .show(); 431 } 432 res.setReady(); 433 } 434 break; 435 436 case JS_CONFIRM: 437 if (mWebChromeClient != null) { 438 final JsResult res = (JsResult) msg.obj; 439 String message = msg.getData().getString("message"); 440 String url = msg.getData().getString("url"); 441 if (!mWebChromeClient.onJsConfirm(mWebView, url, message, 442 res)) { 443 new AlertDialog.Builder(mContext) 444 .setTitle(getJsDialogTitle(url)) 445 .setMessage(message) 446 .setPositiveButton(R.string.ok, 447 new DialogInterface.OnClickListener() { 448 public void onClick( 449 DialogInterface dialog, 450 int which) { 451 res.confirm(); 452 }}) 453 .setNegativeButton(R.string.cancel, 454 new DialogInterface.OnClickListener() { 455 public void onClick( 456 DialogInterface dialog, 457 int which) { 458 res.cancel(); 459 }}) 460 .show(); 461 } 462 // Tell the JsResult that it is ready for client 463 // interaction. 464 res.setReady(); 465 } 466 break; 467 468 case JS_PROMPT: 469 if (mWebChromeClient != null) { 470 final JsPromptResult res = (JsPromptResult) msg.obj; 471 String message = msg.getData().getString("message"); 472 String defaultVal = msg.getData().getString("default"); 473 String url = msg.getData().getString("url"); 474 if (!mWebChromeClient.onJsPrompt(mWebView, url, message, 475 defaultVal, res)) { 476 final LayoutInflater factory = LayoutInflater 477 .from(mContext); 478 final View view = factory.inflate(R.layout.js_prompt, 479 null); 480 final EditText v = (EditText) view 481 .findViewById(R.id.value); 482 v.setText(defaultVal); 483 ((TextView) view.findViewById(R.id.message)) 484 .setText(message); 485 new AlertDialog.Builder(mContext) 486 .setTitle(getJsDialogTitle(url)) 487 .setView(view) 488 .setPositiveButton(R.string.ok, 489 new DialogInterface.OnClickListener() { 490 public void onClick( 491 DialogInterface dialog, 492 int whichButton) { 493 res.confirm(v.getText() 494 .toString()); 495 } 496 }) 497 .setNegativeButton(R.string.cancel, 498 new DialogInterface.OnClickListener() { 499 public void onClick( 500 DialogInterface dialog, 501 int whichButton) { 502 res.cancel(); 503 } 504 }) 505 .setOnCancelListener( 506 new DialogInterface.OnCancelListener() { 507 public void onCancel( 508 DialogInterface dialog) { 509 res.cancel(); 510 } 511 }) 512 .show(); 513 } 514 // Tell the JsResult that it is ready for client 515 // interaction. 516 res.setReady(); 517 } 518 break; 519 520 case JS_UNLOAD: 521 if (mWebChromeClient != null) { 522 final JsResult res = (JsResult) msg.obj; 523 String message = msg.getData().getString("message"); 524 String url = msg.getData().getString("url"); 525 if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, 526 message, res)) { 527 final String m = mContext.getString( 528 R.string.js_dialog_before_unload, message); 529 new AlertDialog.Builder(mContext) 530 .setMessage(m) 531 .setPositiveButton(R.string.ok, 532 new DialogInterface.OnClickListener() { 533 public void onClick( 534 DialogInterface dialog, 535 int which) { 536 res.confirm(); 537 } 538 }) 539 .setNegativeButton(R.string.cancel, 540 new DialogInterface.OnClickListener() { 541 public void onClick( 542 DialogInterface dialog, 543 int which) { 544 res.cancel(); 545 } 546 }) 547 .show(); 548 } 549 res.setReady(); 550 } 551 break; 552 553 case JS_TIMEOUT: 554 if(mWebChromeClient != null) { 555 final JsResult res = (JsResult) msg.obj; 556 if(mWebChromeClient.onJsTimeout()) { 557 res.confirm(); 558 } else { 559 res.cancel(); 560 } 561 res.setReady(); 562 } 563 break; 564 565 case RECEIVED_CERTIFICATE: 566 mWebView.setCertificate((SslCertificate) msg.obj); 567 break; 568 569 case NOTIFY: 570 synchronized (this) { 571 notify(); 572 } 573 break; 574 575 case SCALE_CHANGED: 576 if (mWebViewClient != null) { 577 mWebViewClient.onScaleChanged(mWebView, msg.getData() 578 .getFloat("old"), msg.getData().getFloat("new")); 579 } 580 break; 581 582 case SWITCH_OUT_HISTORY: 583 mWebView.switchOutDrawHistory(); 584 break; 585 586 case ADD_MESSAGE_TO_CONSOLE: 587 String message = msg.getData().getString("message"); 588 String sourceID = msg.getData().getString("sourceID"); 589 int lineNumber = msg.getData().getInt("lineNumber"); 590 mWebChromeClient.addMessageToConsole(message, lineNumber, sourceID); 591 break; 592 } 593 } 594 595 /** 596 * Return the latest progress. 597 */ 598 public int getProgress() { 599 return mLatestProgress; 600 } 601 602 /** 603 * Called by WebCore side to switch out of history Picture drawing mode 604 */ 605 void switchOutDrawHistory() { 606 sendMessage(obtainMessage(SWITCH_OUT_HISTORY)); 607 } 608 609 private String getJsDialogTitle(String url) { 610 String title = url; 611 if (URLUtil.isDataUrl(url)) { 612 // For data: urls, we just display 'JavaScript' similar to Safari. 613 title = mContext.getString(R.string.js_dialog_title_default); 614 } else { 615 try { 616 URL aUrl = new URL(url); 617 // For example: "The page at 'http://www.mit.edu' says:" 618 title = mContext.getString(R.string.js_dialog_title, 619 aUrl.getProtocol() + "://" + aUrl.getHost()); 620 } catch (MalformedURLException ex) { 621 // do nothing. just use the url as the title 622 } 623 } 624 return title; 625 } 626 627 //-------------------------------------------------------------------------- 628 // WebViewClient functions. 629 // NOTE: shouldOverrideKeyEvent is never called from the WebCore thread so 630 // it is not necessary to include it here. 631 //-------------------------------------------------------------------------- 632 633 // Performance probe 634 private long mWebCoreThreadTime; 635 636 public void onPageStarted(String url, Bitmap favicon) { 637 // Do an unsynchronized quick check to avoid posting if no callback has 638 // been set. 639 if (mWebViewClient == null) { 640 return; 641 } 642 // Performance probe 643 if (false) { 644 mWebCoreThreadTime = SystemClock.currentThreadTimeMillis(); 645 Network.getInstance(mContext).startTiming(); 646 } 647 Message msg = obtainMessage(PAGE_STARTED); 648 msg.obj = favicon; 649 msg.getData().putString("url", url); 650 sendMessage(msg); 651 } 652 653 public void onPageFinished(String url) { 654 // Do an unsynchronized quick check to avoid posting if no callback has 655 // been set. 656 if (mWebViewClient == null) { 657 return; 658 } 659 // Performance probe 660 if (false) { 661 Log.d("WebCore", "WebCore thread used " + 662 (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime) 663 + " ms"); 664 Network.getInstance(mContext).stopTiming(); 665 } 666 Message msg = obtainMessage(PAGE_FINISHED, url); 667 sendMessage(msg); 668 } 669 670 public void onTooManyRedirects(Message cancelMsg, Message continueMsg) { 671 // Do an unsynchronized quick check to avoid posting if no callback has 672 // been set. 673 if (mWebViewClient == null) { 674 cancelMsg.sendToTarget(); 675 return; 676 } 677 678 Message msg = obtainMessage(TOO_MANY_REDIRECTS); 679 Bundle bundle = msg.getData(); 680 bundle.putParcelable("cancelMsg", cancelMsg); 681 bundle.putParcelable("continueMsg", continueMsg); 682 sendMessage(msg); 683 } 684 685 public void onReceivedError(int errorCode, String description, 686 String failingUrl) { 687 // Do an unsynchronized quick check to avoid posting if no callback has 688 // been set. 689 if (mWebViewClient == null) { 690 return; 691 } 692 693 Message msg = obtainMessage(REPORT_ERROR); 694 msg.arg1 = errorCode; 695 msg.getData().putString("description", description); 696 msg.getData().putString("failingUrl", failingUrl); 697 sendMessage(msg); 698 } 699 700 public void onFormResubmission(Message dontResend, 701 Message resend) { 702 // Do an unsynchronized quick check to avoid posting if no callback has 703 // been set. 704 if (mWebViewClient == null) { 705 dontResend.sendToTarget(); 706 return; 707 } 708 709 Message msg = obtainMessage(RESEND_POST_DATA); 710 Bundle bundle = msg.getData(); 711 bundle.putParcelable("resend", resend); 712 bundle.putParcelable("dontResend", dontResend); 713 sendMessage(msg); 714 } 715 716 /** 717 * Called by the WebCore side 718 */ 719 public boolean shouldOverrideUrlLoading(String url) { 720 // We have a default behavior if no client exists so always send the 721 // message. 722 ResultTransport<Boolean> res = new ResultTransport<Boolean>(); 723 Message msg = obtainMessage(OVERRIDE_URL); 724 msg.getData().putString("url", url); 725 msg.obj = res; 726 synchronized (this) { 727 sendMessage(msg); 728 try { 729 wait(); 730 } catch (InterruptedException e) { 731 Log.e(LOGTAG, "Caught exception while waiting for overrideUrl"); 732 Log.e(LOGTAG, Log.getStackTraceString(e)); 733 } 734 } 735 return res.getResult().booleanValue(); 736 } 737 738 public void onReceivedHttpAuthRequest(HttpAuthHandler handler, 739 String hostName, String realmName) { 740 // Do an unsynchronized quick check to avoid posting if no callback has 741 // been set. 742 if (mWebViewClient == null) { 743 handler.cancel(); 744 return; 745 } 746 Message msg = obtainMessage(AUTH_REQUEST, handler); 747 msg.getData().putString("host", hostName); 748 msg.getData().putString("realm", realmName); 749 sendMessage(msg); 750 } 751 /** 752 * @hide - hide this because it contains a parameter of type SslError. 753 * SslError is located in a hidden package. 754 */ 755 public void onReceivedSslError(SslErrorHandler handler, SslError error) { 756 // Do an unsynchronized quick check to avoid posting if no callback has 757 // been set. 758 if (mWebViewClient == null) { 759 handler.cancel(); 760 return; 761 } 762 Message msg = obtainMessage(SSL_ERROR); 763 //, handler); 764 HashMap<String, Object> map = new HashMap(); 765 map.put("handler", handler); 766 map.put("error", error); 767 msg.obj = map; 768 sendMessage(msg); 769 } 770 /** 771 * @hide - hide this because it contains a parameter of type SslCertificate, 772 * which is located in a hidden package. 773 */ 774 775 public void onReceivedCertificate(SslCertificate certificate) { 776 // Do an unsynchronized quick check to avoid posting if no callback has 777 // been set. 778 if (mWebViewClient == null) { 779 return; 780 } 781 // here, certificate can be null (if the site is not secure) 782 sendMessage(obtainMessage(RECEIVED_CERTIFICATE, certificate)); 783 } 784 785 public void doUpdateVisitedHistory(String url, boolean isReload) { 786 // Do an unsynchronized quick check to avoid posting if no callback has 787 // been set. 788 if (mWebViewClient == null) { 789 return; 790 } 791 sendMessage(obtainMessage(UPDATE_VISITED, isReload ? 1 : 0, 0, url)); 792 } 793 794 public void onLoadResource(String url) { 795 // Do an unsynchronized quick check to avoid posting if no callback has 796 // been set. 797 if (mWebViewClient == null) { 798 return; 799 } 800 sendMessage(obtainMessage(LOAD_RESOURCE, url)); 801 } 802 803 public void onUnhandledKeyEvent(KeyEvent event) { 804 // Do an unsynchronized quick check to avoid posting if no callback has 805 // been set. 806 if (mWebViewClient == null) { 807 return; 808 } 809 sendMessage(obtainMessage(ASYNC_KEYEVENTS, event)); 810 } 811 812 public void onScaleChanged(float oldScale, float newScale) { 813 // Do an unsynchronized quick check to avoid posting if no callback has 814 // been set. 815 if (mWebViewClient == null) { 816 return; 817 } 818 Message msg = obtainMessage(SCALE_CHANGED); 819 Bundle bundle = msg.getData(); 820 bundle.putFloat("old", oldScale); 821 bundle.putFloat("new", newScale); 822 sendMessage(msg); 823 } 824 825 //-------------------------------------------------------------------------- 826 // DownloadListener functions. 827 //-------------------------------------------------------------------------- 828 829 /** 830 * Starts a download if a download listener has been registered, otherwise 831 * return false. 832 */ 833 public boolean onDownloadStart(String url, String userAgent, 834 String contentDisposition, String mimetype, long contentLength) { 835 // Do an unsynchronized quick check to avoid posting if no callback has 836 // been set. 837 if (mDownloadListener == null) { 838 // Cancel the download if there is no browser client. 839 return false; 840 } 841 842 Message msg = obtainMessage(DOWNLOAD_FILE); 843 Bundle bundle = msg.getData(); 844 bundle.putString("url", url); 845 bundle.putString("userAgent", userAgent); 846 bundle.putString("mimetype", mimetype); 847 bundle.putLong("contentLength", contentLength); 848 bundle.putString("contentDisposition", contentDisposition); 849 sendMessage(msg); 850 return true; 851 } 852 853 854 //-------------------------------------------------------------------------- 855 // WebView specific functions that do not interact with a client. These 856 // functions just need to operate within the UI thread. 857 //-------------------------------------------------------------------------- 858 859 public boolean onSavePassword(String schemePlusHost, String username, 860 String password, Message resumeMsg) { 861 // resumeMsg should be null at this point because we want to create it 862 // within the CallbackProxy. 863 if (DebugFlags.CALLBACK_PROXY) { 864 junit.framework.Assert.assertNull(resumeMsg); 865 } 866 resumeMsg = obtainMessage(NOTIFY); 867 868 Message msg = obtainMessage(SAVE_PASSWORD, resumeMsg); 869 Bundle bundle = msg.getData(); 870 bundle.putString("host", schemePlusHost); 871 bundle.putString("username", username); 872 bundle.putString("password", password); 873 synchronized (this) { 874 sendMessage(msg); 875 try { 876 wait(); 877 } catch (InterruptedException e) { 878 Log.e(LOGTAG, 879 "Caught exception while waiting for onSavePassword"); 880 Log.e(LOGTAG, Log.getStackTraceString(e)); 881 } 882 } 883 // Doesn't matter here 884 return false; 885 } 886 887 //-------------------------------------------------------------------------- 888 // WebChromeClient methods 889 //-------------------------------------------------------------------------- 890 891 public void onProgressChanged(int newProgress) { 892 // Synchronize so that mLatestProgress is up-to-date. 893 synchronized (this) { 894 mLatestProgress = newProgress; 895 if (mWebChromeClient == null) { 896 return; 897 } 898 if (!mProgressUpdatePending) { 899 sendEmptyMessage(PROGRESS); 900 mProgressUpdatePending = true; 901 } 902 } 903 } 904 905 public WebView createWindow(boolean dialog, boolean userGesture) { 906 // Do an unsynchronized quick check to avoid posting if no callback has 907 // been set. 908 if (mWebChromeClient == null) { 909 return null; 910 } 911 912 WebView.WebViewTransport transport = mWebView.new WebViewTransport(); 913 final Message msg = obtainMessage(NOTIFY); 914 msg.obj = transport; 915 synchronized (this) { 916 sendMessage(obtainMessage(CREATE_WINDOW, dialog ? 1 : 0, 917 userGesture ? 1 : 0, msg)); 918 try { 919 wait(); 920 } catch (InterruptedException e) { 921 Log.e(LOGTAG, 922 "Caught exception while waiting for createWindow"); 923 Log.e(LOGTAG, Log.getStackTraceString(e)); 924 } 925 } 926 927 WebView w = transport.getWebView(); 928 if (w != null) { 929 w.getWebViewCore().initializeSubwindow(); 930 } 931 return w; 932 } 933 934 public void onRequestFocus() { 935 // Do an unsynchronized quick check to avoid posting if no callback has 936 // been set. 937 if (mWebChromeClient == null) { 938 return; 939 } 940 941 sendEmptyMessage(REQUEST_FOCUS); 942 } 943 944 public void onCloseWindow(WebView window) { 945 // Do an unsynchronized quick check to avoid posting if no callback has 946 // been set. 947 if (mWebChromeClient == null) { 948 return; 949 } 950 sendMessage(obtainMessage(CLOSE_WINDOW, window)); 951 } 952 953 public void onReceivedIcon(Bitmap icon) { 954 // The current item might be null if the icon was already stored in the 955 // database and this is a new WebView. 956 WebHistoryItem i = mBackForwardList.getCurrentItem(); 957 if (i != null) { 958 i.setFavicon(icon); 959 } 960 // Do an unsynchronized quick check to avoid posting if no callback has 961 // been set. 962 if (mWebChromeClient == null) { 963 return; 964 } 965 sendMessage(obtainMessage(RECEIVED_ICON, icon)); 966 } 967 968 public void onReceivedTitle(String title) { 969 // Do an unsynchronized quick check to avoid posting if no callback has 970 // been set. 971 if (mWebChromeClient == null) { 972 return; 973 } 974 sendMessage(obtainMessage(RECEIVED_TITLE, title)); 975 } 976 977 public void onJsAlert(String url, String message) { 978 // Do an unsynchronized quick check to avoid posting if no callback has 979 // been set. 980 if (mWebChromeClient == null) { 981 return; 982 } 983 JsResult result = new JsResult(this, false); 984 Message alert = obtainMessage(JS_ALERT, result); 985 alert.getData().putString("message", message); 986 alert.getData().putString("url", url); 987 synchronized (this) { 988 sendMessage(alert); 989 try { 990 wait(); 991 } catch (InterruptedException e) { 992 Log.e(LOGTAG, "Caught exception while waiting for jsAlert"); 993 Log.e(LOGTAG, Log.getStackTraceString(e)); 994 } 995 } 996 } 997 998 public boolean onJsConfirm(String url, String message) { 999 // Do an unsynchronized quick check to avoid posting if no callback has 1000 // been set. 1001 if (mWebChromeClient == null) { 1002 return false; 1003 } 1004 JsResult result = new JsResult(this, false); 1005 Message confirm = obtainMessage(JS_CONFIRM, result); 1006 confirm.getData().putString("message", message); 1007 confirm.getData().putString("url", url); 1008 synchronized (this) { 1009 sendMessage(confirm); 1010 try { 1011 wait(); 1012 } catch (InterruptedException e) { 1013 Log.e(LOGTAG, "Caught exception while waiting for jsConfirm"); 1014 Log.e(LOGTAG, Log.getStackTraceString(e)); 1015 } 1016 } 1017 return result.getResult(); 1018 } 1019 1020 public String onJsPrompt(String url, String message, String defaultValue) { 1021 // Do an unsynchronized quick check to avoid posting if no callback has 1022 // been set. 1023 if (mWebChromeClient == null) { 1024 return null; 1025 } 1026 JsPromptResult result = new JsPromptResult(this); 1027 Message prompt = obtainMessage(JS_PROMPT, result); 1028 prompt.getData().putString("message", message); 1029 prompt.getData().putString("default", defaultValue); 1030 prompt.getData().putString("url", url); 1031 synchronized (this) { 1032 sendMessage(prompt); 1033 try { 1034 wait(); 1035 } catch (InterruptedException e) { 1036 Log.e(LOGTAG, "Caught exception while waiting for jsPrompt"); 1037 Log.e(LOGTAG, Log.getStackTraceString(e)); 1038 } 1039 } 1040 return result.getStringResult(); 1041 } 1042 1043 public boolean onJsBeforeUnload(String url, String message) { 1044 // Do an unsynchronized quick check to avoid posting if no callback has 1045 // been set. 1046 if (mWebChromeClient == null) { 1047 return true; 1048 } 1049 JsResult result = new JsResult(this, true); 1050 Message confirm = obtainMessage(JS_UNLOAD, result); 1051 confirm.getData().putString("message", message); 1052 confirm.getData().putString("url", url); 1053 synchronized (this) { 1054 sendMessage(confirm); 1055 try { 1056 wait(); 1057 } catch (InterruptedException e) { 1058 Log.e(LOGTAG, "Caught exception while waiting for jsUnload"); 1059 Log.e(LOGTAG, Log.getStackTraceString(e)); 1060 } 1061 } 1062 return result.getResult(); 1063 } 1064 1065 /** 1066 * Called by WebViewCore to inform the Java side that the current origin 1067 * has overflowed it's database quota. Called in the WebCore thread so 1068 * posts a message to the UI thread that will prompt the WebChromeClient 1069 * for what to do. On return back to C++ side, the WebCore thread will 1070 * sleep pending a new quota value. 1071 * @param url The URL that caused the quota overflow. 1072 * @param databaseIdentifier The identifier of the database that the 1073 * transaction that caused the overflow was running on. 1074 * @param currentQuota The current quota the origin is allowed. 1075 * @param quotaUpdater An instance of a class encapsulating a callback 1076 * to WebViewCore to run when the decision to allow or deny more 1077 * quota has been made. 1078 */ 1079 public void onExceededDatabaseQuota( 1080 String url, String databaseIdentifier, long currentQuota, 1081 WebStorage.QuotaUpdater quotaUpdater) { 1082 if (mWebChromeClient == null) { 1083 quotaUpdater.updateQuota(currentQuota); 1084 return; 1085 } 1086 1087 Message exceededQuota = obtainMessage(EXCEEDED_DATABASE_QUOTA); 1088 HashMap<String, Object> map = new HashMap(); 1089 map.put("databaseIdentifier", databaseIdentifier); 1090 map.put("url", url); 1091 map.put("currentQuota", currentQuota); 1092 map.put("quotaUpdater", quotaUpdater); 1093 exceededQuota.obj = map; 1094 sendMessage(exceededQuota); 1095 } 1096 1097 /** 1098 * Called by WebViewCore when we have a message to be added to the JavaScript 1099 * error console. Sends a message to the Java side with the details. 1100 * @param message The message to add to the console. 1101 * @param lineNumber The lineNumber of the source file on which the error 1102 * occurred. 1103 * @param sourceID The filename of the source file in which the error 1104 * occurred. 1105 * @hide pending API counsel. 1106 */ 1107 public void addMessageToConsole(String message, int lineNumber, String sourceID) { 1108 if (mWebChromeClient == null) { 1109 return; 1110 } 1111 1112 Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE); 1113 msg.getData().putString("message", message); 1114 msg.getData().putString("sourceID", sourceID); 1115 msg.getData().putInt("lineNumber", lineNumber); 1116 sendMessage(msg); 1117 } 1118 1119 /** 1120 * @hide pending API council approval 1121 */ 1122 public boolean onJsTimeout() { 1123 //always interrupt timedout JS by default 1124 if (mWebChromeClient == null) { 1125 return true; 1126 } 1127 JsResult result = new JsResult(this, true); 1128 Message timeout = obtainMessage(JS_TIMEOUT, result); 1129 synchronized (this) { 1130 sendMessage(timeout); 1131 try { 1132 wait(); 1133 } catch (InterruptedException e) { 1134 Log.e(LOGTAG, "Caught exception while waiting for jsUnload"); 1135 Log.e(LOGTAG, Log.getStackTraceString(e)); 1136 } 1137 } 1138 return result.getResult(); 1139 } 1140} 1141