ContentViewCore.java revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 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.content.browser; 6 7import android.app.Activity; 8import android.app.ActivityManager; 9import android.content.Context; 10import android.content.pm.ActivityInfo; 11import android.content.pm.PackageManager; 12import android.content.res.Configuration; 13import android.graphics.Bitmap; 14import android.graphics.Canvas; 15import android.graphics.Color; 16import android.graphics.PointF; 17import android.graphics.Rect; 18import android.os.Build; 19import android.os.Bundle; 20import android.os.Handler; 21import android.os.ResultReceiver; 22import android.os.SystemClock; 23import android.util.Log; 24import android.util.Pair; 25import android.view.ActionMode; 26import android.view.InputDevice; 27import android.view.KeyEvent; 28import android.view.MotionEvent; 29import android.view.View; 30import android.view.ViewGroup; 31import android.view.Surface; 32import android.view.Window; 33import android.view.WindowManager; 34import android.view.accessibility.AccessibilityEvent; 35import android.view.accessibility.AccessibilityNodeInfo; 36import android.view.inputmethod.EditorInfo; 37import android.view.inputmethod.InputConnection; 38import android.view.inputmethod.InputMethodManager; 39 40import org.chromium.base.CalledByNative; 41import org.chromium.base.JNINamespace; 42import org.chromium.base.WeakContext; 43import org.chromium.content.app.AppResource; 44import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate; 45import org.chromium.content.browser.accessibility.AccessibilityInjector; 46import org.chromium.content.common.TraceEvent; 47import org.chromium.ui.gfx.NativeWindow; 48 49/** 50 * Provides a Java-side 'wrapper' around a WebContent (native) instance. 51 * Contains all the major functionality necessary to manage the lifecycle of a ContentView without 52 * being tied to the view system. 53 */ 54@JNINamespace("content") 55public class ContentViewCore implements MotionEventDelegate { 56 private static final String TAG = ContentViewCore.class.getName(); 57 58 // The following constants match the ones in chrome/common/page_transition_types.h. 59 // Add more if you need them. 60 public static final int PAGE_TRANSITION_LINK = 0; 61 public static final int PAGE_TRANSITION_TYPED = 1; 62 public static final int PAGE_TRANSITION_AUTO_BOOKMARK = 2; 63 public static final int PAGE_TRANSITION_START_PAGE = 6; 64 65 // Used when ContentView implements a standalone View. 66 public static final int PERSONALITY_VIEW = 0; 67 // Used for Chrome. 68 public static final int PERSONALITY_CHROME = 1; 69 70 // Used to avoid enabling zooming in / out if resulting zooming will 71 // produce little visible difference. 72 private static final float ZOOM_CONTROLS_EPSILON = 0.007f; 73 74 // To avoid checkerboard, we clamp the fling velocity based on the maximum number of tiles 75 // should be allowed to upload per 100ms. 76 private final int mMaxNumUploadTiles; 77 78 // Personality of the ContentView. 79 private final int mPersonality; 80 81 /** 82 * Interface that consumers of {@link ContentViewCore} must implement to allow the proper 83 * dispatching of view methods through the containing view. 84 * 85 * <p> 86 * All methods with the "super_" prefix should be routed to the parent of the 87 * implementing container view. 88 */ 89 @SuppressWarnings("javadoc") 90 public static interface InternalAccessDelegate { 91 /** 92 * @see View#drawChild(Canvas, View, long) 93 */ 94 boolean drawChild(Canvas canvas, View child, long drawingTime); 95 96 /** 97 * @see View#onKeyUp(keyCode, KeyEvent) 98 */ 99 boolean super_onKeyUp(int keyCode, KeyEvent event); 100 101 /** 102 * @see View#dispatchKeyEventPreIme(KeyEvent) 103 */ 104 boolean super_dispatchKeyEventPreIme(KeyEvent event); 105 106 /** 107 * @see View#dispatchKeyEvent(KeyEvent) 108 */ 109 boolean super_dispatchKeyEvent(KeyEvent event); 110 111 /** 112 * @see View#onGenericMotionEvent(MotionEvent) 113 */ 114 boolean super_onGenericMotionEvent(MotionEvent event); 115 116 /** 117 * @see View#onConfigurationChanged(Configuration) 118 */ 119 void super_onConfigurationChanged(Configuration newConfig); 120 121 /** 122 * @see View#onScrollChanged(int, int, int, int) 123 */ 124 void onScrollChanged(int l, int t, int oldl, int oldt); 125 126 /** 127 * @see View#awakenScrollBars() 128 */ 129 boolean awakenScrollBars(); 130 131 /** 132 * @see View#awakenScrollBars(int, boolean) 133 */ 134 boolean super_awakenScrollBars(int startDelay, boolean invalidate); 135 } 136 137 private final Context mContext; 138 private ViewGroup mContainerView; 139 private InternalAccessDelegate mContainerViewInternals; 140 private WebContentsObserverAndroid mWebContentsObserver; 141 142 private ContentViewClient mContentViewClient; 143 144 private ContentSettings mContentSettings; 145 146 // Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit(). 147 private int mNativeContentViewCore = 0; 148 149 // The vsync monitor is used to lock the compositor to the display refresh rate. 150 private VSyncMonitor mVSyncMonitor; 151 152 // The VSyncMonitor gives the timebase for the actual vsync, but we don't want render until 153 // we have had a chance for input events to propagate to the compositor thread. This takes 154 // 3 ms typically, so we adjust the vsync timestamps forward by a bit to give input events a 155 // chance to arrive. 156 private static final long INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS = 3200; 157 158 private ContentViewGestureHandler mContentViewGestureHandler; 159 private ZoomManager mZoomManager; 160 161 // Currently ContentView's scrolling is handled by the native side. We keep a cached copy of the 162 // scroll offset and content size so that we can display the scrollbar correctly. In the future, 163 // we may switch to tile rendering and handle the scrolling in the Java level. 164 165 // Cached scroll offset from the native 166 private int mNativeScrollX; 167 private int mNativeScrollY; 168 169 // Cached content size from the native 170 private int mContentWidth; 171 private int mContentHeight; 172 173 // Cached size of the viewport 174 private int mViewportWidth; 175 private int mViewportHeight; 176 177 // Cached page scale factor from native 178 private float mNativePageScaleFactor = 1.0f; 179 private float mNativeMinimumScale = 1.0f; 180 private float mNativeMaximumScale = 1.0f; 181 182 private PopupZoomer mPopupZoomer; 183 184 private Runnable mFakeMouseMoveRunnable = null; 185 186 // Only valid when focused on a text / password field. 187 private ImeAdapter mImeAdapter; 188 private ImeAdapter.AdapterInputConnection mInputConnection; 189 190 private SelectionHandleController mSelectionHandleController; 191 private InsertionHandleController mInsertionHandleController; 192 // These offsets in document space with page scale normalized to 1.0. 193 private final PointF mStartHandleNormalizedPoint = new PointF(); 194 private final PointF mEndHandleNormalizedPoint = new PointF(); 195 private final PointF mInsertionHandleNormalizedPoint = new PointF(); 196 197 // Tracks whether a selection is currently active. When applied to selected text, indicates 198 // whether the last selected text is still highlighted. 199 private boolean mHasSelection; 200 private String mLastSelectedText; 201 private boolean mSelectionEditable; 202 private ActionMode mActionMode; 203 204 // Delegate that will handle GET downloads, and be notified of completion of POST downloads. 205 private ContentViewDownloadDelegate mDownloadDelegate; 206 207 // Whether a physical keyboard is connected. 208 private boolean mKeyboardConnected; 209 210 private final VSyncMonitor.Listener mVSyncListener = new VSyncMonitor.Listener() { 211 @Override 212 public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) { 213 TraceEvent.instant("VSync"); 214 if (mNativeContentViewCore == 0 || mVSyncMonitor == null) { 215 return; 216 } 217 // Compensate for input event lag. Input events are delivered immediately on pre-JB 218 // releases, so this adjustment is only done for later versions. 219 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 220 vsyncTimeMicros += INPUT_EVENT_LAG_FROM_VSYNC_MICROSECONDS; 221 } 222 nativeUpdateVSyncParameters(mNativeContentViewCore, vsyncTimeMicros, 223 mVSyncMonitor.getVSyncPeriodInMicroseconds()); 224 } 225 }; 226 227 // The AccessibilityInjector that handles loading Accessibility scripts into the web page. 228 private AccessibilityInjector mAccessibilityInjector; 229 230 // Temporary notification to tell onSizeChanged to focus a form element, 231 // because the OSK was just brought up. 232 private boolean mFocusOnNextSizeChanged = false; 233 private boolean mUnfocusOnNextSizeChanged = false; 234 235 private boolean mNeedUpdateOrientationChanged; 236 237 // Used to keep track of whether we should try to undo the last zoom-to-textfield operation. 238 private boolean mScrolledAndZoomedFocusedEditableNode = false; 239 240 // Whether we use hardware-accelerated drawing. 241 private boolean mHardwareAccelerated = false; 242 243 /** 244 * Enable multi-process ContentView. This should be called by the application before 245 * constructing any ContentView instances. If enabled, ContentView will run renderers in 246 * separate processes up to the number of processes specified by maxRenderProcesses. If this is 247 * not called then the default is to run the renderer in the main application on a separate 248 * thread. 249 * 250 * @param context Context used to obtain the application context. 251 * @param maxRendererProcesses Limit on the number of renderers to use. Each tab runs in its own 252 * process until the maximum number of processes is reached. The special value of 253 * MAX_RENDERERS_SINGLE_PROCESS requests single-process mode where the renderer will run in the 254 * application process in a separate thread. If the special value MAX_RENDERERS_AUTOMATIC is 255 * used then the number of renderers will be determined based on the device memory class. The 256 * maximum number of allowed renderers is capped by MAX_RENDERERS_LIMIT. 257 * @return Whether the process actually needed to be initialized (false if already running). 258 */ 259 public static boolean enableMultiProcess(Context context, int maxRendererProcesses) { 260 return AndroidBrowserProcess.initContentViewProcess(context, maxRendererProcesses); 261 } 262 263 /** 264 * Initialize the process as the platform browser. This must be called before accessing 265 * ContentView in order to treat this as a Chromium browser process. 266 * 267 * @param context Context used to obtain the application context. 268 * @param maxRendererProcesses Same as ContentView.enableMultiProcess() 269 * @return Whether the process actually needed to be initialized (false if already running). 270 */ 271 public static boolean initChromiumBrowserProcess(Context context, int maxRendererProcesses) { 272 return AndroidBrowserProcess.initChromiumBrowserProcess(context, maxRendererProcesses); 273 } 274 275 /** 276 * Constructs a new ContentViewCore. Embedders must call initialize() after constructing 277 * a ContentViewCore and before using it. 278 * 279 * @param context The context used to create this. 280 * @param personality The type of ContentViewCore being created. 281 */ 282 public ContentViewCore(Context context, int personality) { 283 mContext = context; 284 285 // All application resources must be registered by the time the content view is created. 286 // This should be omitted in final release builds where assertions are disabled. 287 // TODO(leandrogracia): re-enable this as soon as crbug.com/136704 is fixed. 288 // assert AppResource.verifyResourceRegistration(); 289 290 WeakContext.initializeWeakContext(context); 291 // By default, ContentView will initialize single process mode. The call to 292 // initContentViewProcess below is ignored if either the ContentView host called 293 // enableMultiProcess() or the platform browser called initChromiumBrowserProcess(). 294 AndroidBrowserProcess.initContentViewProcess( 295 context, AndroidBrowserProcess.MAX_RENDERERS_SINGLE_PROCESS); 296 297 mPersonality = personality; 298 HeapStatsLogger.init(mContext.getApplicationContext()); 299 300 // We should set this constant based on the GPU performance. As it doesn't exist in the 301 // framework yet, we use the memory class as an indicator. Here are some good values that 302 // we determined via manual experimentation: 303 // 304 // Device Screen size Memory class Tiles per 100ms 305 // ================= ================== ============== ===================== 306 // Nexus S 480 x 800 128 9 (3 rows portrait) 307 // Galaxy Nexus 720 x 1280 256 12 (3 rows portrait) 308 // Nexus 7 1280 x 800 384 18 (3 rows landscape) 309 // Nexus 10 2560 x 1600 512 44 (4 rows landscape) 310 // 311 // Here is a spreadsheet with the data, plus a curve fit: 312 // https://docs.google.com/a/chromium.org/spreadsheet/pub?key=0AlNYk7HM2CgQdG1vUWRVWkU3ODRTc1B2SVF3ZTJBUkE&output=html 313 // That gives us tiles-per-100ms of 8, 13, 22, 37 for the devices listed above. 314 // Not too bad, and it should behave reasonably sensibly for unknown devices. 315 // If you want to tweak these constants, please update the spreadsheet appropriately. 316 // 317 // The curve is y = b * m^x, with coefficients as follows: 318 double b = 4.70009671080384; 319 double m = 1.00404437546897; 320 int memoryClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)) 321 .getLargeMemoryClass(); 322 mMaxNumUploadTiles = (int) Math.round(b * Math.pow(m, memoryClass)); 323 } 324 325 /** 326 * @return The context used for creating this ContentViewCore. 327 */ 328 public Context getContext() { 329 return mContext; 330 } 331 332 /** 333 * @return The ViewGroup that all view actions of this ContentViewCore should interact with. 334 */ 335 protected ViewGroup getContainerView() { 336 return mContainerView; 337 } 338 339 /** 340 * Returns a delegate that can be used to add and remove views from the ContainerView. 341 * 342 * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same 343 * way. In particular, the Android WebView has limitations on what implementation details can 344 * be provided via a child view, as they are visible in the API and could introduce 345 * compatibility breaks with existing applications. If in doubt, contact the 346 * android_webview/OWNERS 347 * 348 * @return A ContainerViewDelegate that can be used to add and remove views. 349 */ 350 @CalledByNative 351 public ContainerViewDelegate getContainerViewDelegate() { 352 return new ContainerViewDelegate() { 353 @Override 354 public void addViewToContainerView(View view) { 355 mContainerView.addView(view); 356 } 357 358 @Override 359 public void removeViewFromContainerView(View view) { 360 mContainerView.removeView(view); 361 } 362 }; 363 } 364 365 private ImeAdapter createImeAdapter(Context context) { 366 return new ImeAdapter(context, getSelectionHandleController(), 367 getInsertionHandleController(), 368 new ImeAdapter.ViewEmbedder() { 369 @Override 370 public void onImeEvent(boolean isFinish) { 371 getContentViewClient().onImeEvent(); 372 if (!isFinish) { 373 undoScrollFocusedEditableNodeIntoViewIfNeeded(false); 374 } 375 } 376 377 @Override 378 public void onSetFieldValue() { 379 scrollFocusedEditableNodeIntoView(); 380 } 381 382 @Override 383 public void onDismissInput() { 384 } 385 386 @Override 387 public View getAttachedView() { 388 return mContainerView; 389 } 390 391 @Override 392 public ResultReceiver getNewShowKeyboardReceiver() { 393 return new ResultReceiver(new Handler()) { 394 @Override 395 public void onReceiveResult(int resultCode, Bundle resultData) { 396 if (resultCode == InputMethodManager.RESULT_SHOWN) { 397 // If OSK is newly shown, delay the form focus until 398 // the onSizeChanged (in order to adjust relative to the 399 // new size). 400 mFocusOnNextSizeChanged = true; 401 } else if (resultCode == 402 InputMethodManager.RESULT_UNCHANGED_SHOWN) { 403 // If the OSK was already there, focus the form immediately. 404 scrollFocusedEditableNodeIntoView(); 405 } else { 406 undoScrollFocusedEditableNodeIntoViewIfNeeded(false); 407 } 408 } 409 }; 410 } 411 } 412 ); 413 } 414 415 /** 416 * Returns true if the given Activity has hardware acceleration enabled 417 * in its manifest, or in its foreground window. 418 * 419 * TODO(husky): Remove when initialize() is refactored (see TODO there) 420 * TODO(dtrainor) This is still used by other classes. Make sure to pull some version of this 421 * out before removing it. 422 */ 423 public static boolean hasHardwareAcceleration(Activity activity) { 424 // Has HW acceleration been enabled manually in the current window? 425 Window window = activity.getWindow(); 426 if (window != null) { 427 if ((window.getAttributes().flags 428 & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) { 429 return true; 430 } 431 } 432 433 // Has HW acceleration been enabled in the manifest? 434 try { 435 ActivityInfo info = activity.getPackageManager().getActivityInfo( 436 activity.getComponentName(), 0); 437 if ((info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { 438 return true; 439 } 440 } catch (PackageManager.NameNotFoundException e) { 441 Log.e("Chrome", "getActivityInfo(self) should not fail"); 442 } 443 444 return false; 445 } 446 447 /** 448 * Returns true if the given Context is a HW-accelerated Activity. 449 * 450 * TODO(husky): Remove when initialize() is refactored (see TODO there) 451 */ 452 private static boolean hasHardwareAcceleration(Context context) { 453 if (context instanceof Activity) { 454 return hasHardwareAcceleration((Activity) context); 455 } 456 return false; 457 } 458 459 /** 460 * 461 * @param containerView The view that will act as a container for all views created by this. 462 * @param internalDispatcher Handles dispatching all hidden or super methods to the 463 * containerView. 464 * @param nativeWebContents A pointer to the native web contents. 465 * @param nativeWindow An instance of the NativeWindow. 466 * @param isAccessFromFileURLsGrantedByDefault Default WebSettings configuration. 467 */ 468 // Perform important post-construction set up of the ContentViewCore. 469 // We do not require the containing view in the constructor to allow embedders to create a 470 // ContentViewCore without having fully created its containing view. The containing view 471 // is a vital component of the ContentViewCore, so embedders must exercise caution in what 472 // they do with the ContentViewCore before calling initialize(). 473 // We supply the nativeWebContents pointer here rather than in the constructor to allow us 474 // to set the private browsing mode at a later point for the WebView implementation. 475 // Note that the caller remains the owner of the nativeWebContents and is responsible for 476 // deleting it after destroying the ContentViewCore. 477 public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher, 478 int nativeWebContents, NativeWindow nativeWindow, 479 boolean isAccessFromFileURLsGrantedByDefault) { 480 // Check whether to use hardware acceleration. This is a bit hacky, and 481 // only works if the Context is actually an Activity (as it is in the 482 // Chrome application). 483 // 484 // What we're doing here is checking whether the app has *requested* 485 // hardware acceleration by setting the appropriate flags. This does not 486 // necessarily mean we're going to *get* hardware acceleration -- that's 487 // up to the Android framework. 488 // 489 // TODO(husky): Once the native code has been updated so that the 490 // HW acceleration flag can be set dynamically (Grace is doing this), 491 // move this check into onAttachedToWindow(), where we can test for 492 // HW support directly. 493 mHardwareAccelerated = hasHardwareAcceleration(mContext); 494 mContainerView = containerView; 495 mNativeContentViewCore = nativeInit(mHardwareAccelerated, nativeWebContents, 496 nativeWindow.getNativePointer()); 497 mContentSettings = new ContentSettings( 498 this, mNativeContentViewCore, isAccessFromFileURLsGrantedByDefault); 499 initializeContainerView(internalDispatcher); 500 if (mPersonality == PERSONALITY_VIEW) { 501 setAllUserAgentOverridesInHistory(); 502 } 503 504 mAccessibilityInjector = AccessibilityInjector.newInstance(this); 505 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary(); 506 507 String contentDescription = "Web View"; 508 if (AppResource.STRING_CONTENT_VIEW_CONTENT_DESCRIPTION == 0) { 509 Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified."); 510 } else { 511 contentDescription = mContext.getResources().getString( 512 AppResource.STRING_CONTENT_VIEW_CONTENT_DESCRIPTION); 513 } 514 mContainerView.setContentDescription(contentDescription); 515 mWebContentsObserver = new WebContentsObserverAndroid(this) { 516 @Override 517 public void didStartLoading(String url) { 518 hidePopupDialog(); 519 } 520 }; 521 } 522 523 @CalledByNative 524 void onNativeContentViewCoreDestroyed(int nativeContentViewCore) { 525 assert nativeContentViewCore == mNativeContentViewCore; 526 mNativeContentViewCore = 0; 527 } 528 529 /** 530 * Initializes the View that will contain all Views created by the ContentViewCore. 531 * 532 * @param internalDispatcher Handles dispatching all hidden or super methods to the 533 * containerView. 534 */ 535 private void initializeContainerView(InternalAccessDelegate internalDispatcher) { 536 TraceEvent.begin(); 537 mContainerViewInternals = internalDispatcher; 538 539 mContainerView.setWillNotDraw(false); 540 mContainerView.setFocusable(true); 541 mContainerView.setFocusableInTouchMode(true); 542 mContainerView.setClickable(true); 543 544 if (mPersonality == PERSONALITY_CHROME) { 545 // Doing this in PERSONALITY_VIEW mode causes rendering problems in our 546 // current WebView test case (the HTMLViewer application). 547 // TODO(benm): Figure out why this is the case. 548 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) { 549 mContainerView.setHorizontalScrollBarEnabled(false); 550 mContainerView.setVerticalScrollBarEnabled(false); 551 } 552 } 553 554 mZoomManager = new ZoomManager(mContext, this); 555 mZoomManager.updateMultiTouchSupport(); 556 mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this, mZoomManager); 557 558 mNativeScrollX = mNativeScrollY = 0; 559 mNativePageScaleFactor = 1.0f; 560 initPopupZoomer(mContext); 561 mImeAdapter = createImeAdapter(mContext); 562 mKeyboardConnected = mContainerView.getResources().getConfiguration().keyboard 563 != Configuration.KEYBOARD_NOKEYS; 564 mVSyncMonitor = new VSyncMonitor(mContext, mVSyncListener); 565 TraceEvent.end(); 566 } 567 568 private void initPopupZoomer(Context context){ 569 mPopupZoomer = new PopupZoomer(context); 570 mContainerView.addView(mPopupZoomer); 571 PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() { 572 @Override 573 public boolean onSingleTap(View v, MotionEvent e) { 574 mContainerView.requestFocus(); 575 if (mNativeContentViewCore != 0) { 576 nativeSingleTap(mNativeContentViewCore, e.getEventTime(), (int) e.getX(), 577 (int) e.getY(), true); 578 } 579 return true; 580 } 581 582 @Override 583 public boolean onLongPress(View v, MotionEvent e) { 584 if (mNativeContentViewCore != 0) { 585 nativeLongPress(mNativeContentViewCore, e.getEventTime(), (int) e.getX(), 586 (int) e.getY(), true); 587 } 588 return true; 589 } 590 }; 591 mPopupZoomer.setOnTapListener(listener); 592 } 593 594 /** 595 * @return Whether the configured personality of this ContentView is {@link #PERSONALITY_VIEW}. 596 */ 597 boolean isPersonalityView() { 598 switch (mPersonality) { 599 case PERSONALITY_VIEW: 600 return true; 601 case PERSONALITY_CHROME: 602 return false; 603 default: 604 Log.e(TAG, "Unknown ContentView personality: " + mPersonality); 605 return false; 606 } 607 } 608 609 /** 610 * Destroy the internal state of the ContentView. This method may only be 611 * called after the ContentView has been removed from the view system. No 612 * other methods may be called on this ContentView after this method has 613 * been called. 614 */ 615 public void destroy() { 616 hidePopupDialog(); 617 if (mVSyncMonitor != null) mVSyncMonitor.unregisterListener(); 618 if (mNativeContentViewCore != 0) { 619 nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore); 620 } 621 mNativeContentViewCore = 0; 622 mContentSettings = null; 623 } 624 625 /** 626 * Returns true initially, false after destroy() has been called. 627 * It is illegal to call any other public method after destroy(). 628 */ 629 public boolean isAlive() { 630 return mNativeContentViewCore != 0; 631 } 632 633 /** 634 * This is only useful for passing over JNI to native code that requires ContentViewCore*. 635 * @return native ContentViewCore pointer. 636 */ 637 public int getNativeContentViewCore() { 638 return mNativeContentViewCore; 639 } 640 641 /** 642 * For internal use. Throws IllegalStateException if mNativeContentView is 0. 643 * Use this to ensure we get a useful Java stack trace, rather than a native 644 * crash dump, from use-after-destroy bugs in Java code. 645 */ 646 void checkIsAlive() throws IllegalStateException { 647 if (!isAlive()) { 648 throw new IllegalStateException("ContentView used after destroy() was called"); 649 } 650 } 651 652 public void setContentViewClient(ContentViewClient client) { 653 if (client == null) { 654 throw new IllegalArgumentException("The client can't be null."); 655 } 656 mContentViewClient = client; 657 } 658 659 ContentViewClient getContentViewClient() { 660 if (mContentViewClient == null) { 661 // We use the Null Object pattern to avoid having to perform a null check in this class. 662 // We create it lazily because most of the time a client will be set almost immediately 663 // after ContentView is created. 664 mContentViewClient = new ContentViewClient(); 665 // We don't set the native ContentViewClient pointer here on purpose. The native 666 // implementation doesn't mind a null delegate and using one is better than passing a 667 // Null Object, since we cut down on the number of JNI calls. 668 } 669 return mContentViewClient; 670 } 671 672 public int getBackgroundColor() { 673 if (mNativeContentViewCore != 0) { 674 return nativeGetBackgroundColor(mNativeContentViewCore); 675 } 676 return Color.WHITE; 677 } 678 679 public void setBackgroundColor(int color) { 680 if (mNativeContentViewCore != 0 && getBackgroundColor() != color) { 681 nativeSetBackgroundColor(mNativeContentViewCore, color); 682 } 683 } 684 685 /** 686 * Load url without fixing up the url string. Consumers of ContentView are responsible for 687 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left 688 * off during user input). 689 * 690 * @param pararms Parameters for this load. 691 */ 692 public void loadUrl(LoadUrlParams params) { 693 if (mNativeContentViewCore == 0) return; 694 if (isPersonalityView()) { 695 // For WebView, always use the user agent override, which is set 696 // every time the user agent in ContentSettings is modified. 697 params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE); 698 } 699 700 nativeLoadUrl(mNativeContentViewCore, 701 params.mUrl, 702 params.mLoadUrlType, 703 params.mTransitionType, 704 params.mUaOverrideOption, 705 params.getExtraHeadersString(), 706 params.mPostData, 707 params.mBaseUrlForDataUrl, 708 params.mVirtualUrlForDataUrl, 709 params.mCanLoadLocalResources); 710 } 711 712 void setAllUserAgentOverridesInHistory() { 713 nativeSetAllUserAgentOverridesInHistory(mNativeContentViewCore, 714 mContentSettings.getUserAgentString()); 715 } 716 717 /** 718 * Stops loading the current web contents. 719 */ 720 public void stopLoading() { 721 if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore); 722 } 723 724 /** 725 * Get the URL of the current page. 726 * 727 * @return The URL of the current page. 728 */ 729 public String getUrl() { 730 if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore); 731 return null; 732 } 733 734 /** 735 * Get the title of the current page. 736 * 737 * @return The title of the current page. 738 */ 739 public String getTitle() { 740 if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore); 741 return null; 742 } 743 744 @CalledByNative 745 public int getWidth() { 746 return mViewportWidth; 747 } 748 749 @CalledByNative 750 public int getHeight() { 751 return mViewportHeight; 752 } 753 754 /** 755 * @see android.webkit.WebView#getContentHeight() 756 */ 757 public int getContentHeight() { 758 return (int) (mContentHeight / mNativePageScaleFactor); 759 } 760 761 /** 762 * @see android.webkit.WebView#getContentWidth() 763 */ 764 public int getContentWidth() { 765 return (int) (mContentWidth / mNativePageScaleFactor); 766 } 767 768 public Bitmap getBitmap() { 769 return getBitmap(getWidth(), getHeight()); 770 } 771 772 public Bitmap getBitmap(int width, int height) { 773 if (width == 0 || height == 0 || getWidth() == 0 || getHeight() == 0) return null; 774 775 Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 776 777 if (mNativeContentViewCore != 0 && 778 nativePopulateBitmapFromCompositor(mNativeContentViewCore, b)) { 779 // If we successfully grabbed a bitmap, check if we have to draw the Android overlay 780 // components as well. 781 if (mContainerView.getChildCount() > 0) { 782 Canvas c = new Canvas(b); 783 c.scale(1.0f, -1.0f, width / 2.0f, height / 2.0f); 784 c.scale(width / (float) getWidth(), height / (float) getHeight()); 785 mContainerView.draw(c); 786 } 787 return b; 788 } 789 790 return null; 791 } 792 793 /** 794 * Generates a bitmap of the content that is performance optimized based on capture time. 795 * 796 * <p> 797 * To have a consistent capture time across devices, we will scale down the captured bitmap 798 * where necessary to reduce the time to generate the bitmap. 799 * 800 * @param width The width of the content to be captured. 801 * @param height The height of the content to be captured. 802 * @return A pair of the generated bitmap, and the scale that needs to be applied to return the 803 * bitmap to it's original size (i.e. if the bitmap is scaled down 50%, this 804 * will be 2). 805 */ 806 public Pair<Bitmap, Float> getScaledPerformanceOptimizedBitmap(int width, int height) { 807 float scale = 1f; 808 // On tablets, always scale down to MDPI for performance reasons. 809 if (DeviceUtils.isTablet(getContext())) { 810 scale = getContext().getResources().getDisplayMetrics().density; 811 } 812 return Pair.create( 813 getBitmap((int) (width / scale), (int) (height / scale)), 814 scale); 815 } 816 817 /** 818 * @return Whether the ContentView is covered by an overlay that is more than half 819 * of it's surface. This is used to determine if we need to do a slow bitmap capture or 820 * to show the ContentView without them. 821 */ 822 public boolean hasLargeOverlay() { 823 // TODO(nileshagrawal): Implement this. 824 return false; 825 } 826 827 /** 828 * @return Whether the current WebContents has a previous navigation entry. 829 */ 830 public boolean canGoBack() { 831 return mNativeContentViewCore != 0 && nativeCanGoBack(mNativeContentViewCore); 832 } 833 834 /** 835 * @return Whether the current WebContents has a navigation entry after the current one. 836 */ 837 public boolean canGoForward() { 838 return mNativeContentViewCore != 0 && nativeCanGoForward(mNativeContentViewCore); 839 } 840 841 /** 842 * @param offset The offset into the navigation history. 843 * @return Whether we can move in history by given offset 844 */ 845 public boolean canGoToOffset(int offset) { 846 return mNativeContentViewCore != 0 && nativeCanGoToOffset(mNativeContentViewCore, offset); 847 } 848 849 /** 850 * Navigates to the specified offset from the "current entry". Does nothing if the offset is out 851 * of bounds. 852 * @param offset The offset into the navigation history. 853 */ 854 public void goToOffset(int offset) { 855 if (mNativeContentViewCore != 0) nativeGoToOffset(mNativeContentViewCore, offset); 856 } 857 858 /** 859 * Goes to the navigation entry before the current one. 860 */ 861 public void goBack() { 862 if (mNativeContentViewCore != 0) nativeGoBack(mNativeContentViewCore); 863 } 864 865 /** 866 * Goes to the navigation entry following the current one. 867 */ 868 public void goForward() { 869 if (mNativeContentViewCore != 0) nativeGoForward(mNativeContentViewCore); 870 } 871 872 /** 873 * Reload the current page. 874 */ 875 public void reload() { 876 mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary(); 877 if (mNativeContentViewCore != 0) nativeReload(mNativeContentViewCore); 878 } 879 880 /** 881 * Cancel the pending reload. 882 */ 883 public void cancelPendingReload() { 884 if (mNativeContentViewCore != 0) nativeCancelPendingReload(mNativeContentViewCore); 885 } 886 887 /** 888 * Continue the pending reload. 889 */ 890 public void continuePendingReload() { 891 if (mNativeContentViewCore != 0) nativeContinuePendingReload(mNativeContentViewCore); 892 } 893 894 /** 895 * Clears the ContentViewCore's page history in both the backwards and 896 * forwards directions. 897 */ 898 public void clearHistory() { 899 if (mNativeContentViewCore != 0) nativeClearHistory(mNativeContentViewCore); 900 } 901 902 String getSelectedText() { 903 return mHasSelection ? mLastSelectedText : ""; 904 } 905 906 // End FrameLayout overrides. 907 908 /** 909 * @see {@link android.webkit.WebView#flingScroll(int, int)} 910 */ 911 public void flingScroll(int vx, int vy) { 912 // Notes: 913 // (1) Use large negative values for the x/y parameters so we don't accidentally scroll a 914 // nested frame. 915 // (2) vx and vy are inverted to match WebView behavior. 916 mContentViewGestureHandler.fling( 917 System.currentTimeMillis(), -Integer.MAX_VALUE, -Integer.MIN_VALUE, -vx, -vy); 918 } 919 920 /** 921 * @see View#onTouchEvent(MotionEvent) 922 */ 923 public boolean onTouchEvent(MotionEvent event) { 924 undoScrollFocusedEditableNodeIntoViewIfNeeded(false); 925 return mContentViewGestureHandler.onTouchEvent(event); 926 } 927 928 /** 929 * @return ContentViewGestureHandler for all MotionEvent and gesture related calls. 930 */ 931 ContentViewGestureHandler getContentViewGestureHandler() { 932 return mContentViewGestureHandler; 933 } 934 935 @Override 936 public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) { 937 if (mNativeContentViewCore != 0) { 938 return nativeSendTouchEvent(mNativeContentViewCore, timeMs, action, pts); 939 } 940 return false; 941 } 942 943 @SuppressWarnings("unused") 944 @CalledByNative 945 private void hasTouchEventHandlers(boolean hasTouchHandlers) { 946 mContentViewGestureHandler.hasTouchEventHandlers(hasTouchHandlers); 947 } 948 949 @SuppressWarnings("unused") 950 @CalledByNative 951 private void confirmTouchEvent(boolean handled) { 952 mContentViewGestureHandler.confirmTouchEvent(handled); 953 } 954 955 @Override 956 public boolean sendGesture(int type, long timeMs, int x, int y, Bundle b) { 957 if (mNativeContentViewCore == 0) return false; 958 959 switch (type) { 960 case ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE: 961 nativeShowPressState(mNativeContentViewCore, timeMs, x, y); 962 return true; 963 case ContentViewGestureHandler.GESTURE_DOUBLE_TAP: 964 nativeDoubleTap(mNativeContentViewCore, timeMs, x, y); 965 return true; 966 case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UP: 967 nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false); 968 return true; 969 case ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED: 970 handleTapOrPress(timeMs, x, y, false, 971 b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false)); 972 return true; 973 case ContentViewGestureHandler.GESTURE_LONG_PRESS: 974 handleTapOrPress(timeMs, x, y, true, false); 975 return true; 976 case ContentViewGestureHandler.GESTURE_SCROLL_START: 977 nativeScrollBegin(mNativeContentViewCore, timeMs, x, y); 978 return true; 979 case ContentViewGestureHandler.GESTURE_SCROLL_BY: { 980 int dx = b.getInt(ContentViewGestureHandler.DISTANCE_X); 981 int dy = b.getInt(ContentViewGestureHandler.DISTANCE_Y); 982 nativeScrollBy(mNativeContentViewCore, timeMs, x, y, dx, dy); 983 return true; 984 } 985 case ContentViewGestureHandler.GESTURE_SCROLL_END: 986 nativeScrollEnd(mNativeContentViewCore, timeMs); 987 return true; 988 case ContentViewGestureHandler.GESTURE_FLING_START: 989 nativeFlingStart(mNativeContentViewCore, timeMs, x, y, 990 clampFlingVelocityX(b.getInt(ContentViewGestureHandler.VELOCITY_X, 0)), 991 clampFlingVelocityY(b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0))); 992 return true; 993 case ContentViewGestureHandler.GESTURE_FLING_CANCEL: 994 nativeFlingCancel(mNativeContentViewCore, timeMs); 995 return true; 996 case ContentViewGestureHandler.GESTURE_PINCH_BEGIN: 997 nativePinchBegin(mNativeContentViewCore, timeMs, x, y); 998 return true; 999 case ContentViewGestureHandler.GESTURE_PINCH_BY: 1000 nativePinchBy(mNativeContentViewCore, timeMs, x, y, 1001 b.getFloat(ContentViewGestureHandler.DELTA, 0)); 1002 return true; 1003 case ContentViewGestureHandler.GESTURE_PINCH_END: 1004 nativePinchEnd(mNativeContentViewCore, timeMs); 1005 return true; 1006 default: 1007 return false; 1008 } 1009 } 1010 1011 /** 1012 * Injects the passed JavaScript code in the current page and evaluates it. 1013 * Once evaluated, an asynchronous call to 1014 * ContentViewClient.onJavaScriptEvaluationResult is made. Used in automation 1015 * tests. 1016 * 1017 * @return an id that is passed along in the asynchronous onJavaScriptEvaluationResult callback 1018 * @throws IllegalStateException If the ContentView has been destroyed. 1019 */ 1020 public int evaluateJavaScript(String script) throws IllegalStateException { 1021 checkIsAlive(); 1022 return nativeEvaluateJavaScript(script); 1023 } 1024 1025 /** 1026 * This method should be called when the containing activity is paused. 1027 */ 1028 public void onActivityPause() { 1029 TraceEvent.begin(); 1030 hidePopupDialog(); 1031 nativeOnHide(mNativeContentViewCore); 1032 setAccessibilityState(false); 1033 TraceEvent.end(); 1034 mVSyncMonitor.stop(); 1035 } 1036 1037 /** 1038 * This method should be called when the containing activity is resumed. 1039 */ 1040 public void onActivityResume() { 1041 nativeOnShow(mNativeContentViewCore); 1042 setAccessibilityState(true); 1043 } 1044 1045 /** 1046 * To be called when the ContentView is shown. 1047 */ 1048 public void onShow() { 1049 nativeOnShow(mNativeContentViewCore); 1050 setAccessibilityState(true); 1051 } 1052 1053 /** 1054 * To be called when the ContentView is hidden. 1055 */ 1056 public void onHide() { 1057 hidePopupDialog(); 1058 setAccessibilityState(false); 1059 nativeOnHide(mNativeContentViewCore); 1060 mVSyncMonitor.stop(); 1061 } 1062 1063 /** 1064 * Return the ContentSettings object used to control the settings for this 1065 * ContentViewCore. 1066 * 1067 * Note that when ContentView is used in the PERSONALITY_CHROME role, 1068 * ContentSettings can only be used for retrieving settings values. For 1069 * modifications, ChromeNativePreferences is to be used. 1070 * @return A ContentSettings object that can be used to control this 1071 * ContentViewCore's settings. 1072 */ 1073 public ContentSettings getContentSettings() { 1074 return mContentSettings; 1075 } 1076 1077 @Override 1078 public boolean didUIStealScroll(float x, float y) { 1079 return getContentViewClient().shouldOverrideScroll( 1080 x, y, computeHorizontalScrollOffset(), computeVerticalScrollOffset()); 1081 } 1082 1083 @Override 1084 public boolean hasFixedPageScale() { 1085 return mNativeMinimumScale == mNativeMaximumScale; 1086 } 1087 1088 private void hidePopupDialog() { 1089 SelectPopupDialog.hide(this); 1090 hideHandles(); 1091 hideSelectActionBar(); 1092 } 1093 1094 void hideSelectActionBar() { 1095 if (mActionMode != null) { 1096 mActionMode.finish(); 1097 } 1098 } 1099 1100 /** 1101 * @see View#onAttachedToWindow() 1102 */ 1103 @SuppressWarnings("javadoc") 1104 public void onAttachedToWindow() { 1105 setAccessibilityState(true); 1106 } 1107 1108 /** 1109 * @see View#onDetachedFromWindow() 1110 */ 1111 @SuppressWarnings("javadoc") 1112 public void onDetachedFromWindow() { 1113 setAccessibilityState(false); 1114 } 1115 1116 /** 1117 * @see View#onVisibilityChanged(android.view.View, int) 1118 */ 1119 public void onVisibilityChanged(View changedView, int visibility) { 1120 if (visibility != View.VISIBLE) { 1121 if (mContentSettings.supportZoom()) { 1122 mZoomManager.dismissZoomPicker(); 1123 } 1124 } 1125 } 1126 1127 @CalledByNative 1128 private void onWebPreferencesUpdated() { 1129 mContentSettings.syncSettings(); 1130 } 1131 1132 /** 1133 * @see View#onCreateInputConnection(EditorInfo) 1134 */ 1135 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 1136 if (!mImeAdapter.hasTextInputType()) { 1137 // Although onCheckIsTextEditor will return false in this case, the EditorInfo 1138 // is still used by the InputMethodService. Need to make sure the IME doesn't 1139 // enter fullscreen mode. 1140 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; 1141 } 1142 mInputConnection = ImeAdapter.AdapterInputConnection.getInstance( 1143 mContainerView, mImeAdapter, outAttrs); 1144 return mInputConnection; 1145 } 1146 1147 /** 1148 * @see View#onCheckIsTextEditor() 1149 */ 1150 public boolean onCheckIsTextEditor() { 1151 return mImeAdapter.hasTextInputType(); 1152 } 1153 1154 /** 1155 * @see View#onConfigurationChanged(Configuration) 1156 */ 1157 @SuppressWarnings("javadoc") 1158 public void onConfigurationChanged(Configuration newConfig) { 1159 TraceEvent.begin(); 1160 1161 mKeyboardConnected = newConfig.keyboard != Configuration.KEYBOARD_NOKEYS; 1162 1163 if (mKeyboardConnected) { 1164 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore), 1165 ImeAdapter.sTextInputTypeNone); 1166 InputMethodManager manager = (InputMethodManager) 1167 getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 1168 manager.restartInput(mContainerView); 1169 } 1170 mContainerViewInternals.super_onConfigurationChanged(newConfig); 1171 mNeedUpdateOrientationChanged = true; 1172 TraceEvent.end(); 1173 } 1174 1175 /** 1176 * @see View#onSizeChanged(int, int, int, int) 1177 */ 1178 @SuppressWarnings("javadoc") 1179 public void onSizeChanged(int w, int h, int ow, int oh) { 1180 mPopupZoomer.hide(false); 1181 1182 updateAfterSizeChanged(); 1183 1184 // Update the content size to make sure it is at least the View size 1185 if (mContentWidth < w) mContentWidth = w; 1186 if (mContentHeight < h) mContentHeight = h; 1187 1188 if (mViewportWidth != w || mViewportHeight != h) { 1189 mViewportWidth = w; 1190 mViewportHeight = h; 1191 if (mNativeContentViewCore != 0) { 1192 nativeSetSize(mNativeContentViewCore, mViewportWidth, mViewportHeight); 1193 } 1194 } 1195 } 1196 1197 public void updateAfterSizeChanged() { 1198 // Execute a delayed form focus operation because the OSK was brought 1199 // up earlier. 1200 if (mFocusOnNextSizeChanged) { 1201 scrollFocusedEditableNodeIntoView(); 1202 mFocusOnNextSizeChanged = false; 1203 } else if (mUnfocusOnNextSizeChanged) { 1204 undoScrollFocusedEditableNodeIntoViewIfNeeded(true); 1205 mUnfocusOnNextSizeChanged = false; 1206 } 1207 1208 if (mNeedUpdateOrientationChanged) { 1209 sendOrientationChangeEvent(); 1210 mNeedUpdateOrientationChanged = false; 1211 } 1212 } 1213 1214 private void scrollFocusedEditableNodeIntoView() { 1215 if (mNativeContentViewCore != 0) { 1216 Runnable scrollTask = new Runnable() { 1217 @Override 1218 public void run() { 1219 if (mNativeContentViewCore != 0) { 1220 nativeScrollFocusedEditableNodeIntoView(mNativeContentViewCore); 1221 } 1222 } 1223 }; 1224 1225 scrollTask.run(); 1226 1227 // The native side keeps track of whether the zoom and scroll actually occurred. It is 1228 // more efficient to do it this way and sometimes fire an unnecessary message rather 1229 // than synchronize with the renderer and always have an additional message. 1230 mScrolledAndZoomedFocusedEditableNode = true; 1231 } 1232 } 1233 1234 private void undoScrollFocusedEditableNodeIntoViewIfNeeded(boolean backButtonPressed) { 1235 // The only call to this function that matters is the first call after the 1236 // scrollFocusedEditableNodeIntoView function call. 1237 // If the first call to this function is a result of a back button press we want to undo the 1238 // preceding scroll. If the call is a result of some other action we don't want to perform 1239 // an undo. 1240 // All subsequent calls are ignored since only the scroll function sets 1241 // mScrolledAndZoomedFocusedEditableNode to true. 1242 if (mScrolledAndZoomedFocusedEditableNode && backButtonPressed && 1243 mNativeContentViewCore != 0) { 1244 Runnable scrollTask = new Runnable() { 1245 @Override 1246 public void run() { 1247 if (mNativeContentViewCore != 0) { 1248 nativeUndoScrollFocusedEditableNodeIntoView(mNativeContentViewCore); 1249 } 1250 } 1251 }; 1252 1253 scrollTask.run(); 1254 } 1255 mScrolledAndZoomedFocusedEditableNode = false; 1256 } 1257 1258 /** 1259 * @see View#onFocusedChanged(boolean, int, Rect) 1260 */ 1261 @SuppressWarnings("javadoc") 1262 public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { 1263 if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus); 1264 } 1265 1266 /** 1267 * @see View#onKeyUp(int, KeyEvent) 1268 */ 1269 public boolean onKeyUp(int keyCode, KeyEvent event) { 1270 if (mPopupZoomer.isShowing() && keyCode == KeyEvent.KEYCODE_BACK) { 1271 mPopupZoomer.hide(true); 1272 return true; 1273 } 1274 return mContainerViewInternals.super_onKeyUp(keyCode, event); 1275 } 1276 1277 /** 1278 * @see View#dispatchKeyEventPreIme(KeyEvent) 1279 */ 1280 public boolean dispatchKeyEventPreIme(KeyEvent event) { 1281 try { 1282 TraceEvent.begin(); 1283 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && mImeAdapter.isActive()) { 1284 mUnfocusOnNextSizeChanged = true; 1285 } else { 1286 undoScrollFocusedEditableNodeIntoViewIfNeeded(false); 1287 } 1288 mImeAdapter.dispatchKeyEventPreIme(event); 1289 return mContainerViewInternals.super_dispatchKeyEventPreIme(event); 1290 } finally { 1291 TraceEvent.end(); 1292 } 1293 } 1294 1295 /** 1296 * @see View#dispatchKeyEvent(KeyEvent) 1297 */ 1298 public boolean dispatchKeyEvent(KeyEvent event) { 1299 if (mImeAdapter != null && 1300 !mImeAdapter.isNativeImeAdapterAttached() && mNativeContentViewCore != 0) { 1301 mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore)); 1302 } 1303 // The key handling logic is kind of confusing here. 1304 // The purpose of shouldOverrideKeyEvent() is to filter out some keys that is critical 1305 // to browser function but useless in renderer process (for example, the back button), 1306 // so the browser can still respond to these keys in a timely manner when the renderer 1307 // process is busy/blocked/busted. mImeAdapter.dispatchKeyEvent() forwards the key event 1308 // to the renderer process. If mImeAdapter is bypassed or is not interested to the event, 1309 // fall back to the default dispatcher to propagate the event to sub-views. 1310 return (!getContentViewClient().shouldOverrideKeyEvent(event) 1311 && mImeAdapter.dispatchKeyEvent(event)) 1312 || mContainerViewInternals.super_dispatchKeyEvent(event); 1313 } 1314 1315 /** 1316 * @see View#onHoverEvent(MotionEvent) 1317 * Mouse move events are sent on hover enter, hover move and hover exit. 1318 * They are sent on hover exit because sometimes it acts as both a hover 1319 * move and hover exit. 1320 */ 1321 public boolean onHoverEvent(MotionEvent event) { 1322 TraceEvent.begin("onHoverEvent"); 1323 mContainerView.removeCallbacks(mFakeMouseMoveRunnable); 1324 if (mNativeContentViewCore != 0) { 1325 nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(), 1326 (int) event.getX(), (int) event.getY()); 1327 } 1328 TraceEvent.end("onHoverEvent"); 1329 return true; 1330 } 1331 1332 /** 1333 * @see View#onGenericMotionEvent(MotionEvent) 1334 */ 1335 public boolean onGenericMotionEvent(MotionEvent event) { 1336 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 1337 switch (event.getAction()) { 1338 case MotionEvent.ACTION_SCROLL: 1339 nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(), 1340 (int) event.getX(), (int) event.getY(), 1341 event.getAxisValue(MotionEvent.AXIS_VSCROLL)); 1342 1343 mContainerView.removeCallbacks(mFakeMouseMoveRunnable); 1344 // Send a delayed onMouseMove event so that we end 1345 // up hovering over the right position after the scroll. 1346 final MotionEvent eventFakeMouseMove = MotionEvent.obtain(event); 1347 mFakeMouseMoveRunnable = new Runnable() { 1348 @Override 1349 public void run() { 1350 onHoverEvent(eventFakeMouseMove); 1351 } 1352 }; 1353 mContainerView.postDelayed(mFakeMouseMoveRunnable, 250); 1354 return true; 1355 } 1356 } 1357 return mContainerViewInternals.super_onGenericMotionEvent(event); 1358 } 1359 1360 /** 1361 * @see View#scrollBy(int, int) 1362 * Currently the ContentView scrolling happens in the native side. In 1363 * the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo() 1364 * are overridden, so that View's mScrollX and mScrollY will be unchanged at 1365 * (0, 0). This is critical for drawing ContentView correctly. 1366 */ 1367 public void scrollBy(int x, int y) { 1368 if (mNativeContentViewCore != 0) { 1369 nativeScrollBy(mNativeContentViewCore, System.currentTimeMillis(), 0, 0, x, y); 1370 } 1371 } 1372 1373 /** 1374 * @see View#scrollTo(int, int) 1375 */ 1376 public void scrollTo(int x, int y) { 1377 if (mNativeContentViewCore == 0) return; 1378 int dx = x - mNativeScrollX, dy = y - mNativeScrollY; 1379 if (dx != 0 || dy != 0) { 1380 long time = System.currentTimeMillis(); 1381 nativeScrollBegin(mNativeContentViewCore, time, mNativeScrollX, mNativeScrollY); 1382 nativeScrollBy(mNativeContentViewCore, time, mNativeScrollX, mNativeScrollY, 1383 dx, dy); 1384 nativeScrollEnd(mNativeContentViewCore, time); 1385 } 1386 } 1387 1388 // NOTE: this can go away once ContentView.getScrollX() reports correct values. 1389 // see: b/6029133 1390 public int getNativeScrollXForTest() { 1391 return mNativeScrollX; 1392 } 1393 1394 // NOTE: this can go away once ContentView.getScrollY() reports correct values. 1395 // see: b/6029133 1396 public int getNativeScrollYForTest() { 1397 return mNativeScrollY; 1398 } 1399 1400 /** 1401 * @see View#computeHorizontalScrollExtent() 1402 */ 1403 @SuppressWarnings("javadoc") 1404 public int computeHorizontalScrollExtent() { 1405 return getWidth(); 1406 } 1407 1408 /** 1409 * @see View#computeHorizontalScrollOffset() 1410 */ 1411 @SuppressWarnings("javadoc") 1412 public int computeHorizontalScrollOffset() { 1413 return mNativeScrollX; 1414 } 1415 1416 /** 1417 * @see View#computeHorizontalScrollRange() 1418 */ 1419 @SuppressWarnings("javadoc") 1420 public int computeHorizontalScrollRange() { 1421 return mContentWidth; 1422 } 1423 1424 /** 1425 * @see View#computeVerticalScrollExtent() 1426 */ 1427 @SuppressWarnings("javadoc") 1428 public int computeVerticalScrollExtent() { 1429 return getHeight(); 1430 } 1431 1432 /** 1433 * @see View#computeVerticalScrollOffset() 1434 */ 1435 @SuppressWarnings("javadoc") 1436 public int computeVerticalScrollOffset() { 1437 return mNativeScrollY; 1438 } 1439 1440 /** 1441 * @see View#computeVerticalScrollRange() 1442 */ 1443 @SuppressWarnings("javadoc") 1444 public int computeVerticalScrollRange() { 1445 return mContentHeight; 1446 } 1447 1448 // End FrameLayout overrides. 1449 1450 /** 1451 * @see View#awakenScrollBars(int, boolean) 1452 */ 1453 @SuppressWarnings("javadoc") 1454 public boolean awakenScrollBars(int startDelay, boolean invalidate) { 1455 // For the default implementation of ContentView which draws the scrollBars on the native 1456 // side, calling this function may get us into a bad state where we keep drawing the 1457 // scrollBars, so disable it by always returning false. 1458 if (mContainerView.getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) { 1459 return false; 1460 } else { 1461 return mContainerViewInternals.super_awakenScrollBars(startDelay, invalidate); 1462 } 1463 } 1464 1465 @SuppressWarnings("unused") 1466 @CalledByNative 1467 private void onTabCrash() { 1468 getContentViewClient().onTabCrash(); 1469 } 1470 1471 private void handleTapOrPress( 1472 long timeMs, int x, int y, boolean isLongPress, boolean showPress) { 1473 if (!mContainerView.isFocused()) mContainerView.requestFocus(); 1474 1475 if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(x, y); 1476 1477 if (isLongPress) { 1478 getInsertionHandleController().allowAutomaticShowing(); 1479 getSelectionHandleController().allowAutomaticShowing(); 1480 if (mNativeContentViewCore != 0) { 1481 nativeLongPress(mNativeContentViewCore, timeMs, x, y, false); 1482 } 1483 } else { 1484 if (!showPress && mNativeContentViewCore != 0) { 1485 nativeShowPressState(mNativeContentViewCore, timeMs, x, y); 1486 } 1487 if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing(); 1488 if (mNativeContentViewCore != 0) { 1489 nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false); 1490 } 1491 } 1492 } 1493 1494 void updateMultiTouchZoomSupport() { 1495 mZoomManager.updateMultiTouchSupport(); 1496 } 1497 1498 public boolean isMultiTouchZoomSupported() { 1499 return mZoomManager.isMultiTouchZoomSupported(); 1500 } 1501 1502 void selectPopupMenuItems(int[] indices) { 1503 if (mNativeContentViewCore != 0) { 1504 nativeSelectPopupMenuItems(mNativeContentViewCore, indices); 1505 } 1506 } 1507 1508 /* 1509 * To avoid checkerboard, we clamp the fling velocity based on the maximum number of tiles 1510 * allowed to be uploaded per 100ms. Calculation is limited to one direction. We assume the 1511 * tile size is 256x256. The precise distance / velocity should be calculated based on the 1512 * logic in Scroller.java. As it is almost linear for the first 100ms, we use a simple math. 1513 */ 1514 private int clampFlingVelocityX(int velocity) { 1515 int cols = mMaxNumUploadTiles / (int) (Math.ceil((float) getHeight() / 256) + 1); 1516 int maxVelocity = cols > 0 ? cols * 2560 : 1000; 1517 if (Math.abs(velocity) > maxVelocity) { 1518 return velocity > 0 ? maxVelocity : -maxVelocity; 1519 } else { 1520 return velocity; 1521 } 1522 } 1523 1524 private int clampFlingVelocityY(int velocity) { 1525 int rows = mMaxNumUploadTiles / (int) (Math.ceil((float) getWidth() / 256) + 1); 1526 int maxVelocity = rows > 0 ? rows * 2560 : 1000; 1527 if (Math.abs(velocity) > maxVelocity) { 1528 return velocity > 0 ? maxVelocity : -maxVelocity; 1529 } else { 1530 return velocity; 1531 } 1532 } 1533 1534 /** 1535 * Get the screen orientation from the OS and push it to WebKit. 1536 * 1537 * TODO(husky): Add a hook for mock orientations. 1538 * 1539 * TODO(husky): Currently each new tab starts with an orientation of 0 until you actually 1540 * rotate the device. This is wrong if you actually started in landscape mode. To fix this, we 1541 * need to push the correct orientation, but only after WebKit's Frame object has been fully 1542 * initialized. Need to find a good time to do that. onPageFinished() would probably work but 1543 * it isn't implemented yet. 1544 */ 1545 private void sendOrientationChangeEvent() { 1546 if (mNativeContentViewCore == 0) return; 1547 1548 WindowManager windowManager = 1549 (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); 1550 switch (windowManager.getDefaultDisplay().getRotation()) { 1551 case Surface.ROTATION_90: 1552 nativeSendOrientationChangeEvent(mNativeContentViewCore, 90); 1553 break; 1554 case Surface.ROTATION_180: 1555 nativeSendOrientationChangeEvent(mNativeContentViewCore, 180); 1556 break; 1557 case Surface.ROTATION_270: 1558 nativeSendOrientationChangeEvent(mNativeContentViewCore, -90); 1559 break; 1560 case Surface.ROTATION_0: 1561 nativeSendOrientationChangeEvent(mNativeContentViewCore, 0); 1562 break; 1563 default: 1564 Log.w(TAG, "Unknown rotation!"); 1565 break; 1566 } 1567 } 1568 1569 /** 1570 * Register the delegate to be used when content can not be handled by 1571 * the rendering engine, and should be downloaded instead. This will replace 1572 * the current delegate, if any. 1573 * @param delegate An implementation of ContentViewDownloadDelegate. 1574 */ 1575 public void setDownloadDelegate(ContentViewDownloadDelegate delegate) { 1576 mDownloadDelegate = delegate; 1577 } 1578 1579 // Called by DownloadController. 1580 ContentViewDownloadDelegate getDownloadDelegate() { 1581 return mDownloadDelegate; 1582 } 1583 1584 private SelectionHandleController getSelectionHandleController() { 1585 if (mSelectionHandleController == null) { 1586 mSelectionHandleController = new SelectionHandleController(getContainerView()) { 1587 @Override 1588 public void selectBetweenCoordinates(int x1, int y1, int x2, int y2) { 1589 if (mNativeContentViewCore != 0 && !(x1 == x2 && y1 == y2)) { 1590 nativeSelectBetweenCoordinates(mNativeContentViewCore, x1, y1, x2, y2); 1591 } 1592 } 1593 1594 @Override 1595 public void showHandlesAt(int x1, int y1, int dir1, int x2, int y2, int dir2) { 1596 super.showHandlesAt(x1, y1, dir1, x2, y2, dir2); 1597 mStartHandleNormalizedPoint.set( 1598 (x1 + mNativeScrollX) / mNativePageScaleFactor, 1599 (y1 + mNativeScrollY) / mNativePageScaleFactor); 1600 mEndHandleNormalizedPoint.set( 1601 (x2 + mNativeScrollX) / mNativePageScaleFactor, 1602 (y2 + mNativeScrollY) / mNativePageScaleFactor); 1603 1604 showSelectActionBar(); 1605 } 1606 1607 }; 1608 1609 mSelectionHandleController.hideAndDisallowAutomaticShowing(); 1610 } 1611 1612 return mSelectionHandleController; 1613 } 1614 1615 private InsertionHandleController getInsertionHandleController() { 1616 if (mInsertionHandleController == null) { 1617 mInsertionHandleController = new InsertionHandleController(getContainerView()) { 1618 private static final int AVERAGE_LINE_HEIGHT = 14; 1619 1620 @Override 1621 public void setCursorPosition(int x, int y) { 1622 if (mNativeContentViewCore != 0) { 1623 nativeSelectBetweenCoordinates(mNativeContentViewCore, x, y, x, y); 1624 } 1625 } 1626 1627 @Override 1628 public void paste() { 1629 mImeAdapter.paste(); 1630 hideHandles(); 1631 } 1632 1633 @Override 1634 public int getLineHeight() { 1635 return (int) (mNativePageScaleFactor * AVERAGE_LINE_HEIGHT); 1636 } 1637 1638 @Override 1639 public void showHandleAt(int x, int y) { 1640 super.showHandleAt(x, y); 1641 mInsertionHandleNormalizedPoint.set( 1642 (x + mNativeScrollX) / mNativePageScaleFactor, 1643 (y + mNativeScrollY) / mNativePageScaleFactor); 1644 } 1645 }; 1646 1647 mInsertionHandleController.hideAndDisallowAutomaticShowing(); 1648 } 1649 1650 return mInsertionHandleController; 1651 } 1652 1653 private void updateHandleScreenPositions() { 1654 if (mSelectionHandleController != null && mSelectionHandleController.isShowing()) { 1655 float startX = mStartHandleNormalizedPoint.x * mNativePageScaleFactor - mNativeScrollX; 1656 float startY = mStartHandleNormalizedPoint.y * mNativePageScaleFactor - mNativeScrollY; 1657 mSelectionHandleController.setStartHandlePosition((int) startX, (int) startY); 1658 1659 float endX = mEndHandleNormalizedPoint.x * mNativePageScaleFactor - mNativeScrollX; 1660 float endY = mEndHandleNormalizedPoint.y * mNativePageScaleFactor - mNativeScrollY; 1661 mSelectionHandleController.setEndHandlePosition((int) endX, (int) endY); 1662 } 1663 1664 if (mInsertionHandleController != null && mInsertionHandleController.isShowing()) { 1665 float x = mInsertionHandleNormalizedPoint.x * mNativePageScaleFactor - mNativeScrollX; 1666 float y = mInsertionHandleNormalizedPoint.y * mNativePageScaleFactor - mNativeScrollY; 1667 mInsertionHandleController.setHandlePosition((int) x, (int) y); 1668 } 1669 } 1670 1671 private void hideHandles() { 1672 if (mSelectionHandleController != null) { 1673 mSelectionHandleController.hideAndDisallowAutomaticShowing(); 1674 } 1675 if (mInsertionHandleController != null) { 1676 mInsertionHandleController.hideAndDisallowAutomaticShowing(); 1677 } 1678 } 1679 1680 private void showSelectActionBar() { 1681 if (mActionMode != null) { 1682 mActionMode.invalidate(); 1683 return; 1684 } 1685 1686 // Start a new action mode with a SelectActionModeCallback. 1687 SelectActionModeCallback.ActionHandler actionHandler = 1688 new SelectActionModeCallback.ActionHandler() { 1689 @Override 1690 public boolean selectAll() { 1691 return mImeAdapter.selectAll(); 1692 } 1693 1694 @Override 1695 public boolean cut() { 1696 return mImeAdapter.cut(); 1697 } 1698 1699 @Override 1700 public boolean copy() { 1701 return mImeAdapter.copy(); 1702 } 1703 1704 @Override 1705 public boolean paste() { 1706 return mImeAdapter.paste(); 1707 } 1708 1709 @Override 1710 public boolean isSelectionEditable() { 1711 return mSelectionEditable; 1712 } 1713 1714 @Override 1715 public String getSelectedText() { 1716 return ContentViewCore.this.getSelectedText(); 1717 } 1718 1719 @Override 1720 public void onDestroyActionMode() { 1721 mActionMode = null; 1722 mImeAdapter.unselect(); 1723 getContentViewClient().onContextualActionBarHidden(); 1724 } 1725 }; 1726 mActionMode = mContainerView.startActionMode( 1727 getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler, 1728 nativeIsIncognito(mNativeContentViewCore))); 1729 if (mActionMode == null) { 1730 // There is no ActionMode, so remove the selection. 1731 mImeAdapter.unselect(); 1732 } else { 1733 getContentViewClient().onContextualActionBarShown(); 1734 } 1735 } 1736 1737 public boolean getUseDesktopUserAgent() { 1738 if (mNativeContentViewCore != 0) { 1739 return nativeGetUseDesktopUserAgent(mNativeContentViewCore); 1740 } 1741 return false; 1742 } 1743 1744 /** 1745 * Set whether or not we're using a desktop user agent for the currently loaded page. 1746 * @param override If true, use a desktop user agent. Use a mobile one otherwise. 1747 * @param reloadOnChange Reload the page if the UA has changed. 1748 */ 1749 public void setUseDesktopUserAgent(boolean override, boolean reloadOnChange) { 1750 if (mNativeContentViewCore != 0) { 1751 nativeSetUseDesktopUserAgent(mNativeContentViewCore, override, reloadOnChange); 1752 } 1753 } 1754 1755 public void clearSslPreferences() { 1756 nativeClearSslPreferences(mNativeContentViewCore); 1757 } 1758 1759 /** 1760 * @return Whether the native ContentView has crashed. 1761 */ 1762 public boolean isCrashed() { 1763 if (mNativeContentViewCore == 0) return false; 1764 return nativeCrashed(mNativeContentViewCore); 1765 } 1766 1767 @SuppressWarnings("unused") 1768 @CalledByNative 1769 private void updateContentSize(int width, int height) { 1770 if (mContentWidth != width || mContentHeight != height) { 1771 mPopupZoomer.hide(true); 1772 } 1773 // Make sure the content size is at least the View size 1774 mContentWidth = Math.max(width, getWidth()); 1775 mContentHeight = Math.max(height, getHeight()); 1776 } 1777 1778 @SuppressWarnings("unused") 1779 @CalledByNative 1780 private void updateScrollOffsetAndPageScaleFactor(int x, int y, float scale) { 1781 if (mNativeScrollX == x && mNativeScrollY == y && mNativePageScaleFactor == scale) return; 1782 1783 mContainerViewInternals.onScrollChanged(x, y, mNativeScrollX, mNativeScrollY); 1784 1785 // This function should be called back from native as soon 1786 // as the scroll is applied to the backbuffer. We should only 1787 // update mNativeScrollX/Y here for consistency. 1788 mNativeScrollX = x; 1789 mNativeScrollY = y; 1790 mNativePageScaleFactor = scale; 1791 1792 mPopupZoomer.hide(true); 1793 updateHandleScreenPositions(); 1794 1795 mZoomManager.updateZoomControls(); 1796 } 1797 1798 @SuppressWarnings("unused") 1799 @CalledByNative 1800 private void updatePageScaleLimits(float minimumScale, float maximumScale) { 1801 mNativeMinimumScale = minimumScale; 1802 mNativeMaximumScale = maximumScale; 1803 mZoomManager.updateZoomControls(); 1804 } 1805 1806 @SuppressWarnings("unused") 1807 @CalledByNative 1808 private void imeUpdateAdapter(int nativeImeAdapterAndroid, int textInputType, 1809 String text, int selectionStart, int selectionEnd, 1810 int compositionStart, int compositionEnd, boolean showImeIfNeeded) { 1811 TraceEvent.begin(); 1812 1813 // Non-breaking spaces can cause the IME to get confused. Replace with normal spaces. 1814 text = text.replace('\u00A0', ' '); 1815 1816 mSelectionEditable = (textInputType != ImeAdapter.sTextInputTypeNone); 1817 1818 if (mActionMode != null) mActionMode.invalidate(); 1819 1820 mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType, 1821 text, showImeIfNeeded); 1822 1823 if (mInputConnection != null) { 1824 // In WebKit if there's a composition then the selection will usually be the 1825 // same as the composition, whereas Android IMEs expect the selection to be 1826 // just a caret at the end of the composition. 1827 if (selectionStart == compositionStart && selectionEnd == compositionEnd) { 1828 selectionStart = selectionEnd; 1829 } 1830 mInputConnection.setEditableText(text, selectionStart, selectionEnd, 1831 compositionStart, compositionEnd); 1832 } 1833 TraceEvent.end(); 1834 } 1835 1836 @SuppressWarnings("unused") 1837 @CalledByNative 1838 private void setTitle(String title) { 1839 getContentViewClient().onUpdateTitle(title); 1840 } 1841 1842 /** 1843 * Called (from native) when the <select> popup needs to be shown. 1844 * @param items Items to show. 1845 * @param enabled POPUP_ITEM_TYPEs for items. 1846 * @param multiple Whether the popup menu should support multi-select. 1847 * @param selectedIndices Indices of selected items. 1848 */ 1849 @SuppressWarnings("unused") 1850 @CalledByNative 1851 private void showSelectPopup(String[] items, int[] enabled, boolean multiple, 1852 int[] selectedIndices) { 1853 SelectPopupDialog.show(this, items, enabled, multiple, selectedIndices); 1854 } 1855 1856 @SuppressWarnings("unused") 1857 @CalledByNative 1858 private void showDisambiguationPopup(Rect targetRect, Bitmap zoomedBitmap) { 1859 mPopupZoomer.setBitmap(zoomedBitmap); 1860 mPopupZoomer.show(targetRect); 1861 } 1862 1863 @SuppressWarnings("unused") 1864 @CalledByNative 1865 private void onSelectionChanged(String text) { 1866 mLastSelectedText = text; 1867 } 1868 1869 @SuppressWarnings("unused") 1870 @CalledByNative 1871 private void onSelectionBoundsChanged(Rect startRect, int dir1, Rect endRect, int dir2) { 1872 int x1 = startRect.left; 1873 int y1 = startRect.bottom; 1874 int x2 = endRect.left; 1875 int y2 = endRect.bottom; 1876 if (x1 != x2 || y1 != y2) { 1877 if (mInsertionHandleController != null) { 1878 mInsertionHandleController.hide(); 1879 } 1880 getSelectionHandleController().onSelectionChanged(x1, y1, dir1, x2, y2, dir2); 1881 mHasSelection = true; 1882 } else { 1883 hideSelectActionBar(); 1884 if (x1 != 0 && y1 != 0 1885 && (mSelectionHandleController == null 1886 || !mSelectionHandleController.isDragging()) 1887 && mSelectionEditable) { 1888 // Selection is a caret, and a text field is focused. 1889 if (mSelectionHandleController != null) { 1890 mSelectionHandleController.hide(); 1891 } 1892 getInsertionHandleController().onCursorPositionChanged(x1, y1); 1893 InputMethodManager manager = (InputMethodManager) 1894 getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 1895 if (manager.isWatchingCursor(mContainerView)) { 1896 manager.updateCursor(mContainerView, startRect.left, startRect.top, 1897 startRect.right, startRect.bottom); 1898 } 1899 } else { 1900 // Deselection 1901 if (mSelectionHandleController != null 1902 && !mSelectionHandleController.isDragging()) { 1903 mSelectionHandleController.hideAndDisallowAutomaticShowing(); 1904 } 1905 if (mInsertionHandleController != null) { 1906 mInsertionHandleController.hideAndDisallowAutomaticShowing(); 1907 } 1908 } 1909 mHasSelection = false; 1910 } 1911 } 1912 1913 @SuppressWarnings("unused") 1914 @CalledByNative 1915 private void onEvaluateJavaScriptResult(int id, String jsonResult) { 1916 getContentViewClient().onEvaluateJavaScriptResult(id, jsonResult); 1917 } 1918 1919 @SuppressWarnings("unused") 1920 @CalledByNative 1921 private void showPastePopup(int x, int y) { 1922 getInsertionHandleController() 1923 .showHandleWithPastePopupAt(x - mNativeScrollX, y - mNativeScrollY); 1924 } 1925 1926 /** 1927 * @return Whether a reload happens when this ContentView is activated. 1928 */ 1929 public boolean needsReload() { 1930 return mNativeContentViewCore != 0 && nativeNeedsReload(mNativeContentViewCore); 1931 } 1932 1933 /** 1934 * @see View#hasFocus() 1935 */ 1936 @CalledByNative 1937 public boolean hasFocus() { 1938 return mContainerView.hasFocus(); 1939 } 1940 1941 /** 1942 * Checks whether the ContentViewCore can be zoomed in. 1943 * 1944 * @return True if the ContentViewCore can be zoomed in. 1945 */ 1946 // This method uses the term 'zoom' for legacy reasons, but relates 1947 // to what chrome calls the 'page scale factor'. 1948 public boolean canZoomIn() { 1949 return mNativeMaximumScale - mNativePageScaleFactor > ZOOM_CONTROLS_EPSILON; 1950 } 1951 1952 /** 1953 * Checks whether the ContentViewCore can be zoomed out. 1954 * 1955 * @return True if the ContentViewCore can be zoomed out. 1956 */ 1957 // This method uses the term 'zoom' for legacy reasons, but relates 1958 // to what chrome calls the 'page scale factor'. 1959 public boolean canZoomOut() { 1960 return mNativePageScaleFactor - mNativeMinimumScale > ZOOM_CONTROLS_EPSILON; 1961 } 1962 1963 /** 1964 * Zooms in the ContentViewCore by 25% (or less if that would result in 1965 * zooming in more than possible). 1966 * 1967 * @return True if there was a zoom change, false otherwise. 1968 */ 1969 // This method uses the term 'zoom' for legacy reasons, but relates 1970 // to what chrome calls the 'page scale factor'. 1971 public boolean zoomIn() { 1972 if (!canZoomIn()) { 1973 return false; 1974 } 1975 return zoomByDelta(1.25f); 1976 } 1977 1978 /** 1979 * Zooms out the ContentViewCore by 20% (or less if that would result in 1980 * zooming out more than possible). 1981 * 1982 * @return True if there was a zoom change, false otherwise. 1983 */ 1984 // This method uses the term 'zoom' for legacy reasons, but relates 1985 // to what chrome calls the 'page scale factor'. 1986 public boolean zoomOut() { 1987 if (!canZoomOut()) { 1988 return false; 1989 } 1990 return zoomByDelta(0.8f); 1991 } 1992 1993 /** 1994 * Resets the zoom factor of the ContentViewCore. 1995 * 1996 * @return True if there was a zoom change, false otherwise. 1997 */ 1998 // This method uses the term 'zoom' for legacy reasons, but relates 1999 // to what chrome calls the 'page scale factor'. 2000 public boolean zoomReset() { 2001 // The page scale factor is initialized to mNativeMinimumScale when 2002 // the page finishes loading. Thus sets it back to mNativeMinimumScale. 2003 if (mNativePageScaleFactor - mNativeMinimumScale < ZOOM_CONTROLS_EPSILON) { 2004 return false; 2005 } 2006 return zoomByDelta(mNativeMinimumScale / mNativePageScaleFactor); 2007 } 2008 2009 private boolean zoomByDelta(float delta) { 2010 if (mNativeContentViewCore == 0) { 2011 return false; 2012 } 2013 2014 long timeMs = System.currentTimeMillis(); 2015 int x = getWidth() / 2; 2016 int y = getHeight() / 2; 2017 2018 getContentViewGestureHandler().pinchBegin(timeMs, x, y); 2019 getContentViewGestureHandler().pinchBy(timeMs, x, y, delta); 2020 getContentViewGestureHandler().pinchEnd(timeMs); 2021 2022 return true; 2023 } 2024 2025 /** 2026 * Invokes the graphical zoom picker widget for this ContentView. 2027 */ 2028 @Override 2029 public void invokeZoomPicker() { 2030 if (mContentSettings != null && mContentSettings.supportZoom()) { 2031 mZoomManager.invokeZoomPicker(); 2032 } 2033 } 2034 2035 // Unlike legacy WebView getZoomControls which returns external zoom controls, 2036 // this method returns built-in zoom controls. This method is used in tests. 2037 public View getZoomControlsForTest() { 2038 return mZoomManager.getZoomControlsViewForTest(); 2039 } 2040 2041 /** 2042 * This method injects the supplied Java object into the ContentViewCore. 2043 * The object is injected into the JavaScript context of the main frame, 2044 * using the supplied name. This allows the Java object to be accessed from 2045 * JavaScript. Note that that injected objects will not appear in 2046 * JavaScript until the page is next (re)loaded. For example: 2047 * <pre> view.addJavascriptInterface(new Object(), "injectedObject"); 2048 * view.loadData("<!DOCTYPE html><title></title>", "text/html", null); 2049 * view.loadUrl("javascript:alert(injectedObject.toString())");</pre> 2050 * <p><strong>IMPORTANT:</strong> 2051 * <ul> 2052 * <li> addJavascriptInterface() can be used to allow JavaScript to control 2053 * the host application. This is a powerful feature, but also presents a 2054 * security risk. Use of this method in a ContentViewCore containing 2055 * untrusted content could allow an attacker to manipulate the host 2056 * application in unintended ways, executing Java code with the permissions 2057 * of the host application. Use extreme care when using this method in a 2058 * ContentViewCore which could contain untrusted content. Particular care 2059 * should be taken to avoid unintentional access to inherited methods, such 2060 * as {@link Object#getClass()}. To prevent access to inherited methods, 2061 * set {@code allowInheritedMethods} to {@code false}. In addition, ensure 2062 * that the injected object's public methods return only objects designed 2063 * to be used by untrusted code, and never return a raw Object instance. 2064 * <li> JavaScript interacts with Java objects on a private, background 2065 * thread of the ContentViewCore. Care is therefore required to maintain 2066 * thread safety.</li> 2067 * </ul></p> 2068 * 2069 * @param object The Java object to inject into the ContentViewCore's 2070 * JavaScript context. Null values are ignored. 2071 * @param name The name used to expose the instance in JavaScript. 2072 * @param requireAnnotation Restrict exposed methods to ones with the 2073 * {@link JavascriptInterface} annotation. 2074 */ 2075 public void addJavascriptInterface(Object object, String name, boolean requireAnnotation) { 2076 if (mNativeContentViewCore != 0 && object != null) { 2077 nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requireAnnotation); 2078 } 2079 } 2080 2081 /** 2082 * Removes a previously added JavaScript interface with the given name. 2083 * 2084 * @param name The name of the interface to remove. 2085 */ 2086 public void removeJavascriptInterface(String name) { 2087 if (mNativeContentViewCore != 0) { 2088 nativeRemoveJavascriptInterface(mNativeContentViewCore, name); 2089 } 2090 } 2091 2092 /** 2093 * Return the current scale of the ContentView. 2094 * @return The current scale. 2095 */ 2096 public float getScale() { 2097 return mNativePageScaleFactor; 2098 } 2099 2100 /** 2101 * If the view is ready to draw contents to the screen. In hardware mode, 2102 * the initialization of the surface texture may not occur until after the 2103 * view has been added to the layout. This method will return {@code true} 2104 * once the texture is actually ready. 2105 */ 2106 public boolean isReady() { 2107 // TODO(nileshagrawal): Implement this. 2108 return false; 2109 } 2110 2111 /** 2112 * @return Whether or not the texture view is available or not. 2113 */ 2114 public boolean isAvailable() { 2115 // TODO(nileshagrawal): Implement this. 2116 return false; 2117 } 2118 2119 @CalledByNative 2120 private void startContentIntent(String contentUrl) { 2121 getContentViewClient().onStartContentIntent(getContext(), contentUrl); 2122 } 2123 2124 /** 2125 * Determines whether or not this ContentViewCore can handle this accessibility action. 2126 * @param action The action to perform. 2127 * @return Whether or not this action is supported. 2128 */ 2129 public boolean supportsAccessibilityAction(int action) { 2130 return mAccessibilityInjector.supportsAccessibilityAction(action); 2131 } 2132 2133 /** 2134 * Attempts to perform an accessibility action on the web content. If the accessibility action 2135 * cannot be processed, it returns {@code null}, allowing the caller to know to call the 2136 * super {@link View#performAccessibilityAction(int, Bundle)} method and use that return value. 2137 * Otherwise the return value from this method should be used. 2138 * @param action The action to perform. 2139 * @param arguments Optional action arguments. 2140 * @return Whether the action was performed or {@code null} if the call should be delegated to 2141 * the super {@link View} class. 2142 */ 2143 public boolean performAccessibilityAction(int action, Bundle arguments) { 2144 if (mAccessibilityInjector.supportsAccessibilityAction(action)) { 2145 return mAccessibilityInjector.performAccessibilityAction(action, arguments); 2146 } 2147 2148 return false; 2149 } 2150 2151 /** 2152 * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) 2153 */ 2154 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 2155 mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info); 2156 } 2157 2158 /** 2159 * @see View#onInitializeAccessibilityEvent(AccessibilityEvent) 2160 */ 2161 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 2162 event.setClassName(this.getClass().getName()); 2163 2164 // Identify where the top-left of the screen currently points to. 2165 event.setScrollX(mNativeScrollX); 2166 event.setScrollY(mNativeScrollY); 2167 2168 // The maximum scroll values are determined by taking the content dimensions and 2169 // subtracting off the actual dimensions of the ChromeView. 2170 int maxScrollX = Math.max(0, mContentWidth - getWidth()); 2171 int maxScrollY = Math.max(0, mContentHeight - getHeight()); 2172 event.setScrollable(maxScrollX > 0 || maxScrollY > 0); 2173 2174 // Setting the maximum scroll values requires API level 15 or higher. 2175 final int SDK_VERSION_REQUIRED_TO_SET_SCROLL = 15; 2176 if (Build.VERSION.SDK_INT >= SDK_VERSION_REQUIRED_TO_SET_SCROLL) { 2177 event.setMaxScrollX(maxScrollX); 2178 event.setMaxScrollY(maxScrollY); 2179 } 2180 } 2181 2182 /** 2183 * Returns whether or not accessibility injection is being used. 2184 */ 2185 public boolean isInjectingAccessibilityScript() { 2186 return mAccessibilityInjector.accessibilityIsAvailable(); 2187 } 2188 2189 /** 2190 * Enable or disable accessibility features. 2191 */ 2192 public void setAccessibilityState(boolean state) { 2193 mAccessibilityInjector.setScriptEnabled(state); 2194 } 2195 2196 /** 2197 * Stop any TTS notifications that are currently going on. 2198 */ 2199 public void stopCurrentAccessibilityNotifications() { 2200 mAccessibilityInjector.onPageLostFocus(); 2201 } 2202 2203 /** 2204 * @See android.webkit.WebView#pageDown(boolean) 2205 */ 2206 public boolean pageDown(boolean bottom) { 2207 if (computeVerticalScrollOffset() >= mContentHeight - getHeight()) { 2208 // We seem to already be at the bottom of the page, so no scrolling will occur. 2209 return false; 2210 } 2211 2212 if (bottom) { 2213 scrollTo(computeHorizontalScrollOffset(), mContentHeight - getHeight()); 2214 } else { 2215 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN)); 2216 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_DOWN)); 2217 } 2218 return true; 2219 } 2220 2221 /** 2222 * @See android.webkit.WebView#pageUp(boolean) 2223 */ 2224 public boolean pageUp(boolean top) { 2225 if (computeVerticalScrollOffset() == 0) { 2226 // We seem to already be at the top of the page, so no scrolling will occur. 2227 return false; 2228 } 2229 2230 if (top) { 2231 scrollTo(computeHorizontalScrollOffset(), 0); 2232 } else { 2233 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP)); 2234 dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PAGE_UP)); 2235 } 2236 return true; 2237 } 2238 2239 /** 2240 * Callback factory method for nativeGetNavigationHistory(). 2241 */ 2242 @CalledByNative 2243 private void addToNavigationHistory(Object history, String url, String virtualUrl, 2244 String originalUrl, String title, Bitmap favicon) { 2245 NavigationEntry entry = new NavigationEntry(url, virtualUrl, originalUrl, title, favicon); 2246 ((NavigationHistory) history).addEntry(entry); 2247 } 2248 2249 /** 2250 * Get a copy of the navigation history of the view. 2251 */ 2252 public NavigationHistory getNavigationHistory() { 2253 NavigationHistory history = new NavigationHistory(); 2254 int currentIndex = nativeGetNavigationHistory(mNativeContentViewCore, history); 2255 history.setCurrentEntryIndex(currentIndex); 2256 return history; 2257 } 2258 2259 /** 2260 * Update of the latest vsync parameters. 2261 * @param tickTimeMicros The latest vsync tick time in microseconds. 2262 * @param intervalMicros The vsync interval in microseconds. 2263 */ 2264 public void updateVSync(long tickTimeMicros, long intervalMicros) { 2265 if (mNativeContentViewCore != 0) { 2266 nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros, intervalMicros); 2267 } 2268 } 2269 2270 /** 2271 * Temporary shim for updateVSync. 2272 */ 2273 public void UpdateVSync(long tickTimeMicros, long intervalMicros) { 2274 updateVSync(tickTimeMicros, intervalMicros); 2275 } 2276 2277 private native int nativeInit(boolean hardwareAccelerated, int webContentsPtr, 2278 int windowAndroidPtr); 2279 2280 private native void nativeOnJavaContentViewCoreDestroyed(int nativeContentViewCoreImpl); 2281 2282 private native void nativeLoadUrl( 2283 int nativeContentViewCoreImpl, 2284 String url, 2285 int loadUrlType, 2286 int transitionType, 2287 int uaOverrideOption, 2288 String extraHeaders, 2289 byte[] postData, 2290 String baseUrlForDataUrl, 2291 String virtualUrlForDataUrl, 2292 boolean canLoadLocalResources); 2293 2294 private native void nativeSetAllUserAgentOverridesInHistory(int nativeContentViewCoreImpl, 2295 String userAgentOverride); 2296 2297 private native String nativeGetURL(int nativeContentViewCoreImpl); 2298 2299 private native String nativeGetTitle(int nativeContentViewCoreImpl); 2300 2301 private native boolean nativeIsIncognito(int nativeContentViewCoreImpl); 2302 2303 // Returns true if the native side crashed so that java side can draw a sad tab. 2304 private native boolean nativeCrashed(int nativeContentViewCoreImpl); 2305 2306 private native void nativeSetFocus(int nativeContentViewCoreImpl, boolean focused); 2307 2308 private native void nativeSendOrientationChangeEvent( 2309 int nativeContentViewCoreImpl, int orientation); 2310 2311 private native boolean nativeSendTouchEvent( 2312 int nativeContentViewCoreImpl, long timeMs, int action, TouchPoint[] pts); 2313 2314 private native int nativeSendMouseMoveEvent( 2315 int nativeContentViewCoreImpl, long timeMs, int x, int y); 2316 2317 private native int nativeSendMouseWheelEvent( 2318 int nativeContentViewCoreImpl, long timeMs, int x, int y, float verticalAxis); 2319 2320 private native void nativeScrollBegin(int nativeContentViewCoreImpl, long timeMs, int x, int y); 2321 2322 private native void nativeScrollEnd(int nativeContentViewCoreImpl, long timeMs); 2323 2324 private native void nativeScrollBy( 2325 int nativeContentViewCoreImpl, long timeMs, int x, int y, int deltaX, int deltaY); 2326 2327 private native void nativeFlingStart( 2328 int nativeContentViewCoreImpl, long timeMs, int x, int y, int vx, int vy); 2329 2330 private native void nativeFlingCancel(int nativeContentViewCoreImpl, long timeMs); 2331 2332 private native void nativeSingleTap( 2333 int nativeContentViewCoreImpl, long timeMs, int x, int y, boolean linkPreviewTap); 2334 2335 private native void nativeShowPressState( 2336 int nativeContentViewCoreImpl, long timeMs, int x, int y); 2337 2338 private native void nativeDoubleTap(int nativeContentViewCoreImpl, long timeMs, int x, int y); 2339 2340 private native void nativeLongPress(int nativeContentViewCoreImpl, long timeMs, int x, int y, 2341 boolean linkPreviewTap); 2342 2343 private native void nativePinchBegin(int nativeContentViewCoreImpl, long timeMs, int x, int y); 2344 2345 private native void nativePinchEnd(int nativeContentViewCoreImpl, long timeMs); 2346 2347 private native void nativePinchBy(int nativeContentViewCoreImpl, long timeMs, 2348 int anchorX, int anchorY, float deltaScale); 2349 2350 private native void nativeSelectBetweenCoordinates( 2351 int nativeContentViewCoreImpl, int x1, int y1, int x2, int y2); 2352 2353 private native boolean nativeCanGoBack(int nativeContentViewCoreImpl); 2354 2355 private native boolean nativeCanGoForward(int nativeContentViewCoreImpl); 2356 2357 private native boolean nativeCanGoToOffset(int nativeContentViewCoreImpl, int offset); 2358 2359 private native void nativeGoToOffset(int nativeContentViewCoreImpl, int offset); 2360 2361 private native void nativeGoBack(int nativeContentViewCoreImpl); 2362 2363 private native void nativeGoForward(int nativeContentViewCoreImpl); 2364 2365 private native void nativeStopLoading(int nativeContentViewCoreImpl); 2366 2367 private native void nativeReload(int nativeContentViewCoreImpl); 2368 2369 private native void nativeCancelPendingReload(int nativeContentViewCoreImpl); 2370 2371 private native void nativeContinuePendingReload(int nativeContentViewCoreImpl); 2372 2373 private native void nativeSelectPopupMenuItems(int nativeContentViewCoreImpl, int[] indices); 2374 2375 private native void nativeScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl); 2376 private native void nativeUndoScrollFocusedEditableNodeIntoView(int nativeContentViewCoreImpl); 2377 private native boolean nativeNeedsReload(int nativeContentViewCoreImpl); 2378 2379 private native void nativeClearHistory(int nativeContentViewCoreImpl); 2380 2381 private native int nativeEvaluateJavaScript(String script); 2382 2383 private native int nativeGetNativeImeAdapter(int nativeContentViewCoreImpl); 2384 2385 private native int nativeGetCurrentRenderProcessId(int nativeContentViewCoreImpl); 2386 2387 private native int nativeGetBackgroundColor(int nativeContentViewCoreImpl); 2388 2389 private native void nativeSetBackgroundColor(int nativeContentViewCoreImpl, int color); 2390 2391 private native void nativeOnShow(int nativeContentViewCoreImpl); 2392 private native void nativeOnHide(int nativeContentViewCoreImpl); 2393 2394 private native void nativeSetUseDesktopUserAgent(int nativeContentViewCoreImpl, 2395 boolean enabled, boolean reloadOnChange); 2396 private native boolean nativeGetUseDesktopUserAgent(int nativeContentViewCoreImpl); 2397 2398 private native void nativeClearSslPreferences(int nativeContentViewCoreImpl); 2399 2400 private native void nativeAddJavascriptInterface(int nativeContentViewCoreImpl, Object object, 2401 String name, boolean requireAnnotation); 2402 2403 private native void nativeRemoveJavascriptInterface(int nativeContentViewCoreImpl, String name); 2404 2405 private native int nativeGetNavigationHistory(int nativeContentViewCoreImpl, Object context); 2406 2407 private native void nativeUpdateVSyncParameters(int nativeContentViewCoreImpl, 2408 long timebaseMicros, long intervalMicros); 2409 2410 private native boolean nativePopulateBitmapFromCompositor(int nativeContentViewCoreImpl, 2411 Bitmap bitmap); 2412 2413 private native void nativeSetSize(int nativeContentViewCoreImpl, int width, int height); 2414} 2415