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