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