import org.chromium.content.browser.input.SelectionEventType;
import org.chromium.content.common.ContentSwitches;
import org.chromium.content_public.browser.GestureStateListener;
-import org.chromium.content_public.browser.JavaScriptCallback;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.ViewAndroid;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
/**
* Provides a Java-side 'wrapper' around a WebContent (native) instance.
private final HashSet<Object> mRetainedJavaScriptObjects = new HashSet<Object>();
/**
+ * A {@link ViewAndroidDelegate} that delegates to the current container view.
+ *
+ * <p>This delegate handles the replacement of container views transparently so
+ * that clients can safely hold to instances of this class.
+ */
+ private class ContentViewAndroidDelegate implements ViewAndroidDelegate {
+ /**
+ * Represents the position of an anchor view.
+ */
+ @VisibleForTesting
+ private class Position {
+ private final float mX;
+ private final float mY;
+ private final float mWidth;
+ private final float mHeight;
+
+ public Position(float x, float y, float width, float height) {
+ mX = x;
+ mY = y;
+ mWidth = width;
+ mHeight = height;
+ }
+ }
+
+ /**
+ * The current container view. This view can be updated with
+ * {@link #updateCurrentContainerView()}.
+ */
+ private ViewGroup mCurrentContainerView;
+
+ /**
+ * List of anchor views stored in the order in which they were acquired mapped
+ * to their position.
+ */
+ private Map<View, Position> mAnchorViews = new LinkedHashMap<View, Position>();
+
+ @Override
+ public View acquireAnchorView() {
+ View anchorView = new View(mContext);
+ mAnchorViews.put(anchorView, null);
+ mCurrentContainerView.addView(anchorView);
+ return anchorView;
+ }
+
+ @Override
+ public void setAnchorViewPosition(
+ View view, float x, float y, float width, float height) {
+ mAnchorViews.put(view, new Position(x, y, width, height));
+ doSetAnchorViewPosition(view, x, y, width, height);
+ }
+
+ @SuppressWarnings("deprecation") // AbsoluteLayout
+ private void doSetAnchorViewPosition(
+ View view, float x, float y, float width, float height) {
+ if (view.getParent() == null) {
+ // Ignore. setAnchorViewPosition has been called after the anchor view has
+ // already been released.
+ return;
+ }
+ assert view.getParent() == mCurrentContainerView;
+
+ 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 (mCurrentContainerView instanceof FrameLayout) {
+ int startMargin;
+ if (ApiCompatibilityUtils.isLayoutRtl(mCurrentContainerView)) {
+ startMargin = mCurrentContainerView.getMeasuredWidth()
+ - Math.round((width + x) * scale);
+ } else {
+ startMargin = leftMargin;
+ }
+ if (scaledWidth + startMargin > mCurrentContainerView.getWidth()) {
+ scaledWidth = mCurrentContainerView.getWidth() - startMargin;
+ }
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ scaledWidth, Math.round(height * scale));
+ ApiCompatibilityUtils.setMarginStart(lp, startMargin);
+ lp.topMargin = topMargin;
+ view.setLayoutParams(lp);
+ } else if (mCurrentContainerView 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 mContainerViewAtCreation.getScroll[X/Y]()
+ // as it naturally accounts for scroll differences between
+ // these models.
+ leftMargin += mRenderCoordinates.getScrollXPixInt();
+ topMargin += mRenderCoordinates.getScrollYPixInt();
+
+ android.widget.AbsoluteLayout.LayoutParams lp =
+ new android.widget.AbsoluteLayout.LayoutParams(
+ scaledWidth, (int) (height * scale), leftMargin, topMargin);
+ view.setLayoutParams(lp);
+ } else {
+ Log.e(TAG, "Unknown layout " + mCurrentContainerView.getClass().getName());
+ }
+ }
+
+ @Override
+ public void releaseAnchorView(View anchorView) {
+ mAnchorViews.remove(anchorView);
+ mCurrentContainerView.removeView(anchorView);
+ }
+
+ /**
+ * Updates (or sets for the first time) the current container view to which
+ * this class delegates. Existing anchor views are transferred from the old to
+ * the new container view.
+ */
+ void updateCurrentContainerView() {
+ ViewGroup oldContainerView = mCurrentContainerView;
+ mCurrentContainerView = mContainerView;
+ for (Entry<View, Position> entry : mAnchorViews.entrySet()) {
+ View anchorView = entry.getKey();
+ Position position = entry.getValue();
+ oldContainerView.removeView(anchorView);
+ mCurrentContainerView.addView(anchorView);
+ if (position != null) {
+ doSetAnchorViewPosition(anchorView,
+ position.mX, position.mY, position.mWidth, position.mHeight);
+ }
+ }
+ }
+ }
+
+ /**
* Interface that consumers of {@link ContentViewCore} must implement to allow the proper
* dispatching of view methods through the containing view.
*
private ViewGroup mContainerView;
private InternalAccessDelegate mContainerViewInternals;
private WebContents mWebContents;
- private WebContentsObserverAndroid mWebContentsObserver;
+ private WebContentsObserver mWebContentsObserver;
private ContentViewClient mContentViewClient;
private ActionMode mActionMode;
private boolean mUnselectAllOnActionModeDismiss;
private boolean mPreserveSelectionOnNextLossOfFocus;
+ private SelectActionModeCallback.ActionHandler mActionHandler;
// Delegate that will handle GET downloads, and be notified of completion of POST downloads.
private ContentViewDownloadDelegate mDownloadDelegate;
// screen orientation.
private boolean mFullscreenRequiredForOrientationLock = true;
+ // A ViewAndroidDelegate that delegates to the current container view.
+ private ContentViewAndroidDelegate mViewAndroidDelegate;
+
/**
* Constructs a new ContentViewCore. Embedders must call initialize() after constructing
* a ContentViewCore and before using it.
}
/**
- * Returns a delegate that can be used to add and remove views from the ContainerView.
+ * Returns a delegate that can be used to add and remove views from the current
+ * container view. Clients can safely hold to instances of this class as it handles the
+ * replacement of container views transparently.
*
- * NOTE: Use with care, as not all ContentViewCore users setup their ContainerView in the same
+ * NOTE: Use with care, as not all ContentViewCore users setup their container view in the same
* way. In particular, the Android WebView has limitations on what implementation details can
* be provided via a child view, as they are visible in the API and could introduce
* compatibility breaks with existing applications. If in doubt, contact the
*
* @return A ViewAndroidDelegate that can be used to add and remove views.
*/
- @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(mContext);
- mContainerViewAtCreation.addView(anchorView);
- return anchorView;
- }
-
- @Override
- @SuppressWarnings("deprecation") // AbsoluteLayout
- public void setAnchorViewPosition(
- View view, float x, float y, float width, float height) {
- if (view.getParent() == null) {
- // Ignore. setAnchorViewPosition has been called after the anchor view has
- // already been released.
- return;
- }
- assert view.getParent() == mContainerViewAtCreation;
-
- 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 (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));
- ApiCompatibilityUtils.setMarginStart(lp, startMargin);
- lp.topMargin = topMargin;
- view.setLayoutParams(lp);
- } 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 mContainerViewAtCreation.getScroll[X/Y]()
- // as it naturally accounts for scroll differences between
- // these models.
- leftMargin += mRenderCoordinates.getScrollXPixInt();
- topMargin += mRenderCoordinates.getScrollYPixInt();
-
- android.widget.AbsoluteLayout.LayoutParams lp =
- new android.widget.AbsoluteLayout.LayoutParams(
- scaledWidth, (int) (height * scale), leftMargin, topMargin);
- view.setLayoutParams(lp);
- } else {
- Log.e(TAG, "Unknown layout " + mContainerViewAtCreation.getClass().getName());
- }
- }
-
- @Override
- public void releaseAnchorView(View anchorView) {
- mContainerViewAtCreation.removeView(anchorView);
- }
- };
+ return mViewAndroidDelegate;
}
@VisibleForTesting
return mInputConnection;
}
+ @VisibleForTesting
+ ViewAndroid getViewAndroid() {
+ return mViewAndroid;
+ }
+
private ImeAdapter createImeAdapter(Context context) {
return new ImeAdapter(mInputMethodManagerWrapper,
new ImeAdapter.ImeAdapterDelegate() {
public void onImeEvent() {
mPopupZoomer.hide(true);
getContentViewClient().onImeEvent();
- if (mFocusedNodeEditable) hideTextHandles();
+ if (mFocusedNodeEditable) dismissTextHandles();
}
@Override
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
getContentViewClient().onImeStateChangeRequested(
- resultCode == InputMethodManager.RESULT_SHOWN ||
- resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
+ resultCode == InputMethodManager.RESULT_SHOWN
+ || resultCode == InputMethodManager.RESULT_UNCHANGED_SHOWN);
if (resultCode == InputMethodManager.RESULT_SHOWN) {
// If OSK is newly shown, delay the form focus until
// the onSizeChanged (in order to adjust relative to the
// always be called, crbug.com/294908.
getContainerView().getWindowVisibleDisplayFrame(
mFocusPreOSKViewportRect);
- } else if (hasFocus() && resultCode ==
- InputMethodManager.RESULT_UNCHANGED_SHOWN) {
+ } else if (hasFocus() && resultCode
+ == InputMethodManager.RESULT_UNCHANGED_SHOWN) {
// If the OSK was already there, focus the form immediately.
- scrollFocusedEditableNodeIntoView();
+ assert mWebContents != null;
+ mWebContents.scrollFocusedEditableNodeIntoView();
}
}
};
// deleting it after destroying the ContentViewCore.
public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
long nativeWebContents, WindowAndroid windowAndroid) {
+ createContentViewAndroidDelegate();
setContainerView(containerView);
-
long windowNativePointer = windowAndroid.getNativePointer();
assert windowNativePointer != 0;
- mViewAndroid = new ViewAndroid(windowAndroid, getViewAndroidDelegate());
+ createViewAndroid(windowAndroid);
+
long viewAndroidNativePointer = mViewAndroid.getNativePointer();
assert viewAndroidNativePointer != 0;
mRenderCoordinates.reset();
initPopupZoomer(mContext);
mImeAdapter = createImeAdapter(mContext);
+ attachImeAdapter();
mAccessibilityInjector = AccessibilityInjector.newInstance(this);
- mWebContentsObserver = new WebContentsObserverAndroid(mWebContents) {
+ mWebContentsObserver = new WebContentsObserver(mWebContents) {
+ @Override
+ public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
+ String description, String failingUrl) {
+ // Navigation that fails the provisional load will have the strong binding removed
+ // here. One for which the provisional load is commited will have the strong binding
+ // removed in navigationEntryCommitted() below.
+ if (isProvisionalLoad) determinedProcessVisibility();
+ }
+
@Override
public void didNavigateMainFrame(String url, String baseUrl,
boolean isNavigationToDifferentPage, boolean isFragmentNavigation) {
// No need to reset gesture detection as the detector will have
// been destroyed in the RenderWidgetHostView.
}
+
+ @Override
+ public void navigationEntryCommitted() {
+ determinedProcessVisibility();
+ }
+
+ private void determinedProcessVisibility() {
+ // Signal to the process management logic that we can now rely on the process
+ // visibility signal for binding management. Before the navigation commits, its
+ // renderer is considered background even if the pending navigation happens in the
+ // foreground renderer.
+ ChildProcessLauncher.determinedVisibility(getCurrentRenderProcessId());
+ }
};
}
+ @VisibleForTesting
+ void createContentViewAndroidDelegate() {
+ mViewAndroidDelegate = new ContentViewAndroidDelegate();
+ }
+
+ @VisibleForTesting
+ void createViewAndroid(WindowAndroid windowAndroid) {
+ mViewAndroid = new ViewAndroid(windowAndroid, mViewAndroidDelegate);
+ }
+
/**
* 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>WARNING: This method can also be used to replace the existing container view,
+ * but you should only do it if you have a very good reason to. Replacing the
+ * container view has been designed to support fullscreen in the Webview so it
+ * might not be appropriate for other use cases.
*
* <p>This method only performs a small part of replacing the container view and
* embedders are responsible for:
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.");
- } else {
- contentDescription = mContext.getResources().getString(
- R.string.accessibility_content_view);
- }
- mContainerView.setContentDescription(contentDescription);
- mContainerView.setWillNotDraw(false);
mContainerView.setClickable(true);
+ mViewAndroidDelegate.updateCurrentContainerView();
TraceEvent.end();
}
mContainerViewInternals = internalDispatcher;
}
- private void initPopupZoomer(Context context) {
+ @VisibleForTesting
+ void initPopupZoomer(Context context) {
mPopupZoomer = new PopupZoomer(context);
mPopupZoomer.setOnVisibilityChangedListener(new PopupZoomer.OnVisibilityChangedListener() {
// mContainerView can change, but this OnVisibilityChangedListener can only be used
public void run() {
if (mContainerViewAtCreation.indexOfChild(zoomer) == -1) {
mContainerViewAtCreation.addView(zoomer);
- } else {
- assert false : "PopupZoomer should never be shown without being hidden";
}
}
});
if (mContainerViewAtCreation.indexOfChild(zoomer) != -1) {
mContainerViewAtCreation.removeView(zoomer);
mContainerViewAtCreation.invalidate();
- } else {
- assert false : "PopupZoomer should never be hidden without being shown";
}
}
});
}
/**
- * Shows an interstitial page driven by the passed in delegate.
- *
- * @param url The URL being blocked by the interstitial.
- * @param delegate The delegate handling the interstitial.
- */
- @VisibleForTesting
- public void showInterstitialPage(
- String url, InterstitialPageDelegateAndroid delegate) {
- assert mWebContents != null;
- mWebContents.showInterstitialPage(url, delegate.getNative());
- }
-
- /**
- * @return Whether the page is currently showing an interstitial, such as a bad HTTPS page.
- */
- public boolean isShowingInterstitialPage() {
- assert mWebContents != null;
- return mWebContents.isShowingInterstitialPage();
- }
-
- /**
* @return Viewport width in physical pixels as set from onSizeChanged.
*/
@CalledByNative
- public int getViewportWidthPix() { return mViewportWidthPix; }
+ public int getViewportWidthPix() {
+ return mViewportWidthPix;
+ }
/**
* @return Viewport height in physical pixels as set from onSizeChanged.
*/
@CalledByNative
- public int getViewportHeightPix() { return mViewportHeightPix; }
+ public int getViewportHeightPix() {
+ return mViewportHeightPix;
+ }
/**
* @return Width of underlying physical surface.
*/
@CalledByNative
- public int getPhysicalBackingWidthPix() { return mPhysicalBackingWidthPix; }
+ public int getPhysicalBackingWidthPix() {
+ return mPhysicalBackingWidthPix;
+ }
/**
* @return Height of underlying physical surface.
*/
@CalledByNative
- public int getPhysicalBackingHeightPix() { return mPhysicalBackingHeightPix; }
+ public int getPhysicalBackingHeightPix() {
+ return mPhysicalBackingHeightPix;
+ }
/* TODO(aelias): Remove these when downstream callers disappear. */
@VisibleForTesting
- public int getViewportSizeOffsetWidthPix() { return 0; }
+ public int getViewportSizeOffsetWidthPix() {
+ return 0;
+ }
+
@VisibleForTesting
- public int getViewportSizeOffsetHeightPix() { return getTopControlsLayoutHeightPix(); }
+ public int getViewportSizeOffsetHeightPix() {
+ return getTopControlsLayoutHeightPix();
+ }
/**
* @return The amount that the viewport size given to Blink is shrunk by the URL-bar..
*/
@CalledByNative
- public int getTopControlsLayoutHeightPix() { return mTopControlsLayoutHeightPix; }
+ public int getTopControlsLayoutHeightPix() {
+ return mTopControlsLayoutHeightPix;
+ }
/**
* @see android.webkit.WebView#getContentHeight()
}
/**
- * Inserts the provided markup sandboxed into the frame.
- */
- public void setupTransitionView(String markup) {
- assert mWebContents != null;
- mWebContents.setupTransitionView(markup);
- }
-
- /**
- * Hides transition elements specified by the selector, and activates any
- * exiting-transition stylesheets.
- */
- public void beginExitTransition(String cssSelector) {
- assert mWebContents != null;
- mWebContents.beginExitTransition(cssSelector);
- }
-
- /**
- * Requests the renderer insert a link to the specified stylesheet in the
- * main frame's document.
- */
- public void addStyleSheetByURL(String url) {
- assert mWebContents != null;
- mWebContents.addStyleSheetByURL(url);
- }
-
- /**
- * Injects the passed Javascript code in the current page and evaluates it.
- * If a result is required, pass in a callback.
- * Used in automation tests.
- *
- * @param script The Javascript to execute.
- * @param callback The callback to be fired off when a result is ready. The script's
- * result will be json encoded and passed as the parameter, and the call
- * will be made on the main thread.
- * If no result is required, pass null.
- */
- public void evaluateJavaScript(String script, JavaScriptCallback callback) {
- assert mWebContents != null;
- mWebContents.evaluateJavaScript(script, callback);
- }
-
- /**
- * Post a message to a frame.
- * TODO(sgurun) also add support for transferring a message channel port.
- *
- * @param frameName The name of the frame. If the name is null the message is posted
- * to the main frame.
- * @param message The message
- * @param sourceOrigin The source origin
- * @param targetOrigin The target origin
- */
- public void postMessageToFrame(String frameName, String message,
- String sourceOrigin, String targetOrigin) {
- if (mNativeContentViewCore == 0) return;
- nativePostMessageToFrame(mNativeContentViewCore, frameName, message, sourceOrigin,
- targetOrigin);
- }
-
- /**
* To be called when the ContentView is shown.
*/
public void onShow() {
* @return The ID of the renderer process that backs this tab or
* {@link #INVALID_RENDER_PROCESS_PID} if there is none.
*/
- @VisibleForTesting
public int getCurrentRenderProcessId() {
return nativeGetCurrentRenderProcessId(mNativeContentViewCore);
}
private void hidePopupsAndClearSelection() {
mUnselectAllOnActionModeDismiss = true;
hidePopups();
+ // Clear the selection. The selection is cleared on destroying IME
+ // and also here since we may receive destroy first, for example
+ // when focus is lost in webview.
+ clearUserSelection();
}
private void hidePopupsAndPreserveSelection() {
hidePopups();
}
+ private void clearUserSelection() {
+ if (mFocusedNodeEditable) {
+ if (mInputConnection != null) {
+ int selectionEnd = Selection.getSelectionEnd(mEditable);
+ mInputConnection.setSelection(selectionEnd, selectionEnd);
+ }
+ } else if (mImeAdapter != null) {
+ mImeAdapter.unselect();
+ }
+ }
+
private void hidePopups() {
hideSelectActionBar();
hidePastePopup();
hideSelectPopup();
mPopupZoomer.hide(false);
- if (mUnselectAllOnActionModeDismiss) hideTextHandles();
+ if (mUnselectAllOnActionModeDismiss) dismissTextHandles();
}
private void restoreSelectionPopupsIfNecessary() {
@SuppressWarnings("javadoc")
public void onAttachedToWindow() {
setAccessibilityState(mAccessibilityManager.isEnabled());
+ setTextHandlesTemporarilyHidden(false);
restoreSelectionPopupsIfNecessary();
ScreenOrientationListener.getInstance().addObserver(this, mContext);
GamepadList.onAttachedToWindow(mContext);
@SuppressLint("MissingSuperCall")
public void onDetachedFromWindow() {
setInjectedAccessibility(false);
- hidePopupsAndPreserveSelection();
mZoomControlsDelegate.dismissZoomPicker();
unregisterAccessibilityContentObserver();
ScreenOrientationListener.getInstance().removeObserver(this);
GamepadList.onDetachedFromWindow();
+
+ // WebView uses PopupWindows for handle rendering, which may remain
+ // unintentionally visible even after the WebView has been detached.
+ // Override the handle visibility explicitly to address this, but
+ // preserve the underlying selection for detachment cases like screen
+ // locking and app switching.
+ setTextHandlesTemporarilyHidden(true);
+ hidePopupsAndPreserveSelection();
}
/**
if (!rect.equals(mFocusPreOSKViewportRect)) {
// Only assume the OSK triggered the onSizeChanged if width was preserved.
if (rect.width() == mFocusPreOSKViewportRect.width()) {
- scrollFocusedEditableNodeIntoView();
+ assert mWebContents != null;
+ mWebContents.scrollFocusedEditableNodeIntoView();
}
cancelRequestToScrollFocusedEditableNodeIntoView();
}
mFocusPreOSKViewportRect.setEmpty();
}
- private void scrollFocusedEditableNodeIntoView() {
- assert mWebContents != null;
- mWebContents.scrollFocusedEditableNodeIntoView();
- }
-
- /**
- * Selects the word around the caret, if any.
- * The caller can check if selection actually occurred by listening to OnSelectionChanged.
- */
- public void selectWordAroundCaret() {
- assert mWebContents != null;
- mWebContents.selectWordAroundCaret();
- }
-
/**
* @see View#onWindowFocusChanged(boolean)
*/
return mDownloadDelegate;
}
+ @VisibleForTesting
+ public SelectActionModeCallback.ActionHandler getSelectActionHandler() {
+ return mActionHandler;
+ }
+
private void showSelectActionBar() {
if (mActionMode != null) {
mActionMode.invalidate();
}
// Start a new action mode with a SelectActionModeCallback.
- SelectActionModeCallback.ActionHandler actionHandler =
- new SelectActionModeCallback.ActionHandler() {
- @Override
- public void selectAll() {
- mImeAdapter.selectAll();
- }
-
- @Override
- public void cut() {
- mImeAdapter.cut();
- }
-
- @Override
- public void copy() {
- mImeAdapter.copy();
- }
+ if (mActionHandler == null) {
+ mActionHandler = new SelectActionModeCallback.ActionHandler() {
+ @Override
+ public void selectAll() {
+ mImeAdapter.selectAll();
+ }
- @Override
- public void paste() {
- mImeAdapter.paste();
- }
+ @Override
+ public void cut() {
+ mImeAdapter.cut();
+ }
- @Override
- public void share() {
- final String query = getSelectedText();
- if (TextUtils.isEmpty(query)) return;
-
- Intent send = new Intent(Intent.ACTION_SEND);
- send.setType("text/plain");
- send.putExtra(Intent.EXTRA_TEXT, query);
- try {
- Intent i = Intent.createChooser(send, getContext().getString(
- R.string.actionbar_share));
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(i);
- } catch (android.content.ActivityNotFoundException ex) {
- // If no app handles it, do nothing.
+ @Override
+ public void copy() {
+ mImeAdapter.copy();
}
- }
- @Override
- public void search() {
- final String query = getSelectedText();
- if (TextUtils.isEmpty(query)) return;
-
- // See if ContentViewClient wants to override
- if (getContentViewClient().doesPerformWebSearch()) {
- getContentViewClient().performWebSearch(query);
- return;
+ @Override
+ public void paste() {
+ mImeAdapter.paste();
}
- Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
- i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
- i.putExtra(SearchManager.QUERY, query);
- i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
- if (!(getContext() instanceof Activity)) {
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ @Override
+ public void share() {
+ final String query = getSelectedText();
+ if (TextUtils.isEmpty(query)) return;
+
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ send.putExtra(Intent.EXTRA_TEXT, query);
+ try {
+ Intent i = Intent.createChooser(send, getContext().getString(
+ R.string.actionbar_share));
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(i);
+ } catch (android.content.ActivityNotFoundException ex) {
+ // If no app handles it, do nothing.
+ }
}
- try {
- getContext().startActivity(i);
- } catch (android.content.ActivityNotFoundException ex) {
- // If no app handles it, do nothing.
+
+ @Override
+ public void search() {
+ final String query = getSelectedText();
+ if (TextUtils.isEmpty(query)) return;
+
+ // See if ContentViewClient wants to override
+ if (getContentViewClient().doesPerformWebSearch()) {
+ getContentViewClient().performWebSearch(query);
+ return;
+ }
+
+ Intent i = new Intent(Intent.ACTION_WEB_SEARCH);
+ i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
+ i.putExtra(SearchManager.QUERY, query);
+ i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
+ if (!(getContext() instanceof Activity)) {
+ i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+ try {
+ getContext().startActivity(i);
+ } catch (android.content.ActivityNotFoundException ex) {
+ // If no app handles it, do nothing.
+ }
}
- }
- @Override
- public boolean isSelectionPassword() {
- return mImeAdapter.isSelectionPassword();
- }
+ @Override
+ public boolean isSelectionPassword() {
+ return mImeAdapter.isSelectionPassword();
+ }
- @Override
- public boolean isSelectionEditable() {
- return mFocusedNodeEditable;
- }
+ @Override
+ public boolean isSelectionEditable() {
+ return mFocusedNodeEditable;
+ }
- @Override
- public void onDestroyActionMode() {
- mActionMode = null;
- if (mUnselectAllOnActionModeDismiss) {
- hideTextHandles();
- if (isSelectionEditable()) {
- int selectionEnd = Selection.getSelectionEnd(mEditable);
- mInputConnection.setSelection(selectionEnd, selectionEnd);
- } else {
- mImeAdapter.unselect();
+ @Override
+ public void onDestroyActionMode() {
+ mActionMode = null;
+ if (mUnselectAllOnActionModeDismiss) {
+ dismissTextHandles();
+ clearUserSelection();
}
+ getContentViewClient().onContextualActionBarHidden();
}
- getContentViewClient().onContextualActionBarHidden();
- }
- @Override
- public boolean isShareAvailable() {
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType("text/plain");
- return getContext().getPackageManager().queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
- }
+ @Override
+ public boolean isShareAvailable() {
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("text/plain");
+ return getContext().getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+ }
- @Override
- public boolean isWebSearchAvailable() {
- if (getContentViewClient().doesPerformWebSearch()) return true;
- Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
- intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
- return getContext().getPackageManager().queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
- }
- };
+ @Override
+ public boolean isWebSearchAvailable() {
+ if (getContentViewClient().doesPerformWebSearch()) return true;
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
+ return getContext().getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+ }
+ };
+ }
mActionMode = null;
// On ICS, startActionMode throws an NPE when getParent() is null.
if (mContainerView.getParent() != null) {
assert mWebContents != null;
mActionMode = mContainerView.startActionMode(
- getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler,
+ getContentViewClient().getSelectActionModeCallback(getContext(), mActionHandler,
mWebContents.isIncognito()));
}
mUnselectAllOnActionModeDismiss = true;
return mHasSelection;
}
+ /**
+ * @return Whether the page has an active, touch-controlled insertion handle.
+ */
+ @VisibleForTesting
+ protected boolean hasInsertion() {
+ return mHasInsertion;
+ }
+
private void hidePastePopup() {
if (mPastePopupMenu == null) return;
mPastePopupMenu.hide();
getContentViewClient().onSelectionEvent(eventType, posXDip * scale, posYDip * scale);
}
- private void hideTextHandles() {
+ private void dismissTextHandles() {
mHasSelection = false;
mHasInsertion = false;
- if (mNativeContentViewCore != 0) nativeHideTextHandles(mNativeContentViewCore);
+ if (mNativeContentViewCore != 0) nativeDismissTextHandles(mNativeContentViewCore);
+ }
+
+ private void setTextHandlesTemporarilyHidden(boolean hide) {
+ if (mNativeContentViewCore == 0) return;
+ nativeSetTextHandlesTemporarilyHidden(mNativeContentViewCore, hide);
}
/**
for (int i = 0; i < items.length; i++) {
popupItems.add(new SelectPopupItem(items[i], enabled[i]));
}
- if (DeviceFormFactor.isTablet(mContext) && !multiple) {
+ if (DeviceFormFactor.isTablet(mContext) && !multiple && !isTouchExplorationEnabled()) {
mSelectPopup = new SelectPopupDropdown(this, popupItems, bounds, selectedIndices);
} else {
mSelectPopup = new SelectPopupDialog(this, popupItems, multiple, selectedIndices);
if (!mHasInsertion || !canPaste()) return false;
final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
getPastePopup().showAt(
- (int) mRenderCoordinates.fromDipToPix(xDip),
- (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
+ (int) mRenderCoordinates.fromDipToPix(xDip),
+ (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
return true;
}
@Override
public void paste() {
mImeAdapter.paste();
- hideTextHandles();
+ dismissTextHandles();
}
});
}
* @see View#hasFocus()
*/
@CalledByNative
- public boolean hasFocus() {
+ private boolean hasFocus() {
+ // If the container view is not focusable, we consider it always focused from
+ // Chromium's point of view.
+ if (!mContainerView.isFocusable()) return true;
return mContainerView.hasFocus();
}
return mRenderCoordinates.getPageScaleFactor();
}
- /**
- * If the view is ready to draw contents to the screen. In hardware mode,
- * the initialization of the surface texture may not occur until after the
- * view has been added to the layout. This method will return {@code true}
- * once the texture is actually ready.
- */
- public boolean isReady() {
- assert mWebContents != null;
- return mWebContents.isReady();
- }
-
@CalledByNative
private void startContentIntent(String contentUrl) {
getContentViewClient().onStartContentIntent(getContext(), contentUrl);
return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
}
- if (mNativeAccessibilityAllowed &&
- !mNativeAccessibilityEnabled &&
- mNativeContentViewCore != 0 &&
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ if (mNativeAccessibilityAllowed && !mNativeAccessibilityEnabled
+ && mNativeContentViewCore != 0
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mNativeAccessibilityEnabled = true;
nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
}
try {
// On JellyBean and higher, native accessibility is the default so script
// injection is only allowed if enabled via a flag.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
- !CommandLine.getInstance().hasSwitch(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && !CommandLine.getInstance().hasSwitch(
ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
return false;
}
}
};
contentResolver.registerContentObserver(
- Settings.Secure.getUriFor(accessibilityScriptInjection),
- false,
- contentObserver);
+ Settings.Secure.getUriFor(accessibilityScriptInjection),
+ false,
+ contentObserver);
mAccessibilityScriptInjectionObserver = contentObserver;
}
}
/**
- * Inform WebKit that Fullscreen mode has been exited by the user.
- */
- public void exitFullscreen() {
- assert mWebContents != null;
- mWebContents.exitFullscreen();
- }
-
- /**
- * Changes whether hiding the top controls is enabled.
*
- * @param enableHiding Whether hiding the top controls should be enabled or not.
- * @param enableShowing Whether showing the top controls should be enabled or not.
- * @param animate Whether the transition should be animated or not.
- */
- public void updateTopControlsState(boolean enableHiding, boolean enableShowing,
- boolean animate) {
- assert mWebContents != null;
- mWebContents.updateTopControlsState(
- enableHiding, enableShowing, animate);
- }
-
- /**
* @return The cached copy of render positions and scales.
*/
public RenderCoordinates getRenderCoordinates() {
private native long nativeInit(long webContentsPtr,
long viewAndroidPtr, long windowAndroidPtr, HashSet<Object> retainedObjectSet);
- @CalledByNative
- private ContentVideoViewClient getContentVideoViewClient() {
+ ContentVideoViewClient getContentVideoViewClient() {
return getContentViewClient().getContentVideoViewClient();
}
sendOrientationChangeEvent(orientation);
}
- public void resumeResponseDeferredAtStart() {
- assert mWebContents != null;
- mWebContents.resumeResponseDeferredAtStart();
- }
-
/**
* Set whether the ContentViewCore requires the WebContents to be fullscreen in order to lock
* the screen orientation.
private native void nativeMoveCaret(long nativeContentViewCoreImpl, float x, float y);
- private native void nativeHideTextHandles(long nativeContentViewCoreImpl);
+ private native void nativeDismissTextHandles(long nativeContentViewCoreImpl);
+ private native void nativeSetTextHandlesTemporarilyHidden(
+ long nativeContentViewCoreImpl, boolean hidden);
private native void nativeResetGestureDetection(long nativeContentViewCoreImpl);
long nativeSelectPopupSourceFrame, int[] indices);
- private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, String frameId,
- String message, String sourceOrigin, String targetOrigin);
-
private native long nativeGetNativeImeAdapter(long nativeContentViewCoreImpl);
private native int nativeGetCurrentRenderProcessId(long nativeContentViewCoreImpl);