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 5 package org.chromium.android_webview; 6 7 import android.app.Activity; 8 import android.content.ComponentCallbacks2; 9 import android.content.Context; 10 import android.content.res.Configuration; 11 import android.graphics.Bitmap; 12 import android.graphics.Canvas; 13 import android.graphics.Color; 14 import android.graphics.Paint; 15 import android.graphics.Picture; 16 import android.graphics.Rect; 17 import android.net.http.SslCertificate; 18 import android.os.AsyncTask; 19 import android.os.Build; 20 import android.os.Bundle; 21 import android.os.Message; 22 import android.text.TextUtils; 23 import android.util.Log; 24 import android.view.KeyEvent; 25 import android.view.MotionEvent; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.view.accessibility.AccessibilityEvent; 29 import android.view.accessibility.AccessibilityNodeInfo; 30 import android.view.accessibility.AccessibilityNodeProvider; 31 import android.view.inputmethod.EditorInfo; 32 import android.view.inputmethod.InputConnection; 33 import android.webkit.GeolocationPermissions; 34 import android.webkit.ValueCallback; 35 import android.widget.OverScroller; 36 37 import com.google.common.annotations.VisibleForTesting; 38 39 import org.chromium.base.CalledByNative; 40 import org.chromium.base.JNINamespace; 41 import org.chromium.base.ThreadUtils; 42 import org.chromium.components.navigation_interception.InterceptNavigationDelegate; 43 import org.chromium.components.navigation_interception.NavigationParams; 44 import org.chromium.content.browser.ContentSettings; 45 import org.chromium.content.browser.ContentViewClient; 46 import org.chromium.content.browser.ContentViewCore; 47 import org.chromium.content.browser.ContentViewStatics; 48 import org.chromium.content.browser.LoadUrlParams; 49 import org.chromium.content.browser.NavigationHistory; 50 import org.chromium.content.browser.PageTransitionTypes; 51 import org.chromium.content.common.CleanupReference; 52 import org.chromium.ui.base.ActivityWindowAndroid; 53 import org.chromium.ui.base.WindowAndroid; 54 import org.chromium.ui.gfx.DeviceDisplayInfo; 55 56 import java.io.File; 57 import java.lang.annotation.Annotation; 58 import java.net.MalformedURLException; 59 import java.net.URL; 60 import java.util.ArrayList; 61 import java.util.HashMap; 62 import java.util.List; 63 import java.util.concurrent.Callable; 64 65 /** 66 * Exposes the native AwContents class, and together these classes wrap the ContentViewCore 67 * and Browser components that are required to implement Android WebView API. This is the 68 * primary entry point for the WebViewProvider implementation; it holds a 1:1 object 69 * relationship with application WebView instances. 70 * (We define this class independent of the hidden WebViewProvider interfaces, to allow 71 * continuous build & test in the open source SDK-based tree). 72 */ 73 @JNINamespace("android_webview") 74 public class AwContents { 75 private static final String TAG = "AwContents"; 76 77 private static final String WEB_ARCHIVE_EXTENSION = ".mht"; 78 79 // Used to avoid enabling zooming in / out if resulting zooming will 80 // produce little visible difference. 81 private static final float ZOOM_CONTROLS_EPSILON = 0.007f; 82 83 /** 84 * WebKit hit test related data strcutre. These are used to implement 85 * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView. 86 * All values should be updated together. The native counterpart is 87 * AwHitTestData. 88 */ 89 public static class HitTestData { 90 // Used in getHitTestResult. 91 public int hitTestResultType; 92 public String hitTestResultExtraData; 93 94 // Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc). 95 public String href; 96 public String anchorText; 97 public String imgSrc; 98 } 99 100 /** 101 * Interface that consumers of {@link AwContents} must implement to allow the proper 102 * dispatching of view methods through the containing view. 103 */ 104 public interface InternalAccessDelegate extends ContentViewCore.InternalAccessDelegate { 105 106 /** 107 * @see View#overScrollBy(int, int, int, int, int, int, int, int, boolean); 108 */ overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)109 void overScrollBy(int deltaX, int deltaY, 110 int scrollX, int scrollY, 111 int scrollRangeX, int scrollRangeY, 112 int maxOverScrollX, int maxOverScrollY, 113 boolean isTouchEvent); 114 115 /** 116 * @see View#scrollTo(int, int) 117 */ super_scrollTo(int scrollX, int scrollY)118 void super_scrollTo(int scrollX, int scrollY); 119 120 /** 121 * @see View#setMeasuredDimension(int, int) 122 */ setMeasuredDimension(int measuredWidth, int measuredHeight)123 void setMeasuredDimension(int measuredWidth, int measuredHeight); 124 125 /** 126 * @see View#getScrollBarStyle() 127 */ super_getScrollBarStyle()128 int super_getScrollBarStyle(); 129 130 /** 131 * Requests a callback on the native DrawGL method (see getAwDrawGLFunction) 132 * if called from within onDraw, |canvas| will be non-null and hardware accelerated. 133 * otherwise, |canvas| will be null, and the container view itself will be hardware 134 * accelerated. 135 * 136 * @return false indicates the GL draw request was not accepted, and the caller 137 * should fallback to the SW path. 138 */ requestDrawGL(Canvas canvas)139 boolean requestDrawGL(Canvas canvas); 140 } 141 142 private long mNativeAwContents; 143 private final AwBrowserContext mBrowserContext; 144 private final ViewGroup mContainerView; 145 private ContentViewCore mContentViewCore; 146 private final AwContentsClient mContentsClient; 147 private final AwContentViewClient mContentViewClient; 148 private final AwContentsClientBridge mContentsClientBridge; 149 private final AwWebContentsDelegate mWebContentsDelegate; 150 private final AwContentsIoThreadClient mIoThreadClient; 151 private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate; 152 private final InternalAccessDelegate mInternalAccessAdapter; 153 private final AwLayoutSizer mLayoutSizer; 154 private final AwZoomControls mZoomControls; 155 private final AwScrollOffsetManager mScrollOffsetManager; 156 private OverScrollGlow mOverScrollGlow; 157 // This can be accessed on any thread after construction. See AwContentsIoThreadClient. 158 private final AwSettings mSettings; 159 private final ScrollAccessibilityHelper mScrollAccessibilityHelper; 160 161 private boolean mIsPaused; 162 private boolean mIsViewVisible; 163 private boolean mIsWindowVisible; 164 private boolean mIsAttachedToWindow; 165 private Bitmap mFavicon; 166 private boolean mHasRequestedVisitedHistoryFromClient; 167 // TODO(boliu): This should be in a global context, not per webview. 168 private final double mDIPScale; 169 170 // The base background color, i.e. not accounting for any CSS body from the current page. 171 private int mBaseBackgroundColor = Color.WHITE; 172 private int mLayerType = View.LAYER_TYPE_NONE; 173 174 // Must call nativeUpdateLastHitTestData first to update this before use. 175 private final HitTestData mPossiblyStaleHitTestData = new HitTestData(); 176 177 private final DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler; 178 179 // Bound method for suppling Picture instances to the AwContentsClient. Will be null if the 180 // picture listener API has not yet been enabled, or if it is using invalidation-only mode. 181 private Callable<Picture> mPictureListenerContentProvider; 182 183 private boolean mContainerViewFocused; 184 private boolean mWindowFocused; 185 186 private boolean mClearViewActive; 187 private boolean mPictureListenerEnabled; 188 189 // These come from the compositor and are updated immediately (in contrast to the values in 190 // ContentViewCore, which are updated at end of every frame). 191 private float mPageScaleFactor = 1.0f; 192 private float mContentWidthDip; 193 private float mContentHeightDip; 194 195 private AwAutofillManagerDelegate mAwAutofillManagerDelegate; 196 197 private ComponentCallbacks2 mComponentCallbacks; 198 199 private AwPdfExporter mAwPdfExporter; 200 201 // This flag indicates that ShouldOverrideUrlNavigation should be posted 202 // through the resourcethrottle. This is only used for popup windows. 203 private boolean mDeferredShouldOverrideUrlLoadingIsPendingForPopup; 204 205 private static final class DestroyRunnable implements Runnable { 206 private final long mNativeAwContents; DestroyRunnable(long nativeAwContents)207 private DestroyRunnable(long nativeAwContents) { 208 mNativeAwContents = nativeAwContents; 209 } 210 @Override run()211 public void run() { 212 // This is a no-op if not currently attached. 213 nativeOnDetachedFromWindow(mNativeAwContents); 214 nativeDestroy(mNativeAwContents); 215 } 216 } 217 218 // Reference to the active mNativeAwContents pointer while it is active use 219 // (ie before it is destroyed). 220 private CleanupReference mCleanupReference; 221 222 // A list of references to native pointers where the Java counterpart has been 223 // destroyed, but are held here because they are waiting for onDetachFromWindow 224 // to release GL resources. This is cleared inside onDetachFromWindow. 225 private List<CleanupReference> mPendingDetachCleanupReferences; 226 227 //-------------------------------------------------------------------------------------------- 228 private class IoThreadClientImpl implements AwContentsIoThreadClient { 229 // All methods are called on the IO thread. 230 231 @Override getCacheMode()232 public int getCacheMode() { 233 return mSettings.getCacheMode(); 234 } 235 236 @Override shouldInterceptRequest(final String url, boolean isMainFrame)237 public InterceptedRequestData shouldInterceptRequest(final String url, 238 boolean isMainFrame) { 239 InterceptedRequestData interceptedRequestData; 240 // Return the response directly if the url is default video poster url. 241 interceptedRequestData = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url); 242 if (interceptedRequestData != null) return interceptedRequestData; 243 244 interceptedRequestData = mContentsClient.shouldInterceptRequest(url); 245 246 if (interceptedRequestData == null) { 247 mContentsClient.getCallbackHelper().postOnLoadResource(url); 248 } 249 250 if (isMainFrame && interceptedRequestData != null && 251 interceptedRequestData.getData() == null) { 252 // In this case the intercepted URLRequest job will simulate an empty response 253 // which doesn't trigger the onReceivedError callback. For WebViewClassic 254 // compatibility we synthesize that callback. http://crbug.com/180950 255 mContentsClient.getCallbackHelper().postOnReceivedError( 256 ErrorCodeConversionHelper.ERROR_UNKNOWN, 257 null /* filled in by the glue layer */, url); 258 } 259 return interceptedRequestData; 260 } 261 262 @Override shouldBlockContentUrls()263 public boolean shouldBlockContentUrls() { 264 return !mSettings.getAllowContentAccess(); 265 } 266 267 @Override shouldBlockFileUrls()268 public boolean shouldBlockFileUrls() { 269 return !mSettings.getAllowFileAccess(); 270 } 271 272 @Override shouldBlockNetworkLoads()273 public boolean shouldBlockNetworkLoads() { 274 return mSettings.getBlockNetworkLoads(); 275 } 276 277 @Override onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength)278 public void onDownloadStart(String url, 279 String userAgent, 280 String contentDisposition, 281 String mimeType, 282 long contentLength) { 283 mContentsClient.getCallbackHelper().postOnDownloadStart(url, userAgent, 284 contentDisposition, mimeType, contentLength); 285 } 286 287 @Override newLoginRequest(String realm, String account, String args)288 public void newLoginRequest(String realm, String account, String args) { 289 mContentsClient.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args); 290 } 291 } 292 293 //-------------------------------------------------------------------------------------------- 294 // When the navigation is for a newly created WebView (i.e. a popup), intercept the navigation 295 // here for implementing shouldOverrideUrlLoading. This is to send the shouldOverrideUrlLoading 296 // callback to the correct WebViewClient that is associated with the WebView. 297 // Otherwise, use this delegate only to post onPageStarted messages. 298 // 299 // We are not using WebContentsObserver.didStartLoading because of stale URLs, out of order 300 // onPageStarted's and double onPageStarted's. 301 // 302 private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate { 303 @Override shouldIgnoreNavigation(NavigationParams navigationParams)304 public boolean shouldIgnoreNavigation(NavigationParams navigationParams) { 305 final String url = navigationParams.url; 306 boolean ignoreNavigation = false; 307 if (mDeferredShouldOverrideUrlLoadingIsPendingForPopup) { 308 mDeferredShouldOverrideUrlLoadingIsPendingForPopup = false; 309 // If this is used for all navigations in future, cases for application initiated 310 // load, redirect and backforward should also be filtered out. 311 if (!navigationParams.isPost) { 312 ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url); 313 } 314 } 315 // The shouldOverrideUrlLoading call might have resulted in posting messages to the 316 // UI thread. Using sendMessage here (instead of calling onPageStarted directly) 317 // will allow those to run in order. 318 if (!ignoreNavigation) { 319 mContentsClient.getCallbackHelper().postOnPageStarted(url); 320 } 321 return ignoreNavigation; 322 } 323 } 324 325 //-------------------------------------------------------------------------------------------- 326 private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate { 327 @Override requestLayout()328 public void requestLayout() { 329 mContainerView.requestLayout(); 330 } 331 332 @Override setMeasuredDimension(int measuredWidth, int measuredHeight)333 public void setMeasuredDimension(int measuredWidth, int measuredHeight) { 334 mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight); 335 } 336 337 @Override setFixedLayoutSize(int widthDip, int heightDip)338 public void setFixedLayoutSize(int widthDip, int heightDip) { 339 if (mNativeAwContents == 0) return; 340 nativeSetFixedLayoutSize(mNativeAwContents, widthDip, heightDip); 341 } 342 343 @Override isLayoutParamsHeightWrapContent()344 public boolean isLayoutParamsHeightWrapContent() { 345 return mContainerView.getLayoutParams() != null && 346 mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT; 347 } 348 } 349 350 //-------------------------------------------------------------------------------------------- 351 private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate { 352 @Override overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, boolean isTouchEvent)353 public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, 354 int scrollRangeX, int scrollRangeY, boolean isTouchEvent) { 355 mInternalAccessAdapter.overScrollBy(deltaX, deltaY, scrollX, scrollY, 356 scrollRangeX, scrollRangeY, 0, 0, isTouchEvent); 357 } 358 359 @Override scrollContainerViewTo(int x, int y)360 public void scrollContainerViewTo(int x, int y) { 361 mInternalAccessAdapter.super_scrollTo(x, y); 362 } 363 364 @Override scrollNativeTo(int x, int y)365 public void scrollNativeTo(int x, int y) { 366 if (mNativeAwContents == 0) return; 367 nativeScrollTo(mNativeAwContents, x, y); 368 } 369 370 @Override getContainerViewScrollX()371 public int getContainerViewScrollX() { 372 return mContainerView.getScrollX(); 373 } 374 375 @Override getContainerViewScrollY()376 public int getContainerViewScrollY() { 377 return mContainerView.getScrollY(); 378 } 379 380 @Override invalidate()381 public void invalidate() { 382 mContainerView.invalidate(); 383 } 384 } 385 386 //-------------------------------------------------------------------------------------------- 387 private class AwGestureStateListener implements ContentViewCore.GestureStateListener { 388 @Override onPinchGestureStart()389 public void onPinchGestureStart() { 390 // While it's possible to re-layout the view during a pinch gesture, the effect is very 391 // janky (especially that the page scale update notification comes from the renderer 392 // main thread, not from the impl thread, so it's usually out of sync with what's on 393 // screen). It's also quite expensive to do a re-layout, so we simply postpone 394 // re-layout for the duration of the gesture. This is compatible with what 395 // WebViewClassic does. 396 mLayoutSizer.freezeLayoutRequests(); 397 } 398 399 @Override onPinchGestureEnd()400 public void onPinchGestureEnd() { 401 mLayoutSizer.unfreezeLayoutRequests(); 402 } 403 404 @Override onFlingStartGesture(int velocityX, int velocityY)405 public void onFlingStartGesture(int velocityX, int velocityY) { 406 mScrollOffsetManager.onFlingStartGesture(velocityX, velocityY); 407 } 408 409 @Override onFlingCancelGesture()410 public void onFlingCancelGesture() { 411 mScrollOffsetManager.onFlingCancelGesture(); 412 } 413 414 @Override onUnhandledFlingStartEvent()415 public void onUnhandledFlingStartEvent() { 416 mScrollOffsetManager.onUnhandledFlingStartEvent(); 417 } 418 419 @Override onScrollUpdateGestureConsumed()420 public void onScrollUpdateGestureConsumed() { 421 mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback(); 422 } 423 } 424 425 //-------------------------------------------------------------------------------------------- 426 private class AwComponentCallbacks implements ComponentCallbacks2 { 427 @Override onTrimMemory(int level)428 public void onTrimMemory(int level) { 429 if (mNativeAwContents == 0) return; 430 nativeTrimMemory(mNativeAwContents, level); 431 } 432 433 @Override onLowMemory()434 public void onLowMemory() {} 435 436 @Override onConfigurationChanged(Configuration configuration)437 public void onConfigurationChanged(Configuration configuration) {} 438 }; 439 440 //-------------------------------------------------------------------------------------------- 441 private class AwLayoutChangeListener implements View.OnLayoutChangeListener { 442 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)443 public void onLayoutChange(View v, int left, int top, int right, int bottom, 444 int oldLeft, int oldTop, int oldRight, int oldBottom) { 445 assert v == mContainerView; 446 mLayoutSizer.onLayoutChange(); 447 } 448 } 449 450 /** 451 * @param browserContext the browsing context to associate this view contents with. 452 * @param containerView the view-hierarchy item this object will be bound to. 453 * @param internalAccessAdapter to access private methods on containerView. 454 * @param contentsClient will receive API callbacks from this WebView Contents. 455 * @param awSettings AwSettings instance used to configure the AwContents. 456 * 457 * This constructor uses the default view sizing policy. 458 */ AwContents(AwBrowserContext browserContext, ViewGroup containerView, InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, AwSettings awSettings)459 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, 460 InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, 461 AwSettings awSettings) { 462 this(browserContext, containerView, internalAccessAdapter, contentsClient, awSettings, 463 new AwLayoutSizer()); 464 } 465 466 /** 467 * @param layoutSizer the AwLayoutSizer instance implementing the sizing policy for the view. 468 * 469 * This version of the constructor is used in test code to inject test versions of the above 470 * documented classes. 471 */ AwContents(AwBrowserContext browserContext, ViewGroup containerView, InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, AwSettings settings, AwLayoutSizer layoutSizer)472 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, 473 InternalAccessDelegate internalAccessAdapter, AwContentsClient contentsClient, 474 AwSettings settings, AwLayoutSizer layoutSizer) { 475 mBrowserContext = browserContext; 476 mContainerView = containerView; 477 mInternalAccessAdapter = internalAccessAdapter; 478 mContentsClient = contentsClient; 479 mContentViewClient = new AwContentViewClient(contentsClient, settings); 480 mLayoutSizer = layoutSizer; 481 mSettings = settings; 482 mDIPScale = DeviceDisplayInfo.create(mContainerView.getContext()).getDIPScale(); 483 mLayoutSizer.setDelegate(new AwLayoutSizerDelegate()); 484 mLayoutSizer.setDIPScale(mDIPScale); 485 mWebContentsDelegate = new AwWebContentsDelegateAdapter(contentsClient, mContainerView); 486 mContentsClientBridge = new AwContentsClientBridge(contentsClient); 487 mZoomControls = new AwZoomControls(this); 488 mIoThreadClient = new IoThreadClientImpl(); 489 mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(); 490 491 AwSettings.ZoomSupportChangeListener zoomListener = 492 new AwSettings.ZoomSupportChangeListener() { 493 @Override 494 public void onGestureZoomSupportChanged( 495 boolean supportsDoubleTapZoom, boolean supportsMultiTouchZoom) { 496 mContentViewCore.updateDoubleTapSupport(supportsDoubleTapZoom); 497 mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom); 498 } 499 500 }; 501 mSettings.setZoomListener(zoomListener); 502 mDefaultVideoPosterRequestHandler = new DefaultVideoPosterRequestHandler(mContentsClient); 503 mSettings.setDefaultVideoPosterURL( 504 mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL()); 505 mSettings.setDIPScale(mDIPScale); 506 mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate(), 507 new OverScroller(mContainerView.getContext())); 508 mScrollAccessibilityHelper = new ScrollAccessibilityHelper(mContainerView); 509 510 setOverScrollMode(mContainerView.getOverScrollMode()); 511 setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle()); 512 mContainerView.addOnLayoutChangeListener(new AwLayoutChangeListener()); 513 514 setNewAwContents(nativeInit(mBrowserContext)); 515 516 onVisibilityChanged(mContainerView, mContainerView.getVisibility()); 517 onWindowVisibilityChanged(mContainerView.getWindowVisibility()); 518 } 519 createAndInitializeContentViewCore(ViewGroup containerView, InternalAccessDelegate internalDispatcher, int nativeWebContents, ContentViewCore.GestureStateListener pinchGestureStateListener, ContentViewClient contentViewClient, ContentViewCore.ZoomControlsDelegate zoomControlsDelegate)520 private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView, 521 InternalAccessDelegate internalDispatcher, int nativeWebContents, 522 ContentViewCore.GestureStateListener pinchGestureStateListener, 523 ContentViewClient contentViewClient, 524 ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) { 525 Context context = containerView.getContext(); 526 ContentViewCore contentViewCore = new ContentViewCore(context); 527 contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, 528 context instanceof Activity ? 529 new ActivityWindowAndroid((Activity) context) : 530 new WindowAndroid(context.getApplicationContext())); 531 contentViewCore.setGestureStateListener(pinchGestureStateListener); 532 contentViewCore.setContentViewClient(contentViewClient); 533 contentViewCore.setZoomControlsDelegate(zoomControlsDelegate); 534 return contentViewCore; 535 } 536 537 /** 538 * Common initialization routine for adopting a native AwContents instance into this 539 * java instance. 540 * 541 * TAKE CARE! This method can get called multiple times per java instance. Code accordingly. 542 * ^^^^^^^^^ See the native class declaration for more details on relative object lifetimes. 543 */ setNewAwContents(long newAwContentsPtr)544 private void setNewAwContents(long newAwContentsPtr) { 545 if (mNativeAwContents != 0) { 546 destroy(); 547 mContentViewCore = null; 548 } 549 550 assert mNativeAwContents == 0 && mCleanupReference == null && mContentViewCore == null; 551 552 mNativeAwContents = newAwContentsPtr; 553 // TODO(joth): when the native and java counterparts of AwBrowserContext are hooked up to 554 // each other, we should update |mBrowserContext| according to the newly received native 555 // WebContent's browser context. 556 557 // The native side object has been bound to this java instance, so now is the time to 558 // bind all the native->java relationships. 559 mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents)); 560 561 int nativeWebContents = nativeGetWebContents(mNativeAwContents); 562 mContentViewCore = createAndInitializeContentViewCore( 563 mContainerView, mInternalAccessAdapter, nativeWebContents, 564 new AwGestureStateListener(), mContentViewClient, mZoomControls); 565 nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge, 566 mIoThreadClient, mInterceptNavigationDelegate); 567 mContentsClient.installWebContentsObserver(mContentViewCore); 568 mSettings.setWebContents(nativeWebContents); 569 nativeSetDipScale(mNativeAwContents, (float) mDIPScale); 570 updateGlobalVisibleRect(); 571 572 // The only call to onShow. onHide should never be called. 573 mContentViewCore.onShow(); 574 } 575 576 /** 577 * Called on the "source" AwContents that is opening the popup window to 578 * provide the AwContents to host the pop up content. 579 */ supplyContentsForPopup(AwContents newContents)580 public void supplyContentsForPopup(AwContents newContents) { 581 int popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents); 582 if (popupNativeAwContents == 0) { 583 Log.w(TAG, "Popup WebView bind failed: no pending content."); 584 if (newContents != null) newContents.destroy(); 585 return; 586 } 587 if (newContents == null) { 588 nativeDestroy(popupNativeAwContents); 589 return; 590 } 591 592 newContents.receivePopupContents(popupNativeAwContents); 593 } 594 595 // Recap: supplyContentsForPopup() is called on the parent window's content, this method is 596 // called on the popup window's content. receivePopupContents(int popupNativeAwContents)597 private void receivePopupContents(int popupNativeAwContents) { 598 mDeferredShouldOverrideUrlLoadingIsPendingForPopup = true; 599 // Save existing view state. 600 final boolean wasAttached = mIsAttachedToWindow; 601 final boolean wasViewVisible = mIsViewVisible; 602 final boolean wasWindowVisible = mIsWindowVisible; 603 final boolean wasPaused = mIsPaused; 604 final boolean wasFocused = mContainerViewFocused; 605 final boolean wasWindowFocused = mWindowFocused; 606 607 // Properly clean up existing mContentViewCore and mNativeAwContents. 608 if (wasFocused) onFocusChanged(false, 0, null); 609 if (wasWindowFocused) onWindowFocusChanged(false); 610 if (wasViewVisible) setViewVisibilityInternal(false); 611 if (wasWindowVisible) setWindowVisibilityInternal(false); 612 if (!wasPaused) onPause(); 613 // Not calling onDetachedFromWindow here because native code requires GL context to release 614 // GL resources. This case is properly handled when destroy is called while still attached 615 // to window. 616 617 setNewAwContents(popupNativeAwContents); 618 619 // Finally refresh all view state for mContentViewCore and mNativeAwContents. 620 if (!wasPaused) onResume(); 621 if (wasAttached) onAttachedToWindow(); 622 onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); 623 if (wasWindowVisible) setWindowVisibilityInternal(true); 624 if (wasViewVisible) setViewVisibilityInternal(true); 625 if (wasWindowFocused) onWindowFocusChanged(wasWindowFocused); 626 if (wasFocused) onFocusChanged(true, 0, null); 627 } 628 629 /** 630 * Deletes the native counterpart of this object. Normally happens immediately, 631 * but maybe deferred until the appropriate time for GL resource cleanup. Either way 632 * this is transparent to the caller: after this function returns the object is 633 * effectively dead and methods are no-ops. 634 */ destroy()635 public void destroy() { 636 if (mCleanupReference != null) { 637 // We explicitly do not null out the mContentViewCore reference here 638 // because ContentViewCore already has code to deal with the case 639 // methods are called on it after it's been destroyed, and other 640 // code relies on AwContents.mContentViewCore to be non-null. 641 mContentViewCore.destroy(); 642 mNativeAwContents = 0; 643 644 // We cannot destroy immediately if we are still attached to the window. 645 // Instead if we make sure to null out the native pointer so there is no more native 646 // calls, and delay the actual destroy until onDetachedFromWindow. 647 if (mIsAttachedToWindow) { 648 if (mPendingDetachCleanupReferences == null) { 649 mPendingDetachCleanupReferences = new ArrayList<CleanupReference>(); 650 } 651 mPendingDetachCleanupReferences.add(mCleanupReference); 652 } else { 653 mCleanupReference.cleanupNow(); 654 } 655 mCleanupReference = null; 656 } 657 658 assert !mContentViewCore.isAlive(); 659 assert mNativeAwContents == 0; 660 } 661 662 @VisibleForTesting getContentViewCore()663 public ContentViewCore getContentViewCore() { 664 return mContentViewCore; 665 } 666 667 // Can be called from any thread. getSettings()668 public AwSettings getSettings() { 669 return mSettings; 670 } 671 getPdfExporter()672 public AwPdfExporter getPdfExporter() { 673 // mNativeAwContents can be null, due to destroy(). 674 if (mNativeAwContents == 0) { 675 return null; 676 } 677 if (mAwPdfExporter == null) { 678 mAwPdfExporter = new AwPdfExporter(mContainerView); 679 nativeCreatePdfExporter(mNativeAwContents, mAwPdfExporter); 680 } 681 return mAwPdfExporter; 682 } 683 setAwDrawSWFunctionTable(int functionTablePointer)684 public static void setAwDrawSWFunctionTable(int functionTablePointer) { 685 nativeSetAwDrawSWFunctionTable(functionTablePointer); 686 } 687 setAwDrawGLFunctionTable(int functionTablePointer)688 public static void setAwDrawGLFunctionTable(int functionTablePointer) { 689 nativeSetAwDrawGLFunctionTable(functionTablePointer); 690 } 691 getAwDrawGLFunction()692 public static int getAwDrawGLFunction() { 693 return nativeGetAwDrawGLFunction(); 694 } 695 setShouldDownloadFavicons()696 public static void setShouldDownloadFavicons() { 697 nativeSetShouldDownloadFavicons(); 698 } 699 700 /** 701 * Intended for test code. 702 * @return the number of native instances of this class. 703 */ 704 @VisibleForTesting getNativeInstanceCount()705 public static int getNativeInstanceCount() { 706 return nativeGetNativeInstanceCount(); 707 } 708 getAwDrawGLViewContext()709 public int getAwDrawGLViewContext() { 710 // Only called during early construction, so client should not have had a chance to 711 // call destroy yet. 712 assert mNativeAwContents != 0; 713 714 // Using the native pointer as the returned viewContext. This is matched by the 715 // reinterpret_cast back to BrowserViewRenderer pointer in the native DrawGLFunction. 716 return nativeGetAwDrawGLViewContext(mNativeAwContents); 717 } 718 719 // This is only to avoid heap allocations inside updateGLobalVisibleRect. It should treated 720 // as a local variable in the function and not used anywhere else. 721 private static final Rect sLocalGlobalVisibleRect = new Rect(); 722 723 @CalledByNative updateGlobalVisibleRect()724 private void updateGlobalVisibleRect() { 725 if (mNativeAwContents == 0) return; 726 if (!mContainerView.getGlobalVisibleRect(sLocalGlobalVisibleRect)) { 727 sLocalGlobalVisibleRect.setEmpty(); 728 } 729 730 nativeSetGlobalVisibleRect(mNativeAwContents, sLocalGlobalVisibleRect.left, 731 sLocalGlobalVisibleRect.top, sLocalGlobalVisibleRect.right, 732 sLocalGlobalVisibleRect.bottom); 733 } 734 735 //-------------------------------------------------------------------------------------------- 736 // WebView[Provider] method implementations (where not provided by ContentViewCore) 737 //-------------------------------------------------------------------------------------------- 738 739 // Only valid within onDraw(). 740 private final Rect mClipBoundsTemporary = new Rect(); 741 onDraw(Canvas canvas)742 public void onDraw(Canvas canvas) { 743 if (mNativeAwContents == 0) { 744 canvas.drawColor(getEffectiveBackgroundColor()); 745 return; 746 } 747 748 mScrollOffsetManager.syncScrollOffsetFromOnDraw(); 749 canvas.getClipBounds(mClipBoundsTemporary); 750 751 if (mClearViewActive) { 752 canvas.drawColor(getEffectiveBackgroundColor()); 753 } else if (!nativeOnDraw(mNativeAwContents, canvas, canvas.isHardwareAccelerated(), 754 mContainerView.getScrollX(), mContainerView.getScrollY(), 755 mClipBoundsTemporary.left, mClipBoundsTemporary.top, 756 mClipBoundsTemporary.right, mClipBoundsTemporary.bottom)) { 757 Log.w(TAG, "nativeOnDraw failed; clearing to background color."); 758 canvas.drawColor(getEffectiveBackgroundColor()); 759 } 760 761 if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas, 762 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 763 mScrollOffsetManager.computeMaximumVerticalScrollOffset())) { 764 mContainerView.invalidate(); 765 } 766 } 767 onMeasure(int widthMeasureSpec, int heightMeasureSpec)768 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 769 mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec); 770 } 771 getContentHeightCss()772 public int getContentHeightCss() { 773 return (int) Math.ceil(mContentHeightDip); 774 } 775 getContentWidthCss()776 public int getContentWidthCss() { 777 return (int) Math.ceil(mContentWidthDip); 778 } 779 capturePicture()780 public Picture capturePicture() { 781 if (mNativeAwContents == 0) return null; 782 return new AwPicture(nativeCapturePicture(mNativeAwContents, 783 mScrollOffsetManager.computeHorizontalScrollRange(), 784 mScrollOffsetManager.computeVerticalScrollRange())); 785 } 786 clearView()787 public void clearView() { 788 mClearViewActive = true; 789 syncOnNewPictureStateToNative(); 790 mContainerView.invalidate(); 791 } 792 793 /** 794 * Enable the onNewPicture callback. 795 * @param enabled Flag to enable the callback. 796 * @param invalidationOnly Flag to call back only on invalidation without providing a picture. 797 */ enableOnNewPicture(boolean enabled, boolean invalidationOnly)798 public void enableOnNewPicture(boolean enabled, boolean invalidationOnly) { 799 if (mNativeAwContents == 0) return; 800 if (invalidationOnly) { 801 mPictureListenerContentProvider = null; 802 } else if (enabled && mPictureListenerContentProvider == null) { 803 mPictureListenerContentProvider = new Callable<Picture>() { 804 @Override 805 public Picture call() { 806 return capturePicture(); 807 } 808 }; 809 } 810 mPictureListenerEnabled = enabled; 811 syncOnNewPictureStateToNative(); 812 } 813 syncOnNewPictureStateToNative()814 private void syncOnNewPictureStateToNative() { 815 if (mNativeAwContents == 0) return; 816 nativeEnableOnNewPicture(mNativeAwContents, mPictureListenerEnabled || mClearViewActive); 817 } 818 findAllAsync(String searchString)819 public void findAllAsync(String searchString) { 820 if (mNativeAwContents == 0) return; 821 nativeFindAllAsync(mNativeAwContents, searchString); 822 } 823 findNext(boolean forward)824 public void findNext(boolean forward) { 825 if (mNativeAwContents == 0) return; 826 nativeFindNext(mNativeAwContents, forward); 827 } 828 clearMatches()829 public void clearMatches() { 830 if (mNativeAwContents == 0) return; 831 nativeClearMatches(mNativeAwContents); 832 } 833 834 /** 835 * @return load progress of the WebContents. 836 */ getMostRecentProgress()837 public int getMostRecentProgress() { 838 // WebContentsDelegateAndroid conveniently caches the most recent notified value for us. 839 return mWebContentsDelegate.getMostRecentProgress(); 840 } 841 getFavicon()842 public Bitmap getFavicon() { 843 return mFavicon; 844 } 845 requestVisitedHistoryFromClient()846 private void requestVisitedHistoryFromClient() { 847 ValueCallback<String[]> callback = new ValueCallback<String[]>() { 848 @Override 849 public void onReceiveValue(final String[] value) { 850 ThreadUtils.runOnUiThread(new Runnable() { 851 @Override 852 public void run() { 853 if (mNativeAwContents == 0) return; 854 nativeAddVisitedLinks(mNativeAwContents, value); 855 } 856 }); 857 } 858 }; 859 mContentsClient.getVisitedHistory(callback); 860 } 861 862 /** 863 * Load url without fixing up the url string. Consumers of ContentView are responsible for 864 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left 865 * off during user input). 866 * 867 * @param params Parameters for this load. 868 */ loadUrl(LoadUrlParams params)869 public void loadUrl(LoadUrlParams params) { 870 if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA && 871 !params.isBaseUrlDataScheme()) { 872 // This allows data URLs with a non-data base URL access to file:///android_asset/ and 873 // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also 874 // allow access to file:// URLs (subject to OS level permission checks). 875 params.setCanLoadLocalResources(true); 876 } 877 878 // If we are reloading the same url, then set transition type as reload. 879 if (params.getUrl() != null && 880 params.getUrl().equals(mContentViewCore.getUrl()) && 881 params.getTransitionType() == PageTransitionTypes.PAGE_TRANSITION_LINK) { 882 params.setTransitionType(PageTransitionTypes.PAGE_TRANSITION_RELOAD); 883 } 884 params.setTransitionType( 885 params.getTransitionType() | PageTransitionTypes.PAGE_TRANSITION_FROM_API); 886 887 // For WebView, always use the user agent override, which is set 888 // every time the user agent in AwSettings is modified. 889 params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE); 890 891 // We don't pass extra headers to the content layer, as WebViewClassic 892 // was adding them in a very narrow set of conditions. See http://crbug.com/306873 893 if (mNativeAwContents != 0) { 894 nativeSetExtraHeadersForUrl( 895 mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString()); 896 } 897 params.setExtraHeaders(new HashMap<String, String>()); 898 899 mContentViewCore.loadUrl(params); 900 901 // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit. 902 // Chromium does not use this use code path and the best emulation of this behavior to call 903 // request visited links once on the first URL load of the WebView. 904 if (!mHasRequestedVisitedHistoryFromClient) { 905 mHasRequestedVisitedHistoryFromClient = true; 906 requestVisitedHistoryFromClient(); 907 } 908 909 if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA && 910 params.getBaseUrl() != null) { 911 // Data loads with a base url will be resolved in Blink, and not cause an onPageStarted 912 // event to be sent. Sending the callback directly from here. 913 mContentsClient.getCallbackHelper().postOnPageStarted(params.getBaseUrl()); 914 } 915 } 916 917 /** 918 * Get the URL of the current page. 919 * 920 * @return The URL of the current page or null if it's empty. 921 */ getUrl()922 public String getUrl() { 923 String url = mContentViewCore.getUrl(); 924 if (url == null || url.trim().isEmpty()) return null; 925 return url; 926 } 927 requestFocus()928 public void requestFocus() { 929 if (mNativeAwContents == 0) return; 930 if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) { 931 nativeFocusFirstNode(mNativeAwContents); 932 } 933 } 934 setBackgroundColor(int color)935 public void setBackgroundColor(int color) { 936 mBaseBackgroundColor = color; 937 if (mNativeAwContents != 0) nativeSetBackgroundColor(mNativeAwContents, color); 938 } 939 940 /** 941 * @see android.view.View#setLayerType() 942 */ setLayerType(int layerType, Paint paint)943 public void setLayerType(int layerType, Paint paint) { 944 mLayerType = layerType; 945 updateHardwareAcceleratedFeaturesToggle(); 946 } 947 updateHardwareAcceleratedFeaturesToggle()948 private void updateHardwareAcceleratedFeaturesToggle() { 949 mSettings.setEnableSupportedHardwareAcceleratedFeatures( 950 mIsAttachedToWindow && mContainerView.isHardwareAccelerated() && 951 (mLayerType == View.LAYER_TYPE_NONE || mLayerType == View.LAYER_TYPE_HARDWARE)); 952 } 953 954 getEffectiveBackgroundColor()955 private int getEffectiveBackgroundColor() { 956 // Do not ask the ContentViewCore for the background color, as it will always 957 // report white prior to initial navigation or post destruction, whereas we want 958 // to use the client supplied base value in those cases. 959 if (mNativeAwContents == 0 || !mContentsClient.isCachedRendererBackgroundColorValid()) { 960 return mBaseBackgroundColor; 961 } 962 return mContentsClient.getCachedRendererBackgroundColor(); 963 } 964 isMultiTouchZoomSupported()965 public boolean isMultiTouchZoomSupported() { 966 return mSettings.supportsMultiTouchZoom(); 967 } 968 getZoomControlsForTest()969 public View getZoomControlsForTest() { 970 return mZoomControls.getZoomControlsViewForTest(); 971 } 972 973 /** 974 * @see ContentViewCore#getContentSettings() 975 */ getContentSettings()976 public ContentSettings getContentSettings() { 977 return mContentViewCore.getContentSettings(); 978 } 979 980 /** 981 * @see View#setOverScrollMode(int) 982 */ setOverScrollMode(int mode)983 public void setOverScrollMode(int mode) { 984 if (mode != View.OVER_SCROLL_NEVER) { 985 mOverScrollGlow = new OverScrollGlow(mContainerView); 986 } else { 987 mOverScrollGlow = null; 988 } 989 } 990 991 // TODO(mkosiba): In WebViewClassic these appear in some of the scroll extent calculation 992 // methods but toggling them has no visiual effect on the content (in other words the scrolling 993 // code behaves as if the scrollbar-related padding is in place but the onDraw code doesn't 994 // take that into consideration). 995 // http://crbug.com/269032 996 private boolean mOverlayHorizontalScrollbar = true; 997 private boolean mOverlayVerticalScrollbar = false; 998 999 /** 1000 * @see View#setScrollBarStyle(int) 1001 */ setScrollBarStyle(int style)1002 public void setScrollBarStyle(int style) { 1003 if (style == View.SCROLLBARS_INSIDE_OVERLAY 1004 || style == View.SCROLLBARS_OUTSIDE_OVERLAY) { 1005 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true; 1006 } else { 1007 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false; 1008 } 1009 } 1010 1011 /** 1012 * @see View#setHorizontalScrollbarOverlay(boolean) 1013 */ setHorizontalScrollbarOverlay(boolean overlay)1014 public void setHorizontalScrollbarOverlay(boolean overlay) { 1015 mOverlayHorizontalScrollbar = overlay; 1016 } 1017 1018 /** 1019 * @see View#setVerticalScrollbarOverlay(boolean) 1020 */ setVerticalScrollbarOverlay(boolean overlay)1021 public void setVerticalScrollbarOverlay(boolean overlay) { 1022 mOverlayVerticalScrollbar = overlay; 1023 } 1024 1025 /** 1026 * @see View#overlayHorizontalScrollbar() 1027 */ overlayHorizontalScrollbar()1028 public boolean overlayHorizontalScrollbar() { 1029 return mOverlayHorizontalScrollbar; 1030 } 1031 1032 /** 1033 * @see View#overlayVerticalScrollbar() 1034 */ overlayVerticalScrollbar()1035 public boolean overlayVerticalScrollbar() { 1036 return mOverlayVerticalScrollbar; 1037 } 1038 1039 /** 1040 * Called by the embedder when the scroll offset of the containing view has changed. 1041 * @see View#onScrollChanged(int,int) 1042 */ onContainerViewScrollChanged(int l, int t, int oldl, int oldt)1043 public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) { 1044 // A side-effect of View.onScrollChanged is that the scroll accessibility event being sent 1045 // by the base class implementation. This is completely hidden from the base classes and 1046 // cannot be prevented, which is why we need the code below. 1047 mScrollAccessibilityHelper.removePostedViewScrolledAccessibilityEventCallback(); 1048 mScrollOffsetManager.onContainerViewScrollChanged(l, t); 1049 } 1050 1051 /** 1052 * Called by the embedder when the containing view is to be scrolled or overscrolled. 1053 * @see View#onOverScrolled(int,int,int,int) 1054 */ onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)1055 public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, 1056 boolean clampedY) { 1057 int oldX = mContainerView.getScrollX(); 1058 int oldY = mContainerView.getScrollY(); 1059 1060 mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); 1061 1062 if (mOverScrollGlow != null) { 1063 mOverScrollGlow.pullGlow(mContainerView.getScrollX(), mContainerView.getScrollY(), 1064 oldX, oldY, 1065 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 1066 mScrollOffsetManager.computeMaximumVerticalScrollOffset()); 1067 } 1068 } 1069 1070 /** 1071 * @see android.webkit.WebView#requestChildRectangleOnScreen(View, Rect, boolean) 1072 */ requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)1073 public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) { 1074 return mScrollOffsetManager.requestChildRectangleOnScreen( 1075 child.getLeft() - child.getScrollX(), child.getTop() - child.getScrollY(), 1076 rect, immediate); 1077 } 1078 1079 /** 1080 * @see View.computeScroll() 1081 */ computeScroll()1082 public void computeScroll() { 1083 mScrollOffsetManager.computeScrollAndAbsorbGlow(mOverScrollGlow); 1084 } 1085 1086 /** 1087 * @see View#computeHorizontalScrollRange() 1088 */ computeHorizontalScrollRange()1089 public int computeHorizontalScrollRange() { 1090 return mScrollOffsetManager.computeHorizontalScrollRange(); 1091 } 1092 1093 /** 1094 * @see View#computeHorizontalScrollOffset() 1095 */ computeHorizontalScrollOffset()1096 public int computeHorizontalScrollOffset() { 1097 return mScrollOffsetManager.computeHorizontalScrollOffset(); 1098 } 1099 1100 /** 1101 * @see View#computeVerticalScrollRange() 1102 */ computeVerticalScrollRange()1103 public int computeVerticalScrollRange() { 1104 return mScrollOffsetManager.computeVerticalScrollRange(); 1105 } 1106 1107 /** 1108 * @see View#computeVerticalScrollOffset() 1109 */ computeVerticalScrollOffset()1110 public int computeVerticalScrollOffset() { 1111 return mScrollOffsetManager.computeVerticalScrollOffset(); 1112 } 1113 1114 /** 1115 * @see View#computeVerticalScrollExtent() 1116 */ computeVerticalScrollExtent()1117 public int computeVerticalScrollExtent() { 1118 return mScrollOffsetManager.computeVerticalScrollExtent(); 1119 } 1120 1121 /** 1122 * @see android.webkit.WebView#stopLoading() 1123 */ stopLoading()1124 public void stopLoading() { 1125 mContentViewCore.stopLoading(); 1126 } 1127 1128 /** 1129 * @see android.webkit.WebView#reload() 1130 */ reload()1131 public void reload() { 1132 mContentViewCore.reload(true); 1133 } 1134 1135 /** 1136 * @see android.webkit.WebView#canGoBack() 1137 */ canGoBack()1138 public boolean canGoBack() { 1139 return mContentViewCore.canGoBack(); 1140 } 1141 1142 /** 1143 * @see android.webkit.WebView#goBack() 1144 */ goBack()1145 public void goBack() { 1146 mContentViewCore.goBack(); 1147 } 1148 1149 /** 1150 * @see android.webkit.WebView#canGoForward() 1151 */ canGoForward()1152 public boolean canGoForward() { 1153 return mContentViewCore.canGoForward(); 1154 } 1155 1156 /** 1157 * @see android.webkit.WebView#goForward() 1158 */ goForward()1159 public void goForward() { 1160 mContentViewCore.goForward(); 1161 } 1162 1163 /** 1164 * @see android.webkit.WebView#canGoBackOrForward(int) 1165 */ canGoBackOrForward(int steps)1166 public boolean canGoBackOrForward(int steps) { 1167 return mContentViewCore.canGoToOffset(steps); 1168 } 1169 1170 /** 1171 * @see android.webkit.WebView#goBackOrForward(int) 1172 */ goBackOrForward(int steps)1173 public void goBackOrForward(int steps) { 1174 mContentViewCore.goToOffset(steps); 1175 } 1176 1177 /** 1178 * @see android.webkit.WebView#pauseTimers() 1179 */ pauseTimers()1180 public void pauseTimers() { 1181 ContentViewStatics.setWebKitSharedTimersSuspended(true); 1182 } 1183 1184 /** 1185 * @see android.webkit.WebView#resumeTimers() 1186 */ resumeTimers()1187 public void resumeTimers() { 1188 ContentViewStatics.setWebKitSharedTimersSuspended(false); 1189 } 1190 1191 /** 1192 * @see android.webkit.WebView#onPause() 1193 */ onPause()1194 public void onPause() { 1195 if (mIsPaused || mNativeAwContents == 0) return; 1196 mIsPaused = true; 1197 nativeSetIsPaused(mNativeAwContents, mIsPaused); 1198 } 1199 1200 /** 1201 * @see android.webkit.WebView#onResume() 1202 */ onResume()1203 public void onResume() { 1204 if (!mIsPaused || mNativeAwContents == 0) return; 1205 mIsPaused = false; 1206 nativeSetIsPaused(mNativeAwContents, mIsPaused); 1207 } 1208 1209 /** 1210 * @see android.webkit.WebView#isPaused() 1211 */ isPaused()1212 public boolean isPaused() { 1213 return mIsPaused; 1214 } 1215 1216 /** 1217 * @see android.webkit.WebView#onCreateInputConnection(EditorInfo) 1218 */ onCreateInputConnection(EditorInfo outAttrs)1219 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 1220 return mContentViewCore.onCreateInputConnection(outAttrs); 1221 } 1222 1223 /** 1224 * @see android.webkit.WebView#onKeyUp(int, KeyEvent) 1225 */ onKeyUp(int keyCode, KeyEvent event)1226 public boolean onKeyUp(int keyCode, KeyEvent event) { 1227 return mContentViewCore.onKeyUp(keyCode, event); 1228 } 1229 isDpadEvent(KeyEvent event)1230 private boolean isDpadEvent(KeyEvent event) { 1231 if (event.getAction() == KeyEvent.ACTION_DOWN) { 1232 switch (event.getKeyCode()) { 1233 case KeyEvent.KEYCODE_DPAD_CENTER: 1234 case KeyEvent.KEYCODE_DPAD_DOWN: 1235 case KeyEvent.KEYCODE_DPAD_UP: 1236 case KeyEvent.KEYCODE_DPAD_LEFT: 1237 case KeyEvent.KEYCODE_DPAD_RIGHT: 1238 return true; 1239 } 1240 } 1241 return false; 1242 } 1243 1244 /** 1245 * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent) 1246 */ dispatchKeyEvent(KeyEvent event)1247 public boolean dispatchKeyEvent(KeyEvent event) { 1248 if (isDpadEvent(event)) { 1249 mSettings.setSpatialNavigationEnabled(true); 1250 } 1251 return mContentViewCore.dispatchKeyEvent(event); 1252 } 1253 1254 /** 1255 * Clears the resource cache. Note that the cache is per-application, so this will clear the 1256 * cache for all WebViews used. 1257 * 1258 * @param includeDiskFiles if false, only the RAM cache is cleared 1259 */ clearCache(boolean includeDiskFiles)1260 public void clearCache(boolean includeDiskFiles) { 1261 if (mNativeAwContents == 0) return; 1262 nativeClearCache(mNativeAwContents, includeDiskFiles); 1263 } 1264 documentHasImages(Message message)1265 public void documentHasImages(Message message) { 1266 if (mNativeAwContents == 0) return; 1267 nativeDocumentHasImages(mNativeAwContents, message); 1268 } 1269 saveWebArchive( final String basename, boolean autoname, final ValueCallback<String> callback)1270 public void saveWebArchive( 1271 final String basename, boolean autoname, final ValueCallback<String> callback) { 1272 if (!autoname) { 1273 saveWebArchiveInternal(basename, callback); 1274 return; 1275 } 1276 // If auto-generating the file name, handle the name generation on a background thread 1277 // as it will require I/O access for checking whether previous files existed. 1278 new AsyncTask<Void, Void, String>() { 1279 @Override 1280 protected String doInBackground(Void... params) { 1281 return generateArchiveAutoNamePath(getOriginalUrl(), basename); 1282 } 1283 1284 @Override 1285 protected void onPostExecute(String result) { 1286 saveWebArchiveInternal(result, callback); 1287 } 1288 }.execute(); 1289 } 1290 getOriginalUrl()1291 public String getOriginalUrl() { 1292 NavigationHistory history = mContentViewCore.getNavigationHistory(); 1293 int currentIndex = history.getCurrentEntryIndex(); 1294 if (currentIndex >= 0 && currentIndex < history.getEntryCount()) { 1295 return history.getEntryAtIndex(currentIndex).getOriginalUrl(); 1296 } 1297 return null; 1298 } 1299 1300 /** 1301 * @see ContentViewCore#getNavigationHistory() 1302 */ getNavigationHistory()1303 public NavigationHistory getNavigationHistory() { 1304 return mContentViewCore.getNavigationHistory(); 1305 } 1306 1307 /** 1308 * @see android.webkit.WebView#getTitle() 1309 */ getTitle()1310 public String getTitle() { 1311 return mContentViewCore.getTitle(); 1312 } 1313 1314 /** 1315 * @see android.webkit.WebView#clearHistory() 1316 */ clearHistory()1317 public void clearHistory() { 1318 mContentViewCore.clearHistory(); 1319 } 1320 getHttpAuthUsernamePassword(String host, String realm)1321 public String[] getHttpAuthUsernamePassword(String host, String realm) { 1322 return mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext()) 1323 .getHttpAuthUsernamePassword(host, realm); 1324 } 1325 setHttpAuthUsernamePassword(String host, String realm, String username, String password)1326 public void setHttpAuthUsernamePassword(String host, String realm, String username, 1327 String password) { 1328 mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext()) 1329 .setHttpAuthUsernamePassword(host, realm, username, password); 1330 } 1331 1332 /** 1333 * @see android.webkit.WebView#getCertificate() 1334 */ getCertificate()1335 public SslCertificate getCertificate() { 1336 if (mNativeAwContents == 0) return null; 1337 return SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents)); 1338 } 1339 1340 /** 1341 * @see android.webkit.WebView#clearSslPreferences() 1342 */ clearSslPreferences()1343 public void clearSslPreferences() { 1344 mContentViewCore.clearSslPreferences(); 1345 } 1346 1347 /** 1348 * Method to return all hit test values relevant to public WebView API. 1349 * Note that this expose more data than needed for WebView.getHitTestResult. 1350 * Unsafely returning reference to mutable internal object to avoid excessive 1351 * garbage allocation on repeated calls. 1352 */ getLastHitTestResult()1353 public HitTestData getLastHitTestResult() { 1354 if (mNativeAwContents == 0) return null; 1355 nativeUpdateLastHitTestData(mNativeAwContents); 1356 return mPossiblyStaleHitTestData; 1357 } 1358 1359 /** 1360 * @see android.webkit.WebView#requestFocusNodeHref() 1361 */ requestFocusNodeHref(Message msg)1362 public void requestFocusNodeHref(Message msg) { 1363 if (msg == null || mNativeAwContents == 0) return; 1364 1365 nativeUpdateLastHitTestData(mNativeAwContents); 1366 Bundle data = msg.getData(); 1367 1368 // In order to maintain compatibility with the old WebView's implementation, 1369 // the absolute (full) url is passed in the |url| field, not only the href attribute. 1370 // Note: HitTestData could be cleaned up at this point. See http://crbug.com/290992. 1371 data.putString("url", mPossiblyStaleHitTestData.href); 1372 data.putString("title", mPossiblyStaleHitTestData.anchorText); 1373 data.putString("src", mPossiblyStaleHitTestData.imgSrc); 1374 msg.setData(data); 1375 msg.sendToTarget(); 1376 } 1377 1378 /** 1379 * @see android.webkit.WebView#requestImageRef() 1380 */ requestImageRef(Message msg)1381 public void requestImageRef(Message msg) { 1382 if (msg == null || mNativeAwContents == 0) return; 1383 1384 nativeUpdateLastHitTestData(mNativeAwContents); 1385 Bundle data = msg.getData(); 1386 data.putString("url", mPossiblyStaleHitTestData.imgSrc); 1387 msg.setData(data); 1388 msg.sendToTarget(); 1389 } 1390 1391 @VisibleForTesting getPageScaleFactor()1392 public float getPageScaleFactor() { 1393 return mPageScaleFactor; 1394 } 1395 1396 /** 1397 * @see android.webkit.WebView#getScale() 1398 * 1399 * Please note that the scale returned is the page scale multiplied by 1400 * the screen density factor. See CTS WebViewTest.testSetInitialScale. 1401 */ getScale()1402 public float getScale() { 1403 return (float)(mPageScaleFactor * mDIPScale); 1404 } 1405 1406 /** 1407 * @see android.webkit.WebView#flingScroll(int, int) 1408 */ flingScroll(int velocityX, int velocityY)1409 public void flingScroll(int velocityX, int velocityY) { 1410 mScrollOffsetManager.flingScroll(velocityX, velocityY); 1411 } 1412 1413 /** 1414 * @see android.webkit.WebView#pageUp(boolean) 1415 */ pageUp(boolean top)1416 public boolean pageUp(boolean top) { 1417 return mScrollOffsetManager.pageUp(top); 1418 } 1419 1420 /** 1421 * @see android.webkit.WebView#pageDown(boolean) 1422 */ pageDown(boolean bottom)1423 public boolean pageDown(boolean bottom) { 1424 return mScrollOffsetManager.pageDown(bottom); 1425 } 1426 1427 /** 1428 * @see android.webkit.WebView#canZoomIn() 1429 */ 1430 // This method uses the term 'zoom' for legacy reasons, but relates 1431 // to what chrome calls the 'page scale factor'. canZoomIn()1432 public boolean canZoomIn() { 1433 final float zoomInExtent = mContentViewCore.getRenderCoordinates().getMaxPageScaleFactor() 1434 - mPageScaleFactor; 1435 return zoomInExtent > ZOOM_CONTROLS_EPSILON; 1436 } 1437 1438 /** 1439 * @see android.webkit.WebView#canZoomOut() 1440 */ 1441 // This method uses the term 'zoom' for legacy reasons, but relates 1442 // to what chrome calls the 'page scale factor'. canZoomOut()1443 public boolean canZoomOut() { 1444 final float zoomOutExtent = mPageScaleFactor 1445 - mContentViewCore.getRenderCoordinates().getMinPageScaleFactor(); 1446 return zoomOutExtent > ZOOM_CONTROLS_EPSILON; 1447 } 1448 1449 /** 1450 * @see android.webkit.WebView#zoomIn() 1451 */ 1452 // This method uses the term 'zoom' for legacy reasons, but relates 1453 // to what chrome calls the 'page scale factor'. zoomIn()1454 public boolean zoomIn() { 1455 if (!canZoomIn()) { 1456 return false; 1457 } 1458 return mContentViewCore.pinchByDelta(1.25f); 1459 } 1460 1461 /** 1462 * @see android.webkit.WebView#zoomOut() 1463 */ 1464 // This method uses the term 'zoom' for legacy reasons, but relates 1465 // to what chrome calls the 'page scale factor'. zoomOut()1466 public boolean zoomOut() { 1467 if (!canZoomOut()) { 1468 return false; 1469 } 1470 return mContentViewCore.pinchByDelta(0.8f); 1471 } 1472 1473 /** 1474 * @see android.webkit.WebView#invokeZoomPicker() 1475 */ invokeZoomPicker()1476 public void invokeZoomPicker() { 1477 mContentViewCore.invokeZoomPicker(); 1478 } 1479 1480 /** 1481 * @see ContentViewCore.evaluateJavaScript(String, ContentViewCore.JavaScriptCallback) 1482 */ evaluateJavaScript(String script, final ValueCallback<String> callback)1483 public void evaluateJavaScript(String script, final ValueCallback<String> callback) { 1484 ContentViewCore.JavaScriptCallback jsCallback = null; 1485 if (callback != null) { 1486 jsCallback = new ContentViewCore.JavaScriptCallback() { 1487 @Override 1488 public void handleJavaScriptResult(String jsonResult) { 1489 callback.onReceiveValue(jsonResult); 1490 } 1491 }; 1492 } 1493 1494 mContentViewCore.evaluateJavaScript(script, jsCallback); 1495 } 1496 1497 /** 1498 * @see ContentViewCore.evaluateJavaScriptEvenIfNotYetNavigated(String) 1499 */ evaluateJavaScriptEvenIfNotYetNavigated(String script)1500 public void evaluateJavaScriptEvenIfNotYetNavigated(String script) { 1501 mContentViewCore.evaluateJavaScriptEvenIfNotYetNavigated(script); 1502 } 1503 1504 //-------------------------------------------------------------------------------------------- 1505 // View and ViewGroup method implementations 1506 //-------------------------------------------------------------------------------------------- 1507 1508 /** 1509 * @see android.webkit.View#onTouchEvent() 1510 */ onTouchEvent(MotionEvent event)1511 public boolean onTouchEvent(MotionEvent event) { 1512 if (mNativeAwContents == 0) return false; 1513 1514 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1515 mSettings.setSpatialNavigationEnabled(false); 1516 } 1517 1518 mScrollOffsetManager.setProcessingTouchEvent(true); 1519 boolean rv = mContentViewCore.onTouchEvent(event); 1520 mScrollOffsetManager.setProcessingTouchEvent(false); 1521 1522 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1523 int actionIndex = event.getActionIndex(); 1524 1525 // Note this will trigger IPC back to browser even if nothing is hit. 1526 nativeRequestNewHitTestDataAt(mNativeAwContents, 1527 (int) Math.round(event.getX(actionIndex) / mDIPScale), 1528 (int) Math.round(event.getY(actionIndex) / mDIPScale)); 1529 } 1530 1531 if (mOverScrollGlow != null && event.getActionMasked() == MotionEvent.ACTION_UP) { 1532 mOverScrollGlow.releaseAll(); 1533 } 1534 1535 return rv; 1536 } 1537 1538 /** 1539 * @see android.view.View#onHoverEvent() 1540 */ onHoverEvent(MotionEvent event)1541 public boolean onHoverEvent(MotionEvent event) { 1542 return mContentViewCore.onHoverEvent(event); 1543 } 1544 1545 /** 1546 * @see android.view.View#onGenericMotionEvent() 1547 */ onGenericMotionEvent(MotionEvent event)1548 public boolean onGenericMotionEvent(MotionEvent event) { 1549 return mContentViewCore.onGenericMotionEvent(event); 1550 } 1551 1552 /** 1553 * @see android.view.View#onConfigurationChanged() 1554 */ onConfigurationChanged(Configuration newConfig)1555 public void onConfigurationChanged(Configuration newConfig) { 1556 mContentViewCore.onConfigurationChanged(newConfig); 1557 } 1558 1559 /** 1560 * @see android.view.View#onAttachedToWindow() 1561 * 1562 * Note that this is also called from receivePopupContents. 1563 */ onAttachedToWindow()1564 public void onAttachedToWindow() { 1565 if (mNativeAwContents == 0) return; 1566 mIsAttachedToWindow = true; 1567 1568 mContentViewCore.onAttachedToWindow(); 1569 nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(), 1570 mContainerView.getHeight()); 1571 updateHardwareAcceleratedFeaturesToggle(); 1572 1573 if (mComponentCallbacks != null) return; 1574 mComponentCallbacks = new AwComponentCallbacks(); 1575 mContainerView.getContext().registerComponentCallbacks(mComponentCallbacks); 1576 } 1577 1578 /** 1579 * @see android.view.View#onDetachedFromWindow() 1580 */ onDetachedFromWindow()1581 public void onDetachedFromWindow() { 1582 mIsAttachedToWindow = false; 1583 hideAutofillPopup(); 1584 if (mNativeAwContents != 0) { 1585 nativeOnDetachedFromWindow(mNativeAwContents); 1586 } 1587 1588 mContentViewCore.onDetachedFromWindow(); 1589 updateHardwareAcceleratedFeaturesToggle(); 1590 1591 if (mComponentCallbacks != null) { 1592 mContainerView.getContext().unregisterComponentCallbacks(mComponentCallbacks); 1593 mComponentCallbacks = null; 1594 } 1595 1596 mScrollAccessibilityHelper.removePostedCallbacks(); 1597 1598 if (mPendingDetachCleanupReferences != null) { 1599 for (int i = 0; i < mPendingDetachCleanupReferences.size(); ++i) { 1600 mPendingDetachCleanupReferences.get(i).cleanupNow(); 1601 } 1602 mPendingDetachCleanupReferences = null; 1603 } 1604 } 1605 1606 /** 1607 * @see android.view.View#onWindowFocusChanged() 1608 */ onWindowFocusChanged(boolean hasWindowFocus)1609 public void onWindowFocusChanged(boolean hasWindowFocus) { 1610 mWindowFocused = hasWindowFocus; 1611 mContentViewCore.onWindowFocusChanged(hasWindowFocus); 1612 } 1613 1614 /** 1615 * @see android.view.View#onFocusChanged() 1616 */ onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)1617 public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 1618 mContainerViewFocused = focused; 1619 mContentViewCore.onFocusChanged(focused); 1620 } 1621 1622 /** 1623 * @see android.view.View#onSizeChanged() 1624 */ onSizeChanged(int w, int h, int ow, int oh)1625 public void onSizeChanged(int w, int h, int ow, int oh) { 1626 if (mNativeAwContents == 0) return; 1627 mScrollOffsetManager.setContainerViewSize(w, h); 1628 // The AwLayoutSizer needs to go first so that if we're in fixedLayoutSize mode the update 1629 // to enter fixedLayoutSize mode is sent before the first resize update. 1630 mLayoutSizer.onSizeChanged(w, h, ow, oh); 1631 mContentViewCore.onPhysicalBackingSizeChanged(w, h); 1632 mContentViewCore.onSizeChanged(w, h, ow, oh); 1633 nativeOnSizeChanged(mNativeAwContents, w, h, ow, oh); 1634 } 1635 1636 /** 1637 * @see android.view.View#onVisibilityChanged() 1638 */ onVisibilityChanged(View changedView, int visibility)1639 public void onVisibilityChanged(View changedView, int visibility) { 1640 boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE; 1641 if (mIsViewVisible == viewVisible) return; 1642 setViewVisibilityInternal(viewVisible); 1643 } 1644 1645 /** 1646 * @see android.view.View#onWindowVisibilityChanged() 1647 */ onWindowVisibilityChanged(int visibility)1648 public void onWindowVisibilityChanged(int visibility) { 1649 boolean windowVisible = visibility == View.VISIBLE; 1650 if (mIsWindowVisible == windowVisible) return; 1651 setWindowVisibilityInternal(windowVisible); 1652 } 1653 setViewVisibilityInternal(boolean visible)1654 private void setViewVisibilityInternal(boolean visible) { 1655 mIsViewVisible = visible; 1656 if (mNativeAwContents == 0) return; 1657 nativeSetViewVisibility(mNativeAwContents, mIsViewVisible); 1658 } 1659 setWindowVisibilityInternal(boolean visible)1660 private void setWindowVisibilityInternal(boolean visible) { 1661 mIsWindowVisible = visible; 1662 if (mNativeAwContents == 0) return; 1663 nativeSetWindowVisibility(mNativeAwContents, mIsWindowVisible); 1664 } 1665 1666 /** 1667 * Key for opaque state in bundle. Note this is only public for tests. 1668 */ 1669 public static final String SAVE_RESTORE_STATE_KEY = "WEBVIEW_CHROMIUM_STATE"; 1670 1671 /** 1672 * Save the state of this AwContents into provided Bundle. 1673 * @return False if saving state failed. 1674 */ saveState(Bundle outState)1675 public boolean saveState(Bundle outState) { 1676 if (mNativeAwContents == 0 || outState == null) return false; 1677 1678 byte[] state = nativeGetOpaqueState(mNativeAwContents); 1679 if (state == null) return false; 1680 1681 outState.putByteArray(SAVE_RESTORE_STATE_KEY, state); 1682 return true; 1683 } 1684 1685 /** 1686 * Restore the state of this AwContents into provided Bundle. 1687 * @param inState Must be a bundle returned by saveState. 1688 * @return False if restoring state failed. 1689 */ restoreState(Bundle inState)1690 public boolean restoreState(Bundle inState) { 1691 if (mNativeAwContents == 0 || inState == null) return false; 1692 1693 byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY); 1694 if (state == null) return false; 1695 1696 boolean result = nativeRestoreFromOpaqueState(mNativeAwContents, state); 1697 1698 // The onUpdateTitle callback normally happens when a page is loaded, 1699 // but is optimized out in the restoreState case because the title is 1700 // already restored. See WebContentsImpl::UpdateTitleForEntry. So we 1701 // call the callback explicitly here. 1702 if (result) mContentsClient.onReceivedTitle(mContentViewCore.getTitle()); 1703 1704 return result; 1705 } 1706 1707 /** 1708 * @see ContentViewCore#addPossiblyUnsafeJavascriptInterface(Object, String, Class) 1709 */ addPossiblyUnsafeJavascriptInterface(Object object, String name, Class<? extends Annotation> requiredAnnotation)1710 public void addPossiblyUnsafeJavascriptInterface(Object object, String name, 1711 Class<? extends Annotation> requiredAnnotation) { 1712 mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name, requiredAnnotation); 1713 } 1714 1715 /** 1716 * @see android.webkit.WebView#removeJavascriptInterface(String) 1717 */ removeJavascriptInterface(String interfaceName)1718 public void removeJavascriptInterface(String interfaceName) { 1719 mContentViewCore.removeJavascriptInterface(interfaceName); 1720 } 1721 1722 /** 1723 * If native accessibility (not script injection) is enabled, and if this is 1724 * running on JellyBean or later, returns an AccessibilityNodeProvider that 1725 * implements native accessibility for this view. Returns null otherwise. 1726 * @return The AccessibilityNodeProvider, if available, or null otherwise. 1727 */ getAccessibilityNodeProvider()1728 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 1729 return mContentViewCore.getAccessibilityNodeProvider(); 1730 } 1731 1732 /** 1733 * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) 1734 */ onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)1735 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1736 mContentViewCore.onInitializeAccessibilityNodeInfo(info); 1737 } 1738 1739 /** 1740 * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent) 1741 */ onInitializeAccessibilityEvent(AccessibilityEvent event)1742 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1743 mContentViewCore.onInitializeAccessibilityEvent(event); 1744 } 1745 supportsAccessibilityAction(int action)1746 public boolean supportsAccessibilityAction(int action) { 1747 return mContentViewCore.supportsAccessibilityAction(action); 1748 } 1749 1750 /** 1751 * @see android.webkit.WebView#performAccessibilityAction(int, Bundle) 1752 */ performAccessibilityAction(int action, Bundle arguments)1753 public boolean performAccessibilityAction(int action, Bundle arguments) { 1754 return mContentViewCore.performAccessibilityAction(action, arguments); 1755 } 1756 1757 /** 1758 * @see android.webkit.WebView#clearFormData() 1759 */ hideAutofillPopup()1760 public void hideAutofillPopup() { 1761 if (mAwAutofillManagerDelegate != null) 1762 mAwAutofillManagerDelegate.hideAutofillPopup(); 1763 } 1764 setNetworkAvailable(boolean networkUp)1765 public void setNetworkAvailable(boolean networkUp) { 1766 if (mNativeAwContents == 0) return; 1767 nativeSetJsOnlineProperty(mNativeAwContents, networkUp); 1768 } 1769 1770 //-------------------------------------------------------------------------------------------- 1771 // Methods called from native via JNI 1772 //-------------------------------------------------------------------------------------------- 1773 1774 @CalledByNative onDocumentHasImagesResponse(boolean result, Message message)1775 private static void onDocumentHasImagesResponse(boolean result, Message message) { 1776 message.arg1 = result ? 1 : 0; 1777 message.sendToTarget(); 1778 } 1779 1780 @CalledByNative onReceivedTouchIconUrl(String url, boolean precomposed)1781 private void onReceivedTouchIconUrl(String url, boolean precomposed) { 1782 mContentsClient.onReceivedTouchIconUrl(url, precomposed); 1783 } 1784 1785 @CalledByNative onReceivedIcon(Bitmap bitmap)1786 private void onReceivedIcon(Bitmap bitmap) { 1787 mContentsClient.onReceivedIcon(bitmap); 1788 mFavicon = bitmap; 1789 } 1790 1791 /** Callback for generateMHTML. */ 1792 @CalledByNative generateMHTMLCallback( String path, long size, ValueCallback<String> callback)1793 private static void generateMHTMLCallback( 1794 String path, long size, ValueCallback<String> callback) { 1795 if (callback == null) return; 1796 callback.onReceiveValue(size < 0 ? null : path); 1797 } 1798 1799 @CalledByNative 1800 private void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) { 1801 mContentsClient.onReceivedHttpAuthRequest(handler, host, realm); 1802 } 1803 1804 private class AwGeolocationCallback implements GeolocationPermissions.Callback { 1805 1806 @Override 1807 public void invoke(final String origin, final boolean allow, final boolean retain) { 1808 ThreadUtils.runOnUiThread(new Runnable() { 1809 @Override 1810 public void run() { 1811 if (retain) { 1812 if (allow) { 1813 mBrowserContext.getGeolocationPermissions().allow(origin); 1814 } else { 1815 mBrowserContext.getGeolocationPermissions().deny(origin); 1816 } 1817 } 1818 if (mNativeAwContents == 0) return; 1819 nativeInvokeGeolocationCallback(mNativeAwContents, allow, origin); 1820 } 1821 }); 1822 } 1823 } 1824 1825 @CalledByNative 1826 private void onGeolocationPermissionsShowPrompt(String origin) { 1827 if (mNativeAwContents == 0) return; 1828 AwGeolocationPermissions permissions = mBrowserContext.getGeolocationPermissions(); 1829 // Reject if geoloaction is disabled, or the origin has a retained deny 1830 if (!mSettings.getGeolocationEnabled()) { 1831 nativeInvokeGeolocationCallback(mNativeAwContents, false, origin); 1832 return; 1833 } 1834 // Allow if the origin has a retained allow 1835 if (permissions.hasOrigin(origin)) { 1836 nativeInvokeGeolocationCallback(mNativeAwContents, permissions.isOriginAllowed(origin), 1837 origin); 1838 return; 1839 } 1840 mContentsClient.onGeolocationPermissionsShowPrompt( 1841 origin, new AwGeolocationCallback()); 1842 } 1843 1844 @CalledByNative 1845 private void onGeolocationPermissionsHidePrompt() { 1846 mContentsClient.onGeolocationPermissionsHidePrompt(); 1847 } 1848 1849 @CalledByNative 1850 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, 1851 boolean isDoneCounting) { 1852 mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); 1853 } 1854 1855 @CalledByNative 1856 public void onNewPicture() { 1857 // Clear up any results from a previous clearView call 1858 if (mClearViewActive) { 1859 mClearViewActive = false; 1860 mContainerView.invalidate(); 1861 syncOnNewPictureStateToNative(); 1862 } 1863 1864 // Don't call capturePicture() here but instead defer it until the posted task runs within 1865 // the callback helper, to avoid doubling back into the renderer compositor in the middle 1866 // of the notification it is sending up to here. 1867 mContentsClient.getCallbackHelper().postOnNewPicture(mPictureListenerContentProvider); 1868 } 1869 1870 // Called as a result of nativeUpdateLastHitTestData. 1871 @CalledByNative 1872 private void updateHitTestData( 1873 int type, String extra, String href, String anchorText, String imgSrc) { 1874 mPossiblyStaleHitTestData.hitTestResultType = type; 1875 mPossiblyStaleHitTestData.hitTestResultExtraData = extra; 1876 mPossiblyStaleHitTestData.href = href; 1877 mPossiblyStaleHitTestData.anchorText = anchorText; 1878 mPossiblyStaleHitTestData.imgSrc = imgSrc; 1879 } 1880 1881 @CalledByNative 1882 private boolean requestDrawGL(Canvas canvas) { 1883 return mInternalAccessAdapter.requestDrawGL(canvas); 1884 } 1885 1886 private static final boolean SUPPORTS_ON_ANIMATION = 1887 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; 1888 1889 @CalledByNative 1890 private void postInvalidateOnAnimation() { 1891 if (SUPPORTS_ON_ANIMATION) { 1892 mContainerView.postInvalidateOnAnimation(); 1893 } else { 1894 mContainerView.postInvalidate(); 1895 } 1896 } 1897 1898 @CalledByNative 1899 private int[] getLocationOnScreen() { 1900 int[] result = new int[2]; 1901 mContainerView.getLocationOnScreen(result); 1902 return result; 1903 } 1904 1905 @CalledByNative 1906 private void onWebLayoutPageScaleFactorChanged(float webLayoutPageScaleFactor) { 1907 // This change notification comes from the renderer thread, not from the cc/ impl thread. 1908 mLayoutSizer.onPageScaleChanged(webLayoutPageScaleFactor); 1909 } 1910 1911 @CalledByNative 1912 private void onWebLayoutContentsSizeChanged(int widthCss, int heightCss) { 1913 // This change notification comes from the renderer thread, not from the cc/ impl thread. 1914 mLayoutSizer.onContentSizeChanged(widthCss, heightCss); 1915 } 1916 1917 @CalledByNative 1918 private void setMaxContainerViewScrollOffset(int maxX, int maxY) { 1919 mScrollOffsetManager.setMaxScrollOffset(maxX, maxY); 1920 } 1921 1922 @CalledByNative 1923 private void scrollContainerViewTo(int x, int y) { 1924 mScrollOffsetManager.scrollContainerViewTo(x, y); 1925 } 1926 1927 @CalledByNative 1928 private boolean isFlingActive() { 1929 return mScrollOffsetManager.isFlingActive(); 1930 } 1931 1932 @CalledByNative 1933 private void setContentsSize(int widthDip, int heightDip) { 1934 mContentWidthDip = widthDip; 1935 mContentHeightDip = heightDip; 1936 } 1937 1938 @CalledByNative 1939 private void setPageScaleFactor(float pageScaleFactor) { 1940 if (mPageScaleFactor == pageScaleFactor) 1941 return; 1942 float oldPageScaleFactor = mPageScaleFactor; 1943 mPageScaleFactor = pageScaleFactor; 1944 mContentsClient.getCallbackHelper().postOnScaleChangedScaled( 1945 (float) (oldPageScaleFactor * mDIPScale), (float) (mPageScaleFactor * mDIPScale)); 1946 } 1947 1948 @CalledByNative 1949 private void setAwAutofillManagerDelegate(AwAutofillManagerDelegate delegate) { 1950 mAwAutofillManagerDelegate = delegate; 1951 delegate.init(mContentViewCore); 1952 } 1953 1954 @CalledByNative 1955 private void didOverscroll(int deltaX, int deltaY) { 1956 if (mOverScrollGlow != null) { 1957 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY); 1958 } 1959 1960 mScrollOffsetManager.overScrollBy(deltaX, deltaY); 1961 1962 if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) { 1963 mContainerView.invalidate(); 1964 } 1965 } 1966 1967 // ------------------------------------------------------------------------------------------- 1968 // Helper methods 1969 // ------------------------------------------------------------------------------------------- 1970 1971 private void saveWebArchiveInternal(String path, final ValueCallback<String> callback) { 1972 if (path == null || mNativeAwContents == 0) { 1973 ThreadUtils.runOnUiThread(new Runnable() { 1974 @Override 1975 public void run() { 1976 callback.onReceiveValue(null); 1977 } 1978 }); 1979 } else { 1980 nativeGenerateMHTML(mNativeAwContents, path, callback); 1981 } 1982 } 1983 1984 /** 1985 * Try to generate a pathname for saving an MHTML archive. This roughly follows WebView's 1986 * autoname logic. 1987 */ 1988 private static String generateArchiveAutoNamePath(String originalUrl, String baseName) { 1989 String name = null; 1990 if (originalUrl != null && !originalUrl.isEmpty()) { 1991 try { 1992 String path = new URL(originalUrl).getPath(); 1993 int lastSlash = path.lastIndexOf('/'); 1994 if (lastSlash > 0) { 1995 name = path.substring(lastSlash + 1); 1996 } else { 1997 name = path; 1998 } 1999 } catch (MalformedURLException e) { 2000 // If it fails parsing the URL, we'll just rely on the default name below. 2001 } 2002 } 2003 2004 if (TextUtils.isEmpty(name)) name = "index"; 2005 2006 String testName = baseName + name + WEB_ARCHIVE_EXTENSION; 2007 if (!new File(testName).exists()) return testName; 2008 2009 for (int i = 1; i < 100; i++) { 2010 testName = baseName + name + "-" + i + WEB_ARCHIVE_EXTENSION; 2011 if (!new File(testName).exists()) return testName; 2012 } 2013 2014 Log.e(TAG, "Unable to auto generate archive name for path: " + baseName); 2015 return null; 2016 } 2017 2018 public void extractSmartClipData(int x, int y, int width, int height) { 2019 mContentViewCore.extractSmartClipData(x, y, width, height); 2020 } 2021 2022 public void setSmartClipDataListener(ContentViewCore.SmartClipDataListener listener) { 2023 mContentViewCore.setSmartClipDataListener(listener); 2024 } 2025 2026 //-------------------------------------------------------------------------------------------- 2027 // Native methods 2028 //-------------------------------------------------------------------------------------------- 2029 2030 private static native long nativeInit(AwBrowserContext browserContext); 2031 private static native void nativeDestroy(long nativeAwContents); 2032 private static native void nativeSetAwDrawSWFunctionTable(int functionTablePointer); 2033 private static native void nativeSetAwDrawGLFunctionTable(int functionTablePointer); 2034 private static native int nativeGetAwDrawGLFunction(); 2035 private static native int nativeGetNativeInstanceCount(); 2036 private static native void nativeSetShouldDownloadFavicons(); 2037 private native void nativeSetJavaPeers(long nativeAwContents, AwContents awContents, 2038 AwWebContentsDelegate webViewWebContentsDelegate, 2039 AwContentsClientBridge contentsClientBridge, 2040 AwContentsIoThreadClient ioThreadClient, 2041 InterceptNavigationDelegate navigationInterceptionDelegate); 2042 private native int nativeGetWebContents(long nativeAwContents); 2043 2044 private native void nativeDocumentHasImages(long nativeAwContents, Message message); 2045 private native void nativeGenerateMHTML( 2046 long nativeAwContents, String path, ValueCallback<String> callback); 2047 2048 private native void nativeAddVisitedLinks(long nativeAwContents, String[] visitedLinks); 2049 private native boolean nativeOnDraw(long nativeAwContents, Canvas canvas, 2050 boolean isHardwareAccelerated, int scrollX, int scrollY, 2051 int clipLeft, int clipTop, int clipRight, int clipBottom); 2052 private native void nativeSetGlobalVisibleRect(long nativeAwContents, int visibleLeft, 2053 int visibleTop, int visibleRight, int visibleBottom); 2054 private native void nativeFindAllAsync(long nativeAwContents, String searchString); 2055 private native void nativeFindNext(long nativeAwContents, boolean forward); 2056 private native void nativeClearMatches(long nativeAwContents); 2057 private native void nativeClearCache(long nativeAwContents, boolean includeDiskFiles); 2058 private native byte[] nativeGetCertificate(long nativeAwContents); 2059 2060 // Coordinates in desity independent pixels. 2061 private native void nativeRequestNewHitTestDataAt(long nativeAwContents, int x, int y); 2062 private native void nativeUpdateLastHitTestData(long nativeAwContents); 2063 2064 private native void nativeOnSizeChanged(long nativeAwContents, int w, int h, int ow, int oh); 2065 private native void nativeScrollTo(long nativeAwContents, int x, int y); 2066 private native void nativeSetViewVisibility(long nativeAwContents, boolean visible); 2067 private native void nativeSetWindowVisibility(long nativeAwContents, boolean visible); 2068 private native void nativeSetIsPaused(long nativeAwContents, boolean paused); 2069 private native void nativeOnAttachedToWindow(long nativeAwContents, int w, int h); 2070 private static native void nativeOnDetachedFromWindow(long nativeAwContents); 2071 private native void nativeSetDipScale(long nativeAwContents, float dipScale); 2072 private native void nativeSetFixedLayoutSize(long nativeAwContents, 2073 int widthDip, int heightDip); 2074 2075 // Returns null if save state fails. 2076 private native byte[] nativeGetOpaqueState(long nativeAwContents); 2077 2078 // Returns false if restore state fails. 2079 private native boolean nativeRestoreFromOpaqueState(long nativeAwContents, byte[] state); 2080 2081 private native int nativeReleasePopupAwContents(long nativeAwContents); 2082 private native void nativeFocusFirstNode(long nativeAwContents); 2083 private native void nativeSetBackgroundColor(long nativeAwContents, int color); 2084 2085 private native int nativeGetAwDrawGLViewContext(long nativeAwContents); 2086 private native long nativeCapturePicture(long nativeAwContents, int width, int height); 2087 private native void nativeEnableOnNewPicture(long nativeAwContents, boolean enabled); 2088 private native void nativeSetExtraHeadersForUrl(long nativeAwContents, 2089 String url, String extraHeaders); 2090 2091 private native void nativeInvokeGeolocationCallback( 2092 long nativeAwContents, boolean value, String requestingFrame); 2093 2094 private native void nativeSetJsOnlineProperty(long nativeAwContents, boolean networkUp); 2095 2096 private native void nativeTrimMemory(long nativeAwContents, int level); 2097 2098 private native void nativeCreatePdfExporter(long nativeAwContents, AwPdfExporter awPdfExporter); 2099 } 2100