Chromium Code Reviews| Index: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
| index 0a5293ba55910c06f9f875e7fbe0115e5489ad2b..e6a621f95e04e0a0be61ea0d946162b1c6c3dfd0 100644 |
| --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
| +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java |
| @@ -6,16 +6,12 @@ package org.chromium.content.browser; |
| import android.annotation.SuppressLint; |
| import android.annotation.TargetApi; |
| -import android.app.Activity; |
| -import android.app.SearchManager; |
| import android.app.assist.AssistStructure.ViewNode; |
| import android.content.ClipData; |
| import android.content.ClipDescription; |
| import android.content.ClipboardManager; |
| -import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| -import android.content.pm.PackageManager; |
| import android.content.res.Configuration; |
| import android.graphics.Bitmap; |
| import android.graphics.Rect; |
| @@ -24,8 +20,6 @@ import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.ResultReceiver; |
| import android.os.SystemClock; |
| -import android.provider.Browser; |
| -import android.text.TextUtils; |
| import android.util.Pair; |
| import android.view.ActionMode; |
| import android.view.DragEvent; |
| @@ -37,7 +31,6 @@ import android.view.Surface; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewStructure; |
| -import android.view.WindowManager; |
| import android.view.accessibility.AccessibilityEvent; |
| import android.view.accessibility.AccessibilityManager; |
| import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; |
| @@ -48,29 +41,21 @@ import android.view.inputmethod.InputConnection; |
| import android.view.inputmethod.InputMethodManager; |
| import org.chromium.base.CommandLine; |
| -import org.chromium.base.Log; |
| import org.chromium.base.ObserverList; |
| import org.chromium.base.ObserverList.RewindableIterator; |
| import org.chromium.base.TraceEvent; |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.base.annotations.CalledByNative; |
| import org.chromium.base.annotations.JNINamespace; |
| -import org.chromium.base.metrics.RecordUserAction; |
| -import org.chromium.content.R; |
| import org.chromium.content.browser.accessibility.BrowserAccessibilityManager; |
| import org.chromium.content.browser.accessibility.captioning.CaptioningBridgeFactory; |
| import org.chromium.content.browser.accessibility.captioning.SystemCaptioningBridge; |
| import org.chromium.content.browser.accessibility.captioning.TextTrackSettings; |
| import org.chromium.content.browser.input.AnimationIntervalProvider; |
| -import org.chromium.content.browser.input.FloatingPastePopupMenu; |
| import org.chromium.content.browser.input.ImeAdapter; |
| import org.chromium.content.browser.input.InputMethodManagerWrapper; |
| import org.chromium.content.browser.input.JoystickScrollProvider; |
| import org.chromium.content.browser.input.JoystickZoomProvider; |
| -import org.chromium.content.browser.input.LGEmailActionModeWorkaround; |
| -import org.chromium.content.browser.input.LegacyPastePopupMenu; |
| -import org.chromium.content.browser.input.PastePopupMenu; |
| -import org.chromium.content.browser.input.PastePopupMenu.PastePopupMenuDelegate; |
| import org.chromium.content.browser.input.SelectPopup; |
| import org.chromium.content.browser.input.SelectPopupDialog; |
| import org.chromium.content.browser.input.SelectPopupDropdown; |
| @@ -345,11 +330,6 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| // Only valid when focused on a text / password field. |
| private ImeAdapter mImeAdapter; |
| - // Lazily created paste popup menu, triggered either via long press in an |
| - // editable region or from tapping the insertion handle. |
| - private PastePopupMenu mPastePopupMenu; |
| - private boolean mWasPastePopupShowingOnInsertionDragStart; |
| - |
| // Size of the viewport in physical pixels as set from onSizeChanged. |
| private int mViewportWidthPix; |
| private int mViewportHeightPix; |
| @@ -370,22 +350,9 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| private boolean mIsMobileOptimizedHint; |
| - // Tracks whether a selection is currently active. When applied to selected text, indicates |
| - // whether the last selected text is still highlighted. |
| - private boolean mHasSelection; |
| - private boolean mHasInsertion; |
| - private boolean mDraggingSelection; |
| - private String mLastSelectedText; |
| - private boolean mFocusedNodeEditable; |
| - private boolean mFocusedNodeIsPassword; |
| private WebActionMode mActionMode; |
| - private boolean mFloatingActionModeCreationFailed; |
| - private boolean mUnselectAllOnActionModeDismiss; |
| + private boolean mDraggingSelection; |
| private boolean mPreserveSelectionOnNextLossOfFocus; |
| - private WebActionModeCallback.ActionHandler mActionHandler; |
| - |
| - // Selection rectangle in DIP. |
| - private final Rect mSelectionRect = new Rect(); |
| // Whether native accessibility, i.e. without any script injection, is allowed. |
| private boolean mNativeAccessibilityAllowed; |
| @@ -497,6 +464,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| mGestureStateListenersIterator = mGestureStateListeners.rewindableIterator(); |
| mWindowAndroidChangedObservers = new ObserverList<WindowAndroidChangedObserver>(); |
| + mActionMode = WebActionMode.EMPTY; |
|
boliu
2016/10/26 00:20:57
I think this is probably overkill. CVC is not cons
Jinsuk Kim
2016/10/26 06:11:39
Done.
|
| } |
| /** |
| @@ -530,6 +498,14 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| return nativeGetJavaWindowAndroid(mNativeContentViewCore); |
| } |
| + /** |
| + * @return The WebActionMode that handles select action mode on web contents. |
| + */ |
| + @VisibleForTesting |
| + public WebActionMode getActionMode() { |
|
boliu
2016/10/26 00:20:57
can you rename this to getWebActionModeForTesting?
Jinsuk Kim
2016/10/26 06:11:39
Done.
|
| + return mActionMode; |
| + } |
| + |
| @Override |
| public void addWindowAndroidChangedObserver(WindowAndroidChangedObserver observer) { |
| mWindowAndroidChangedObservers.addObserver(observer); |
| @@ -584,7 +560,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| public void onImeEvent() { |
| mPopupZoomer.hide(true); |
| getContentViewClient().onImeEvent(); |
| - if (mFocusedNodeEditable) dismissTextHandles(); |
| + if (isFocusedNodeEditable()) mWebContents.dismissTextHandles(); |
| } |
| @Override |
| @@ -633,6 +609,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| * containerView. |
| * @param webContents A WebContents instance to connect to. |
| * @param windowAndroid An instance of the WindowAndroid. |
| + * @param actionMode WebActionMode instance that handles select action mode. |
|
boliu
2016/10/26 00:20:57
comment out of date now
Jinsuk Kim
2016/10/26 06:11:39
Done.
|
| */ |
| // Perform important post-construction set up of the ContentViewCore. |
| // We do not require the containing view in the constructor to allow embedders to create a |
| @@ -645,8 +622,10 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| // deleting it after destroying the ContentViewCore. |
| public void initialize(ViewAndroidDelegate viewDelegate, |
| InternalAccessDelegate internalDispatcher, WebContents webContents, |
| - WindowAndroid windowAndroid) { |
| + WindowAndroid windowAndroid, ActionMode.Callback actionModeCallback) { |
| mViewAndroidDelegate = viewDelegate; |
| + mActionMode = new WebActionMode(mContext, windowAndroid, webContents, |
| + viewDelegate.getContainerView(), mRenderCoordinates, actionModeCallback); |
| setContainerView(viewDelegate.getContainerView()); |
| long windowNativePointer = windowAndroid.getNativePointer(); |
| assert windowNativePointer != 0; |
| @@ -681,7 +660,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| // TODO(yusufo): Rename this call to be general for tab reparenting. |
| // Clean up cached popups that may have been created with an old activity. |
| mSelectPopup = null; |
| - mPastePopupMenu = null; |
| + destroyPastePopup(); |
|
boliu
2016/10/26 00:20:57
I'll ask yusufo later why this isn't going through
Jinsuk Kim
2016/10/26 06:11:39
Acknowledged.
|
| addDisplayAndroidObserverIfNeeded(); |
| @@ -730,12 +709,14 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| TraceEvent.begin("ContentViewCore.setContainerView"); |
| if (mContainerView != null) { |
| assert mOverscrollRefreshHandler == null; |
| - mPastePopupMenu = null; |
| - hidePopupsAndClearSelection(); |
| + hideSelectPopupWithCancelMessage(); |
| + mPopupZoomer.hide(false); |
| + destroyPastePopup(); |
|
boliu
2016/10/26 00:20:57
this should be handled inside WebActionMode.setCon
Jinsuk Kim
2016/10/26 06:11:39
Done.
|
| } |
| mContainerView = containerView; |
| mContainerView.setClickable(true); |
| + mActionMode.setContainerView(containerView); |
| } finally { |
| TraceEvent.end("ContentViewCore.setContainerView"); |
| } |
| @@ -857,7 +838,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| } |
| mGestureStateListeners.clear(); |
| hidePopupsAndPreserveSelection(); |
| - mPastePopupMenu = null; |
| + destroyPastePopup(); |
| // See warning in javadoc before adding more clean up code here. |
| } |
| @@ -989,21 +970,14 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| */ |
| @VisibleForTesting |
| public String getSelectedText() { |
| - return mHasSelection ? mLastSelectedText : ""; |
| - } |
| - |
| - /** |
| - * @return Whether the current selection is editable (false if no text selected). |
| - */ |
| - public boolean isSelectionEditable() { |
| - return mHasSelection ? mFocusedNodeEditable : false; |
| + return mActionMode.getSelectedText(); |
| } |
| /** |
| * @return Whether the current focused node is editable. |
| */ |
| public boolean isFocusedNodeEditable() { |
| - return mFocusedNodeEditable; |
| + return mActionMode.isSelectionEditable(); |
| } |
| /** |
| @@ -1013,8 +987,6 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| return GamepadList.isGamepadAPIActive(); |
| } |
| - // End FrameLayout overrides. |
| - |
| /** |
| * @see View#onTouchEvent(MotionEvent) |
| */ |
| @@ -1317,37 +1289,40 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| } |
| private void hidePopupsAndClearSelection() { |
| - mUnselectAllOnActionModeDismiss = true; |
| + setUnselectAllOnDismiss(true); |
| hidePopups(); |
| } |
| @CalledByNative |
| private void hidePopupsAndPreserveSelection() { |
| - mUnselectAllOnActionModeDismiss = false; |
| + setUnselectAllOnDismiss(false); |
| hidePopups(); |
| } |
| + private void setUnselectAllOnDismiss(boolean flag) { |
| + mActionMode.setUnselectAllOnDismiss(flag); |
| + } |
| + |
| private void hidePopups() { |
| - hideSelectActionMode(); |
| + destroySelectActionMode(); |
| hidePastePopup(); |
| - hideSelectPopupWithCancelMesage(); |
| + hideSelectPopupWithCancelMessage(); |
| mPopupZoomer.hide(false); |
| - if (mUnselectAllOnActionModeDismiss) dismissTextHandles(); |
| } |
| private void restoreSelectionPopupsIfNecessary() { |
| - if (mHasSelection && mActionMode == null) showSelectActionMode(true); |
| + if (hasSelection()) showSelectActionMode(true); |
| } |
| - public void hideSelectActionMode() { |
| - if (mActionMode != null) { |
| - mActionMode.finish(); |
| - mActionMode = null; |
| - } |
| + /** |
| + * Hide action mode and put into destroyed state. |
| + */ |
| + public void destroySelectActionMode() { |
| + mActionMode.finishActionMode(); |
| } |
| public boolean isSelectActionBarShowing() { |
| - return mActionMode != null; |
| + return mActionMode.isValid(); |
| } |
| private void resetGestureDetection() { |
| @@ -1511,7 +1486,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| public void onWindowFocusChanged(boolean hasWindowFocus) { |
| mImeAdapter.onWindowFocusChanged(hasWindowFocus); |
| if (!hasWindowFocus) resetGestureDetection(); |
| - if (mActionMode != null) mActionMode.onWindowFocusChanged(hasWindowFocus); |
| + mActionMode.onWindowFocusChanged(hasWindowFocus); |
| for (mGestureStateListenersIterator.rewind(); mGestureStateListenersIterator.hasNext();) { |
| mGestureStateListenersIterator.next().onWindowFocusChanged(hasWindowFocus); |
| } |
| @@ -1519,7 +1494,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| public void onFocusChanged(boolean gainFocus) { |
| mImeAdapter.onViewFocusChanged(gainFocus); |
| - mJoystickScrollProvider.setEnabled(gainFocus && !mFocusedNodeEditable); |
| + mJoystickScrollProvider.setEnabled(gainFocus && !isFocusedNodeEditable()); |
| if (gainFocus) { |
| restoreSelectionPopupsIfNecessary(); |
| @@ -1836,267 +1811,25 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| nativeSendOrientationChangeEvent(mNativeContentViewCore, orientation); |
| } |
| - @VisibleForTesting |
| - public WebActionModeCallback.ActionHandler getSelectActionHandler() { |
| - return mActionHandler; |
| + public WebActionModeDelegate getActionModeDelegate() { |
| + return mActionMode; |
| } |
| - private void showSelectActionMode(boolean allowFallbackIfFloatingActionModeCreationFails) { |
| - if (mActionMode != null) { |
| + private void showSelectActionMode(boolean allowFallback) { |
| + // Just refreshes the view if it is already showing. |
| + if (mActionMode.isValid()) { |
| mActionMode.invalidate(); |
| return; |
| } |
| - |
| - // Start a new action mode with a WebActionModeCallback. |
| - if (mActionHandler == null) { |
| - mActionHandler = new WebActionModeCallback.ActionHandler() { |
| - /** |
| - * Android Intent size limitations prevent sending over a megabyte of data. Limit |
| - * query lengths to 100kB because other things may be added to the Intent. |
| - */ |
| - private static final int MAX_SHARE_QUERY_LENGTH = 100000; |
| - |
| - /** Google search doesn't support requests slightly larger than this. */ |
| - private static final int MAX_SEARCH_QUERY_LENGTH = 1000; |
| - |
| - // All WebContents actions record a user action internally. |
| - @Override |
| - public void selectAll() { |
| - mWebContents.selectAll(); |
| - // Even though the above statement logged a SelectAll user action, we want to |
| - // track whether the focus was in an editable field, so log that too. |
| - if (isFocusedNodeEditable()) { |
| - RecordUserAction.record("MobileActionMode.SelectAllWasEditable"); |
| - } else { |
| - RecordUserAction.record("MobileActionMode.SelectAllWasNonEditable"); |
| - } |
| - } |
| - |
| - @Override |
| - public void cut() { |
| - mWebContents.cut(); |
| - } |
| - |
| - @Override |
| - public void copy() { |
| - mWebContents.copy(); |
| - } |
| - |
| - @Override |
| - public void paste() { |
| - mWebContents.paste(); |
| - } |
| - |
| - @Override |
| - public void share() { |
| - RecordUserAction.record("MobileActionMode.Share"); |
| - final String query = sanitizeQuery(getSelectedText(), MAX_SHARE_QUERY_LENGTH); |
| - 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 processText(Intent intent) { |
| - RecordUserAction.record("MobileActionMode.ProcessTextIntent"); |
| - assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; |
| - |
| - final String query = sanitizeQuery(getSelectedText(), MAX_SEARCH_QUERY_LENGTH); |
| - if (TextUtils.isEmpty(query)) return; |
| - |
| - intent.putExtra(Intent.EXTRA_PROCESS_TEXT, query); |
| - try { |
| - if (getContentViewClient().doesPerformProcessText()) { |
| - getContentViewClient().startProcessTextIntent(intent); |
| - } else { |
| - getWindowAndroid().showIntent( |
| - intent, new WindowAndroid.IntentCallback() { |
| - @Override |
| - public void onIntentCompleted(WindowAndroid window, |
| - int resultCode, ContentResolver contentResolver, |
| - Intent data) { |
| - onReceivedProcessTextResult(resultCode, data); |
| - } |
| - }, null); |
| - } |
| - } catch (android.content.ActivityNotFoundException ex) { |
| - // If no app handles it, do nothing. |
| - } |
| - } |
| - |
| - @Override |
| - public void search() { |
| - RecordUserAction.record("MobileActionMode.WebSearch"); |
| - final String query = sanitizeQuery(getSelectedText(), MAX_SEARCH_QUERY_LENGTH); |
| - 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()); |
| - 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 mFocusedNodeIsPassword; |
| - } |
| - |
| - @Override |
| - public boolean isSelectionEditable() { |
| - return mFocusedNodeEditable; |
| - } |
| - |
| - @Override |
| - public boolean isInsertion() { |
| - return mHasInsertion; |
| - } |
| - |
| - @Override |
| - public void onDestroyActionMode() { |
| - mActionMode = null; |
| - if (mUnselectAllOnActionModeDismiss) { |
| - dismissTextHandles(); |
| - clearSelection(); |
| - } |
| - if (!supportsFloatingActionMode()) { |
| - getContentViewClient().onContextualActionBarHidden(); |
| - } |
| - } |
| - |
| - @Override |
| - public void onGetContentRect(Rect outRectPix) { |
| - final float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); |
| - outRectPix.set((int) (mSelectionRect.left * deviceScale), |
| - (int) (mSelectionRect.top * deviceScale), |
| - (int) (mSelectionRect.right * deviceScale), |
| - (int) (mSelectionRect.bottom * deviceScale)); |
| - |
| - // The selection coordinates are relative to the content viewport, but we need |
| - // coordinates relative to the containing View. |
| - outRectPix.offset(0, (int) mRenderCoordinates.getContentOffsetYPix()); |
| - } |
| - |
| - @Override |
| - public boolean isIncognito() { |
| - return mWebContents.isIncognito(); |
| - } |
| - |
| - @Override |
| - public boolean isSelectActionModeAllowed(int actionModeItem) { |
| - boolean isAllowedByClient = |
| - getContentViewClient().isSelectActionModeAllowed(actionModeItem); |
| - if (actionModeItem == WebActionModeCallback.MENU_ITEM_SHARE) { |
| - return isAllowedByClient && isShareAvailable(); |
| - } |
| - |
| - if (actionModeItem == WebActionModeCallback.MENU_ITEM_WEB_SEARCH) { |
| - return isAllowedByClient && isWebSearchAvailable(); |
| - } |
| - |
| - return isAllowedByClient; |
| - } |
| - |
| - private boolean isShareAvailable() { |
| - Intent intent = new Intent(Intent.ACTION_SEND); |
| - intent.setType("text/plain"); |
| - return getContext().getPackageManager().queryIntentActivities(intent, |
| - PackageManager.MATCH_DEFAULT_ONLY).size() > 0; |
| - } |
| - |
| - private 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; |
| - } |
| - |
| - private String sanitizeQuery(String query, int maxLength) { |
| - if (TextUtils.isEmpty(query) || query.length() < maxLength) return query; |
| - Log.w(TAG, "Truncating oversized query (" + query.length() + ")."); |
| - return query.substring(0, maxLength) + "…"; |
| - } |
| - }; |
| - } |
| - mActionMode = null; |
| - // On ICS, startActionMode throws an NPE when getParent() is null. |
| - if (mContainerView.getParent() != null) { |
| - assert mWebContents != null; |
| - ActionMode actionMode = startActionMode(allowFallbackIfFloatingActionModeCreationFails); |
| - if (actionMode != null) { |
| - // This is to work around an LGE email issue. See crbug.com/651706 for more details. |
| - LGEmailActionModeWorkaround.runIfNecessary(mContext, actionMode); |
| - mActionMode = new WebActionMode(actionMode, mContainerView); |
| - } |
| - } |
| - mUnselectAllOnActionModeDismiss = true; |
| - if (mActionMode == null) { |
| + mActionMode.showActionMode(allowFallback); |
| + if (!mActionMode.isValid()) { |
| // There is no ActionMode, so remove the selection. |
| clearSelection(); |
| - } else { |
| - // TODO(jdduke): Refactor ContentViewClient.onContextualActionBarShown to be aware |
| - // of non-action-bar (i.e., floating) ActionMode instances, crbug.com/524666. |
| - if (!supportsFloatingActionMode()) { |
| - getContentViewClient().onContextualActionBarShown(); |
| - } |
| } |
| } |
| - private boolean supportsFloatingActionMode() { |
| - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false; |
| - return !mFloatingActionModeCreationFailed; |
| - } |
| - |
| - private ActionMode startActionMode(boolean allowFallbackIfFloatingActionModeCreationFails) { |
| - WebActionModeCallback callback = |
| - new WebActionModeCallback(mContainerView.getContext(), mActionHandler); |
| - if (supportsFloatingActionMode()) { |
| - ActionMode actionMode = startFloatingActionMode(callback); |
| - if (actionMode != null) return actionMode; |
| - mFloatingActionModeCreationFailed = true; |
| - if (!allowFallbackIfFloatingActionModeCreationFails) return null; |
| - } |
| - return startDefaultActionMode(callback); |
| - } |
| - |
| - private ActionMode startDefaultActionMode(WebActionModeCallback callback) { |
| - return mContainerView.startActionMode(callback); |
| - } |
| - |
| - @TargetApi(Build.VERSION_CODES.M) |
| - private ActionMode startFloatingActionMode(WebActionModeCallback callback) { |
| - ActionMode.Callback2 callback2 = new FloatingWebActionModeCallback(callback); |
| - return mContainerView.startActionMode(callback2, ActionMode.TYPE_FLOATING); |
| - } |
| - |
| - private void invalidateActionModeContentRect() { |
| - if (mActionMode != null) mActionMode.invalidateContentRect(); |
| - } |
| - |
| private void updateActionModeVisibility() { |
| - if (mActionMode == null) return; |
| + if (!mActionMode.isValid()) return; |
|
boliu
2016/10/26 00:20:57
move this check into WebActionMode
Jinsuk Kim
2016/10/26 06:11:39
Can be removed here safely as canHide() in WAM.hid
|
| // The active fling count isn't reliable with WebView, so only use the |
| // active touch scroll signal for hiding. The fling animation movement |
| // will naturally hide the ActionMode by invalidating its content rect. |
| @@ -2108,7 +1841,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| * end if applicable. |
| */ |
| public void clearSelection() { |
| - if (mFocusedNodeEditable) { |
| + if (isFocusedNodeEditable()) { |
| mImeAdapter.moveCursorToSelectionEnd(); |
| } else { |
| // This method can be called during shutdown, guard against null accordingly. |
| @@ -2128,7 +1861,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| */ |
| @VisibleForTesting |
| public boolean hasSelection() { |
| - return mHasSelection; |
| + return mActionMode.hasSelection(); |
| } |
| /** |
| @@ -2136,36 +1869,22 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| */ |
| @VisibleForTesting |
| public boolean hasInsertion() { |
| - return mHasInsertion; |
| + return mActionMode.isInsertion(); |
| } |
| // All coordinates are in DIP. |
| @CalledByNative |
| private void onSelectionEvent( |
| int eventType, int xAnchor, int yAnchor, int left, int top, int right, int bottom) { |
| - // Ensure the provided selection coordinates form a non-empty rect, as required by |
| - // the selection action mode. |
| - if (left == right) ++right; |
| - if (top == bottom) ++bottom; |
| + mActionMode.onSelectionEvent(eventType, xAnchor, yAnchor, left, top, right, bottom, |
| + isScrollInProgress()); |
| switch (eventType) { |
| case SelectionEventType.SELECTION_HANDLES_SHOWN: |
| - mSelectionRect.set(left, top, right, bottom); |
| - mHasSelection = true; |
| - mUnselectAllOnActionModeDismiss = true; |
| showSelectActionMode(true); |
| break; |
| - case SelectionEventType.SELECTION_HANDLES_MOVED: |
| - mSelectionRect.set(left, top, right, bottom); |
| - invalidateActionModeContentRect(); |
| - break; |
| - |
| case SelectionEventType.SELECTION_HANDLES_CLEARED: |
| - mHasSelection = false; |
| mDraggingSelection = false; |
| - mUnselectAllOnActionModeDismiss = false; |
| - hideSelectActionMode(); |
| - mSelectionRect.setEmpty(); |
| break; |
| case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: |
| @@ -2177,54 +1896,10 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| mDraggingSelection = false; |
| updateActionModeVisibility(); |
| break; |
| - |
| - case SelectionEventType.INSERTION_HANDLE_SHOWN: |
| - mSelectionRect.set(left, top, right, bottom); |
| - mHasInsertion = true; |
| - break; |
| - |
| - case SelectionEventType.INSERTION_HANDLE_MOVED: |
| - mSelectionRect.set(left, top, right, bottom); |
| - if (!isScrollInProgress() && isPastePopupShowing()) { |
| - showPastePopup(xAnchor, yAnchor); |
| - } else { |
| - hidePastePopup(); |
| - } |
| - break; |
| - |
| - case SelectionEventType.INSERTION_HANDLE_TAPPED: |
| - if (mWasPastePopupShowingOnInsertionDragStart) { |
| - hidePastePopup(); |
| - } else { |
| - showPastePopup(xAnchor, yAnchor); |
| - } |
| - mWasPastePopupShowingOnInsertionDragStart = false; |
| - break; |
| - |
| - case SelectionEventType.INSERTION_HANDLE_CLEARED: |
| - hidePastePopup(); |
| - mHasInsertion = false; |
| - mSelectionRect.setEmpty(); |
| - break; |
| - |
| - case SelectionEventType.INSERTION_HANDLE_DRAG_STARTED: |
| - mWasPastePopupShowingOnInsertionDragStart = isPastePopupShowing(); |
| - hidePastePopup(); |
| - break; |
| - |
| - case SelectionEventType.INSERTION_HANDLE_DRAG_STOPPED: |
| - if (mWasPastePopupShowingOnInsertionDragStart) { |
| - showPastePopup(xAnchor, yAnchor); |
| - } |
| - mWasPastePopupShowingOnInsertionDragStart = false; |
| - break; |
| - case SelectionEventType.SELECTION_ESTABLISHED: |
| - case SelectionEventType.SELECTION_DISSOLVED: |
| - break; |
| - |
| default: |
| - assert false : "Invalid selection event type."; |
| + break; |
| } |
| + |
| if (mContextualSearchClient != null) { |
| final float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); |
| int xAnchorPix = (int) (xAnchor * deviceScale); |
| @@ -2233,10 +1908,6 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| } |
| } |
| - private void dismissTextHandles() { |
| - if (mNativeContentViewCore != 0) nativeDismissTextHandles(mNativeContentViewCore); |
| - } |
| - |
| private void setTextHandlesTemporarilyHidden(boolean hide) { |
| if (mNativeContentViewCore == 0) return; |
| nativeSetTextHandlesTemporarilyHidden(mNativeContentViewCore, hide); |
| @@ -2353,18 +2024,11 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| mImeAdapter.updateState(text, selectionStart, selectionEnd, compositionStart, |
| compositionEnd, isNonImeChange, inBatchEditMode); |
| - if (mActionMode != null) { |
| - final boolean actionModeConfigurationChanged = |
| - focusedNodeEditable != mFocusedNodeEditable |
| - || focusedNodeIsPassword != mFocusedNodeIsPassword; |
| - if (actionModeConfigurationChanged) mActionMode.invalidate(); |
| - } |
| - |
| - mFocusedNodeIsPassword = focusedNodeIsPassword; |
| - if (focusedNodeEditable != mFocusedNodeEditable) { |
| - mFocusedNodeEditable = focusedNodeEditable; |
| - mJoystickScrollProvider.setEnabled(!mFocusedNodeEditable); |
| - getContentViewClient().onFocusedNodeEditabilityChanged(mFocusedNodeEditable); |
| + boolean editableToggled = (focusedNodeEditable != isFocusedNodeEditable()); |
| + mActionMode.updateSelectionState(focusedNodeEditable, focusedNodeIsPassword); |
| + if (editableToggled) { |
| + mJoystickScrollProvider.setEnabled(!focusedNodeEditable); |
| + getContentViewClient().onFocusedNodeEditabilityChanged(focusedNodeEditable); |
| } |
| } finally { |
| TraceEvent.end("ContentViewCore.updateImeAdapter"); |
| @@ -2438,7 +2102,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| * Called when the <select> popup needs to be hidden. This calls |
| * nativeSelectPopupMenuItems() with null indices. |
| */ |
| - private void hideSelectPopupWithCancelMesage() { |
| + private void hideSelectPopupWithCancelMessage() { |
| if (mSelectPopup != null) mSelectPopup.hide(true); |
| } |
| @@ -2500,7 +2164,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| @SuppressWarnings("unused") |
| @CalledByNative |
| private void onSelectionChanged(String text) { |
| - mLastSelectedText = text; |
| + mActionMode.setSelectedText(text); |
| if (mContextualSearchClient != null) { |
| mContextualSearchClient.onSelectionChanged(text); |
| } |
| @@ -2514,56 +2178,20 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| @VisibleForTesting |
| public boolean isPastePopupShowing() { |
|
boliu
2016/10/26 00:20:57
can you double check all these VisibleForTesting m
Jinsuk Kim
2016/10/26 06:11:39
Yes it can. Done.
|
| - if (mPastePopupMenu != null) return mPastePopupMenu.isShowing(); |
| - return false; |
| + return mActionMode.isPastePopupShowing(); |
| } |
| - // Coordinates are in DIP. |
| @CalledByNative |
| private void showPastePopup(int x, int y) { |
| - if (mContainerView.getParent() == null || mContainerView.getVisibility() != View.VISIBLE) { |
| - return; |
| - } |
| - |
| - if (!mHasInsertion || !canPaste()) return; |
|
boliu
2016/10/26 00:20:57
this line changed on trunk, probably should rebase
Jinsuk Kim
2016/10/26 06:11:39
Rebased.
|
| - |
| - PastePopupMenu pastePopupMenu = getPastePopup(); |
| - if (pastePopupMenu == null) return; |
| - |
| - final float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); |
| - final int xPix = (int) (x * deviceScale); |
| - final int yPix = (int) (y * deviceScale); |
| - final float topControlsShownPix = mRenderCoordinates.getContentOffsetYPix(); |
| - try { |
| - pastePopupMenu.show(xPix, (int) (yPix + topControlsShownPix)); |
| - } catch (WindowManager.BadTokenException e) { |
| - } |
| + mActionMode.showPastePopup(x, y); |
| } |
| private void hidePastePopup() { |
| - if (mPastePopupMenu != null) mPastePopupMenu.hide(); |
| + mActionMode.hidePastePopup(); |
| } |
| - private PastePopupMenu getPastePopup() { |
| - if (mPastePopupMenu == null) { |
| - PastePopupMenuDelegate delegate = new PastePopupMenuDelegate() { |
| - @Override |
| - public void paste() { |
| - mWebContents.paste(); |
| - dismissTextHandles(); |
| - } |
| - }; |
| - Context windowContext = getWindowAndroid().getContext().get(); |
| - if (windowContext == null) return null; |
| - if (supportsFloatingActionMode()) { |
| - mPastePopupMenu = new FloatingPastePopupMenu( |
| - windowContext, getContainerView(), delegate); |
| - } else { |
| - mPastePopupMenu = new LegacyPastePopupMenu( |
| - windowContext, getContainerView(), delegate); |
| - } |
| - } |
| - return mPastePopupMenu; |
| + private void destroyPastePopup() { |
| + mActionMode.destroyPastePopup(); |
| } |
| private boolean canPaste() { |
| @@ -3021,17 +2649,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| * @param data the reply that contains the processed text. |
| */ |
| public void onReceivedProcessTextResult(int resultCode, Intent data) { |
| - if (mWebContents == null || !isSelectionEditable() || resultCode != Activity.RESULT_OK |
| - || data == null) { |
| - return; |
| - } |
| - |
| - CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT); |
| - if (result != null) { |
| - // TODO(hush): Use a variant of replace that re-selects the replaced text. |
| - // crbug.com/546710 |
| - mWebContents.replace(result.toString()); |
| - } |
| + mActionMode.onReceivedProcessTextResult(resultCode, data); |
| } |
| /** |
| @@ -3239,7 +2857,7 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| // ActionMode#invalidate() won't be able to re-layout the floating |
| // action mode menu items according to the new rotation. So Chrome |
| // has to re-create the action mode. |
| - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && mActionMode != null) { |
| + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && mActionMode.isValid()) { |
| hidePopupsAndPreserveSelection(); |
| showSelectActionMode(true); |
| } |
| @@ -3385,7 +3003,6 @@ public class ContentViewCore implements AccessibilityStateChangeListener, Displa |
| private native void nativePinchBy(long nativeContentViewCoreImpl, long timeMs, |
| float anchorX, float anchorY, float deltaScale); |
| - private native void nativeDismissTextHandles(long nativeContentViewCoreImpl); |
| private native void nativeSetTextHandlesTemporarilyHidden( |
| long nativeContentViewCoreImpl, boolean hidden); |