WebViewContentsClientAdapter.java revision 48c8233468f8c735d8504c8410b86cc373d679ed
1/* 2 * Copyright (C) 2012 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 com.android.webview.chromium; 18 19import android.content.ActivityNotFoundException; 20import android.content.Context; 21import android.content.Intent; 22import android.graphics.Bitmap; 23import android.graphics.BitmapFactory; 24import android.graphics.Canvas; 25import android.graphics.Color; 26import android.graphics.Picture; 27import android.net.http.ErrorStrings; 28import android.net.http.SslError; 29import android.net.Uri; 30import android.os.Build; 31import android.os.Handler; 32import android.os.Looper; 33import android.os.Message; 34import android.provider.Browser; 35import android.util.Log; 36import android.view.KeyEvent; 37import android.view.View; 38import android.webkit.ConsoleMessage; 39import android.webkit.DownloadListener; 40import android.webkit.GeolocationPermissions; 41import android.webkit.JsDialogHelper; 42import android.webkit.JsPromptResult; 43import android.webkit.JsResult; 44import android.webkit.SslErrorHandler; 45import android.webkit.ValueCallback; 46import android.webkit.WebChromeClient; 47import android.webkit.WebChromeClient.CustomViewCallback; 48import android.webkit.WebResourceResponse; 49import android.webkit.WebView; 50import android.webkit.WebViewClient; 51 52import org.chromium.android_webview.AwContentsClient; 53import org.chromium.android_webview.AwHttpAuthHandler; 54import org.chromium.android_webview.InterceptedRequestData; 55import org.chromium.android_webview.JsPromptResultReceiver; 56import org.chromium.android_webview.JsResultReceiver; 57import org.chromium.base.ThreadUtils; 58import org.chromium.content.browser.ContentView; 59import org.chromium.content.browser.ContentViewClient; 60import org.chromium.content.common.TraceEvent; 61 62import java.net.URISyntaxException; 63import java.util.concurrent.atomic.AtomicBoolean; 64 65 66/** 67 * An adapter class that forwards the callbacks from {@link ContentViewClient} 68 * to the appropriate {@link WebViewClient} or {@link WebChromeClient}. 69 * 70 * An instance of this class is associated with one {@link WebViewChromium} 71 * instance. A WebViewChromium is a WebView implementation provider (that is 72 * android.webkit.WebView delegates all functionality to it) and has exactly 73 * one corresponding {@link ContentView} instance. 74 * 75 * A {@link ContentViewClient} may be shared between multiple {@link ContentView}s, 76 * and hence multiple WebViews. Many WebViewClient methods pass the source 77 * WebView as an argument. This means that we either need to pass the 78 * corresponding ContentView to the corresponding ContentViewClient methods, 79 * or use an instance of ContentViewClientAdapter per WebViewChromium, to 80 * allow the source WebView to be injected by ContentViewClientAdapter. We 81 * choose the latter, because it makes for a cleaner design. 82 */ 83public class WebViewContentsClientAdapter extends AwContentsClient { 84 // TAG is chosen for consistency with classic webview tracing. 85 private static final String TAG = "WebViewCallback"; 86 // Enables API callback tracing 87 private static final boolean TRACE = android.webkit.DebugFlags.TRACE_CALLBACK; 88 // The WebView instance that this adapter is serving. 89 private final WebView mWebView; 90 // The WebViewClient instance that was passed to WebView.setWebViewClient(). 91 private WebViewClient mWebViewClient; 92 // The WebChromeClient instance that was passed to WebView.setContentViewClient(). 93 private WebChromeClient mWebChromeClient; 94 // The listener receiving find-in-page API results. 95 private WebView.FindListener mFindListener; 96 // The listener receiving notifications of screen updates. 97 private WebView.PictureListener mPictureListener; 98 99 private DownloadListener mDownloadListener; 100 101 private Handler mUiThreadHandler; 102 103 private static final int NEW_WEBVIEW_CREATED = 100; 104 105 /** 106 * Adapter constructor. 107 * 108 * @param webView the {@link WebView} instance that this adapter is serving. 109 */ 110 WebViewContentsClientAdapter(WebView webView) { 111 if (webView == null) { 112 throw new IllegalArgumentException("webView can't be null"); 113 } 114 115 mWebView = webView; 116 setWebViewClient(null); 117 118 mUiThreadHandler = new Handler() { 119 120 @Override 121 public void handleMessage(Message msg) { 122 switch(msg.what) { 123 case NEW_WEBVIEW_CREATED: 124 WebView.WebViewTransport t = (WebView.WebViewTransport) msg.obj; 125 WebView newWebView = t.getWebView(); 126 if (newWebView == mWebView) { 127 throw new IllegalArgumentException( 128 "Parent WebView cannot host it's own popup window. Please " + 129 "use WebSettings.setSupportMultipleWindows(false)"); 130 } 131 132 if (newWebView != null && newWebView.copyBackForwardList().getSize() != 0) { 133 throw new IllegalArgumentException( 134 "New WebView for popup window must not have been previously " + 135 "navigated."); 136 } 137 138 WebViewChromium.completeWindowCreation(mWebView, newWebView); 139 break; 140 default: 141 throw new IllegalStateException(); 142 } 143 } 144 }; 145 146 } 147 148 // WebViewClassic is coded in such a way that even if a null WebViewClient is set, 149 // certain actions take place. 150 // We choose to replicate this behavior by using a NullWebViewClient implementation (also known 151 // as the Null Object pattern) rather than duplicating the WebViewClassic approach in 152 // ContentView. 153 static class NullWebViewClient extends WebViewClient { 154 @Override 155 public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { 156 // TODO: Investigate more and add a test case. 157 // This is a copy of what Clank does. The WebViewCore key handling code and Clank key 158 // handling code differ enough that it's not trivial to figure out how keycodes are 159 // being filtered. 160 int keyCode = event.getKeyCode(); 161 if (keyCode == KeyEvent.KEYCODE_MENU || 162 keyCode == KeyEvent.KEYCODE_HOME || 163 keyCode == KeyEvent.KEYCODE_BACK || 164 keyCode == KeyEvent.KEYCODE_CALL || 165 keyCode == KeyEvent.KEYCODE_ENDCALL || 166 keyCode == KeyEvent.KEYCODE_POWER || 167 keyCode == KeyEvent.KEYCODE_HEADSETHOOK || 168 keyCode == KeyEvent.KEYCODE_CAMERA || 169 keyCode == KeyEvent.KEYCODE_FOCUS || 170 keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || 171 keyCode == KeyEvent.KEYCODE_VOLUME_MUTE || 172 keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 173 return true; 174 } 175 return false; 176 } 177 178 @Override 179 public boolean shouldOverrideUrlLoading(WebView view, String url) { 180 Intent intent; 181 // Perform generic parsing of the URI to turn it into an Intent. 182 try { 183 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); 184 } catch (URISyntaxException ex) { 185 Log.w(TAG, "Bad URI " + url + ": " + ex.getMessage()); 186 return false; 187 } 188 // Sanitize the Intent, ensuring web pages can not bypass browser 189 // security (only access to BROWSABLE activities). 190 intent.addCategory(Intent.CATEGORY_BROWSABLE); 191 intent.setComponent(null); 192 // Pass the package name as application ID so that the intent from the 193 // same application can be opened in the same tab. 194 intent.putExtra(Browser.EXTRA_APPLICATION_ID, 195 view.getContext().getPackageName()); 196 try { 197 view.getContext().startActivity(intent); 198 } catch (ActivityNotFoundException ex) { 199 Log.w(TAG, "No application can handle " + url); 200 return false; 201 } 202 return true; 203 } 204 } 205 206 void setWebViewClient(WebViewClient client) { 207 if (client != null) { 208 mWebViewClient = client; 209 } else { 210 mWebViewClient = new NullWebViewClient(); 211 } 212 } 213 214 void setWebChromeClient(WebChromeClient client) { 215 mWebChromeClient = client; 216 } 217 218 void setDownloadListener(DownloadListener listener) { 219 mDownloadListener = listener; 220 } 221 222 void setFindListener(WebView.FindListener listener) { 223 mFindListener = listener; 224 } 225 226 void setPictureListener(WebView.PictureListener listener) { 227 mPictureListener = listener; 228 } 229 230 //-------------------------------------------------------------------------------------------- 231 // Adapter for all the methods. 232 //-------------------------------------------------------------------------------------------- 233 234 /** 235 * @see AwContentsClient#getVisitedHistory 236 */ 237 @Override 238 public void getVisitedHistory(ValueCallback<String[]> callback) { 239 TraceEvent.begin(); 240 if (mWebChromeClient != null) { 241 if (TRACE) Log.d(TAG, "getVisitedHistory"); 242 mWebChromeClient.getVisitedHistory(callback); 243 } 244 TraceEvent.end(); 245 } 246 247 /** 248 * @see AwContentsClient#doUpdateVisiteHistory(String, boolean) 249 */ 250 @Override 251 public void doUpdateVisitedHistory(String url, boolean isReload) { 252 TraceEvent.begin(); 253 if (TRACE) Log.d(TAG, "doUpdateVisitedHistory=" + url + " reload=" + isReload); 254 mWebViewClient.doUpdateVisitedHistory(mWebView, url, isReload); 255 TraceEvent.end(); 256 } 257 258 /** 259 * @see AwContentsClient#onProgressChanged(int) 260 */ 261 @Override 262 public void onProgressChanged(int progress) { 263 TraceEvent.begin(); 264 if (mWebChromeClient != null) { 265 if (TRACE) Log.d(TAG, "onProgressChanged=" + progress); 266 mWebChromeClient.onProgressChanged(mWebView, progress); 267 } 268 TraceEvent.end(); 269 } 270 271 /** 272 * @see AwContentsClient#shouldInterceptRequest(java.lang.String) 273 */ 274 @Override 275 public InterceptedRequestData shouldInterceptRequest(String url) { 276 TraceEvent.begin(); 277 if (TRACE) Log.d(TAG, "shouldInterceptRequest=" + url); 278 WebResourceResponse response = mWebViewClient.shouldInterceptRequest(mWebView, url); 279 TraceEvent.end(); 280 if (response == null) return null; 281 return new InterceptedRequestData( 282 response.getMimeType(), 283 response.getEncoding(), 284 response.getData()); 285 } 286 287 /** 288 * @see AwContentsClient#shouldOverrideUrlLoading(java.lang.String) 289 */ 290 @Override 291 public boolean shouldOverrideUrlLoading(String url) { 292 TraceEvent.begin(); 293 if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + url); 294 boolean result = mWebViewClient.shouldOverrideUrlLoading(mWebView, url); 295 TraceEvent.end(); 296 return result; 297 } 298 299 /** 300 * @see AwContentsClient#onUnhandledKeyEvent(android.view.KeyEvent) 301 */ 302 @Override 303 public void onUnhandledKeyEvent(KeyEvent event) { 304 TraceEvent.begin(); 305 if (TRACE) Log.d(TAG, "onUnhandledKeyEvent"); 306 mWebViewClient.onUnhandledKeyEvent(mWebView, event); 307 TraceEvent.end(); 308 } 309 310 /** 311 * @see AwContentsClient#onConsoleMessage(android.webkit.ConsoleMessage) 312 */ 313 @Override 314 public boolean onConsoleMessage(ConsoleMessage consoleMessage) { 315 TraceEvent.begin(); 316 boolean result; 317 if (mWebChromeClient != null) { 318 if (TRACE) Log.d(TAG, "onConsoleMessage: " + consoleMessage.message()); 319 result = mWebChromeClient.onConsoleMessage(consoleMessage); 320 String message = consoleMessage.message(); 321 if (result && message != null && message.startsWith("[blocked]")) { 322 Log.e(TAG, "Blocked URL: " + message); 323 } 324 } else { 325 result = false; 326 } 327 TraceEvent.end(); 328 return result; 329 } 330 331 /** 332 * @see AwContentsClient#onFindResultReceived(int,int,boolean) 333 */ 334 @Override 335 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, 336 boolean isDoneCounting) { 337 if (mFindListener == null) return; 338 TraceEvent.begin(); 339 if (TRACE) Log.d(TAG, "onFindResultReceived"); 340 mFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); 341 TraceEvent.end(); 342 } 343 344 /** 345 * @See AwContentsClient#onNewPicture(Picture) 346 */ 347 @Override 348 public void onNewPicture(Picture picture) { 349 if (mPictureListener == null) return; 350 TraceEvent.begin(); 351 if (TRACE) Log.d(TAG, "onNewPicture"); 352 mPictureListener.onNewPicture(mWebView, picture); 353 TraceEvent.end(); 354 } 355 356 @Override 357 public void onLoadResource(String url) { 358 TraceEvent.begin(); 359 if (TRACE) Log.d(TAG, "onLoadResource=" + url); 360 mWebViewClient.onLoadResource(mWebView, url); 361 TraceEvent.end(); 362 } 363 364 @Override 365 public boolean onCreateWindow(boolean isDialog, boolean isUserGesture) { 366 Message m = mUiThreadHandler.obtainMessage( 367 NEW_WEBVIEW_CREATED, mWebView.new WebViewTransport()); 368 TraceEvent.begin(); 369 boolean result; 370 if (mWebChromeClient != null) { 371 if (TRACE) Log.d(TAG, "onCreateWindow"); 372 result = mWebChromeClient.onCreateWindow(mWebView, isDialog, isUserGesture, m); 373 } else { 374 result = false; 375 } 376 TraceEvent.end(); 377 return result; 378 } 379 380 /** 381 * @see AwContentsClient#onCloseWindow() 382 */ 383 @Override 384 public void onCloseWindow() { 385 TraceEvent.begin(); 386 if (mWebChromeClient != null) { 387 if (TRACE) Log.d(TAG, "onCloseWindow"); 388 mWebChromeClient.onCloseWindow(mWebView); 389 } 390 TraceEvent.end(); 391 } 392 393 /** 394 * @see AwContentsClient#onRequestFocus() 395 */ 396 @Override 397 public void onRequestFocus() { 398 TraceEvent.begin(); 399 if (mWebChromeClient != null) { 400 if (TRACE) Log.d(TAG, "onRequestFocus"); 401 mWebChromeClient.onRequestFocus(mWebView); 402 } 403 TraceEvent.end(); 404 } 405 406 /** 407 * @see AwContentsClient#onReceivedTouchIconUrl(String url, boolean precomposed) 408 */ 409 @Override 410 public void onReceivedTouchIconUrl(String url, boolean precomposed) { 411 TraceEvent.begin(); 412 if (mWebChromeClient != null) { 413 if (TRACE) Log.d(TAG, "onReceivedTouchIconUrl=" + url); 414 mWebChromeClient.onReceivedTouchIconUrl(mWebView, url, precomposed); 415 } 416 TraceEvent.end(); 417 } 418 419 /** 420 * @see AwContentsClient#onReceivedIcon(Bitmap bitmap) 421 */ 422 @Override 423 public void onReceivedIcon(Bitmap bitmap) { 424 TraceEvent.begin(); 425 if (mWebChromeClient != null) { 426 if (TRACE) Log.d(TAG, "onReceivedIcon"); 427 mWebChromeClient.onReceivedIcon(mWebView, bitmap); 428 } 429 TraceEvent.end(); 430 } 431 432 /** 433 * @see ContentViewClient#onPageStarted(String) 434 */ 435 @Override 436 public void onPageStarted(String url) { 437 TraceEvent.begin(); 438 if (TRACE) Log.d(TAG, "onPageStarted=" + url); 439 mWebViewClient.onPageStarted(mWebView, url, mWebView.getFavicon()); 440 TraceEvent.end(); 441 } 442 443 /** 444 * @see ContentViewClient#onPageFinished(String) 445 */ 446 @Override 447 public void onPageFinished(String url) { 448 TraceEvent.begin(); 449 if (TRACE) Log.d(TAG, "onPageFinished=" + url); 450 mWebViewClient.onPageFinished(mWebView, url); 451 TraceEvent.end(); 452 453 // See b/8208948 454 // This fakes an onNewPicture callback after onPageFinished to allow 455 // CTS tests to run in an un-flaky manner. This is required as the 456 // path for sending Picture updates in Chromium are decoupled from the 457 // page loading callbacks, i.e. the Chrome compositor may draw our 458 // content and send the Picture before onPageStarted or onPageFinished 459 // are invoked. The CTS harness discards any pictures it receives before 460 // onPageStarted is invoked, so in the case we get the Picture before that and 461 // no further updates after onPageStarted, we'll fail the test by timing 462 // out waiting for a Picture. 463 // To ensure backwards compatibility, we need to defer sending Picture updates 464 // until onPageFinished has been invoked. This work is being done 465 // upstream, and we can revert this hack when it lands. 466 if (mPictureListener != null) { 467 ThreadUtils.postOnUiThreadDelayed(new Runnable() { 468 @Override 469 public void run() { 470 UnimplementedWebViewApi.invoke(); 471 if (mPictureListener != null) { 472 if (TRACE) Log.d(TAG, "onPageFinished-fake"); 473 mPictureListener.onNewPicture(mWebView, new Picture()); 474 } 475 } 476 }, 100); 477 } 478 } 479 480 /** 481 * @see ContentViewClient#onReceivedError(int,String,String) 482 */ 483 @Override 484 public void onReceivedError(int errorCode, String description, String failingUrl) { 485 if (description == null || description.isEmpty()) { 486 // ErrorStrings is @hidden, so we can't do this in AwContents. 487 // Normally the net/ layer will set a valid description, but for synthesized callbacks 488 // (like in the case for intercepted requests) AwContents will pass in null. 489 description = ErrorStrings.getString(errorCode, mWebView.getContext()); 490 } 491 TraceEvent.begin(); 492 if (TRACE) Log.d(TAG, "onReceivedError=" + failingUrl); 493 mWebViewClient.onReceivedError(mWebView, errorCode, description, failingUrl); 494 TraceEvent.end(); 495 } 496 497 /** 498 * @see ContentViewClient#onReceivedTitle(String) 499 */ 500 @Override 501 public void onReceivedTitle(String title) { 502 TraceEvent.begin(); 503 if (mWebChromeClient != null) { 504 if (TRACE) Log.d(TAG, "onReceivedTitle"); 505 mWebChromeClient.onReceivedTitle(mWebView, title); 506 } 507 TraceEvent.end(); 508 } 509 510 511 /** 512 * @see ContentViewClient#shouldOverrideKeyEvent(KeyEvent) 513 */ 514 @Override 515 public boolean shouldOverrideKeyEvent(KeyEvent event) { 516 // TODO(joth): The expression here is a workaround for http://b/7697782 :- 517 // 1. The check for system key should be made in AwContents or ContentViewCore, 518 // before shouldOverrideKeyEvent() is called at all. 519 // 2. shouldOverrideKeyEvent() should be called in onKeyDown/onKeyUp, not from 520 // dispatchKeyEvent(). 521 if (event.isSystem()) return true; 522 TraceEvent.begin(); 523 if (TRACE) Log.d(TAG, "shouldOverrideKeyEvent"); 524 boolean result = mWebViewClient.shouldOverrideKeyEvent(mWebView, event); 525 TraceEvent.end(); 526 return result; 527 } 528 529 530 /** 531 * @see ContentViewClient#onStartContentIntent(Context, String) 532 * Callback when detecting a click on a content link. 533 */ 534 // TODO: Delete this method when removed from base class. 535 public void onStartContentIntent(Context context, String contentUrl) { 536 TraceEvent.begin(); 537 if (TRACE) Log.d(TAG, "shouldOverrideUrlLoading=" + contentUrl); 538 mWebViewClient.shouldOverrideUrlLoading(mWebView, contentUrl); 539 TraceEvent.end(); 540 } 541 542 @Override 543 public void onGeolocationPermissionsShowPrompt(String origin, 544 GeolocationPermissions.Callback callback) { 545 TraceEvent.begin(); 546 if (mWebChromeClient != null) { 547 if (TRACE) Log.d(TAG, "onGeolocationPermissionsShowPrompt"); 548 mWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback); 549 } 550 TraceEvent.end(); 551 } 552 553 @Override 554 public void onGeolocationPermissionsHidePrompt() { 555 TraceEvent.begin(); 556 if (mWebChromeClient != null) { 557 if (TRACE) Log.d(TAG, "onGeolocationPermissionsHidePrompt"); 558 mWebChromeClient.onGeolocationPermissionsHidePrompt(); 559 } 560 TraceEvent.end(); 561 } 562 563 private static class JsPromptResultReceiverAdapter implements JsResult.ResultReceiver { 564 private JsPromptResultReceiver mChromePromptResultReceiver; 565 private JsResultReceiver mChromeResultReceiver; 566 // We hold onto the JsPromptResult here, just to avoid the need to downcast 567 // in onJsResultComplete. 568 private final JsPromptResult mPromptResult = new JsPromptResult(this); 569 570 public JsPromptResultReceiverAdapter(JsPromptResultReceiver receiver) { 571 mChromePromptResultReceiver = receiver; 572 } 573 574 public JsPromptResultReceiverAdapter(JsResultReceiver receiver) { 575 mChromeResultReceiver = receiver; 576 } 577 578 public JsPromptResult getPromptResult() { 579 return mPromptResult; 580 } 581 582 @Override 583 public void onJsResultComplete(JsResult result) { 584 if (mChromePromptResultReceiver != null) { 585 if (mPromptResult.getResult()) { 586 mChromePromptResultReceiver.confirm(mPromptResult.getStringResult()); 587 } else { 588 mChromePromptResultReceiver.cancel(); 589 } 590 } else { 591 if (mPromptResult.getResult()) { 592 mChromeResultReceiver.confirm(); 593 } else { 594 mChromeResultReceiver.cancel(); 595 } 596 } 597 } 598 } 599 600 @Override 601 public void handleJsAlert(String url, String message, JsResultReceiver receiver) { 602 TraceEvent.begin(); 603 if (mWebChromeClient != null) { 604 final JsPromptResult res = 605 new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 606 if (TRACE) Log.d(TAG, "onJsAlert"); 607 if (!mWebChromeClient.onJsAlert(mWebView, url, message, res)) { 608 new JsDialogHelper(res, JsDialogHelper.ALERT, null, message, url) 609 .showDialog(mWebView.getContext()); 610 } 611 } else { 612 receiver.cancel(); 613 } 614 TraceEvent.end(); 615 } 616 617 @Override 618 public void handleJsBeforeUnload(String url, String message, JsResultReceiver receiver) { 619 TraceEvent.begin(); 620 if (mWebChromeClient != null) { 621 final JsPromptResult res = 622 new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 623 if (TRACE) Log.d(TAG, "onJsBeforeUnload"); 624 if (!mWebChromeClient.onJsBeforeUnload(mWebView, url, message, res)) { 625 new JsDialogHelper(res, JsDialogHelper.UNLOAD, null, message, url) 626 .showDialog(mWebView.getContext()); 627 } 628 } else { 629 receiver.cancel(); 630 } 631 TraceEvent.end(); 632 } 633 634 @Override 635 public void handleJsConfirm(String url, String message, JsResultReceiver receiver) { 636 TraceEvent.begin(); 637 if (mWebChromeClient != null) { 638 final JsPromptResult res = 639 new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 640 if (TRACE) Log.d(TAG, "onJsConfirm"); 641 if (!mWebChromeClient.onJsConfirm(mWebView, url, message, res)) { 642 new JsDialogHelper(res, JsDialogHelper.CONFIRM, null, message, url) 643 .showDialog(mWebView.getContext()); 644 } 645 } else { 646 receiver.cancel(); 647 } 648 TraceEvent.end(); 649 } 650 651 @Override 652 public void handleJsPrompt(String url, String message, String defaultValue, 653 JsPromptResultReceiver receiver) { 654 TraceEvent.begin(); 655 if (mWebChromeClient != null) { 656 final JsPromptResult res = 657 new JsPromptResultReceiverAdapter(receiver).getPromptResult(); 658 if (TRACE) Log.d(TAG, "onJsPrompt"); 659 if (!mWebChromeClient.onJsPrompt(mWebView, url, message, defaultValue, res)) { 660 new JsDialogHelper(res, JsDialogHelper.PROMPT, defaultValue, message, url) 661 .showDialog(mWebView.getContext()); 662 } 663 } else { 664 receiver.cancel(); 665 } 666 TraceEvent.end(); 667 } 668 669 @Override 670 public void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) { 671 TraceEvent.begin(); 672 if (TRACE) Log.d(TAG, "onReceivedHttpAuthRequest=" + host); 673 mWebViewClient.onReceivedHttpAuthRequest(mWebView, 674 new AwHttpAuthHandlerAdapter(handler), host, realm); 675 TraceEvent.end(); 676 } 677 678 @Override 679 public void onReceivedSslError(final ValueCallback<Boolean> callback, SslError error) { 680 SslErrorHandler handler = new SslErrorHandler() { 681 @Override 682 public void proceed() { 683 postProceed(true); 684 } 685 @Override 686 public void cancel() { 687 postProceed(false); 688 } 689 private void postProceed(final boolean proceed) { 690 post(new Runnable() { 691 @Override 692 public void run() { 693 callback.onReceiveValue(proceed); 694 } 695 }); 696 } 697 }; 698 TraceEvent.begin(); 699 if (TRACE) Log.d(TAG, "onReceivedSslError"); 700 mWebViewClient.onReceivedSslError(mWebView, handler, error); 701 TraceEvent.end(); 702 } 703 704 @Override 705 public void onReceivedLoginRequest(String realm, String account, String args) { 706 TraceEvent.begin(); 707 if (TRACE) Log.d(TAG, "onReceivedLoginRequest=" + realm); 708 mWebViewClient.onReceivedLoginRequest(mWebView, realm, account, args); 709 TraceEvent.end(); 710 } 711 712 @Override 713 public void onFormResubmission(Message dontResend, Message resend) { 714 TraceEvent.begin(); 715 if (TRACE) Log.d(TAG, "onFormResubmission"); 716 mWebViewClient.onFormResubmission(mWebView, dontResend, resend); 717 TraceEvent.end(); 718 } 719 720 @Override 721 public void onDownloadStart(String url, 722 String userAgent, 723 String contentDisposition, 724 String mimeType, 725 long contentLength) { 726 if (mDownloadListener != null) { 727 TraceEvent.begin(); 728 if (TRACE) Log.d(TAG, "onDownloadStart"); 729 mDownloadListener.onDownloadStart(url, 730 userAgent, 731 contentDisposition, 732 mimeType, 733 contentLength); 734 TraceEvent.end(); 735 } 736 } 737 738 @Override 739 public void showFileChooser(final ValueCallback<String[]> uploadFileCallback, 740 final AwContentsClient.FileChooserParams fileChooserParams) { 741 if (mWebChromeClient == null) { 742 uploadFileCallback.onReceiveValue(null); 743 return; 744 } 745 TraceEvent.begin(); 746 /* 747 // TODO: Call new API here when it's added in frameworks/base - see http://ag/335990 748 WebChromeClient.FileChooserParams p = new WebChromeClient.FileChooserParams(); 749 p.mode = fileChooserParams.mode; 750 p.acceptTypes = fileChooserParams.acceptTypes; 751 p.title = fileChooserParams.title; 752 p.defaultFilename = fileChooserParams.defaultFilename; 753 p.capture = fileChooserParams.capture; 754 if (TRACE) Log.d(TAG, "showFileChooser"); 755 if (Build.VERSION.SDK_INT > VERSION_CODES.KIT_KAT && 756 !mWebChromeClient.showFileChooser(mWebView, uploadFileCallback, p)) { 757 return; 758 } 759 if (mWebView.getContext().getApplicationInfo().targetSdkVersion > 760 Build.VERSION_CODES.KIT_KAT) { 761 uploadFileCallback.onReceiveValue(null); 762 return; 763 } 764 */ 765 ValueCallback<Uri> innerCallback = new ValueCallback<Uri>() { 766 final AtomicBoolean completed = new AtomicBoolean(false); 767 @Override 768 public void onReceiveValue(Uri uri) { 769 if (completed.getAndSet(true)) return; 770 uploadFileCallback.onReceiveValue( 771 uri == null ? null : new String[] { uri.toString() }); 772 } 773 }; 774 if (TRACE) Log.d(TAG, "openFileChooser"); 775 mWebChromeClient.openFileChooser(innerCallback, fileChooserParams.acceptTypes, 776 fileChooserParams.capture ? "*" : ""); 777 TraceEvent.end(); 778 } 779 780 @Override 781 public void onScaleChangedScaled(float oldScale, float newScale) { 782 TraceEvent.begin(); 783 if (TRACE) Log.d(TAG, " onScaleChangedScaled"); 784 mWebViewClient.onScaleChanged(mWebView, oldScale, newScale); 785 TraceEvent.end(); 786 } 787 788 @Override 789 public void onShowCustomView(View view, CustomViewCallback cb) { 790 TraceEvent.begin(); 791 if (mWebChromeClient != null) { 792 if (TRACE) Log.d(TAG, "onShowCustomView"); 793 mWebChromeClient.onShowCustomView(view, cb); 794 } 795 TraceEvent.end(); 796 } 797 798 @Override 799 public void onHideCustomView() { 800 TraceEvent.begin(); 801 if (mWebChromeClient != null) { 802 if (TRACE) Log.d(TAG, "onHideCustomView"); 803 mWebChromeClient.onHideCustomView(); 804 } 805 TraceEvent.end(); 806 } 807 808 @Override 809 protected View getVideoLoadingProgressView() { 810 TraceEvent.begin(); 811 View result; 812 if (mWebChromeClient != null) { 813 if (TRACE) Log.d(TAG, "getVideoLoadingProgressView"); 814 result = mWebChromeClient.getVideoLoadingProgressView(); 815 } else { 816 result = null; 817 } 818 TraceEvent.end(); 819 return result; 820 } 821 822 @Override 823 public Bitmap getDefaultVideoPoster() { 824 TraceEvent.begin(); 825 Bitmap result = null; 826 if (mWebChromeClient != null) { 827 if (TRACE) Log.d(TAG, "getDefaultVideoPoster"); 828 result = mWebChromeClient.getDefaultVideoPoster(); 829 } 830 if (result == null) { 831 // The ic_media_video_poster icon is transparent so we need to draw it on a gray 832 // background. 833 Bitmap poster = BitmapFactory.decodeResource( 834 mWebView.getContext().getResources(), 835 com.android.internal.R.drawable.ic_media_video_poster); 836 result = Bitmap.createBitmap(poster.getWidth(), poster.getHeight(), poster.getConfig()); 837 result.eraseColor(Color.GRAY); 838 Canvas canvas = new Canvas(result); 839 canvas.drawBitmap(poster, 0f, 0f, null); 840 } 841 TraceEvent.end(); 842 return result; 843 } 844 845 private static class AwHttpAuthHandlerAdapter extends android.webkit.HttpAuthHandler { 846 private AwHttpAuthHandler mAwHandler; 847 848 public AwHttpAuthHandlerAdapter(AwHttpAuthHandler awHandler) { 849 mAwHandler = awHandler; 850 } 851 852 @Override 853 public void proceed(String username, String password) { 854 if (username == null) { 855 username = ""; 856 } 857 858 if (password == null) { 859 password = ""; 860 } 861 mAwHandler.proceed(username, password); 862 } 863 864 @Override 865 public void cancel() { 866 mAwHandler.cancel(); 867 } 868 869 @Override 870 public boolean useHttpAuthUsernamePassword() { 871 return mAwHandler.isFirstAttempt(); 872 } 873 } 874} 875