Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.content.browser; | 5 package org.chromium.content.browser; |
| 6 | 6 |
| 7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
| 8 import android.app.Activity; | 8 import android.app.Activity; |
| 9 import android.app.SearchManager; | 9 import android.app.SearchManager; |
| 10 import android.content.ClipData; | 10 import android.content.ClipData; |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 // of adding and removing it once we switch to Android O SDK. The show/hide method | 86 // of adding and removing it once we switch to Android O SDK. The show/hide method |
| 87 // does not require ordering information. | 87 // does not require ordering information. |
| 88 private static final int MENU_ITEM_ORDER_TEXT_PROCESS_START = 100; | 88 private static final int MENU_ITEM_ORDER_TEXT_PROCESS_START = 100; |
| 89 | 89 |
| 90 private final Context mContext; | 90 private final Context mContext; |
| 91 private final WindowAndroid mWindowAndroid; | 91 private final WindowAndroid mWindowAndroid; |
| 92 private final WebContents mWebContents; | 92 private final WebContents mWebContents; |
| 93 private final RenderCoordinates mRenderCoordinates; | 93 private final RenderCoordinates mRenderCoordinates; |
| 94 private ActionMode.Callback mCallback; | 94 private ActionMode.Callback mCallback; |
| 95 | 95 |
| 96 // Selection rectangle in DIP. | 96 // Selection rectangle in DIP. |
|
boliu
2017/05/04 20:36:11
no longer in DIP? also it has the Y offset thing
amaralp
2017/05/05 01:14:32
Reverted to it being in DIP
| |
| 97 private final Rect mSelectionRect = new Rect(); | 97 private final Rect mSelectionRect = new Rect(); |
| 98 | 98 |
| 99 // Self-repeating task that repeatedly hides the ActionMode. This is | 99 // Self-repeating task that repeatedly hides the ActionMode. This is |
| 100 // required because ActionMode only exposes a temporary hide routine. | 100 // required because ActionMode only exposes a temporary hide routine. |
| 101 private final Runnable mRepeatingHideRunnable; | 101 private final Runnable mRepeatingHideRunnable; |
| 102 | 102 |
| 103 private View mView; | 103 private View mView; |
| 104 private ActionMode mActionMode; | 104 private ActionMode mActionMode; |
| 105 private MenuDescriptor mActionMenuDescriptor; | 105 private MenuDescriptor mActionMenuDescriptor; |
| 106 | 106 |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 261 if (!isActionModeValid()) clearSelection(); | 261 if (!isActionModeValid()) clearSelection(); |
| 262 } | 262 } |
| 263 | 263 |
| 264 @TargetApi(Build.VERSION_CODES.M) | 264 @TargetApi(Build.VERSION_CODES.M) |
| 265 private ActionMode startFloatingActionMode() { | 265 private ActionMode startFloatingActionMode() { |
| 266 ActionMode actionMode = mView.startActionMode( | 266 ActionMode actionMode = mView.startActionMode( |
| 267 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING); | 267 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING); |
| 268 return actionMode; | 268 return actionMode; |
| 269 } | 269 } |
| 270 | 270 |
| 271 void createAndShowPastePopup(int x, int y, boolean canSelectAll, boolean can EditRichly) { | 271 void createAndShowPastePopup( |
| 272 int left, int top, int right, int bottom, boolean canSelectAll, bool ean canEditRichly) { | |
| 272 if (mView.getParent() == null || mView.getVisibility() != View.VISIBLE) { | 273 if (mView.getParent() == null || mView.getVisibility() != View.VISIBLE) { |
| 273 return; | 274 return; |
| 274 } | 275 } |
| 275 | 276 |
| 276 if (!supportsFloatingActionMode() && !canPaste()) return; | 277 if (!supportsFloatingActionMode() && !canPaste()) return; |
| 277 destroyPastePopup(); | 278 destroyPastePopup(); |
| 279 setSelectionRect(left, top, right, bottom); | |
| 278 mCanSelectAllForPastePopup = canSelectAll; | 280 mCanSelectAllForPastePopup = canSelectAll; |
| 279 mCanEditRichlyForPastePopup = canEditRichly; | 281 mCanEditRichlyForPastePopup = canEditRichly; |
| 280 PastePopupMenuDelegate delegate = new PastePopupMenuDelegate() { | 282 PastePopupMenuDelegate delegate = new PastePopupMenuDelegate() { |
| 281 @Override | 283 @Override |
| 282 public void paste() { | 284 public void paste() { |
| 283 mWebContents.paste(); | 285 mWebContents.paste(); |
| 284 mWebContents.dismissTextHandles(); | 286 mWebContents.dismissTextHandles(); |
| 285 } | 287 } |
| 286 | 288 |
| 287 @Override | 289 @Override |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 310 return SelectionPopupController.this.canPasteAsPlainText(); | 312 return SelectionPopupController.this.canPasteAsPlainText(); |
| 311 } | 313 } |
| 312 }; | 314 }; |
| 313 Context windowContext = mWindowAndroid.getContext().get(); | 315 Context windowContext = mWindowAndroid.getContext().get(); |
| 314 if (windowContext == null) return; | 316 if (windowContext == null) return; |
| 315 if (supportsFloatingActionMode()) { | 317 if (supportsFloatingActionMode()) { |
| 316 mPastePopupMenu = new FloatingPastePopupMenu(windowContext, mView, d elegate); | 318 mPastePopupMenu = new FloatingPastePopupMenu(windowContext, mView, d elegate); |
| 317 } else { | 319 } else { |
| 318 mPastePopupMenu = new LegacyPastePopupMenu(windowContext, mView, del egate); | 320 mPastePopupMenu = new LegacyPastePopupMenu(windowContext, mView, del egate); |
| 319 } | 321 } |
| 320 showPastePopup(x, y); | 322 showPastePopup(); |
| 321 } | 323 } |
| 322 | 324 |
| 323 private void showPastePopup(int x, int y) { | 325 private void showPastePopup() { |
| 324 // Coordinates are in DIP. | |
| 325 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); | |
| 326 final int xPix = (int) (x * deviceScale); | |
| 327 final int yPix = (int) (y * deviceScale); | |
| 328 final float browserControlsShownPix = mRenderCoordinates.getContentOffse tYPix(); | |
| 329 try { | 326 try { |
| 330 mPastePopupMenu.show(xPix, (int) (yPix + browserControlsShownPix)); | 327 mPastePopupMenu.show(mSelectionRect); |
| 331 } catch (WindowManager.BadTokenException e) { | 328 } catch (WindowManager.BadTokenException e) { |
| 332 } | 329 } |
| 333 } | 330 } |
| 334 | 331 |
| 335 @Override | 332 @Override |
| 336 public boolean supportsFloatingActionMode() { | 333 public boolean supportsFloatingActionMode() { |
| 337 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; | 334 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; |
| 338 } | 335 } |
| 339 | 336 |
| 340 void destroyPastePopup() { | 337 void destroyPastePopup() { |
| (...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 671 * Called when an ActionMode needs to be positioned on screen, potentially o ccluding view | 668 * Called when an ActionMode needs to be positioned on screen, potentially o ccluding view |
| 672 * content. Note this may be called on a per-frame basis. | 669 * content. Note this may be called on a per-frame basis. |
| 673 * | 670 * |
| 674 * @param mode The ActionMode that requires positioning. | 671 * @param mode The ActionMode that requires positioning. |
| 675 * @param view The View that originated the ActionMode, in whose coordinates the Rect should | 672 * @param view The View that originated the ActionMode, in whose coordinates the Rect should |
| 676 * be provided. | 673 * be provided. |
| 677 * @param outRect The Rect to be populated with the content position. | 674 * @param outRect The Rect to be populated with the content position. |
| 678 */ | 675 */ |
| 679 @Override | 676 @Override |
| 680 public void onGetContentRect(ActionMode mode, View view, Rect outRect) { | 677 public void onGetContentRect(ActionMode mode, View view, Rect outRect) { |
| 678 outRect.set(mSelectionRect); | |
| 679 } | |
| 680 | |
| 681 private void setSelectionRect(int left, int top, int right, int bottom) { | |
| 681 float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); | 682 float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); |
| 682 outRect.set((int) (mSelectionRect.left * deviceScale), | 683 mSelectionRect.set((int) (left * deviceScale), (int) (top * deviceScale) , |
| 683 (int) (mSelectionRect.top * deviceScale), | 684 (int) (right * deviceScale), (int) (bottom * deviceScale)); |
| 684 (int) (mSelectionRect.right * deviceScale), | |
| 685 (int) (mSelectionRect.bottom * deviceScale)); | |
| 686 | |
| 687 // The selection coordinates are relative to the content viewport, but w e need | 685 // The selection coordinates are relative to the content viewport, but w e need |
| 688 // coordinates relative to the containing View. | 686 // coordinates relative to the containing View. |
| 689 outRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix()); | 687 mSelectionRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix() ); |
| 690 } | 688 } |
| 691 | 689 |
| 692 /** | 690 /** |
| 693 * Perform an action that depends on the semantics of the selected text. | 691 * Perform an action that depends on the semantics of the selected text. |
| 694 */ | 692 */ |
| 695 @VisibleForTesting | 693 @VisibleForTesting |
| 696 void doAssistAction() { | 694 void doAssistAction() { |
| 697 if (mClassificationResult == null || !mClassificationResult.hasNamedActi on()) return; | 695 if (mClassificationResult == null || !mClassificationResult.hasNamedActi on()) return; |
| 698 | 696 |
| 699 assert mClassificationResult.onClickListener != null | 697 assert mClassificationResult.onClickListener != null |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 909 } | 907 } |
| 910 | 908 |
| 911 void restoreSelectionPopupsIfNecessary() { | 909 void restoreSelectionPopupsIfNecessary() { |
| 912 if (mHasSelection && !isActionModeValid()) { | 910 if (mHasSelection && !isActionModeValid()) { |
| 913 showActionModeOrClearOnFailure(); | 911 showActionModeOrClearOnFailure(); |
| 914 } | 912 } |
| 915 } | 913 } |
| 916 | 914 |
| 917 // All coordinates are in DIP. | 915 // All coordinates are in DIP. |
| 918 @CalledByNative | 916 @CalledByNative |
| 919 private void onSelectionEvent( | 917 private void onSelectionEvent(int eventType, int left, int top, int right, i nt bottom) { |
| 920 int eventType, int xAnchor, int yAnchor, int left, int top, int righ t, int bottom) { | |
| 921 // Ensure the provided selection coordinates form a non-empty rect, as r equired by | 918 // Ensure the provided selection coordinates form a non-empty rect, as r equired by |
| 922 // the selection action mode. | 919 // the selection action mode. |
| 923 if (left == right) ++right; | 920 if (left == right) ++right; |
| 924 if (top == bottom) ++bottom; | 921 if (top == bottom) ++bottom; |
| 925 switch (eventType) { | 922 switch (eventType) { |
| 926 case SelectionEventType.SELECTION_HANDLES_SHOWN: | 923 case SelectionEventType.SELECTION_HANDLES_SHOWN: |
| 927 mSelectionRect.set(left, top, right, bottom); | 924 setSelectionRect(left, top, right, bottom); |
| 928 mHasSelection = true; | 925 mHasSelection = true; |
| 929 mUnselectAllOnDismiss = true; | 926 mUnselectAllOnDismiss = true; |
| 930 if (mSelectionClient == null || !mSelectionClient.sendsSelection PopupUpdates()) { | 927 if (mSelectionClient == null || !mSelectionClient.sendsSelection PopupUpdates()) { |
| 931 showActionModeOrClearOnFailure(); | 928 showActionModeOrClearOnFailure(); |
| 932 } else { | 929 } else { |
| 933 // Rely on |mSelectionClient| sending a classification reque st and the request | 930 // Rely on |mSelectionClient| sending a classification reque st and the request |
| 934 // always calling onClassified() callback. | 931 // always calling onClassified() callback. |
| 935 mPendingShowActionMode = true; | 932 mPendingShowActionMode = true; |
| 936 } | 933 } |
| 937 break; | 934 break; |
| 938 | 935 |
| 939 case SelectionEventType.SELECTION_HANDLES_MOVED: | 936 case SelectionEventType.SELECTION_HANDLES_MOVED: |
| 940 mSelectionRect.set(left, top, right, bottom); | 937 setSelectionRect(left, top, right, bottom); |
| 941 if (mPendingShowActionMode) { | 938 if (mPendingShowActionMode) { |
| 942 showActionModeOrClearOnFailure(); | 939 showActionModeOrClearOnFailure(); |
| 943 } else { | 940 } else { |
| 944 invalidateContentRect(); | 941 invalidateContentRect(); |
| 945 } | 942 } |
| 946 break; | 943 break; |
| 947 | 944 |
| 948 case SelectionEventType.SELECTION_HANDLES_CLEARED: | 945 case SelectionEventType.SELECTION_HANDLES_CLEARED: |
| 949 mHasSelection = false; | 946 mHasSelection = false; |
| 950 mUnselectAllOnDismiss = false; | 947 mUnselectAllOnDismiss = false; |
| 951 mSelectionRect.setEmpty(); | 948 mSelectionRect.setEmpty(); |
| 952 finishActionMode(); | 949 finishActionMode(); |
| 953 break; | 950 break; |
| 954 | 951 |
| 955 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: | 952 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: |
| 956 hideActionMode(true); | 953 hideActionMode(true); |
| 957 break; | 954 break; |
| 958 | 955 |
| 959 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: | 956 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: |
| 960 if (mSelectionClient == null || !mSelectionClient.sendsSelection PopupUpdates()) { | 957 if (mSelectionClient == null || !mSelectionClient.sendsSelection PopupUpdates()) { |
| 961 hideActionMode(false); | 958 hideActionMode(false); |
| 962 } | 959 } |
| 963 // Otherwise rely on |mSelectionClient| sending a classification request and the | 960 // Otherwise rely on |mSelectionClient| sending a classification request and the |
| 964 // request always calling onClassified() callback. | 961 // request always calling onClassified() callback. |
| 965 break; | 962 break; |
| 966 | 963 |
| 967 case SelectionEventType.INSERTION_HANDLE_SHOWN: | 964 case SelectionEventType.INSERTION_HANDLE_SHOWN: |
| 968 mSelectionRect.set(left, top, right, bottom); | 965 setSelectionRect(left, top, right, bottom); |
| 969 mIsInsertion = true; | 966 mIsInsertion = true; |
| 970 break; | 967 break; |
| 971 | 968 |
| 972 case SelectionEventType.INSERTION_HANDLE_MOVED: | 969 case SelectionEventType.INSERTION_HANDLE_MOVED: |
| 973 mSelectionRect.set(left, top, right, bottom); | 970 setSelectionRect(left, top, right, bottom); |
| 974 if (!mScrollInProgress && isPastePopupShowing()) { | 971 if (!mScrollInProgress && isPastePopupShowing()) { |
| 975 showPastePopup(xAnchor, yAnchor); | 972 showPastePopup(); |
| 976 } else { | 973 } else { |
| 977 destroyPastePopup(); | 974 destroyPastePopup(); |
| 978 } | 975 } |
| 979 break; | 976 break; |
| 980 | 977 |
| 981 case SelectionEventType.INSERTION_HANDLE_TAPPED: | 978 case SelectionEventType.INSERTION_HANDLE_TAPPED: |
| 982 if (mWasPastePopupShowingOnInsertionDragStart) { | 979 if (mWasPastePopupShowingOnInsertionDragStart) { |
| 983 destroyPastePopup(); | 980 destroyPastePopup(); |
| 984 } else { | 981 } else { |
| 985 mWebContents.showContextMenuAtPoint(xAnchor, yAnchor); | 982 mWebContents.showContextMenuAtPoint( |
| 983 mSelectionRect.centerX(), mSelectionRect.centerY()); | |
|
boliu
2017/05/04 20:36:11
from the xAnchorPix computation below, seems like
amaralp
2017/05/05 01:14:32
Changed it back to bottom left and css pixel
| |
| 986 } | 984 } |
| 987 mWasPastePopupShowingOnInsertionDragStart = false; | 985 mWasPastePopupShowingOnInsertionDragStart = false; |
| 988 break; | 986 break; |
| 989 | 987 |
| 990 case SelectionEventType.INSERTION_HANDLE_CLEARED: | 988 case SelectionEventType.INSERTION_HANDLE_CLEARED: |
| 991 destroyPastePopup(); | 989 destroyPastePopup(); |
| 992 mIsInsertion = false; | 990 mIsInsertion = false; |
| 993 mSelectionRect.setEmpty(); | 991 mSelectionRect.setEmpty(); |
| 994 break; | 992 break; |
| 995 | 993 |
| 996 case SelectionEventType.INSERTION_HANDLE_DRAG_STARTED: | 994 case SelectionEventType.INSERTION_HANDLE_DRAG_STARTED: |
| 997 mWasPastePopupShowingOnInsertionDragStart = isPastePopupShowing( ); | 995 mWasPastePopupShowingOnInsertionDragStart = isPastePopupShowing( ); |
| 998 destroyPastePopup(); | 996 destroyPastePopup(); |
| 999 break; | 997 break; |
| 1000 | 998 |
| 1001 case SelectionEventType.INSERTION_HANDLE_DRAG_STOPPED: | 999 case SelectionEventType.INSERTION_HANDLE_DRAG_STOPPED: |
| 1002 if (mWasPastePopupShowingOnInsertionDragStart) { | 1000 if (mWasPastePopupShowingOnInsertionDragStart) { |
| 1003 mWebContents.showContextMenuAtPoint(xAnchor, yAnchor); | 1001 mWebContents.showContextMenuAtPoint( |
| 1002 mSelectionRect.centerX(), mSelectionRect.centerY()); | |
| 1004 } | 1003 } |
| 1005 mWasPastePopupShowingOnInsertionDragStart = false; | 1004 mWasPastePopupShowingOnInsertionDragStart = false; |
| 1006 break; | 1005 break; |
| 1007 | 1006 |
| 1008 default: | 1007 default: |
| 1009 assert false : "Invalid selection event type."; | 1008 assert false : "Invalid selection event type."; |
| 1010 } | 1009 } |
| 1011 | 1010 |
| 1012 if (mSelectionClient != null) { | 1011 if (mSelectionClient != null) { |
| 1013 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); | 1012 final float deviceScale = mRenderCoordinates.getDeviceScaleFactor(); |
| 1014 int xAnchorPix = (int) (xAnchor * deviceScale); | 1013 int xAnchorPix = (int) (mSelectionRect.left * deviceScale); |
|
boliu
2017/05/04 20:36:11
isn't mSelectionRect already multiplied by scale?
amaralp
2017/05/05 01:14:32
Yeah, made it not be multiplied by scale.
| |
| 1015 int yAnchorPix = (int) (yAnchor * deviceScale); | 1014 int yAnchorPix = (int) (mSelectionRect.bottom * deviceScale); |
| 1016 mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix) ; | 1015 mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix) ; |
| 1017 } | 1016 } |
| 1018 } | 1017 } |
| 1019 | 1018 |
| 1020 /** | 1019 /** |
| 1021 * Clears the current text selection. Note that we will try to move cursor t o selection | 1020 * Clears the current text selection. Note that we will try to move cursor t o selection |
| 1022 * end if applicable. | 1021 * end if applicable. |
| 1023 */ | 1022 */ |
| 1024 void clearSelection() { | 1023 void clearSelection() { |
| 1025 if (mWebContents == null || !isActionModeSupported()) return; | 1024 if (mWebContents == null || !isActionModeSupported()) return; |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1134 if (mPendingShowActionMode) return; | 1133 if (mPendingShowActionMode) return; |
| 1135 } | 1134 } |
| 1136 | 1135 |
| 1137 // Rely on this method to clear |mHidden| and unhide the action mode . | 1136 // Rely on this method to clear |mHidden| and unhide the action mode . |
| 1138 showActionModeOrClearOnFailure(); | 1137 showActionModeOrClearOnFailure(); |
| 1139 } | 1138 } |
| 1140 }; | 1139 }; |
| 1141 | 1140 |
| 1142 private native void nativeInit(WebContents webContents); | 1141 private native void nativeInit(WebContents webContents); |
| 1143 } | 1142 } |
| OLD | NEW |