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