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