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.app.Activity; 8import android.content.ComponentCallbacks2; 9import android.content.Context; 10import android.content.res.Configuration; 11import android.graphics.Bitmap; 12import android.graphics.Canvas; 13import android.graphics.Color; 14import android.graphics.Paint; 15import android.graphics.Picture; 16import android.graphics.Rect; 17import android.net.http.SslCertificate; 18import android.os.AsyncTask; 19import android.os.Build; 20import android.os.Bundle; 21import android.os.Message; 22import android.text.TextUtils; 23import android.util.Log; 24import android.view.KeyEvent; 25import android.view.MotionEvent; 26import android.view.View; 27import android.view.ViewGroup; 28import android.view.accessibility.AccessibilityEvent; 29import android.view.accessibility.AccessibilityNodeInfo; 30import android.view.accessibility.AccessibilityNodeProvider; 31import android.view.inputmethod.EditorInfo; 32import android.view.inputmethod.InputConnection; 33import android.webkit.GeolocationPermissions; 34import android.webkit.ValueCallback; 35import android.widget.OverScroller; 36 37import com.google.common.annotations.VisibleForTesting; 38 39import org.chromium.base.CalledByNative; 40import org.chromium.base.JNINamespace; 41import org.chromium.base.ThreadUtils; 42import org.chromium.components.navigation_interception.InterceptNavigationDelegate; 43import org.chromium.components.navigation_interception.NavigationParams; 44import org.chromium.content.browser.ContentSettings; 45import org.chromium.content.browser.ContentViewClient; 46import org.chromium.content.browser.ContentViewCore; 47import org.chromium.content.browser.ContentViewStatics; 48import org.chromium.content.browser.LoadUrlParams; 49import org.chromium.content.browser.NavigationHistory; 50import org.chromium.content.browser.PageTransitionTypes; 51import org.chromium.content.common.CleanupReference; 52import org.chromium.ui.base.ActivityWindowAndroid; 53import org.chromium.ui.base.WindowAndroid; 54import org.chromium.ui.gfx.DeviceDisplayInfo; 55 56import java.io.File; 57import java.lang.annotation.Annotation; 58import java.net.MalformedURLException; 59import java.net.URL; 60import java.util.ArrayList; 61import java.util.HashMap; 62import java.util.List; 63import 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") 74public 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 */ 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 */ 118 void super_scrollTo(int scrollX, int scrollY); 119 120 /** 121 * @see View#setMeasuredDimension(int, int) 122 */ 123 void setMeasuredDimension(int measuredWidth, int measuredHeight); 124 125 /** 126 * @see View#getScrollBarStyle() 127 */ 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 */ 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; 207 private DestroyRunnable(long nativeAwContents) { 208 mNativeAwContents = nativeAwContents; 209 } 210 @Override 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 232 public int getCacheMode() { 233 return mSettings.getCacheMode(); 234 } 235 236 @Override 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 263 public boolean shouldBlockContentUrls() { 264 return !mSettings.getAllowContentAccess(); 265 } 266 267 @Override 268 public boolean shouldBlockFileUrls() { 269 return !mSettings.getAllowFileAccess(); 270 } 271 272 @Override 273 public boolean shouldBlockNetworkLoads() { 274 return mSettings.getBlockNetworkLoads(); 275 } 276 277 @Override 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 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 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 328 public void requestLayout() { 329 mContainerView.requestLayout(); 330 } 331 332 @Override 333 public void setMeasuredDimension(int measuredWidth, int measuredHeight) { 334 mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight); 335 } 336 337 @Override 338 public void setFixedLayoutSize(int widthDip, int heightDip) { 339 if (mNativeAwContents == 0) return; 340 nativeSetFixedLayoutSize(mNativeAwContents, widthDip, heightDip); 341 } 342 343 @Override 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 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 360 public void scrollContainerViewTo(int x, int y) { 361 mInternalAccessAdapter.super_scrollTo(x, y); 362 } 363 364 @Override 365 public void scrollNativeTo(int x, int y) { 366 if (mNativeAwContents == 0) return; 367 nativeScrollTo(mNativeAwContents, x, y); 368 } 369 370 @Override 371 public int getContainerViewScrollX() { 372 return mContainerView.getScrollX(); 373 } 374 375 @Override 376 public int getContainerViewScrollY() { 377 return mContainerView.getScrollY(); 378 } 379 380 @Override 381 public void invalidate() { 382 mContainerView.invalidate(); 383 } 384 } 385 386 //-------------------------------------------------------------------------------------------- 387 private class AwGestureStateListener implements ContentViewCore.GestureStateListener { 388 @Override 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 400 public void onPinchGestureEnd() { 401 mLayoutSizer.unfreezeLayoutRequests(); 402 } 403 404 @Override 405 public void onFlingStartGesture(int velocityX, int velocityY) { 406 mScrollOffsetManager.onFlingStartGesture(velocityX, velocityY); 407 } 408 409 @Override 410 public void onFlingCancelGesture() { 411 mScrollOffsetManager.onFlingCancelGesture(); 412 } 413 414 @Override 415 public void onUnhandledFlingStartEvent() { 416 mScrollOffsetManager.onUnhandledFlingStartEvent(); 417 } 418 419 @Override 420 public void onScrollUpdateGestureConsumed() { 421 mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback(); 422 } 423 } 424 425 //-------------------------------------------------------------------------------------------- 426 private class AwComponentCallbacks implements ComponentCallbacks2 { 427 @Override 428 public void onTrimMemory(int level) { 429 if (mNativeAwContents == 0) return; 430 nativeTrimMemory(mNativeAwContents, level); 431 } 432 433 @Override 434 public void onLowMemory() {} 435 436 @Override 437 public void onConfigurationChanged(Configuration configuration) {} 438 }; 439 440 //-------------------------------------------------------------------------------------------- 441 private class AwLayoutChangeListener implements View.OnLayoutChangeListener { 442 @Override 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 */ 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 */ 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 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 */ 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 */ 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. 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 */ 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 663 public ContentViewCore getContentViewCore() { 664 return mContentViewCore; 665 } 666 667 // Can be called from any thread. 668 public AwSettings getSettings() { 669 return mSettings; 670 } 671 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 684 public static void setAwDrawSWFunctionTable(int functionTablePointer) { 685 nativeSetAwDrawSWFunctionTable(functionTablePointer); 686 } 687 688 public static void setAwDrawGLFunctionTable(int functionTablePointer) { 689 nativeSetAwDrawGLFunctionTable(functionTablePointer); 690 } 691 692 public static int getAwDrawGLFunction() { 693 return nativeGetAwDrawGLFunction(); 694 } 695 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 705 public static int getNativeInstanceCount() { 706 return nativeGetNativeInstanceCount(); 707 } 708 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 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 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 768 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 769 mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec); 770 } 771 772 public int getContentHeightCss() { 773 return (int) Math.ceil(mContentHeightDip); 774 } 775 776 public int getContentWidthCss() { 777 return (int) Math.ceil(mContentWidthDip); 778 } 779 780 public Picture capturePicture() { 781 if (mNativeAwContents == 0) return null; 782 return new AwPicture(nativeCapturePicture(mNativeAwContents, 783 mScrollOffsetManager.computeHorizontalScrollRange(), 784 mScrollOffsetManager.computeVerticalScrollRange())); 785 } 786 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 */ 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 814 private void syncOnNewPictureStateToNative() { 815 if (mNativeAwContents == 0) return; 816 nativeEnableOnNewPicture(mNativeAwContents, mPictureListenerEnabled || mClearViewActive); 817 } 818 819 public void findAllAsync(String searchString) { 820 if (mNativeAwContents == 0) return; 821 nativeFindAllAsync(mNativeAwContents, searchString); 822 } 823 824 public void findNext(boolean forward) { 825 if (mNativeAwContents == 0) return; 826 nativeFindNext(mNativeAwContents, forward); 827 } 828 829 public void clearMatches() { 830 if (mNativeAwContents == 0) return; 831 nativeClearMatches(mNativeAwContents); 832 } 833 834 /** 835 * @return load progress of the WebContents. 836 */ 837 public int getMostRecentProgress() { 838 // WebContentsDelegateAndroid conveniently caches the most recent notified value for us. 839 return mWebContentsDelegate.getMostRecentProgress(); 840 } 841 842 public Bitmap getFavicon() { 843 return mFavicon; 844 } 845 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 */ 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 */ 922 public String getUrl() { 923 String url = mContentViewCore.getUrl(); 924 if (url == null || url.trim().isEmpty()) return null; 925 return url; 926 } 927 928 public void requestFocus() { 929 if (mNativeAwContents == 0) return; 930 if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) { 931 nativeFocusFirstNode(mNativeAwContents); 932 } 933 } 934 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 */ 943 public void setLayerType(int layerType, Paint paint) { 944 mLayerType = layerType; 945 updateHardwareAcceleratedFeaturesToggle(); 946 } 947 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 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 965 public boolean isMultiTouchZoomSupported() { 966 return mSettings.supportsMultiTouchZoom(); 967 } 968 969 public View getZoomControlsForTest() { 970 return mZoomControls.getZoomControlsViewForTest(); 971 } 972 973 /** 974 * @see ContentViewCore#getContentSettings() 975 */ 976 public ContentSettings getContentSettings() { 977 return mContentViewCore.getContentSettings(); 978 } 979 980 /** 981 * @see View#setOverScrollMode(int) 982 */ 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 */ 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 */ 1014 public void setHorizontalScrollbarOverlay(boolean overlay) { 1015 mOverlayHorizontalScrollbar = overlay; 1016 } 1017 1018 /** 1019 * @see View#setVerticalScrollbarOverlay(boolean) 1020 */ 1021 public void setVerticalScrollbarOverlay(boolean overlay) { 1022 mOverlayVerticalScrollbar = overlay; 1023 } 1024 1025 /** 1026 * @see View#overlayHorizontalScrollbar() 1027 */ 1028 public boolean overlayHorizontalScrollbar() { 1029 return mOverlayHorizontalScrollbar; 1030 } 1031 1032 /** 1033 * @see View#overlayVerticalScrollbar() 1034 */ 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 */ 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 */ 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 */ 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 */ 1082 public void computeScroll() { 1083 mScrollOffsetManager.computeScrollAndAbsorbGlow(mOverScrollGlow); 1084 } 1085 1086 /** 1087 * @see View#computeHorizontalScrollRange() 1088 */ 1089 public int computeHorizontalScrollRange() { 1090 return mScrollOffsetManager.computeHorizontalScrollRange(); 1091 } 1092 1093 /** 1094 * @see View#computeHorizontalScrollOffset() 1095 */ 1096 public int computeHorizontalScrollOffset() { 1097 return mScrollOffsetManager.computeHorizontalScrollOffset(); 1098 } 1099 1100 /** 1101 * @see View#computeVerticalScrollRange() 1102 */ 1103 public int computeVerticalScrollRange() { 1104 return mScrollOffsetManager.computeVerticalScrollRange(); 1105 } 1106 1107 /** 1108 * @see View#computeVerticalScrollOffset() 1109 */ 1110 public int computeVerticalScrollOffset() { 1111 return mScrollOffsetManager.computeVerticalScrollOffset(); 1112 } 1113 1114 /** 1115 * @see View#computeVerticalScrollExtent() 1116 */ 1117 public int computeVerticalScrollExtent() { 1118 return mScrollOffsetManager.computeVerticalScrollExtent(); 1119 } 1120 1121 /** 1122 * @see android.webkit.WebView#stopLoading() 1123 */ 1124 public void stopLoading() { 1125 mContentViewCore.stopLoading(); 1126 } 1127 1128 /** 1129 * @see android.webkit.WebView#reload() 1130 */ 1131 public void reload() { 1132 mContentViewCore.reload(true); 1133 } 1134 1135 /** 1136 * @see android.webkit.WebView#canGoBack() 1137 */ 1138 public boolean canGoBack() { 1139 return mContentViewCore.canGoBack(); 1140 } 1141 1142 /** 1143 * @see android.webkit.WebView#goBack() 1144 */ 1145 public void goBack() { 1146 mContentViewCore.goBack(); 1147 } 1148 1149 /** 1150 * @see android.webkit.WebView#canGoForward() 1151 */ 1152 public boolean canGoForward() { 1153 return mContentViewCore.canGoForward(); 1154 } 1155 1156 /** 1157 * @see android.webkit.WebView#goForward() 1158 */ 1159 public void goForward() { 1160 mContentViewCore.goForward(); 1161 } 1162 1163 /** 1164 * @see android.webkit.WebView#canGoBackOrForward(int) 1165 */ 1166 public boolean canGoBackOrForward(int steps) { 1167 return mContentViewCore.canGoToOffset(steps); 1168 } 1169 1170 /** 1171 * @see android.webkit.WebView#goBackOrForward(int) 1172 */ 1173 public void goBackOrForward(int steps) { 1174 mContentViewCore.goToOffset(steps); 1175 } 1176 1177 /** 1178 * @see android.webkit.WebView#pauseTimers() 1179 */ 1180 public void pauseTimers() { 1181 ContentViewStatics.setWebKitSharedTimersSuspended(true); 1182 } 1183 1184 /** 1185 * @see android.webkit.WebView#resumeTimers() 1186 */ 1187 public void resumeTimers() { 1188 ContentViewStatics.setWebKitSharedTimersSuspended(false); 1189 } 1190 1191 /** 1192 * @see android.webkit.WebView#onPause() 1193 */ 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 */ 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 */ 1212 public boolean isPaused() { 1213 return mIsPaused; 1214 } 1215 1216 /** 1217 * @see android.webkit.WebView#onCreateInputConnection(EditorInfo) 1218 */ 1219 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 1220 return mContentViewCore.onCreateInputConnection(outAttrs); 1221 } 1222 1223 /** 1224 * @see android.webkit.WebView#onKeyUp(int, KeyEvent) 1225 */ 1226 public boolean onKeyUp(int keyCode, KeyEvent event) { 1227 return mContentViewCore.onKeyUp(keyCode, event); 1228 } 1229 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 */ 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 */ 1260 public void clearCache(boolean includeDiskFiles) { 1261 if (mNativeAwContents == 0) return; 1262 nativeClearCache(mNativeAwContents, includeDiskFiles); 1263 } 1264 1265 public void documentHasImages(Message message) { 1266 if (mNativeAwContents == 0) return; 1267 nativeDocumentHasImages(mNativeAwContents, message); 1268 } 1269 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 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 */ 1303 public NavigationHistory getNavigationHistory() { 1304 return mContentViewCore.getNavigationHistory(); 1305 } 1306 1307 /** 1308 * @see android.webkit.WebView#getTitle() 1309 */ 1310 public String getTitle() { 1311 return mContentViewCore.getTitle(); 1312 } 1313 1314 /** 1315 * @see android.webkit.WebView#clearHistory() 1316 */ 1317 public void clearHistory() { 1318 mContentViewCore.clearHistory(); 1319 } 1320 1321 public String[] getHttpAuthUsernamePassword(String host, String realm) { 1322 return mBrowserContext.getHttpAuthDatabase(mContentViewCore.getContext()) 1323 .getHttpAuthUsernamePassword(host, realm); 1324 } 1325 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 1402 public float getScale() { 1403 return (float)(mPageScaleFactor * mDIPScale); 1404 } 1405 1406 /** 1407 * @see android.webkit.WebView#flingScroll(int, int) 1408 */ 1409 public void flingScroll(int velocityX, int velocityY) { 1410 mScrollOffsetManager.flingScroll(velocityX, velocityY); 1411 } 1412 1413 /** 1414 * @see android.webkit.WebView#pageUp(boolean) 1415 */ 1416 public boolean pageUp(boolean top) { 1417 return mScrollOffsetManager.pageUp(top); 1418 } 1419 1420 /** 1421 * @see android.webkit.WebView#pageDown(boolean) 1422 */ 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'. 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'. 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'. 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'. 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 */ 1476 public void invokeZoomPicker() { 1477 mContentViewCore.invokeZoomPicker(); 1478 } 1479 1480 /** 1481 * @see ContentViewCore.evaluateJavaScript(String, ContentViewCore.JavaScriptCallback) 1482 */ 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 */ 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 */ 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 */ 1541 public boolean onHoverEvent(MotionEvent event) { 1542 return mContentViewCore.onHoverEvent(event); 1543 } 1544 1545 /** 1546 * @see android.view.View#onGenericMotionEvent() 1547 */ 1548 public boolean onGenericMotionEvent(MotionEvent event) { 1549 return mContentViewCore.onGenericMotionEvent(event); 1550 } 1551 1552 /** 1553 * @see android.view.View#onConfigurationChanged() 1554 */ 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 */ 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 */ 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 */ 1609 public void onWindowFocusChanged(boolean hasWindowFocus) { 1610 mWindowFocused = hasWindowFocus; 1611 mContentViewCore.onWindowFocusChanged(hasWindowFocus); 1612 } 1613 1614 /** 1615 * @see android.view.View#onFocusChanged() 1616 */ 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 */ 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 */ 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 */ 1648 public void onWindowVisibilityChanged(int visibility) { 1649 boolean windowVisible = visibility == View.VISIBLE; 1650 if (mIsWindowVisible == windowVisible) return; 1651 setWindowVisibilityInternal(windowVisible); 1652 } 1653 1654 private void setViewVisibilityInternal(boolean visible) { 1655 mIsViewVisible = visible; 1656 if (mNativeAwContents == 0) return; 1657 nativeSetViewVisibility(mNativeAwContents, mIsViewVisible); 1658 } 1659 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 */ 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 */ 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 */ 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 */ 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 */ 1728 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 1729 return mContentViewCore.getAccessibilityNodeProvider(); 1730 } 1731 1732 /** 1733 * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) 1734 */ 1735 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1736 mContentViewCore.onInitializeAccessibilityNodeInfo(info); 1737 } 1738 1739 /** 1740 * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent) 1741 */ 1742 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1743 mContentViewCore.onInitializeAccessibilityEvent(event); 1744 } 1745 1746 public boolean supportsAccessibilityAction(int action) { 1747 return mContentViewCore.supportsAccessibilityAction(action); 1748 } 1749 1750 /** 1751 * @see android.webkit.WebView#performAccessibilityAction(int, Bundle) 1752 */ 1753 public boolean performAccessibilityAction(int action, Bundle arguments) { 1754 return mContentViewCore.performAccessibilityAction(action, arguments); 1755 } 1756 1757 /** 1758 * @see android.webkit.WebView#clearFormData() 1759 */ 1760 public void hideAutofillPopup() { 1761 if (mAwAutofillManagerDelegate != null) 1762 mAwAutofillManagerDelegate.hideAutofillPopup(); 1763 } 1764 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 1775 private static void onDocumentHasImagesResponse(boolean result, Message message) { 1776 message.arg1 = result ? 1 : 0; 1777 message.sendToTarget(); 1778 } 1779 1780 @CalledByNative 1781 private void onReceivedTouchIconUrl(String url, boolean precomposed) { 1782 mContentsClient.onReceivedTouchIconUrl(url, precomposed); 1783 } 1784 1785 @CalledByNative 1786 private void onReceivedIcon(Bitmap bitmap) { 1787 mContentsClient.onReceivedIcon(bitmap); 1788 mFavicon = bitmap; 1789 } 1790 1791 /** Callback for generateMHTML. */ 1792 @CalledByNative 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