AwContents.java revision bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3
1// Copyright 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.android_webview; 6 7import android.content.pm.PackageManager; 8import android.content.res.Configuration; 9import android.graphics.Bitmap; 10import android.graphics.Canvas; 11import android.graphics.Color; 12import android.graphics.Picture; 13import android.graphics.Rect; 14import android.net.http.SslCertificate; 15import android.os.AsyncTask; 16import android.os.Build; 17import android.os.Bundle; 18import android.os.Message; 19import android.os.Process; 20import android.text.TextUtils; 21import android.util.Log; 22import android.view.KeyEvent; 23import android.view.MotionEvent; 24import android.view.View; 25import android.view.ViewGroup; 26import android.view.ViewTreeObserver; 27import android.view.accessibility.AccessibilityEvent; 28import android.view.accessibility.AccessibilityNodeInfo; 29import android.view.accessibility.AccessibilityNodeProvider; 30import android.view.inputmethod.EditorInfo; 31import android.view.inputmethod.InputConnection; 32import android.webkit.GeolocationPermissions; 33import android.webkit.ValueCallback; 34 35import com.google.common.annotations.VisibleForTesting; 36 37import org.chromium.base.CalledByNative; 38import org.chromium.base.JNINamespace; 39import org.chromium.base.ThreadUtils; 40import org.chromium.content.browser.ContentSettings; 41import org.chromium.content.browser.ContentVideoView; 42import org.chromium.content.browser.ContentViewClient; 43import org.chromium.content.browser.ContentViewCore; 44import org.chromium.content.browser.ContentViewStatics; 45import org.chromium.content.browser.LoadUrlParams; 46import org.chromium.content.browser.NavigationHistory; 47import org.chromium.content.browser.PageTransitionTypes; 48import org.chromium.content.common.CleanupReference; 49import org.chromium.components.navigation_interception.InterceptNavigationDelegate; 50import org.chromium.components.navigation_interception.NavigationParams; 51import org.chromium.net.GURLUtils; 52import org.chromium.ui.gfx.DeviceDisplayInfo; 53 54import java.io.File; 55import java.lang.annotation.Annotation; 56import java.net.MalformedURLException; 57import java.net.URL; 58import java.util.concurrent.Callable; 59 60/** 61 * Exposes the native AwContents class, and together these classes wrap the ContentViewCore 62 * and Browser components that are required to implement Android WebView API. This is the 63 * primary entry point for the WebViewProvider implementation; it holds a 1:1 object 64 * relationship with application WebView instances. 65 * (We define this class independent of the hidden WebViewProvider interfaces, to allow 66 * continuous build & test in the open source SDK-based tree). 67 */ 68@JNINamespace("android_webview") 69public class AwContents { 70 private static final String TAG = "AwContents"; 71 72 private static final String WEB_ARCHIVE_EXTENSION = ".mht"; 73 74 /** 75 * WebKit hit test related data strcutre. These are used to implement 76 * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView. 77 * All values should be updated together. The native counterpart is 78 * AwHitTestData. 79 */ 80 public static class HitTestData { 81 // Used in getHitTestResult. 82 public int hitTestResultType; 83 public String hitTestResultExtraData; 84 85 // Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc). 86 public String href; 87 public String anchorText; 88 public String imgSrc; 89 } 90 91 /** 92 * Interface that consumers of {@link AwContents} must implement to allow the proper 93 * dispatching of view methods through the containing view. 94 */ 95 public interface InternalAccessDelegate extends ContentViewCore.InternalAccessDelegate { 96 /** 97 * @see View#onScrollChanged(int, int, int, int) 98 * 99 * TODO(mkosiba): WebViewClassic calls this, AwContents doesn't. Check if there 100 * are any cases we're missing, if not - remove. 101 */ 102 void onScrollChanged(int lPix, int tPix, int oldlPix, int oldtPix); 103 104 /** 105 * @see View#overScrollBy(int, int, int, int, int, int, int, int, boolean); 106 */ 107 void overScrollBy(int deltaX, int deltaY, 108 int scrollX, int scrollY, 109 int scrollRangeX, int scrollRangeY, 110 int maxOverScrollX, int maxOverScrollY, 111 boolean isTouchEvent); 112 113 /** 114 * @see View#scrollTo(int, int) 115 */ 116 void super_scrollTo(int scrollX, int scrollY); 117 118 /** 119 * @see View#setMeasuredDimension(int, int) 120 */ 121 void setMeasuredDimension(int measuredWidth, int measuredHeight); 122 123 /** 124 * Requests a callback on the native DrawGL method (see getAwDrawGLFunction) 125 * if called from within onDraw, |canvas| will be non-null and hardware accelerated. 126 * otherwise, |canvas| will be null, and the container view itself will be hardware 127 * accelerated. 128 * 129 * @return false indicates the GL draw request was not accepted, and the caller 130 * should fallback to the SW path. 131 */ 132 boolean requestDrawGL(Canvas canvas); 133 } 134 135 private int mNativeAwContents; 136 private final AwBrowserContext mBrowserContext; 137 private final ViewGroup mContainerView; 138 private ContentViewCore mContentViewCore; 139 private final AwContentsClient mContentsClient; 140 private final AwContentsClientBridge mContentsClientBridge; 141 private final AwWebContentsDelegate mWebContentsDelegate; 142 private final AwContentsIoThreadClient mIoThreadClient; 143 private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate; 144 private final InternalAccessDelegate mInternalAccessAdapter; 145 private final AwLayoutSizer mLayoutSizer; 146 private final AwZoomControls mZoomControls; 147 private final AwScrollOffsetManager mScrollOffsetManager; 148 private OverScrollGlow mOverScrollGlow; 149 // This can be accessed on any thread after construction. See AwContentsIoThreadClient. 150 private final AwSettings mSettings; 151 152 private boolean mIsPaused; 153 private boolean mIsVisible; // Equivalent to windowVisible && viewVisible && !mIsPaused. 154 private boolean mIsAttachedToWindow; 155 private Bitmap mFavicon; 156 private boolean mHasRequestedVisitedHistoryFromClient; 157 // TODO(boliu): This should be in a global context, not per webview. 158 private final double mDIPScale; 159 160 // The base background color, i.e. not accounting for any CSS body from the current page. 161 private int mBaseBackgroundColor = Color.WHITE; 162 163 // Must call nativeUpdateLastHitTestData first to update this before use. 164 private final HitTestData mPossiblyStaleHitTestData = new HitTestData(); 165 166 private DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler; 167 168 // Bound method for suppling Picture instances to the AwContentsClient. Will be null if the 169 // picture listener API has not yet been enabled, or if it is using invalidation-only mode. 170 private Callable<Picture> mPictureListenerContentProvider; 171 172 private int mLastGlobalVisibleWidth; 173 private int mLastGlobalVisibleHeight; 174 175 private boolean mContainerViewFocused; 176 private boolean mWindowFocused; 177 178 private AwAutofillManagerDelegate mAwAutofillManagerDelegate; 179 180 private static final class DestroyRunnable implements Runnable { 181 private int mNativeAwContents; 182 private DestroyRunnable(int nativeAwContents) { 183 mNativeAwContents = nativeAwContents; 184 } 185 @Override 186 public void run() { 187 nativeDestroy(mNativeAwContents); 188 } 189 } 190 191 private CleanupReference mCleanupReference; 192 193 //-------------------------------------------------------------------------------------------- 194 private class IoThreadClientImpl implements AwContentsIoThreadClient { 195 // All methods are called on the IO thread. 196 197 @Override 198 public int getCacheMode() { 199 return mSettings.getCacheMode(); 200 } 201 202 @Override 203 public InterceptedRequestData shouldInterceptRequest(final String url, 204 boolean isMainFrame) { 205 InterceptedRequestData interceptedRequestData; 206 // Return the response directly if the url is default video poster url. 207 interceptedRequestData = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url); 208 if (interceptedRequestData != null) return interceptedRequestData; 209 210 interceptedRequestData = mContentsClient.shouldInterceptRequest(url); 211 212 if (interceptedRequestData == null) { 213 mContentsClient.getCallbackHelper().postOnLoadResource(url); 214 } 215 216 if (isMainFrame && interceptedRequestData != null && 217 interceptedRequestData.getData() == null) { 218 // In this case the intercepted URLRequest job will simulate an empty response 219 // which doesn't trigger the onReceivedError callback. For WebViewClassic 220 // compatibility we synthesize that callback. http://crbug.com/180950 221 mContentsClient.getCallbackHelper().postOnReceivedError( 222 ErrorCodeConversionHelper.ERROR_UNKNOWN, 223 null /* filled in by the glue layer */, url); 224 } 225 return interceptedRequestData; 226 } 227 228 @Override 229 public boolean shouldBlockContentUrls() { 230 return !mSettings.getAllowContentAccess(); 231 } 232 233 @Override 234 public boolean shouldBlockFileUrls() { 235 return !mSettings.getAllowFileAccess(); 236 } 237 238 @Override 239 public boolean shouldBlockNetworkLoads() { 240 return mSettings.getBlockNetworkLoads(); 241 } 242 243 @Override 244 public void onDownloadStart(String url, 245 String userAgent, 246 String contentDisposition, 247 String mimeType, 248 long contentLength) { 249 mContentsClient.getCallbackHelper().postOnDownloadStart(url, userAgent, 250 contentDisposition, mimeType, contentLength); 251 } 252 253 @Override 254 public void newLoginRequest(String realm, String account, String args) { 255 mContentsClient.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args); 256 } 257 } 258 259 //-------------------------------------------------------------------------------------------- 260 private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate { 261 private String mLastLoadUrlAddress; 262 263 public void onUrlLoadRequested(String url) { 264 mLastLoadUrlAddress = url; 265 } 266 267 @Override 268 public boolean shouldIgnoreNavigation(NavigationParams navigationParams) { 269 final String url = navigationParams.url; 270 final int transitionType = navigationParams.pageTransitionType; 271 final boolean isLoadUrl = 272 (transitionType & PageTransitionTypes.PAGE_TRANSITION_FROM_API) != 0; 273 final boolean isBackForward = 274 (transitionType & PageTransitionTypes.PAGE_TRANSITION_FORWARD_BACK) != 0; 275 final boolean isReload = 276 (transitionType & PageTransitionTypes.PAGE_TRANSITION_CORE_MASK) == 277 PageTransitionTypes.PAGE_TRANSITION_RELOAD; 278 final boolean isRedirect = navigationParams.isRedirect; 279 280 boolean ignoreNavigation = false; 281 282 // Any navigation from loadUrl, goBack/Forward, or reload, are considered application 283 // initiated and hence will not yield a shouldOverrideUrlLoading() callback. 284 // TODO(joth): Using PageTransitionTypes should be sufficient to determine all app 285 // initiated navigations, and so mLastLoadUrlAddress should be removed. 286 if ((isLoadUrl && !isRedirect) || isBackForward || isReload || 287 mLastLoadUrlAddress != null && mLastLoadUrlAddress.equals(url)) { 288 // Support the case where the user clicks on a link that takes them back to the 289 // same page. 290 mLastLoadUrlAddress = null; 291 292 // If the embedder requested the load of a certain URL via the loadUrl API, then we 293 // do not offer it to AwContentsClient.shouldOverrideUrlLoading. 294 // The embedder is also not allowed to intercept POST requests because of 295 // crbug.com/155250. 296 } else if (!navigationParams.isPost) { 297 ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url); 298 } 299 300 // The existing contract is that shouldOverrideUrlLoading callbacks are delivered before 301 // onPageStarted callbacks; third party apps depend on this behavior. 302 // Using a ResouceThrottle to implement the navigation interception feature results in 303 // the WebContentsObserver.didStartLoading callback happening before the 304 // ResourceThrottle has a chance to run. 305 // To preserve the ordering the onPageStarted callback is synthesized from the 306 // shouldOverrideUrlLoading, and only if the navigation was not ignored (this 307 // balances out with the onPageFinished callback, which is suppressed in the 308 // AwContentsClient if the navigation was ignored). 309 if (!ignoreNavigation) { 310 // The shouldOverrideUrlLoading call might have resulted in posting messages to the 311 // UI thread. Using sendMessage here (instead of calling onPageStarted directly) 312 // will allow those to run in order. 313 mContentsClient.getCallbackHelper().postOnPageStarted(url); 314 } 315 316 return ignoreNavigation; 317 } 318 } 319 320 //-------------------------------------------------------------------------------------------- 321 private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate { 322 @Override 323 public void requestLayout() { 324 mContainerView.requestLayout(); 325 } 326 327 @Override 328 public void setMeasuredDimension(int measuredWidth, int measuredHeight) { 329 mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight); 330 } 331 } 332 333 //-------------------------------------------------------------------------------------------- 334 // NOTE: This content size change notification comes from the compositor and reflects the size 335 // of the content on screen (but not neccessarily in the renderer main thread). 336 private class AwContentUpdateFrameInfoListener 337 implements ContentViewCore.UpdateFrameInfoListener { 338 @Override 339 public void onFrameInfoUpdated(float widthCss, float heightCss, float pageScaleFactor) { 340 int widthPix = (int) Math.floor(widthCss * mDIPScale * pageScaleFactor); 341 int heightPix = (int) Math.floor(heightCss * mDIPScale * pageScaleFactor); 342 mScrollOffsetManager.setContentSize(widthPix, heightPix); 343 344 nativeSetDisplayedPageScaleFactor(mNativeAwContents, pageScaleFactor); 345 } 346 } 347 348 //-------------------------------------------------------------------------------------------- 349 private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate { 350 @Override 351 public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, 352 int scrollRangeX, int scrollRangeY) { 353 mInternalAccessAdapter.overScrollBy(deltaX, deltaY, scrollX, scrollY, 354 scrollRangeX, scrollRangeY, 0, 0, true); 355 } 356 357 @Override 358 public void scrollContainerViewTo(int x, int y) { 359 mInternalAccessAdapter.super_scrollTo(x, y); 360 } 361 362 @Override 363 public void scrollNativeTo(int x, int y) { 364 nativeScrollTo(mNativeAwContents, x, y); 365 } 366 367 @Override 368 public int getContainerViewScrollX() { 369 return mContainerView.getScrollX(); 370 } 371 372 @Override 373 public int getContainerViewScrollY() { 374 return mContainerView.getScrollY(); 375 } 376 } 377 378 //-------------------------------------------------------------------------------------------- 379 private class AwPinchGestureStateListener implements ContentViewCore.PinchGestureStateListener { 380 @Override 381 public void onPinchGestureStart() { 382 // While it's possible to re-layout the view during a pinch gesture, the effect is very 383 // janky (especially that the page scale update notification comes from the renderer 384 // main thread, not from the impl thread, so it's usually out of sync with what's on 385 // screen). It's also quite expensive to do a re-layout, so we simply postpone 386 // re-layout for the duration of the gesture. This is compatible with what 387 // WebViewClassic does. 388 mLayoutSizer.freezeLayoutRequests(); 389 } 390 391 public void onPinchGestureEnd() { 392 mLayoutSizer.unfreezeLayoutRequests(); 393 } 394 } 395 396 //-------------------------------------------------------------------------------------------- 397 private class ScrollChangeListener implements ViewTreeObserver.OnScrollChangedListener { 398 @Override 399 public void onScrollChanged() { 400 // We do this to cover the case that when the view hierarchy is scrolled, 401 // more of the containing view becomes visible (i.e. a containing view 402 // with a width/height of "wrap_content" and dimensions greater than 403 // that of the screen). 404 AwContents.this.updatePhysicalBackingSizeIfNeeded(); 405 } 406 }; 407 408 private ScrollChangeListener mScrollChangeListener; 409 410 /** 411 * @param browserContext the browsing context to associate this view contents with. 412 * @param containerView the view-hierarchy item this object will be bound to. 413 * @param internalAccessAdapter to access private methods on containerView. 414 * @param contentsClient will receive API callbacks from this WebView Contents 415 * @param isAccessFromFileURLsGrantedByDefault passed to AwSettings. 416 * 417 * This constructor uses the default view sizing policy. 418 */ 419 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, 420 InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, 421 boolean isAccessFromFileURLsGrantedByDefault) { 422 this(browserContext, containerView, internalAccessAdapter, contentsClient, 423 isAccessFromFileURLsGrantedByDefault, new AwLayoutSizer()); 424 } 425 426 private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView, 427 InternalAccessDelegate internalDispatcher, int nativeWebContents, 428 ContentViewCore.PinchGestureStateListener pinchGestureStateListener, 429 ContentViewClient contentViewClient, 430 ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) { 431 ContentViewCore contentViewCore = new ContentViewCore(containerView.getContext()); 432 // Note INPUT_EVENTS_DELIVERED_IMMEDIATELY is passed to avoid triggering vsync in the 433 // compositor, not because input events are delivered immediately. 434 contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, null, 435 ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY); 436 contentViewCore.setPinchGestureStateListener(pinchGestureStateListener); 437 contentViewCore.setContentViewClient(contentViewClient); 438 contentViewCore.setZoomControlsDelegate(zoomControlsDelegate); 439 return contentViewCore; 440 } 441 442 /** 443 * @param layoutSizer the AwLayoutSizer instance implementing the sizing policy for the view. 444 * 445 * This version of the constructor is used in test code to inject test versions of the above 446 * documented classes 447 */ 448 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, 449 InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, 450 boolean isAccessFromFileURLsGrantedByDefault, AwLayoutSizer layoutSizer) { 451 mBrowserContext = browserContext; 452 mContainerView = containerView; 453 mInternalAccessAdapter = internalAccessAdapter; 454 mContentsClient = contentsClient; 455 mLayoutSizer = layoutSizer; 456 mDIPScale = DeviceDisplayInfo.create(containerView.getContext()).getDIPScale(); 457 mLayoutSizer.setDelegate(new AwLayoutSizerDelegate()); 458 mLayoutSizer.setDIPScale(mDIPScale); 459 mWebContentsDelegate = new AwWebContentsDelegateAdapter(contentsClient, 460 mLayoutSizer.getPreferredSizeChangedListener()); 461 mContentsClientBridge = new AwContentsClientBridge(contentsClient); 462 mZoomControls = new AwZoomControls(this); 463 mIoThreadClient = new IoThreadClientImpl(); 464 mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(); 465 466 boolean hasInternetPermission = containerView.getContext().checkPermission( 467 android.Manifest.permission.INTERNET, 468 Process.myPid(), 469 Process.myUid()) == PackageManager.PERMISSION_GRANTED; 470 AwSettings.ZoomSupportChangeListener zoomListener = 471 new AwSettings.ZoomSupportChangeListener() { 472 @Override 473 public void onMultiTouchZoomSupportChanged(boolean supportsMultiTouchZoom) { 474 mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom); 475 } 476 }; 477 mSettings = new AwSettings(hasInternetPermission, zoomListener, 478 isAccessFromFileURLsGrantedByDefault, mDIPScale); 479 mDefaultVideoPosterRequestHandler = new DefaultVideoPosterRequestHandler(mContentsClient); 480 mSettings.setDefaultVideoPosterURL( 481 mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL()); 482 mContentsClient.setDIPScale(mDIPScale); 483 mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate()); 484 485 setOverScrollMode(mContainerView.getOverScrollMode()); 486 487 setNewAwContents(nativeInit(browserContext)); 488 } 489 490 /** 491 * Common initialization routine for adopting a native AwContents instance into this 492 * java instance. 493 * 494 * TAKE CARE! This method can get called multiple times per java instance. Code accordingly. 495 * ^^^^^^^^^ See the native class declaration for more details on relative object lifetimes. 496 */ 497 private void setNewAwContents(int newAwContentsPtr) { 498 if (mNativeAwContents != 0) { 499 destroy(); 500 mContentViewCore = null; 501 } 502 503 assert mNativeAwContents == 0 && mCleanupReference == null && mContentViewCore == null; 504 505 mNativeAwContents = newAwContentsPtr; 506 // TODO(joth): when the native and java counterparts of AwBrowserContext are hooked up to 507 // each other, we should update |mBrowserContext| according to the newly received native 508 // WebContent's browser context. 509 510 // The native side object has been bound to this java instance, so now is the time to 511 // bind all the native->java relationships. 512 mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents)); 513 514 int nativeWebContents = nativeGetWebContents(mNativeAwContents); 515 mContentViewCore = createAndInitializeContentViewCore( 516 mContainerView, mInternalAccessAdapter, nativeWebContents, 517 new AwPinchGestureStateListener(), mContentsClient.getContentViewClient(), 518 mZoomControls); 519 nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge, 520 mIoThreadClient, mInterceptNavigationDelegate); 521 mContentsClient.installWebContentsObserver(mContentViewCore); 522 mContentViewCore.setUpdateFrameInfoListener(new AwContentUpdateFrameInfoListener()); 523 mSettings.setWebContents(nativeWebContents); 524 nativeSetDipScale(mNativeAwContents, (float) mDIPScale); 525 } 526 527 /** 528 * Called on the "source" AwContents that is opening the popup window to 529 * provide the AwContents to host the pop up content. 530 */ 531 public void supplyContentsForPopup(AwContents newContents) { 532 int popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents); 533 if (popupNativeAwContents == 0) { 534 Log.w(TAG, "Popup WebView bind failed: no pending content."); 535 if (newContents != null) newContents.destroy(); 536 return; 537 } 538 if (newContents == null) { 539 nativeDestroy(popupNativeAwContents); 540 return; 541 } 542 543 newContents.receivePopupContents(popupNativeAwContents); 544 } 545 546 // Recap: supplyContentsForPopup() is called on the parent window's content, this method is 547 // called on the popup window's content. 548 private void receivePopupContents(int popupNativeAwContents) { 549 // Save existing view state. 550 final boolean wasAttached = mIsAttachedToWindow; 551 final boolean wasVisible = getContainerViewVisible(); 552 final boolean wasPaused = mIsPaused; 553 final boolean wasFocused = mWindowFocused; 554 555 // Properly clean up existing mContentViewCore and mNativeAwContents. 556 if (wasFocused) onWindowFocusChanged(false); 557 if (wasVisible) setVisibilityInternal(false); 558 // TODO(boliu): This may destroy GL resources outside of functor. 559 if (wasAttached) onDetachedFromWindow(); 560 if (!wasPaused) onPause(); 561 562 setNewAwContents(popupNativeAwContents); 563 564 // Finally refresh all view state for mContentViewCore and mNativeAwContents. 565 if (!wasPaused) onResume(); 566 if (wasAttached) onAttachedToWindow(); 567 mLastGlobalVisibleWidth = 0; 568 mLastGlobalVisibleHeight = 0; 569 onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); 570 if (wasVisible) setVisibilityInternal(true); 571 if (wasFocused) onWindowFocusChanged(true); 572 } 573 574 public void destroy() { 575 mContentViewCore.destroy(); 576 // We explicitly do not null out the mContentViewCore reference here 577 // because ContentViewCore already has code to deal with the case 578 // methods are called on it after it's been destroyed, and other 579 // code relies on AwContents.mContentViewCore to be non-null. 580 581 if (mCleanupReference != null) mCleanupReference.cleanupNow(); 582 mNativeAwContents = 0; 583 mCleanupReference = null; 584 } 585 586 @VisibleForTesting 587 public ContentViewCore getContentViewCore() { 588 return mContentViewCore; 589 } 590 591 // Can be called from any thread. 592 public AwSettings getSettings() { 593 return mSettings; 594 } 595 596 public static void setAwDrawSWFunctionTable(int functionTablePointer) { 597 nativeSetAwDrawSWFunctionTable(functionTablePointer); 598 } 599 600 public static void setAwDrawGLFunctionTable(int functionTablePointer) { 601 nativeSetAwDrawGLFunctionTable(functionTablePointer); 602 } 603 604 public static int getAwDrawGLFunction() { 605 return nativeGetAwDrawGLFunction(); 606 } 607 608 /** 609 * Intended for test code. 610 * @return the number of native instances of this class. 611 */ 612 @VisibleForTesting 613 public static int getNativeInstanceCount() { 614 return nativeGetNativeInstanceCount(); 615 } 616 617 public int getAwDrawGLViewContext() { 618 // Using the native pointer as the returned viewContext. This is matched by the 619 // reinterpret_cast back to BrowserViewRenderer pointer in the native DrawGLFunction. 620 return nativeGetAwDrawGLViewContext(mNativeAwContents); 621 } 622 623 // Only valid within updatePhysicalBackingSizeIfNeeded(). 624 private final Rect mGlobalVisibleBoundsTemporary = new Rect(); 625 626 private void updatePhysicalBackingSizeIfNeeded() { 627 // We musn't let the physical backing size get too big, otherwise we 628 // will try to allocate a SurfaceTexture beyond what the GL driver can 629 // cope with. In most cases, limiting the SurfaceTexture size to that 630 // of the visible bounds of the WebView will be good enough i.e. the maximum 631 // SurfaceTexture dimensions will match the screen dimensions). 632 mContainerView.getGlobalVisibleRect(mGlobalVisibleBoundsTemporary); 633 int width = mGlobalVisibleBoundsTemporary.width(); 634 int height = mGlobalVisibleBoundsTemporary.height(); 635 if (width != mLastGlobalVisibleWidth || height != mLastGlobalVisibleHeight) { 636 mLastGlobalVisibleWidth = width; 637 mLastGlobalVisibleHeight = height; 638 mContentViewCore.onPhysicalBackingSizeChanged(width, height); 639 } 640 } 641 642 //-------------------------------------------------------------------------------------------- 643 // WebView[Provider] method implementations (where not provided by ContentViewCore) 644 //-------------------------------------------------------------------------------------------- 645 646 // Only valid within onDraw(). 647 private final Rect mClipBoundsTemporary = new Rect(); 648 649 public void onDraw(Canvas canvas) { 650 if (mNativeAwContents == 0) { 651 canvas.drawColor(getEffectiveBackgroundColor()); 652 return; 653 } 654 655 mScrollOffsetManager.syncScrollOffsetFromOnDraw(); 656 657 canvas.getClipBounds(mClipBoundsTemporary); 658 if (!nativeOnDraw(mNativeAwContents, canvas, canvas.isHardwareAccelerated(), 659 mContainerView.getScrollX(), mContainerView.getScrollY(), 660 mClipBoundsTemporary.left, mClipBoundsTemporary.top, 661 mClipBoundsTemporary.right, mClipBoundsTemporary.bottom )) { 662 Log.w(TAG, "nativeOnDraw failed; clearing to background color."); 663 canvas.drawColor(getEffectiveBackgroundColor()); 664 } 665 666 if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas, 667 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 668 mScrollOffsetManager.computeMaximumVerticalScrollOffset())) { 669 mContainerView.invalidate(); 670 } 671 } 672 673 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 674 mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec); 675 } 676 677 public int getContentHeightCss() { 678 return (int) Math.ceil(mContentViewCore.getContentHeightCss()); 679 } 680 681 public int getContentWidthCss() { 682 return (int) Math.ceil(mContentViewCore.getContentWidthCss()); 683 } 684 685 public Picture capturePicture() { 686 return nativeCapturePicture(mNativeAwContents); 687 } 688 689 /** 690 * Enable the onNewPicture callback. 691 * @param enabled Flag to enable the callback. 692 * @param invalidationOnly Flag to call back only on invalidation without providing a picture. 693 */ 694 public void enableOnNewPicture(boolean enabled, boolean invalidationOnly) { 695 if (invalidationOnly) { 696 mPictureListenerContentProvider = null; 697 } else if (enabled && mPictureListenerContentProvider == null) { 698 mPictureListenerContentProvider = new Callable<Picture>() { 699 @Override 700 public Picture call() { 701 return capturePicture(); 702 } 703 }; 704 } 705 nativeEnableOnNewPicture(mNativeAwContents, enabled); 706 } 707 708 public void findAllAsync(String searchString) { 709 if (mNativeAwContents == 0) return; 710 nativeFindAllAsync(mNativeAwContents, searchString); 711 } 712 713 public void findNext(boolean forward) { 714 if (mNativeAwContents == 0) return; 715 nativeFindNext(mNativeAwContents, forward); 716 } 717 718 public void clearMatches() { 719 if (mNativeAwContents == 0) return; 720 nativeClearMatches(mNativeAwContents); 721 } 722 723 /** 724 * @return load progress of the WebContents. 725 */ 726 public int getMostRecentProgress() { 727 // WebContentsDelegateAndroid conveniently caches the most recent notified value for us. 728 return mWebContentsDelegate.getMostRecentProgress(); 729 } 730 731 public Bitmap getFavicon() { 732 return mFavicon; 733 } 734 735 private void requestVisitedHistoryFromClient() { 736 ValueCallback<String[]> callback = new ValueCallback<String[]>() { 737 @Override 738 public void onReceiveValue(final String[] value) { 739 ThreadUtils.runOnUiThread(new Runnable() { 740 @Override 741 public void run() { 742 if (mNativeAwContents == 0) return; 743 nativeAddVisitedLinks(mNativeAwContents, value); 744 } 745 }); 746 } 747 }; 748 mContentsClient.getVisitedHistory(callback); 749 } 750 751 /** 752 * Load url without fixing up the url string. Consumers of ContentView are responsible for 753 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left 754 * off during user input). 755 * 756 * @param pararms Parameters for this load. 757 */ 758 public void loadUrl(LoadUrlParams params) { 759 if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA && 760 !params.isBaseUrlDataScheme()) { 761 // This allows data URLs with a non-data base URL access to file:///android_asset/ and 762 // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also 763 // allow access to file:// URLs (subject to OS level permission checks). 764 params.setCanLoadLocalResources(true); 765 } 766 767 // If we are reloading the same url, then set transition type as reload. 768 if (params.getUrl() != null && 769 params.getUrl().equals(mContentViewCore.getUrl()) && 770 params.getTransitionType() == PageTransitionTypes.PAGE_TRANSITION_LINK) { 771 params.setTransitionType(PageTransitionTypes.PAGE_TRANSITION_RELOAD); 772 } 773 params.setTransitionType( 774 params.getTransitionType() | PageTransitionTypes.PAGE_TRANSITION_FROM_API); 775 776 // For WebView, always use the user agent override, which is set 777 // every time the user agent in AwSettings is modified. 778 params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE); 779 780 mContentViewCore.loadUrl(params); 781 782 suppressInterceptionForThisNavigation(); 783 784 // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit. 785 // Chromium does not use this use code path and the best emulation of this behavior to call 786 // request visited links once on the first URL load of the WebView. 787 if (!mHasRequestedVisitedHistoryFromClient) { 788 mHasRequestedVisitedHistoryFromClient = true; 789 requestVisitedHistoryFromClient(); 790 } 791 } 792 793 private void suppressInterceptionForThisNavigation() { 794 if (mInterceptNavigationDelegate != null) { 795 // getUrl returns a sanitized address in the same format that will be used for 796 // callbacks, so it's safe to use string comparison as an equality check later on. 797 mInterceptNavigationDelegate.onUrlLoadRequested(mContentViewCore.getUrl()); 798 } 799 } 800 801 /** 802 * Get the URL of the current page. 803 * 804 * @return The URL of the current page or null if it's empty. 805 */ 806 public String getUrl() { 807 String url = mContentViewCore.getUrl(); 808 if (url == null || url.trim().isEmpty()) return null; 809 return url; 810 } 811 812 public void requestFocus() { 813 if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) { 814 nativeFocusFirstNode(mNativeAwContents); 815 } 816 } 817 818 public void setBackgroundColor(int color) { 819 mBaseBackgroundColor = color; 820 if (mNativeAwContents != 0) nativeSetBackgroundColor(mNativeAwContents, color); 821 } 822 823 private int getEffectiveBackgroundColor() { 824 // Do not ask the ContentViewCore for the background color, as it will always 825 // report white prior to initial navigation or post destruction, whereas we want 826 // to use the client supplied base value in those cases. 827 if (mNativeAwContents == 0 || !mContentsClient.isCachedRendererBackgroundColorValid()) { 828 return mBaseBackgroundColor; 829 } 830 return mContentsClient.getCachedRendererBackgroundColor(); 831 } 832 833 public boolean isMultiTouchZoomSupported() { 834 return mSettings.supportsMultiTouchZoom(); 835 } 836 837 public View getZoomControlsForTest() { 838 return mZoomControls.getZoomControlsViewForTest(); 839 } 840 841 /** 842 * @see ContentViewCore#getContentSettings() 843 */ 844 public ContentSettings getContentSettings() { 845 return mContentViewCore.getContentSettings(); 846 } 847 848 /** 849 * @see View#setOverScrollMode(int) 850 */ 851 public void setOverScrollMode(int mode) { 852 if (mode != View.OVER_SCROLL_NEVER) { 853 mOverScrollGlow = new OverScrollGlow(mContainerView); 854 } else { 855 mOverScrollGlow = null; 856 } 857 } 858 859 /** 860 * Called by the embedder when the scroll offset of the containing view has changed. 861 * @see View#onScrollChanged(int,int) 862 */ 863 public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) { 864 mScrollOffsetManager.onContainerViewScrollChanged(l, t); 865 } 866 867 /** 868 * Called by the embedder when the containing view is to be scrolled or overscrolled. 869 * @see View#onOverScrolled(int,int,int,int) 870 */ 871 public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, 872 boolean clampedY) { 873 int oldX = mContainerView.getScrollX(); 874 int oldY = mContainerView.getScrollY(); 875 876 mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); 877 878 if (mOverScrollGlow != null) { 879 mOverScrollGlow.pullGlow(mContainerView.getScrollX(), mContainerView.getScrollY(), 880 oldX, oldY, 881 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 882 mScrollOffsetManager.computeMaximumVerticalScrollOffset()); 883 } 884 } 885 886 /** 887 * @see View#computeHorizontalScrollRange() 888 */ 889 public int computeHorizontalScrollRange() { 890 return mScrollOffsetManager.computeHorizontalScrollRange(); 891 } 892 893 /** 894 * @see View#computeHorizontalScrollOffset() 895 */ 896 public int computeHorizontalScrollOffset() { 897 return mScrollOffsetManager.computeHorizontalScrollOffset(); 898 } 899 900 /** 901 * @see View#computeVerticalScrollRange() 902 */ 903 public int computeVerticalScrollRange() { 904 return mScrollOffsetManager.computeVerticalScrollRange(); 905 } 906 907 /** 908 * @see View#computeVerticalScrollOffset() 909 */ 910 public int computeVerticalScrollOffset() { 911 return mScrollOffsetManager.computeVerticalScrollOffset(); 912 } 913 914 /** 915 * @see View#computeVerticalScrollExtent() 916 */ 917 public int computeVerticalScrollExtent() { 918 return mScrollOffsetManager.computeVerticalScrollExtent(); 919 } 920 921 /** 922 * @see android.webkit.WebView#stopLoading() 923 */ 924 public void stopLoading() { 925 mContentViewCore.stopLoading(); 926 } 927 928 /** 929 * @see android.webkit.WebView#reload() 930 */ 931 public void reload() { 932 mContentViewCore.reload(); 933 } 934 935 /** 936 * @see android.webkit.WebView#canGoBack() 937 */ 938 public boolean canGoBack() { 939 return mContentViewCore.canGoBack(); 940 } 941 942 /** 943 * @see android.webkit.WebView#goBack() 944 */ 945 public void goBack() { 946 mContentViewCore.goBack(); 947 948 suppressInterceptionForThisNavigation(); 949 } 950 951 /** 952 * @see android.webkit.WebView#canGoForward() 953 */ 954 public boolean canGoForward() { 955 return mContentViewCore.canGoForward(); 956 } 957 958 /** 959 * @see android.webkit.WebView#goForward() 960 */ 961 public void goForward() { 962 mContentViewCore.goForward(); 963 964 suppressInterceptionForThisNavigation(); 965 } 966 967 /** 968 * @see android.webkit.WebView#canGoBackOrForward(int) 969 */ 970 public boolean canGoBackOrForward(int steps) { 971 return mContentViewCore.canGoToOffset(steps); 972 } 973 974 /** 975 * @see android.webkit.WebView#goBackOrForward(int) 976 */ 977 public void goBackOrForward(int steps) { 978 mContentViewCore.goToOffset(steps); 979 980 suppressInterceptionForThisNavigation(); 981 } 982 983 /** 984 * @see android.webkit.WebView#pauseTimers() 985 */ 986 // TODO(kristianm): Remove 987 public void pauseTimers() { 988 ContentViewStatics.setWebKitSharedTimersSuspended(true); 989 } 990 991 /** 992 * @see android.webkit.WebView#resumeTimers() 993 */ 994 // TODO(kristianm): Remove 995 public void resumeTimers() { 996 ContentViewStatics.setWebKitSharedTimersSuspended(false); 997 } 998 999 /** 1000 * @see android.webkit.WebView#onPause() 1001 */ 1002 public void onPause() { 1003 mIsPaused = true; 1004 updateVisibilityState(); 1005 mContentViewCore.onActivityPause(); 1006 } 1007 1008 /** 1009 * @see android.webkit.WebView#onResume() 1010 */ 1011 public void onResume() { 1012 mIsPaused = false; 1013 updateVisibilityState(); 1014 // Not calling mContentViewCore.onActivityResume because it is the same 1015 // as onShow, but we do not want to call onShow yet, since AwContents 1016 // visibility depends on other things. TODO(boliu): Clean this up. 1017 } 1018 1019 /** 1020 * @see android.webkit.WebView#isPaused() 1021 */ 1022 public boolean isPaused() { 1023 return mIsPaused; 1024 } 1025 1026 /** 1027 * @see android.webkit.WebView#onCreateInputConnection(EditorInfo) 1028 */ 1029 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 1030 return mContentViewCore.onCreateInputConnection(outAttrs); 1031 } 1032 1033 /** 1034 * @see android.webkit.WebView#onKeyUp(int, KeyEvent) 1035 */ 1036 public boolean onKeyUp(int keyCode, KeyEvent event) { 1037 return mContentViewCore.onKeyUp(keyCode, event); 1038 } 1039 1040 /** 1041 * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent) 1042 */ 1043 public boolean dispatchKeyEvent(KeyEvent event) { 1044 return mContentViewCore.dispatchKeyEvent(event); 1045 } 1046 1047 /** 1048 * Clears the resource cache. Note that the cache is per-application, so this will clear the 1049 * cache for all WebViews used. 1050 * 1051 * @param includeDiskFiles if false, only the RAM cache is cleared 1052 */ 1053 public void clearCache(boolean includeDiskFiles) { 1054 if (mNativeAwContents == 0) return; 1055 nativeClearCache(mNativeAwContents, includeDiskFiles); 1056 } 1057 1058 public void documentHasImages(Message message) { 1059 if (mNativeAwContents == 0) return; 1060 nativeDocumentHasImages(mNativeAwContents, message); 1061 } 1062 1063 public void saveWebArchive( 1064 final String basename, boolean autoname, final ValueCallback<String> callback) { 1065 if (!autoname) { 1066 saveWebArchiveInternal(basename, callback); 1067 return; 1068 } 1069 // If auto-generating the file name, handle the name generation on a background thread 1070 // as it will require I/O access for checking whether previous files existed. 1071 new AsyncTask<Void, Void, String>() { 1072 @Override 1073 protected String doInBackground(Void... params) { 1074 return generateArchiveAutoNamePath(getOriginalUrl(), basename); 1075 } 1076 1077 @Override 1078 protected void onPostExecute(String result) { 1079 saveWebArchiveInternal(result, callback); 1080 } 1081 }.execute(); 1082 } 1083 1084 public String getOriginalUrl() { 1085 NavigationHistory history = mContentViewCore.getNavigationHistory(); 1086 int currentIndex = history.getCurrentEntryIndex(); 1087 if (currentIndex >= 0 && currentIndex < history.getEntryCount()) { 1088 return history.getEntryAtIndex(currentIndex).getOriginalUrl(); 1089 } 1090 return null; 1091 } 1092 1093 /** 1094 * @see ContentViewCore#getNavigationHistory() 1095 */ 1096 public NavigationHistory getNavigationHistory() { 1097 return mContentViewCore.getNavigationHistory(); 1098 } 1099 1100 /** 1101 * @see android.webkit.WebView#getTitle() 1102 */ 1103 public String getTitle() { 1104 return mContentViewCore.getTitle(); 1105 } 1106 1107 /** 1108 * @see android.webkit.WebView#clearHistory() 1109 */ 1110 public void clearHistory() { 1111 mContentViewCore.clearHistory(); 1112 } 1113 1114 public String[] getHttpAuthUsernamePassword(String host, String realm) { 1115 return mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext()) 1116 .getHttpAuthUsernamePassword(host, realm); 1117 } 1118 1119 public void setHttpAuthUsernamePassword(String host, String realm, String username, 1120 String password) { 1121 mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext()) 1122 .setHttpAuthUsernamePassword(host, realm, username, password); 1123 } 1124 1125 /** 1126 * @see android.webkit.WebView#getCertificate() 1127 */ 1128 public SslCertificate getCertificate() { 1129 if (mNativeAwContents == 0) return null; 1130 return SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents)); 1131 } 1132 1133 /** 1134 * @see android.webkit.WebView#clearSslPreferences() 1135 */ 1136 public void clearSslPreferences() { 1137 mContentViewCore.clearSslPreferences(); 1138 } 1139 1140 /** 1141 * Method to return all hit test values relevant to public WebView API. 1142 * Note that this expose more data than needed for WebView.getHitTestResult. 1143 * Unsafely returning reference to mutable internal object to avoid excessive 1144 * garbage allocation on repeated calls. 1145 */ 1146 public HitTestData getLastHitTestResult() { 1147 if (mNativeAwContents == 0) return null; 1148 nativeUpdateLastHitTestData(mNativeAwContents); 1149 return mPossiblyStaleHitTestData; 1150 } 1151 1152 /** 1153 * @see android.webkit.WebView#requestFocusNodeHref() 1154 */ 1155 public void requestFocusNodeHref(Message msg) { 1156 if (msg == null || mNativeAwContents == 0) return; 1157 1158 nativeUpdateLastHitTestData(mNativeAwContents); 1159 Bundle data = msg.getData(); 1160 data.putString("url", mPossiblyStaleHitTestData.href); 1161 data.putString("title", mPossiblyStaleHitTestData.anchorText); 1162 data.putString("src", mPossiblyStaleHitTestData.imgSrc); 1163 msg.setData(data); 1164 msg.sendToTarget(); 1165 } 1166 1167 /** 1168 * @see android.webkit.WebView#requestImageRef() 1169 */ 1170 public void requestImageRef(Message msg) { 1171 if (msg == null || mNativeAwContents == 0) return; 1172 1173 nativeUpdateLastHitTestData(mNativeAwContents); 1174 Bundle data = msg.getData(); 1175 data.putString("url", mPossiblyStaleHitTestData.imgSrc); 1176 msg.setData(data); 1177 msg.sendToTarget(); 1178 } 1179 1180 /** 1181 * @see android.webkit.WebView#getScale() 1182 * 1183 * Please note that the scale returned is the page scale multiplied by 1184 * the screen density factor. See CTS WebViewTest.testSetInitialScale. 1185 */ 1186 public float getScale() { 1187 return (float)(mContentViewCore.getScale() * mDIPScale); 1188 } 1189 1190 /** 1191 * @see android.webkit.WebView#flingScroll(int, int) 1192 */ 1193 public void flingScroll(int vx, int vy) { 1194 mContentViewCore.flingScroll(vx, vy); 1195 } 1196 1197 /** 1198 * @see android.webkit.WebView#pageUp(boolean) 1199 */ 1200 public boolean pageUp(boolean top) { 1201 return mContentViewCore.pageUp(top); 1202 } 1203 1204 /** 1205 * @see android.webkit.WebView#pageDown(boolean) 1206 */ 1207 public boolean pageDown(boolean bottom) { 1208 return mContentViewCore.pageDown(bottom); 1209 } 1210 1211 /** 1212 * @see android.webkit.WebView#canZoomIn() 1213 */ 1214 public boolean canZoomIn() { 1215 return mContentViewCore.canZoomIn(); 1216 } 1217 1218 /** 1219 * @see android.webkit.WebView#canZoomOut() 1220 */ 1221 public boolean canZoomOut() { 1222 return mContentViewCore.canZoomOut(); 1223 } 1224 1225 /** 1226 * @see android.webkit.WebView#zoomIn() 1227 */ 1228 public boolean zoomIn() { 1229 return mContentViewCore.zoomIn(); 1230 } 1231 1232 /** 1233 * @see android.webkit.WebView#zoomOut() 1234 */ 1235 public boolean zoomOut() { 1236 return mContentViewCore.zoomOut(); 1237 } 1238 1239 /** 1240 * @see android.webkit.WebView#invokeZoomPicker() 1241 */ 1242 public void invokeZoomPicker() { 1243 mContentViewCore.invokeZoomPicker(); 1244 } 1245 1246 /** 1247 * @see ContentViewCore.evaluateJavaScript(String, ContentViewCOre.JavaScriptCallback) 1248 */ 1249 public void evaluateJavaScript(String script, final ValueCallback<String> callback) { 1250 ContentViewCore.JavaScriptCallback jsCallback = null; 1251 if (callback != null) { 1252 jsCallback = new ContentViewCore.JavaScriptCallback() { 1253 @Override 1254 public void handleJavaScriptResult(String jsonResult) { 1255 callback.onReceiveValue(jsonResult); 1256 } 1257 }; 1258 } 1259 1260 mContentViewCore.evaluateJavaScript(script, jsCallback); 1261 } 1262 1263 //-------------------------------------------------------------------------------------------- 1264 // View and ViewGroup method implementations 1265 //-------------------------------------------------------------------------------------------- 1266 1267 /** 1268 * @see android.webkit.View#onTouchEvent() 1269 */ 1270 public boolean onTouchEvent(MotionEvent event) { 1271 if (mNativeAwContents == 0) return false; 1272 boolean rv = mContentViewCore.onTouchEvent(event); 1273 1274 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1275 int actionIndex = event.getActionIndex(); 1276 1277 // Note this will trigger IPC back to browser even if nothing is hit. 1278 nativeRequestNewHitTestDataAt(mNativeAwContents, 1279 (int)Math.round(event.getX(actionIndex) / mDIPScale), 1280 (int)Math.round(event.getY(actionIndex) / mDIPScale)); 1281 } 1282 1283 if (mOverScrollGlow != null && event.getActionMasked() == MotionEvent.ACTION_UP) { 1284 mOverScrollGlow.releaseAll(); 1285 } 1286 1287 return rv; 1288 } 1289 1290 /** 1291 * @see android.view.View#onHoverEvent() 1292 */ 1293 public boolean onHoverEvent(MotionEvent event) { 1294 return mContentViewCore.onHoverEvent(event); 1295 } 1296 1297 /** 1298 * @see android.view.View#onGenericMotionEvent() 1299 */ 1300 public boolean onGenericMotionEvent(MotionEvent event) { 1301 return mContentViewCore.onGenericMotionEvent(event); 1302 } 1303 1304 /** 1305 * @see android.view.View#onConfigurationChanged() 1306 */ 1307 public void onConfigurationChanged(Configuration newConfig) { 1308 mContentViewCore.onConfigurationChanged(newConfig); 1309 } 1310 1311 /** 1312 * @see android.view.View#onAttachedToWindow() 1313 * 1314 * Note that this is also called from receivePopupContents. 1315 */ 1316 public void onAttachedToWindow() { 1317 mIsAttachedToWindow = true; 1318 if (mScrollChangeListener == null) { 1319 mScrollChangeListener = new ScrollChangeListener(); 1320 } 1321 mContainerView.getViewTreeObserver().addOnScrollChangedListener(mScrollChangeListener); 1322 1323 mContentViewCore.onAttachedToWindow(); 1324 nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(), 1325 mContainerView.getHeight()); 1326 1327 // This is for the case where this is created by restoreState, which 1328 // needs to call to NavigationController::LoadIfNecessary to actually 1329 // load the restored page. 1330 if (!mIsPaused) onResume(); 1331 } 1332 1333 /** 1334 * @see android.view.View#onDetachedFromWindow() 1335 */ 1336 public void onDetachedFromWindow() { 1337 mIsAttachedToWindow = false; 1338 if (mNativeAwContents != 0) { 1339 nativeOnDetachedFromWindow(mNativeAwContents); 1340 } 1341 1342 if (mScrollChangeListener != null) { 1343 mContainerView.getViewTreeObserver().removeOnScrollChangedListener( 1344 mScrollChangeListener); 1345 mScrollChangeListener = null; 1346 } 1347 1348 mContentViewCore.onDetachedFromWindow(); 1349 } 1350 1351 /** 1352 * @see android.view.View#onWindowFocusChanged() 1353 */ 1354 public void onWindowFocusChanged(boolean hasWindowFocus) { 1355 mWindowFocused = hasWindowFocus; 1356 mContentViewCore.onFocusChanged(mContainerViewFocused && mWindowFocused); 1357 } 1358 1359 /** 1360 * @see android.view.View#onFocusChanged() 1361 */ 1362 public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 1363 mContainerViewFocused = focused; 1364 mContentViewCore.onFocusChanged(mContainerViewFocused && mWindowFocused); 1365 } 1366 1367 /** 1368 * @see android.view.View#onSizeChanged() 1369 */ 1370 public void onSizeChanged(int w, int h, int ow, int oh) { 1371 if (mNativeAwContents == 0) return; 1372 mScrollOffsetManager.setContainerViewSize(w, h); 1373 updatePhysicalBackingSizeIfNeeded(); 1374 mContentViewCore.onSizeChanged(w, h, ow, oh); 1375 nativeOnSizeChanged(mNativeAwContents, w, h, ow, oh); 1376 } 1377 1378 /** 1379 * @see android.view.View#onVisibilityChanged() 1380 */ 1381 public void onVisibilityChanged(View changedView, int visibility) { 1382 updateVisibilityState(); 1383 } 1384 1385 /** 1386 * @see android.view.View#onWindowVisibilityChanged() 1387 */ 1388 public void onWindowVisibilityChanged(int visibility) { 1389 updateVisibilityState(); 1390 } 1391 1392 private void updateVisibilityState() { 1393 boolean visible = getContainerViewVisible(); 1394 if (mIsVisible == visible) return; 1395 1396 setVisibilityInternal(visible); 1397 } 1398 1399 private boolean getContainerViewVisible() { 1400 boolean windowVisible = mContainerView.getWindowVisibility() == View.VISIBLE; 1401 boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE; 1402 1403 return windowVisible && viewVisible && !mIsPaused; 1404 } 1405 1406 private void setVisibilityInternal(boolean visible) { 1407 // Note that this skips mIsVisible check and unconditionally sets 1408 // visibility. In general, callers should use updateVisibilityState 1409 // instead. 1410 mIsVisible = visible; 1411 if (mIsVisible) { 1412 mContentViewCore.onShow(); 1413 } else { 1414 mContentViewCore.onHide(); 1415 } 1416 nativeSetVisibility(mNativeAwContents, mIsVisible); 1417 } 1418 1419 /** 1420 * Key for opaque state in bundle. Note this is only public for tests. 1421 */ 1422 public static final String SAVE_RESTORE_STATE_KEY = "WEBVIEW_CHROMIUM_STATE"; 1423 1424 /** 1425 * Save the state of this AwContents into provided Bundle. 1426 * @return False if saving state failed. 1427 */ 1428 public boolean saveState(Bundle outState) { 1429 if (outState == null) return false; 1430 1431 byte[] state = nativeGetOpaqueState(mNativeAwContents); 1432 if (state == null) return false; 1433 1434 outState.putByteArray(SAVE_RESTORE_STATE_KEY, state); 1435 return true; 1436 } 1437 1438 /** 1439 * Restore the state of this AwContents into provided Bundle. 1440 * @param inState Must be a bundle returned by saveState. 1441 * @return False if restoring state failed. 1442 */ 1443 public boolean restoreState(Bundle inState) { 1444 if (inState == null) return false; 1445 1446 byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY); 1447 if (state == null) return false; 1448 1449 boolean result = nativeRestoreFromOpaqueState(mNativeAwContents, state); 1450 1451 // The onUpdateTitle callback normally happens when a page is loaded, 1452 // but is optimized out in the restoreState case because the title is 1453 // already restored. See WebContentsImpl::UpdateTitleForEntry. So we 1454 // call the callback explicitly here. 1455 if (result) mContentsClient.onReceivedTitle(mContentViewCore.getTitle()); 1456 1457 return result; 1458 } 1459 1460 /** 1461 * @see ContentViewCore#addPossiblyUnsafeJavascriptInterface(Object, String, Class) 1462 */ 1463 public void addPossiblyUnsafeJavascriptInterface(Object object, String name, 1464 Class<? extends Annotation> requiredAnnotation) { 1465 mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name, requiredAnnotation); 1466 } 1467 1468 /** 1469 * @see android.webkit.WebView#removeJavascriptInterface(String) 1470 */ 1471 public void removeJavascriptInterface(String interfaceName) { 1472 mContentViewCore.removeJavascriptInterface(interfaceName); 1473 } 1474 1475 /** 1476 * If native accessibility (not script injection) is enabled, and if this is 1477 * running on JellyBean or later, returns an AccessibilityNodeProvider that 1478 * implements native accessibility for this view. Returns null otherwise. 1479 * @return The AccessibilityNodeProvider, if available, or null otherwise. 1480 */ 1481 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 1482 return mContentViewCore.getAccessibilityNodeProvider(); 1483 } 1484 1485 /** 1486 * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) 1487 */ 1488 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1489 mContentViewCore.onInitializeAccessibilityNodeInfo(info); 1490 } 1491 1492 /** 1493 * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent) 1494 */ 1495 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1496 mContentViewCore.onInitializeAccessibilityEvent(event); 1497 } 1498 1499 public boolean supportsAccessibilityAction(int action) { 1500 return mContentViewCore.supportsAccessibilityAction(action); 1501 } 1502 1503 /** 1504 * @see android.webkit.WebView#performAccessibilityAction(int, Bundle) 1505 */ 1506 public boolean performAccessibilityAction(int action, Bundle arguments) { 1507 return mContentViewCore.performAccessibilityAction(action, arguments); 1508 } 1509 1510 /** 1511 * @see android.webkit.WebView#clearFormData() 1512 */ 1513 public void hideAutofillPopup() { 1514 if (mAwAutofillManagerDelegate != null) 1515 mAwAutofillManagerDelegate.hideAutofillPopup(); 1516 } 1517 1518 //-------------------------------------------------------------------------------------------- 1519 // Methods called from native via JNI 1520 //-------------------------------------------------------------------------------------------- 1521 1522 @CalledByNative 1523 private static void onDocumentHasImagesResponse(boolean result, Message message) { 1524 message.arg1 = result ? 1 : 0; 1525 message.sendToTarget(); 1526 } 1527 1528 @CalledByNative 1529 private void onReceivedTouchIconUrl(String url, boolean precomposed) { 1530 mContentsClient.onReceivedTouchIconUrl(url, precomposed); 1531 } 1532 1533 @CalledByNative 1534 private void onReceivedIcon(Bitmap bitmap) { 1535 mContentsClient.onReceivedIcon(bitmap); 1536 mFavicon = bitmap; 1537 } 1538 1539 /** Callback for generateMHTML. */ 1540 @CalledByNative 1541 private static void generateMHTMLCallback( 1542 String path, long size, ValueCallback<String> callback) { 1543 if (callback == null) return; 1544 callback.onReceiveValue(size < 0 ? null : path); 1545 } 1546 1547 @CalledByNative 1548 private void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) { 1549 mContentsClient.onReceivedHttpAuthRequest(handler, host, realm); 1550 } 1551 1552 private class AwGeolocationCallback implements GeolocationPermissions.Callback { 1553 1554 @Override 1555 public void invoke(final String origin, final boolean allow, final boolean retain) { 1556 ThreadUtils.runOnUiThread(new Runnable() { 1557 @Override 1558 public void run() { 1559 if (retain) { 1560 if (allow) { 1561 mBrowserContext.getGeolocationPermissions().allow(origin); 1562 } else { 1563 mBrowserContext.getGeolocationPermissions().deny(origin); 1564 } 1565 } 1566 nativeInvokeGeolocationCallback(mNativeAwContents, allow, origin); 1567 } 1568 }); 1569 } 1570 } 1571 1572 @CalledByNative 1573 private void onGeolocationPermissionsShowPrompt(String origin) { 1574 AwGeolocationPermissions permissions = mBrowserContext.getGeolocationPermissions(); 1575 // Reject if geoloaction is disabled, or the origin has a retained deny 1576 if (!mSettings.getGeolocationEnabled()) { 1577 nativeInvokeGeolocationCallback(mNativeAwContents, false, origin); 1578 return; 1579 } 1580 // Allow if the origin has a retained allow 1581 if (permissions.hasOrigin(origin)) { 1582 nativeInvokeGeolocationCallback(mNativeAwContents, permissions.isOriginAllowed(origin), 1583 origin); 1584 return; 1585 } 1586 mContentsClient.onGeolocationPermissionsShowPrompt( 1587 origin, new AwGeolocationCallback()); 1588 } 1589 1590 @CalledByNative 1591 private void onGeolocationPermissionsHidePrompt() { 1592 mContentsClient.onGeolocationPermissionsHidePrompt(); 1593 } 1594 1595 @CalledByNative 1596 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, 1597 boolean isDoneCounting) { 1598 mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); 1599 } 1600 1601 @CalledByNative 1602 public void onNewPicture() { 1603 // Don't call capturePicture() here but instead defer it until the posted task runs within 1604 // the callback helper, to avoid doubling back into the renderer compositor in the middle 1605 // of the notification it is sending up to here. 1606 mContentsClient.getCallbackHelper().postOnNewPicture(mPictureListenerContentProvider); 1607 } 1608 1609 // Called as a result of nativeUpdateLastHitTestData. 1610 @CalledByNative 1611 private void updateHitTestData( 1612 int type, String extra, String href, String anchorText, String imgSrc) { 1613 mPossiblyStaleHitTestData.hitTestResultType = type; 1614 mPossiblyStaleHitTestData.hitTestResultExtraData = extra; 1615 mPossiblyStaleHitTestData.href = href; 1616 mPossiblyStaleHitTestData.anchorText = anchorText; 1617 mPossiblyStaleHitTestData.imgSrc = imgSrc; 1618 } 1619 1620 @CalledByNative 1621 private boolean requestDrawGL(Canvas canvas) { 1622 return mInternalAccessAdapter.requestDrawGL(canvas); 1623 } 1624 1625 private static final boolean SUPPORTS_ON_ANIMATION = 1626 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; 1627 1628 @CalledByNative 1629 private void postInvalidateOnAnimation() { 1630 if (SUPPORTS_ON_ANIMATION) { 1631 mContainerView.postInvalidateOnAnimation(); 1632 } else { 1633 mContainerView.postInvalidate(); 1634 } 1635 } 1636 1637 @CalledByNative 1638 private boolean performLongClick() { 1639 return mContainerView.performLongClick(); 1640 } 1641 1642 @CalledByNative 1643 private int[] getLocationOnScreen() { 1644 int[] result = new int[2]; 1645 mContainerView.getLocationOnScreen(result); 1646 return result; 1647 } 1648 1649 @CalledByNative 1650 private void onWebLayoutPageScaleFactorChanged(float webLayoutPageScaleFactor) { 1651 // This change notification comes from the renderer thread, not from the cc/ impl thread. 1652 mLayoutSizer.onPageScaleChanged(webLayoutPageScaleFactor); 1653 } 1654 1655 @CalledByNative 1656 private void scrollContainerViewTo(int x, int y) { 1657 mScrollOffsetManager.scrollContainerViewTo(x, y); 1658 } 1659 1660 @CalledByNative 1661 private void setAwAutofillManagerDelegate(AwAutofillManagerDelegate delegate) { 1662 mAwAutofillManagerDelegate = delegate; 1663 delegate.init(mContentViewCore, mDIPScale); 1664 } 1665 1666 @CalledByNative 1667 private void didOverscroll(int deltaX, int deltaY) { 1668 if (mOverScrollGlow != null) { 1669 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY); 1670 } 1671 1672 mScrollOffsetManager.overscrollBy(deltaX, deltaY); 1673 1674 if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) { 1675 mContainerView.invalidate(); 1676 } 1677 } 1678 1679 // ------------------------------------------------------------------------------------------- 1680 // Helper methods 1681 // ------------------------------------------------------------------------------------------- 1682 1683 private void saveWebArchiveInternal(String path, final ValueCallback<String> callback) { 1684 if (path == null || mNativeAwContents == 0) { 1685 ThreadUtils.runOnUiThread(new Runnable() { 1686 @Override 1687 public void run() { 1688 callback.onReceiveValue(null); 1689 } 1690 }); 1691 } else { 1692 nativeGenerateMHTML(mNativeAwContents, path, callback); 1693 } 1694 } 1695 1696 /** 1697 * Try to generate a pathname for saving an MHTML archive. This roughly follows WebView's 1698 * autoname logic. 1699 */ 1700 private static String generateArchiveAutoNamePath(String originalUrl, String baseName) { 1701 String name = null; 1702 if (originalUrl != null && !originalUrl.isEmpty()) { 1703 try { 1704 String path = new URL(originalUrl).getPath(); 1705 int lastSlash = path.lastIndexOf('/'); 1706 if (lastSlash > 0) { 1707 name = path.substring(lastSlash + 1); 1708 } else { 1709 name = path; 1710 } 1711 } catch (MalformedURLException e) { 1712 // If it fails parsing the URL, we'll just rely on the default name below. 1713 } 1714 } 1715 1716 if (TextUtils.isEmpty(name)) name = "index"; 1717 1718 String testName = baseName + name + WEB_ARCHIVE_EXTENSION; 1719 if (!new File(testName).exists()) return testName; 1720 1721 for (int i = 1; i < 100; i++) { 1722 testName = baseName + name + "-" + i + WEB_ARCHIVE_EXTENSION; 1723 if (!new File(testName).exists()) return testName; 1724 } 1725 1726 Log.e(TAG, "Unable to auto generate archive name for path: " + baseName); 1727 return null; 1728 } 1729 1730 //-------------------------------------------------------------------------------------------- 1731 // Native methods 1732 //-------------------------------------------------------------------------------------------- 1733 1734 private static native int nativeInit(AwBrowserContext browserContext); 1735 private static native void nativeDestroy(int nativeAwContents); 1736 private static native void nativeSetAwDrawSWFunctionTable(int functionTablePointer); 1737 private static native void nativeSetAwDrawGLFunctionTable(int functionTablePointer); 1738 private static native int nativeGetAwDrawGLFunction(); 1739 private static native int nativeGetNativeInstanceCount(); 1740 private native void nativeSetJavaPeers(int nativeAwContents, AwContents awContents, 1741 AwWebContentsDelegate webViewWebContentsDelegate, 1742 AwContentsClientBridge contentsClientBridge, 1743 AwContentsIoThreadClient ioThreadClient, 1744 InterceptNavigationDelegate navigationInterceptionDelegate); 1745 private native int nativeGetWebContents(int nativeAwContents); 1746 1747 private native void nativeDocumentHasImages(int nativeAwContents, Message message); 1748 private native void nativeGenerateMHTML( 1749 int nativeAwContents, String path, ValueCallback<String> callback); 1750 1751 private native void nativeAddVisitedLinks(int nativeAwContents, String[] visitedLinks); 1752 private native boolean nativeOnDraw(int nativeAwContents, Canvas canvas, 1753 boolean isHardwareAccelerated, int scrollX, int ScrollY, 1754 int clipLeft, int clipTop, int clipRight, int clipBottom); 1755 private native void nativeFindAllAsync(int nativeAwContents, String searchString); 1756 private native void nativeFindNext(int nativeAwContents, boolean forward); 1757 private native void nativeClearMatches(int nativeAwContents); 1758 private native void nativeClearCache(int nativeAwContents, boolean includeDiskFiles); 1759 private native byte[] nativeGetCertificate(int nativeAwContents); 1760 1761 // Coordinates in desity independent pixels. 1762 private native void nativeRequestNewHitTestDataAt(int nativeAwContents, int x, int y); 1763 private native void nativeUpdateLastHitTestData(int nativeAwContents); 1764 1765 private native void nativeOnSizeChanged(int nativeAwContents, int w, int h, int ow, int oh); 1766 private native void nativeScrollTo(int nativeAwContents, int x, int y); 1767 private native void nativeSetVisibility(int nativeAwContents, boolean visible); 1768 private native void nativeOnAttachedToWindow(int nativeAwContents, int w, int h); 1769 private native void nativeOnDetachedFromWindow(int nativeAwContents); 1770 private native void nativeSetDipScale(int nativeAwContents, float dipScale); 1771 private native void nativeSetDisplayedPageScaleFactor(int nativeAwContents, 1772 float pageScaleFactor); 1773 1774 // Returns null if save state fails. 1775 private native byte[] nativeGetOpaqueState(int nativeAwContents); 1776 1777 // Returns false if restore state fails. 1778 private native boolean nativeRestoreFromOpaqueState(int nativeAwContents, byte[] state); 1779 1780 private native int nativeReleasePopupAwContents(int nativeAwContents); 1781 private native void nativeFocusFirstNode(int nativeAwContents); 1782 private native void nativeSetBackgroundColor(int nativeAwContents, int color); 1783 1784 private native int nativeGetAwDrawGLViewContext(int nativeAwContents); 1785 private native Picture nativeCapturePicture(int nativeAwContents); 1786 private native void nativeEnableOnNewPicture(int nativeAwContents, boolean enabled); 1787 1788 private native void nativeInvokeGeolocationCallback( 1789 int nativeAwContents, boolean value, String requestingFrame); 1790} 1791