import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import com.google.common.annotations.VisibleForTesting;
+import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.CalledByNative;
import org.chromium.base.CommandLine;
import org.chromium.base.JNINamespace;
import org.chromium.content.browser.accessibility.AccessibilityInjector;
import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
import org.chromium.content.browser.input.AdapterInputConnection;
+import org.chromium.content.browser.input.GamepadList;
import org.chromium.content.browser.input.HandleView;
import org.chromium.content.browser.input.ImeAdapter;
import org.chromium.content.browser.input.ImeAdapter.AdapterInputConnectionFactory;
import org.chromium.content.browser.input.InputMethodManagerWrapper;
import org.chromium.content.browser.input.InsertionHandleController;
+import org.chromium.content.browser.input.SelectPopup;
import org.chromium.content.browser.input.SelectPopupDialog;
+import org.chromium.content.browser.input.SelectPopupDropdown;
import org.chromium.content.browser.input.SelectPopupItem;
import org.chromium.content.browser.input.SelectionHandleController;
import org.chromium.content.common.ContentSwitches;
// Length of the delay (in ms) before fading in handles after the last page movement.
private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
+ // These values are obtained from Samsung.
+ // TODO(changwan): refactor SPen related code into a separate class. See
+ // http://crbug.com/398169.
+ private static final int SPEN_ACTION_DOWN = 211;
+ private static final int SPEN_ACTION_UP = 212;
+ private static final int SPEN_ACTION_MOVE = 213;
+ private static final int SPEN_ACTION_CANCEL = 214;
+ private static Boolean sIsSPenSupported;
+
// If the embedder adds a JavaScript interface object that contains an indirect reference to
// the ContentViewCore, then storing a strong ref to the interface object on the native
// side would prevent garbage collection of the ContentViewCore (as that strong ref would
* extractSmartClipData are available.
*/
public interface SmartClipDataListener {
- public void onSmartClipDataExtracted(String result);
- }
-
- private VSyncManager.Provider mVSyncProvider;
- private VSyncManager.Listener mVSyncListener;
- private int mVSyncSubscriberCount;
- private boolean mVSyncListenerRegistered;
-
- // To avoid IPC delay we use input events to directly trigger a vsync signal in the renderer.
- // When we do this, we also need to avoid sending the real vsync signal for the current
- // frame to avoid double-ticking. This flag is used to inhibit the next vsync notification.
- private boolean mDidSignalVSyncUsingInputEvent;
-
- public VSyncManager.Listener getVSyncListener(VSyncManager.Provider vsyncProvider) {
- if (mVSyncProvider != null && mVSyncListenerRegistered) {
- mVSyncProvider.unregisterVSyncListener(mVSyncListener);
- mVSyncListenerRegistered = false;
- }
-
- mVSyncProvider = vsyncProvider;
- mVSyncListener = new VSyncManager.Listener() {
- @Override
- public void updateVSync(long tickTimeMicros, long intervalMicros) {
- if (mNativeContentViewCore != 0) {
- nativeUpdateVSyncParameters(mNativeContentViewCore, tickTimeMicros,
- intervalMicros);
- }
- }
-
- @Override
- public void onVSync(long frameTimeMicros) {
- animateIfNecessary(frameTimeMicros);
-
- if (mRequestedVSyncForInput) {
- mRequestedVSyncForInput = false;
- removeVSyncSubscriber();
- }
- if (mNativeContentViewCore != 0) {
- nativeOnVSync(mNativeContentViewCore, frameTimeMicros);
- }
- }
- };
-
- if (mVSyncSubscriberCount > 0) {
- // addVSyncSubscriber() is called before getVSyncListener.
- vsyncProvider.registerVSyncListener(mVSyncListener);
- mVSyncListenerRegistered = true;
- }
-
- return mVSyncListener;
- }
-
- @CalledByNative
- void addVSyncSubscriber() {
- if (!isVSyncNotificationEnabled()) {
- mDidSignalVSyncUsingInputEvent = false;
- }
- if (mVSyncProvider != null && !mVSyncListenerRegistered) {
- mVSyncProvider.registerVSyncListener(mVSyncListener);
- mVSyncListenerRegistered = true;
- }
- mVSyncSubscriberCount++;
- }
-
- @CalledByNative
- void removeVSyncSubscriber() {
- if (mVSyncProvider != null && mVSyncSubscriberCount == 1) {
- assert mVSyncListenerRegistered;
- mVSyncProvider.unregisterVSyncListener(mVSyncListener);
- mVSyncListenerRegistered = false;
- }
- mVSyncSubscriberCount--;
- assert mVSyncSubscriberCount >= 0;
- }
-
- @CalledByNative
- private void resetVSyncNotification() {
- while (isVSyncNotificationEnabled()) removeVSyncSubscriber();
- mVSyncSubscriberCount = 0;
- mVSyncListenerRegistered = false;
- mNeedAnimate = false;
- }
-
- private boolean isVSyncNotificationEnabled() {
- return mVSyncProvider != null && mVSyncListenerRegistered;
- }
-
- @CalledByNative
- private void setNeedsAnimate() {
- if (!mNeedAnimate) {
- mNeedAnimate = true;
- addVSyncSubscriber();
- }
+ public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
}
private final Context mContext;
// Native pointer to C++ ContentViewCoreImpl object which will be set by nativeInit().
private long mNativeContentViewCore = 0;
- private boolean mInForeground = false;
-
private final ObserverList<GestureStateListener> mGestureStateListeners;
private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
private ZoomControlsDelegate mZoomControlsDelegate;
private PopupZoomer mPopupZoomer;
- private SelectPopupDialog mSelectPopupDialog;
+ private SelectPopup mSelectPopup;
private Runnable mFakeMouseMoveRunnable = null;
private int mOverdrawBottomHeightPix;
private int mViewportSizeOffsetWidthPix;
private int mViewportSizeOffsetHeightPix;
- private int mLocationInWindowX;
- private int mLocationInWindowY;
// Cached copy of all positions and scales as reported by the renderer.
private final RenderCoordinates mRenderCoordinates;
// because the OSK was just brought up.
private final Rect mFocusPreOSKViewportRect = new Rect();
- // Whether we received a new frame since consumePendingRendererFrame() was last called.
- private boolean mPendingRendererFrame = false;
-
- // Whether we should animate at the next vsync tick.
- private boolean mNeedAnimate = false;
-
- // Whether we requested a proactive vsync event in response to touch input.
- // This reduces the latency of responding to input by ensuring the renderer
- // is sent a BeginFrame for every touch event we receive. Otherwise the
- // renderer's SetNeedsBeginFrame message would get serviced at the next
- // vsync.
- private boolean mRequestedVSyncForInput = false;
-
- // On single tap this will store the x, y coordinates of the touch.
- private int mSingleTapX;
- private int mSingleTapY;
+ // On tap this will store the x, y coordinates of the touch.
+ private int mLastTapX;
+ private int mLastTapY;
// Whether a touch scroll sequence is active, used to hide text selection
// handles. Note that a scroll sequence will *always* bound a pinch
// a focused element.
// Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new
// state must be reflected to this to keep consistency.
- private Editable mEditable;
+ private final Editable mEditable;
/**
* PID used to indicate an invalid render process.
// if there is no render process.
public static final int INVALID_RENDER_PROCESS_PID = 0;
+ // Offsets for the events that passes through this ContentViewCore.
+ private float mCurrentTouchOffsetX;
+ private float mCurrentTouchOffsetY;
+
+ // Offsets for smart clip
+ private int mSmartClipOffsetX;
+ private int mSmartClipOffsetY;
+
/**
* Constructs a new ContentViewCore. Embedders must call initialize() after constructing
* a ContentViewCore and before using it.
@VisibleForTesting
public ViewAndroidDelegate getViewAndroidDelegate() {
return new ViewAndroidDelegate() {
+ // mContainerView can change, but this ViewAndroidDelegate can only be used to
+ // add and remove views from the mContainerViewAtCreation.
+ private final ViewGroup mContainerViewAtCreation = mContainerView;
+
@Override
public View acquireAnchorView() {
- View anchorView = new View(getContext());
- mContainerView.addView(anchorView);
+ View anchorView = new View(mContext);
+ mContainerViewAtCreation.addView(anchorView);
return anchorView;
}
@SuppressWarnings("deprecation") // AbsoluteLayout
public void setAnchorViewPosition(
View view, float x, float y, float width, float height) {
- assert view.getParent() == mContainerView;
+ assert view.getParent() == mContainerViewAtCreation;
- float scale = (float) DeviceDisplayInfo.create(getContext()).getDIPScale();
+ float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
// The anchor view should not go outside the bounds of the ContainerView.
int leftMargin = Math.round(x * scale);
int topMargin = Math.round(mRenderCoordinates.getContentOffsetYPix() + y * scale);
int scaledWidth = Math.round(width * scale);
// ContentViewCore currently only supports these two container view types.
- if (mContainerView instanceof FrameLayout) {
- if (scaledWidth + leftMargin > mContainerView.getWidth()) {
- scaledWidth = mContainerView.getWidth() - leftMargin;
+ if (mContainerViewAtCreation instanceof FrameLayout) {
+ int startMargin;
+ if (ApiCompatibilityUtils.isLayoutRtl(mContainerViewAtCreation)) {
+ startMargin = mContainerViewAtCreation.getMeasuredWidth()
+ - Math.round((width + x) * scale);
+ } else {
+ startMargin = leftMargin;
+ }
+ if (scaledWidth + startMargin > mContainerViewAtCreation.getWidth()) {
+ scaledWidth = mContainerViewAtCreation.getWidth() - startMargin;
}
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
scaledWidth, Math.round(height * scale));
- lp.leftMargin = leftMargin;
+ ApiCompatibilityUtils.setMarginStart(lp, startMargin);
lp.topMargin = topMargin;
view.setLayoutParams(lp);
- } else if (mContainerView instanceof android.widget.AbsoluteLayout) {
+ } else if (mContainerViewAtCreation instanceof android.widget.AbsoluteLayout) {
// This fixes the offset due to a difference in
// scrolling model of WebView vs. Chrome.
- // TODO(sgurun) fix this to use mContainerView.getScroll[X/Y]()
+ // TODO(sgurun) fix this to use mContainerViewAtCreation.getScroll[X/Y]()
// as it naturally accounts for scroll differences between
// these models.
leftMargin += mRenderCoordinates.getScrollXPixInt();
scaledWidth, (int) (height * scale), leftMargin, topMargin);
view.setLayoutParams(lp);
} else {
- Log.e(TAG, "Unknown layout " + mContainerView.getClass().getName());
+ Log.e(TAG, "Unknown layout " + mContainerViewAtCreation.getClass().getName());
}
}
@Override
public void releaseAnchorView(View anchorView) {
- mContainerView.removeView(anchorView);
+ mContainerViewAtCreation.removeView(anchorView);
}
};
}
return mInputConnection;
}
- @VisibleForTesting
- public void setContainerViewForTest(ViewGroup view) {
- mContainerView = view;
- }
-
private ImeAdapter createImeAdapter(Context context) {
return new ImeAdapter(mInputMethodManagerWrapper,
new ImeAdapter.ImeAdapterDelegate() {
}
@Override
- public void onSetFieldValue() {
- scrollFocusedEditableNodeIntoView();
- }
-
- @Override
public void onDismissInput() {
getContentViewClient().onImeStateChangeRequested(false);
}
// always be called, crbug.com/294908.
getContainerView().getWindowVisibleDisplayFrame(
mFocusPreOSKViewportRect);
- } else if (resultCode ==
+ } else if (hasFocus() && resultCode ==
InputMethodManager.RESULT_UNCHANGED_SHOWN) {
// If the OSK was already there, focus the form immediately.
scrollFocusedEditableNodeIntoView();
// deleting it after destroying the ContentViewCore.
public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
long nativeWebContents, WindowAndroid windowAndroid) {
- mContainerView = containerView;
- mPositionObserver = new ViewPositionObserver(mContainerView);
+ setContainerView(containerView);
+
mPositionListener = new PositionObserver.Listener() {
@Override
public void onPositionChanged(int x, int y) {
};
mNativeContentViewCore = nativeInit(
- nativeWebContents, viewAndroidNativePointer, windowNativePointer);
+ nativeWebContents, viewAndroidNativePointer, windowNativePointer,
+ mRetainedJavaScriptObjects);
mWebContents = nativeGetWebContentsAndroid(mNativeContentViewCore);
mContentSettings = new ContentSettings(this, mNativeContentViewCore);
- initializeContainerView(internalDispatcher);
+
+ setContainerViewInternals(internalDispatcher);
+ mRenderCoordinates.reset();
+ initPopupZoomer(mContext);
+ mImeAdapter = createImeAdapter(mContext);
mAccessibilityInjector = AccessibilityInjector.newInstance(this);
+ mWebContentsObserver = new WebContentsObserverAndroid(this) {
+ @Override
+ public void didNavigateMainFrame(String url, String baseUrl,
+ boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
+ if (!isNavigationToDifferentPage) return;
+ hidePopups();
+ resetScrollInProgress();
+ resetGestureDetection();
+ }
+
+ @Override
+ public void renderProcessGone(boolean wasOomProtected) {
+ hidePopups();
+ resetScrollInProgress();
+ // No need to reset gesture detection as the detector will have
+ // been destroyed in the RenderWidgetHostView.
+ }
+ };
+ }
+
+ /**
+ * Sets a new container view for this {@link ContentViewCore}.
+ *
+ * <p>WARNING: This is not a general purpose method and has been designed with WebView
+ * fullscreen in mind. Please be aware that it might not be appropriate for other use cases
+ * and that it has a number of limitations. For example the PopupZoomer only works with the
+ * container view with which this ContentViewCore has been initialized.
+ *
+ * <p>This method only performs a small part of replacing the container view and
+ * embedders are responsible for:
+ * <ul>
+ * <li>Disconnecting the old container view from this ContentViewCore</li>
+ * <li>Updating the InternalAccessDelegate</li>
+ * <li>Reconciling the state of this ContentViewCore with the new container view</li>
+ * <li>Tearing down and recreating the native GL rendering where appropriate</li>
+ * <li>etc.</li>
+ * </ul>
+ */
+ public void setContainerView(ViewGroup containerView) {
+ TraceEvent.begin();
+ if (mContainerView != null) {
+ mPositionObserver.removeListener(mPositionListener);
+ mSelectionHandleController = null;
+ mInsertionHandleController = null;
+ mInputConnection = null;
+ }
+
+ mContainerView = containerView;
+ mPositionObserver = new ViewPositionObserver(mContainerView);
String contentDescription = "Web View";
if (R.string.accessibility_content_view == 0) {
Log.w(TAG, "Setting contentDescription to 'Web View' as no value was specified.");
R.string.accessibility_content_view);
}
mContainerView.setContentDescription(contentDescription);
- mWebContentsObserver = new WebContentsObserverAndroid(this) {
- @Override
- public void didStartLoading(String url) {
- hidePopupDialog();
- resetScrollInProgress();
- resetGestureDetectors();
- }
- };
+ mContainerView.setWillNotDraw(false);
+ mContainerView.setClickable(true);
+ TraceEvent.end();
}
@CalledByNative
mContainerViewInternals = internalDispatcher;
}
- /**
- * Initializes the View that will contain all Views created by the ContentViewCore.
- *
- * @param internalDispatcher Handles dispatching all hidden or super methods to the
- * containerView.
- */
- private void initializeContainerView(InternalAccessDelegate internalDispatcher) {
- TraceEvent.begin();
- mContainerViewInternals = internalDispatcher;
-
- mContainerView.setWillNotDraw(false);
- mContainerView.setClickable(true);
-
- mRenderCoordinates.reset();
- onRenderCoordinatesUpdated();
-
- initPopupZoomer(mContext);
- mImeAdapter = createImeAdapter(mContext);
- TraceEvent.end();
- }
-
private void initPopupZoomer(Context context) {
mPopupZoomer = new PopupZoomer(context);
mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
+ // mContainerView can change, but this OnVisibilityChangedListener can only be used
+ // to add and remove views from the mContainerViewAtCreation.
+ private final ViewGroup mContainerViewAtCreation = mContainerView;
+
@Override
public void onPopupZoomerShown(final PopupZoomer zoomer) {
- mContainerView.post(new Runnable() {
+ mContainerViewAtCreation.post(new Runnable() {
@Override
public void run() {
- if (mContainerView.indexOfChild(zoomer) == -1) {
- mContainerView.addView(zoomer);
+ if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
+ mContainerViewAtCreation.addView(zoomer);
} else {
assert false : "PopupZoomer should never be shown without being hidden";
}
@Override
public void onPopupZoomerHidden(final PopupZoomer zoomer) {
- mContainerView.post(new Runnable() {
+ mContainerViewAtCreation.post(new Runnable() {
@Override
public void run() {
- if (mContainerView.indexOfChild(zoomer) != -1) {
- mContainerView.removeView(zoomer);
- mContainerView.invalidate();
+ if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
+ mContainerViewAtCreation.removeView(zoomer);
+ mContainerViewAtCreation.invalidate();
} else {
assert false : "PopupZoomer should never be hidden without being shown";
}
// TODO(yongsheng): LONG_TAP is not enabled in PopupZoomer. So need to dispatch a LONG_TAP
// gesture if a user completes a tap on PopupZoomer UI after a LONG_PRESS gesture.
PopupZoomer.OnTapListener listener = new PopupZoomer.OnTapListener() {
+ // mContainerView can change, but this OnTapListener can only be used
+ // with the mContainerViewAtCreation.
+ private final ViewGroup mContainerViewAtCreation = mContainerView;
+
@Override
public boolean onSingleTap(View v, MotionEvent e) {
- mContainerView.requestFocus();
+ mContainerViewAtCreation.requestFocus();
if (mNativeContentViewCore != 0) {
nativeSingleTap(mNativeContentViewCore, e.getEventTime(), e.getX(), e.getY());
}
nativeOnJavaContentViewCoreDestroyed(mNativeContentViewCore);
}
mWebContents = null;
- resetVSyncNotification();
- mVSyncProvider = null;
if (mViewAndroid != null) mViewAndroid.destroy();
mNativeContentViewCore = 0;
mContentSettings = null;
mContentViewClient = client;
}
- ContentViewClient getContentViewClient() {
+ @VisibleForTesting
+ public ContentViewClient getContentViewClient() {
if (mContentViewClient == null) {
// We use the Null Object pattern to avoid having to perform a null check in this class.
// We create it lazily because most of the time a client will be set almost immediately
params.mUrl,
params.mLoadUrlType,
params.mTransitionType,
+ params.getReferrer() != null ? params.getReferrer().getUrl() : null,
+ params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
params.mUaOverrideOption,
params.getExtraHeadersString(),
params.mPostData,
params.mBaseUrlForDataUrl,
params.mVirtualUrlForDataUrl,
- params.mCanLoadLocalResources);
+ params.mCanLoadLocalResources,
+ params.mIsRendererInitiated);
}
/**
* Stops loading the current web contents.
*/
public void stopLoading() {
- if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
+ if (mWebContents != null) mWebContents.stop();
}
/**
* @return The title of the current page.
*/
public String getTitle() {
- if (mNativeContentViewCore != 0) return nativeGetTitle(mNativeContentViewCore);
- return null;
+ return mWebContents == null ? null : mWebContents.getTitle();
}
/**
}
/**
- * Mark any new frames that have arrived since this function was last called as non-pending.
- *
- * @return Whether there was a pending frame from the renderer.
- */
- public boolean consumePendingRendererFrame() {
- boolean hadPendingFrame = mPendingRendererFrame;
- mPendingRendererFrame = false;
- return hadPendingFrame;
- }
-
- /**
* @return Viewport width in physical pixels as set from onSizeChanged.
*/
@CalledByNative
// End FrameLayout overrides.
/**
+ * TODO(changwan): refactor SPen related code into a separate class. See
+ * http://crbug.com/398169.
+ * @return Whether SPen is supported on the device.
+ */
+ public static boolean isSPenSupported(Context context) {
+ if (sIsSPenSupported == null)
+ sIsSPenSupported = detectSPenSupport(context);
+ return sIsSPenSupported.booleanValue();
+ }
+
+ private static boolean detectSPenSupport(Context context) {
+ if (!"SAMSUNG".equalsIgnoreCase(Build.MANUFACTURER))
+ return false;
+
+ final FeatureInfo[] infos = context.getPackageManager().getSystemAvailableFeatures();
+ for (FeatureInfo info : infos) {
+ if ("com.sec.feature.spen_usp".equalsIgnoreCase(info.name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Convert SPen event action into normal event action.
+ * TODO(changwan): refactor SPen related code into a separate class. See
+ * http://crbug.com/398169.
+ *
+ * @param eventActionMasked Input event action. It is assumed that it is masked as the values
+ cannot be ORed.
+ * @return Event action after the conversion
+ */
+ public static int convertSPenEventAction(int eventActionMasked) {
+ // S-Pen support: convert to normal stylus event handling
+ switch (eventActionMasked) {
+ case SPEN_ACTION_DOWN:
+ return MotionEvent.ACTION_DOWN;
+ case SPEN_ACTION_UP:
+ return MotionEvent.ACTION_UP;
+ case SPEN_ACTION_MOVE:
+ return MotionEvent.ACTION_MOVE;
+ case SPEN_ACTION_CANCEL:
+ return MotionEvent.ACTION_CANCEL;
+ default:
+ return eventActionMasked;
+ }
+ }
+
+ /**
* @see View#onTouchEvent(MotionEvent)
*/
public boolean onTouchEvent(MotionEvent event) {
- cancelRequestToScrollFocusedEditableNodeIntoView();
+ TraceEvent.begin("onTouchEvent");
+ try {
+ cancelRequestToScrollFocusedEditableNodeIntoView();
+
+ int eventAction = event.getActionMasked();
+
+ if (isSPenSupported(mContext))
+ eventAction = convertSPenEventAction(eventAction);
+
+ // Only these actions have any effect on gesture detection. Other
+ // actions have no corresponding WebTouchEvent type and may confuse the
+ // touch pipline, so we ignore them entirely.
+ if (eventAction != MotionEvent.ACTION_DOWN
+ && eventAction != MotionEvent.ACTION_UP
+ && eventAction != MotionEvent.ACTION_CANCEL
+ && eventAction != MotionEvent.ACTION_MOVE
+ && eventAction != MotionEvent.ACTION_POINTER_DOWN
+ && eventAction != MotionEvent.ACTION_POINTER_UP) {
+ return false;
+ }
- if (!mRequestedVSyncForInput) {
- mRequestedVSyncForInput = true;
- addVSyncSubscriber();
- }
+ if (mNativeContentViewCore == 0) return false;
- final int eventAction = event.getActionMasked();
+ // A zero offset is quite common, in which case the unnecessary copy should be avoided.
+ MotionEvent offset = null;
+ if (mCurrentTouchOffsetX != 0 || mCurrentTouchOffsetY != 0) {
+ offset = createOffsetMotionEvent(event);
+ event = offset;
+ }
- // Only these actions have any effect on gesture detection. Other
- // actions have no corresponding WebTouchEvent type and may confuse the
- // touch pipline, so we ignore them entirely.
- if (eventAction != MotionEvent.ACTION_DOWN
- && eventAction != MotionEvent.ACTION_UP
- && eventAction != MotionEvent.ACTION_CANCEL
- && eventAction != MotionEvent.ACTION_MOVE
- && eventAction != MotionEvent.ACTION_POINTER_DOWN
- && eventAction != MotionEvent.ACTION_POINTER_UP) {
- return false;
+ final int pointerCount = event.getPointerCount();
+ final boolean consumed = nativeOnTouchEvent(mNativeContentViewCore, event,
+ event.getEventTime(), eventAction,
+ pointerCount, event.getHistorySize(), event.getActionIndex(),
+ event.getX(), event.getY(),
+ pointerCount > 1 ? event.getX(1) : 0,
+ pointerCount > 1 ? event.getY(1) : 0,
+ event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
+ event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
+ event.getRawX(), event.getRawY(),
+ event.getToolType(0),
+ pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
+ event.getButtonState());
+
+ if (offset != null) offset.recycle();
+ return consumed;
+ } finally {
+ TraceEvent.end("onTouchEvent");
}
-
- if (mNativeContentViewCore == 0) return false;
- final int pointerCount = event.getPointerCount();
- return nativeOnTouchEvent(mNativeContentViewCore, event,
- event.getEventTime(), eventAction,
- pointerCount, event.getHistorySize(), event.getActionIndex(),
- event.getX(), event.getY(),
- pointerCount > 1 ? event.getX(1) : 0,
- pointerCount > 1 ? event.getY(1) : 0,
- event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
- event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0);
}
public void setIgnoreRemainingTouchEvents() {
- if (mNativeContentViewCore == 0) return;
- nativeIgnoreRemainingTouchEvents(mNativeContentViewCore);
+ resetGestureDetection();
}
public boolean isScrollInProgress() {
@SuppressWarnings("unused")
@CalledByNative
+ private void onSingleTapEventAck(boolean consumed, int x, int y) {
+ for (mGestureStateListenersIterator.rewind();
+ mGestureStateListenersIterator.hasNext();) {
+ mGestureStateListenersIterator.next().onSingleTap(consumed, x, y);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
private void onDoubleTapEventAck() {
temporarilyHideTextHandles();
}
}
/**
+ * Cancel any fling gestures active.
+ * @param timeMs Current time (in milliseconds).
+ */
+ public void cancelFling(long timeMs) {
+ if (mNativeContentViewCore == 0) return;
+ nativeFlingCancel(mNativeContentViewCore, timeMs);
+ }
+
+ /**
* Add a listener that gets alerted on gesture state changes.
* @param listener Listener to add.
*/
}
}
+ /**
+ * Requests the renderer insert a link to the specified stylesheet in the
+ * main frame's document.
+ */
+ void addStyleSheetByURL(String url) {
+ nativeAddStyleSheetByURL(mNativeContentViewCore, url);
+ }
+
/** Callback interface for evaluateJavaScript(). */
public interface JavaScriptCallback {
void handleJavaScriptResult(String jsonResult);
*/
public void onShow() {
assert mNativeContentViewCore != 0;
- if (!mInForeground) {
- ChildProcessLauncher.getBindingManager().setInForeground(getCurrentRenderProcessId(),
- true);
- }
- mInForeground = true;
nativeOnShow(mNativeContentViewCore);
setAccessibilityState(mAccessibilityManager.isEnabled());
}
*/
public void onHide() {
assert mNativeContentViewCore != 0;
- if (mInForeground) {
- ChildProcessLauncher.getBindingManager().setInForeground(getCurrentRenderProcessId(),
- false);
- }
- mInForeground = false;
- hidePopupDialog();
+ hidePopups();
setInjectedAccessibility(false);
nativeOnHide(mNativeContentViewCore);
}
return mContentSettings;
}
- private void onRenderCoordinatesUpdated() {
- if (mNativeContentViewCore == 0) return;
-
- // We disable double tap zoom for pages that have a width=device-width
- // or narrower viewport (indicating that this is a mobile-optimized or
- // responsive web design, so text will be legible without zooming).
- // We also disable it for pages that disallow the user from zooming in
- // or out (even if they don't have a device-width or narrower viewport).
- nativeSetDoubleTapSupportForPageEnabled(mNativeContentViewCore,
- !mRenderCoordinates.hasMobileViewport() && !mRenderCoordinates.hasFixedPageScale());
- }
-
- private void hidePopupDialog() {
- if (mSelectPopupDialog != null) {
- mSelectPopupDialog.hide();
- mSelectPopupDialog = null;
- }
+ private void hidePopups() {
+ hideSelectPopup();
hideHandles();
hideSelectActionBar();
}
- void hideSelectActionBar() {
+ public void hideSelectActionBar() {
if (mActionMode != null) {
mActionMode.finish();
mActionMode = null;
return mActionMode != null;
}
- private void resetGestureDetectors() {
+ private void resetGestureDetection() {
if (mNativeContentViewCore == 0) return;
- nativeResetGestureDetectors(mNativeContentViewCore);
+ nativeResetGestureDetection(mNativeContentViewCore);
}
/**
setAccessibilityState(mAccessibilityManager.isEnabled());
ScreenOrientationListener.getInstance().addObserver(this, mContext);
+ GamepadList.onAttachedToWindow(mContext);
}
/**
@SuppressLint("MissingSuperCall")
public void onDetachedFromWindow() {
setInjectedAccessibility(false);
- hidePopupDialog();
+ hidePopups();
mZoomControlsDelegate.dismissZoomPicker();
unregisterAccessibilityContentObserver();
ScreenOrientationListener.getInstance().removeObserver(this);
+ GamepadList.onDetachedFromWindow();
}
/**
TraceEvent.begin();
if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
- mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
- ImeAdapter.getTextInputTypeNone());
+ if (mNativeContentViewCore != 0) {
+ mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore),
+ ImeAdapter.getTextInputTypeNone());
+ }
mInputMethodManagerWrapper.restartInput(mContainerView);
}
mContainerViewInternals.super_onConfigurationChanged(newConfig);
}
/**
- * Called when the ContentView's position in the activity window changed. This information is
- * used for cropping screenshots.
- */
- public void onLocationInWindowChanged(int x, int y) {
- mLocationInWindowX = x;
- mLocationInWindowY = y;
- }
-
- /**
* Called when the underlying surface the compositor draws to changes size.
* This may be larger than the viewport size.
*/
}
/**
+ * Selects the word around the caret, if any.
+ * The caller can check if selection actually occurred by listening to OnSelectionChanged.
+ */
+ public void selectWordAroundCaret() {
+ if (mNativeContentViewCore == 0) return;
+ nativeSelectWordAroundCaret(mNativeContentViewCore);
+ }
+
+ /**
* @see View#onWindowFocusChanged(boolean)
*/
public void onWindowFocusChanged(boolean hasWindowFocus) {
- if (!hasWindowFocus) {
- if (mNativeContentViewCore == 0) return;
- nativeOnWindowFocusLost(mNativeContentViewCore);
- }
+ if (!hasWindowFocus) resetGestureDetection();
}
public void onFocusChanged(boolean gainFocus) {
if (!gainFocus) {
hideImeIfNeeded();
+ cancelRequestToScrollFocusedEditableNodeIntoView();
}
if (mNativeContentViewCore != 0) nativeSetFocus(mNativeContentViewCore, gainFocus);
}
* @see View#dispatchKeyEvent(KeyEvent)
*/
public boolean dispatchKeyEvent(KeyEvent event) {
+ if (GamepadList.dispatchKeyEvent(event)) return true;
if (getContentViewClient().shouldOverrideKeyEvent(event)) {
return mContainerViewInternals.super_dispatchKeyEvent(event);
}
*/
public boolean onHoverEvent(MotionEvent event) {
TraceEvent.begin("onHoverEvent");
+ MotionEvent offset = createOffsetMotionEvent(event);
+ try {
+ if (mBrowserAccessibilityManager != null) {
+ return mBrowserAccessibilityManager.onHoverEvent(offset);
+ }
- if (mBrowserAccessibilityManager != null) {
- return mBrowserAccessibilityManager.onHoverEvent(event);
- }
+ // Work around Android bug where the x, y coordinates of a hover exit
+ // event are incorrect when touch exploration is on.
+ if (mTouchExplorationEnabled && offset.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+ return true;
+ }
- // Work around Android bug where the x, y coordinates of a hover exit
- // event are incorrect when touch exploration is on.
- if (mTouchExplorationEnabled && event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+ mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
+ if (mNativeContentViewCore != 0) {
+ nativeSendMouseMoveEvent(mNativeContentViewCore, offset.getEventTime(),
+ offset.getX(), offset.getY());
+ }
return true;
+ } finally {
+ offset.recycle();
+ TraceEvent.end("onHoverEvent");
}
-
- mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
- if (mNativeContentViewCore != 0) {
- nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
- event.getX(), event.getY());
- }
- TraceEvent.end("onHoverEvent");
- return true;
}
/**
* @see View#onGenericMotionEvent(MotionEvent)
*/
public boolean onGenericMotionEvent(MotionEvent event) {
+ if (GamepadList.onGenericMotionEvent(event)) return true;
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL:
+ if (mNativeContentViewCore == 0) return false;
+
nativeSendMouseWheelEvent(mNativeContentViewCore, event.getEventTime(),
event.getX(), event.getY(),
event.getAxisValue(MotionEvent.AXIS_VSCROLL));
@Override
public void run() {
onHoverEvent(eventFakeMouseMove);
+ eventFakeMouseMove.recycle();
}
};
mContainerView.postDelayed(mFakeMouseMoveRunnable, 250);
}
/**
+ * Sets the current amount to offset incoming touch events by. This is used to handle content
+ * moving and not lining up properly with the android input system.
+ * @param dx The X offset in pixels to shift touch events.
+ * @param dy The Y offset in pixels to shift touch events.
+ */
+ public void setCurrentMotionEventOffsets(float dx, float dy) {
+ mCurrentTouchOffsetX = dx;
+ mCurrentTouchOffsetY = dy;
+ }
+
+ private MotionEvent createOffsetMotionEvent(MotionEvent src) {
+ MotionEvent dst = MotionEvent.obtain(src);
+ dst.offsetLocation(mCurrentTouchOffsetX, mCurrentTouchOffsetY);
+ return dst;
+ }
+
+ /**
* @see View#scrollBy(int, int)
* Currently the ContentView scrolling happens in the native side. In
* the Java view system, it is always pinned at (0, 0). scrollBy() and scrollTo()
if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
+ mLastTapX = (int) xPix;
+ mLastTapY = (int) yPix;
+
if (type == GestureEventType.LONG_PRESS
|| type == GestureEventType.LONG_TAP) {
getInsertionHandleController().allowAutomaticShowing();
getSelectionHandleController().allowAutomaticShowing();
} else {
- setClickXAndY((int) xPix, (int) yPix);
if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
}
}
- private void setClickXAndY(int x, int y) {
- mSingleTapX = x;
- mSingleTapY = y;
- }
-
/**
- * @return The x coordinate for the last point that a singleTap gesture was initiated from.
+ * @return The x coordinate for the last point that a tap or press gesture was initiated from.
*/
- public int getSingleTapX() {
- return mSingleTapX;
+ public int getLastTapX() {
+ return mLastTapX;
}
/**
- * @return The y coordinate for the last point that a singleTap gesture was initiated from.
+ * @return The y coordinate for the last point that a tap or press gesture was initiated from.
*/
- public int getSingleTapY() {
- return mSingleTapY;
+ public int getLastTapY() {
+ return mLastTapY;
}
public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
if (mNativeContentViewCore != 0) {
nativeSelectPopupMenuItems(mNativeContentViewCore, indices);
}
- mSelectPopupDialog = null;
+ mSelectPopup = null;
}
/**
@Override
public void showHandles(int startDir, int endDir) {
+ final boolean wasShowing = isShowing();
super.showHandles(startDir, endDir);
- showSelectActionBar();
+ if (!wasShowing || mActionMode == null) showSelectActionBar();
}
};
}
@Override
+ public boolean isSelectionPassword() {
+ return mImeAdapter.isSelectionPassword();
+ }
+
+ @Override
public boolean isSelectionEditable() {
return mSelectionEditable;
}
}
public void clearSslPreferences() {
- nativeClearSslPreferences(mNativeContentViewCore);
+ if (mNativeContentViewCore != 0) nativeClearSslPreferences(mNativeContentViewCore);
}
private boolean isSelectionHandleShowing() {
viewportWidth, viewportHeight,
pageScaleFactor, minPageScaleFactor, maxPageScaleFactor,
contentOffsetYPix);
- onRenderCoordinatesUpdated();
if (scrollChanged || contentOffsetChanged) {
for (mGestureStateListenersIterator.rewind();
getContentViewClient().onOffsetsForFullscreenChanged(
controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
- mPendingRendererFrame = true;
if (mBrowserAccessibilityManager != null) {
mBrowserAccessibilityManager.notifyFrameInfoInitialized();
}
@CalledByNative
private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType,
String text, int selectionStart, int selectionEnd,
- int compositionStart, int compositionEnd, boolean showImeIfNeeded, boolean requireAck) {
+ int compositionStart, int compositionEnd, boolean showImeIfNeeded,
+ boolean isNonImeChange) {
TraceEvent.begin();
mSelectionEditable = (textInputType != ImeAdapter.getTextInputTypeNone());
- if (mActionMode != null) mActionMode.invalidate();
-
- mImeAdapter.attachAndShowIfNeeded(nativeImeAdapterAndroid, textInputType, showImeIfNeeded);
+ mImeAdapter.updateKeyboardVisibility(
+ nativeImeAdapterAndroid, textInputType, showImeIfNeeded);
if (mInputConnection != null) {
mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart,
- compositionEnd, requireAck);
+ compositionEnd, isNonImeChange);
}
+
+ if (mActionMode != null) mActionMode.invalidate();
TraceEvent.end();
}
*/
@SuppressWarnings("unused")
@CalledByNative
- private void showSelectPopup(String[] items, int[] enabled, boolean multiple,
+ private void showSelectPopup(Rect bounds, String[] items, int[] enabled, boolean multiple,
int[] selectedIndices) {
if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) {
selectPopupMenuItems(null);
return;
}
- if (mSelectPopupDialog != null) {
- mSelectPopupDialog.hide();
- mSelectPopupDialog = null;
- }
assert items.length == enabled.length;
List<SelectPopupItem> popupItems = new ArrayList<SelectPopupItem>();
for (int i = 0; i < items.length; i++) {
popupItems.add(new SelectPopupItem(items[i], enabled[i]));
}
- mSelectPopupDialog = SelectPopupDialog.show(this, popupItems, multiple, selectedIndices);
+ hidePopups();
+ if (DeviceUtils.isTablet(mContext) && !multiple) {
+ mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
+ } else {
+ mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
+ }
+ mSelectPopup.show();
}
/**
- * @return The visible select popup dialog being shown.
+ * Called when the <select> popup needs to be hidden.
*/
- public SelectPopupDialog getSelectPopupForTest() {
- return mSelectPopupDialog;
+ @CalledByNative
+ private void hideSelectPopup() {
+ if (mSelectPopup != null) mSelectPopup.hide();
+ }
+
+ /**
+ * @return The visible select popup being shown.
+ */
+ public SelectPopup getSelectPopupForTest() {
+ return mSelectPopup;
}
@SuppressWarnings("unused")
@SuppressWarnings("unused")
@CalledByNative
+ private void showSelectionHandlesAutomatically() {
+ getSelectionHandleController().allowAutomaticShowing();
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
int focusDir, boolean isAnchorFirst) {
// All coordinates are in DIP.
@SuppressWarnings("unused")
@CalledByNative
- private void onRenderProcessSwap(int oldPid, int newPid) {
- if (!mInForeground) {
- ChildProcessLauncher.getBindingManager().setInForeground(newPid, false);
- } else if (oldPid != newPid) {
- ChildProcessLauncher.getBindingManager().setInForeground(oldPid, false);
- ChildProcessLauncher.getBindingManager().setInForeground(newPid, true);
- }
-
- attachImeAdapter();
- }
-
- @SuppressWarnings("unused")
- @CalledByNative
- private void onWebContentsConnected() {
+ private void onRenderProcessChange() {
attachImeAdapter();
}
Class<? extends Annotation> requiredAnnotation) {
if (mNativeContentViewCore != 0 && object != null) {
mJavaScriptInterfaces.put(name, object);
- nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation,
- mRetainedJavaScriptObjects);
+ nativeAddJavascriptInterface(mNativeContentViewCore, object, name, requiredAnnotation);
}
}
}
@CalledByNative
- private int getLocationInWindowX() {
- return mLocationInWindowX;
- }
-
- @CalledByNative
- private int getLocationInWindowY() {
- return mLocationInWindowY;
- }
-
- @CalledByNative
private static Rect createRect(int x, int y, int right, int bottom) {
return new Rect(x, y, right, bottom);
}
- private boolean onAnimate(long frameTimeMicros) {
- if (mNativeContentViewCore == 0) return false;
- return nativeOnAnimate(mNativeContentViewCore, frameTimeMicros);
- }
-
- private void animateIfNecessary(long frameTimeMicros) {
- if (mNeedAnimate) {
- mNeedAnimate = onAnimate(frameTimeMicros);
- if (!mNeedAnimate) removeVSyncSubscriber();
- }
- }
-
public void extractSmartClipData(int x, int y, int width, int height) {
if (mNativeContentViewCore != 0) {
+ x += mSmartClipOffsetX;
+ y += mSmartClipOffsetY;
nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
}
}
+ /**
+ * Set offsets for smart clip.
+ *
+ * <p>This should be called if there is a viewport change introduced by,
+ * e.g., show and hide of a location bar.
+ *
+ * @param offsetX Offset for X position.
+ * @param offsetY Offset for Y position.
+ */
+ public void setSmartClipOffsets(int offsetX, int offsetY) {
+ mSmartClipOffsetX = offsetX;
+ mSmartClipOffsetY = offsetY;
+ }
+
@CalledByNative
- private void onSmartClipDataExtracted(String result) {
+ private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
if (mSmartClipDataListener != null ) {
- mSmartClipDataListener.onSmartClipDataExtracted(result);
+ mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
}
}
mSmartClipDataListener = listener;
}
+ public void setBackgroundOpaque(boolean opaque) {
+ if (mNativeContentViewCore != 0) {
+ nativeSetBackgroundOpaque(mNativeContentViewCore, opaque);
+ }
+ }
+
/**
* Offer a long press gesture to the embedding View, primarily for WebView compatibility.
*
}
private native long nativeInit(long webContentsPtr,
- long viewAndroidPtr, long windowAndroidPtr);
+ long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
@CalledByNative
private ContentVideoViewClient getContentVideoViewClient() {
String url,
int loadUrlType,
int transitionType,
+ String referrerUrl,
+ int referrerPolicy,
int uaOverrideOption,
String extraHeaders,
byte[] postData,
String baseUrlForDataUrl,
String virtualUrlForDataUrl,
- boolean canLoadLocalResources);
+ boolean canLoadLocalResources,
+ boolean isRendererInitiated);
private native String nativeGetURL(long nativeContentViewCoreImpl);
- private native String nativeGetTitle(long nativeContentViewCoreImpl);
-
private native void nativeShowInterstitialPage(
long nativeContentViewCoreImpl, String url, long nativeInterstitialPageDelegateAndroid);
private native boolean nativeIsShowingInterstitialPage(long nativeContentViewCoreImpl);
long timeMs, int action, int pointerCount, int historySize, int actionIndex,
float x0, float y0, float x1, float y1,
int pointerId0, int pointerId1,
- float touchMajor0, float touchMajor1);
+ float touchMajor0, float touchMajor1,
+ float rawX, float rawY,
+ int androidToolType0, int androidToolType1, int androidButtonState);
private native int nativeSendMouseMoveEvent(
long nativeContentViewCoreImpl, long timeMs, float x, float y);
private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
- private native void nativeResetGestureDetectors(long nativeContentViewCoreImpl);
-
- private native void nativeIgnoreRemainingTouchEvents(long nativeContentViewCoreImpl);
-
- private native void nativeOnWindowFocusLost(long nativeContentViewCoreImpl);
-
- private native void nativeSetDoubleTapSupportForPageEnabled(
- long nativeContentViewCoreImpl, boolean enabled);
+ private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
private native void nativeSetDoubleTapSupportEnabled(
long nativeContentViewCoreImpl, boolean enabled);
private native void nativeSetMultiTouchZoomSupportEnabled(
private native void nativeLoadIfNecessary(long nativeContentViewCoreImpl);
private native void nativeRequestRestoreLoad(long nativeContentViewCoreImpl);
- private native void nativeStopLoading(long nativeContentViewCoreImpl);
-
private native void nativeReload(long nativeContentViewCoreImpl, boolean checkForRepost);
private native void nativeReloadIgnoringCache(
long nativeContentViewCoreImpl, boolean checkForRepost);
private native void nativeScrollFocusedEditableNodeIntoView(long nativeContentViewCoreImpl);
+ private native void nativeSelectWordAroundCaret(long nativeContentViewCoreImpl);
+
private native void nativeClearHistory(long nativeContentViewCoreImpl);
+ private native void nativeAddStyleSheetByURL(long nativeContentViewCoreImpl,
+ String stylesheetUrl);
+
private native void nativeEvaluateJavaScript(long nativeContentViewCoreImpl,
String script, JavaScriptCallback callback, boolean startRenderer);
long nativeContentViewCoreImpl, boolean allow);
private native void nativeAddJavascriptInterface(long nativeContentViewCoreImpl, Object object,
- String name, Class requiredAnnotation, HashSet<Object> retainedObjectSet);
+ String name, Class requiredAnnotation);
private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreImpl,
String name);
private native String nativeGetOriginalUrlForActiveNavigationEntry(
long nativeContentViewCoreImpl);
- private native void nativeUpdateVSyncParameters(long nativeContentViewCoreImpl,
- long timebaseMicros, long intervalMicros);
-
- private native void nativeOnVSync(long nativeContentViewCoreImpl, long frameTimeMicros);
-
- private native boolean nativeOnAnimate(long nativeContentViewCoreImpl, long frameTimeMicros);
-
private native void nativeWasResized(long nativeContentViewCoreImpl);
private native boolean nativeIsRenderWidgetHostViewReady(long nativeContentViewCoreImpl);
private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl,
int x, int y, int w, int h);
+ private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque);
}