Bug 1451780 - Add password field detection for selection actions; r?esawin draft
authorJim Chen <nchen@mozilla.com>
Mon, 09 Apr 2018 10:21:10 -0400
changeset 779237 99cd0c72d41b742a77ba2ae16c623230d60fe0f0
parent 779146 30d72755b1749953d438199456f1524ce84ab5e5
child 779242 7a8675b1bf5517f521c2f0413900012c4186635c
push id105712
push userbmo:nchen@mozilla.com
push dateMon, 09 Apr 2018 14:21:34 +0000
reviewersesawin
bugs1451780
milestone61.0a1
Bug 1451780 - Add password field detection for selection actions; r?esawin Detect password fields and don't show cut/copy actions for passwords. MozReview-Commit-ID: FA8V0LCkP3H
mobile/android/chrome/geckoview/GeckoViewSelectionActionContent.js
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
--- a/mobile/android/chrome/geckoview/GeckoViewSelectionActionContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewSelectionActionContent.js
@@ -25,21 +25,21 @@ class GeckoViewSelectionActionContent ex
     super(aModuleName, aMessageManager);
 
     this._seqNo = 0;
     this._isActive = false;
     this._previousMessage = {};
 
     this._actions = [{
       id: "org.mozilla.geckoview.CUT",
-      predicate: e => !e.collapsed && e.selectionEditable,
+      predicate: e => !e.collapsed && e.selectionEditable && !this._isPasswordField(e),
       perform: _ => this._domWindowUtils.sendContentCommandEvent("cut"),
     }, {
       id: "org.mozilla.geckoview.COPY",
-      predicate: e => !e.collapsed,
+      predicate: e => !e.collapsed && !this._isPasswordField(e),
       perform: _ => this._domWindowUtils.sendContentCommandEvent("copy"),
     }, {
       id: "org.mozilla.geckoview.PASTE",
       predicate: e => e.selectionEditable &&
                       Services.clipboard.hasDataMatchingFlavors(
                           ["text/unicode"], 1, Ci.nsIClipboard.kGlobalClipboard),
       perform: _ => this._domWindowUtils.sendContentCommandEvent("paste"),
     }, {
@@ -65,16 +65,28 @@ class GeckoViewSelectionActionContent ex
     }];
   }
 
   get _domWindowUtils() {
     return content.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
   }
 
+  _isPasswordField(aEvent) {
+    if (!aEvent.selectionEditable) {
+      return false;
+    }
+
+    const win = aEvent.target.defaultView;
+    const focus = aEvent.target.activeElement;
+    return win && win.HTMLInputElement &&
+           focus instanceof win.HTMLInputElement &&
+           !focus.mozIsTextField(/* excludePassword */ true);
+  }
+
   _getSelectionController(aEvent) {
     if (aEvent.selectionEditable) {
       const focus = aEvent.target.activeElement;
       if (focus instanceof Ci.nsIDOMNSEditableElement && focus.editor) {
         return focus.editor.selectionController;
       }
     }
 
@@ -131,18 +143,17 @@ class GeckoViewSelectionActionContent ex
    * (mozcaretstatechanged) events.
    */
   handleEvent(aEvent) {
     let reason = aEvent.reason;
 
     if (this._isActive && !aEvent.caretVisible) {
       // For mozcaretstatechanged, "visibilitychange" means the caret is hidden.
       reason = "visibilitychange";
-    } else if (this._isActive &&
-               !aEvent.collapsed &&
+    } else if (!aEvent.collapsed &&
                !aEvent.selectionVisible) {
       reason = "invisibleselection";
     } else if (aEvent.selectionEditable &&
                aEvent.collapsed &&
                reason !== "longpressonemptycontent" &&
                reason !== "taponcaret") {
       // Don't show selection actions when merely focusing on an editor or
       // repositioning the cursor. Wait until long press or the caret is tapped
@@ -156,23 +167,25 @@ class GeckoViewSelectionActionContent ex
          "releasecaret",
          "taponcaret",
          "updateposition"].includes(reason)) {
 
       const actions = this._actions.filter(
           action => action.predicate.call(this, aEvent));
 
       const offset = this._getFrameOffset(aEvent);
+      const password = this._isPasswordField(aEvent);
 
       const msg = {
         type: "GeckoView:ShowSelectionAction",
         seqNo: this._seqNo,
         collapsed: aEvent.collapsed,
         editable: aEvent.selectionEditable,
-        selection: aEvent.selectedTextContent,
+        password,
+        selection: password ? "" : aEvent.selectedTextContent,
         clientRect: !aEvent.boundingClientRect ? null : {
           left: aEvent.boundingClientRect.left + offset.left,
           top: aEvent.boundingClientRect.top + offset.top,
           right: aEvent.boundingClientRect.right + offset.left,
           bottom: aEvent.boundingClientRect.bottom + offset.top,
         },
         actions: actions.map(action => action.id),
       };
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -1807,16 +1807,20 @@ public class GeckoSession extends LayerS
          * The selection is collapsed at a single position.
          */
         final int FLAG_IS_COLLAPSED = 1;
         /**
          * The selection is inside editable content such as an input element or
          * contentEditable node.
          */
         final int FLAG_IS_EDITABLE = 2;
+        /**
+         * The selection is inside a password field.
+         */
+        final int FLAG_IS_PASSWORD = 4;
 
         @StringDef({ACTION_CUT,
                     ACTION_COPY,
                     ACTION_DELETE,
                     ACTION_PASTE,
                     ACTION_SELECT_ALL,
                     ACTION_UNSELECT,
                     ACTION_COLLAPSE_TO_START,
@@ -1882,17 +1886,19 @@ public class GeckoSession extends LayerS
              * coordinates.
              */
             public final RectF clientRect;
 
             /* package */ Selection(final GeckoBundle bundle) {
                 flags = (bundle.getBoolean("collapsed") ?
                          SelectionActionDelegate.FLAG_IS_COLLAPSED : 0) |
                         (bundle.getBoolean("editable") ?
-                         SelectionActionDelegate.FLAG_IS_EDITABLE : 0);
+                         SelectionActionDelegate.FLAG_IS_EDITABLE : 0) |
+                        (bundle.getBoolean("password") ?
+                         SelectionActionDelegate.FLAG_IS_PASSWORD : 0);
                 text = bundle.getString("selection");
 
                 final GeckoBundle rectBundle = bundle.getBundle("clientRect");
                 if (rectBundle == null) {
                     clientRect = null;
                 } else {
                     clientRect = new RectF((float) rectBundle.getDouble("left"),
                                            (float) rectBundle.getDouble("top"),