Bug 1546851 - Ensure that the context menu is closed when performing a paste. r=geckoview-reviewers,droeh
authorEmily Toop <etoop@mozilla.com>
Thu, 02 May 2019 15:06:58 +0000
changeset 531130 6057bafee921747d5415ef5e99d8954e00f05b8d
parent 531129 7a92af28d8e12b106e9e1738cbca57c8d88983ac
child 531131 f5f8345a5c873fc99459f53d2eb14247cbc5473a
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeckoview-reviewers, droeh
bugs1546851
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1546851 - Ensure that the context menu is closed when performing a paste. r=geckoview-reviewers,droeh This issue was caused by the updating of the caret position after the paste occurs firing an `updateposition` event resulting in an `GeckoView:ShowSelectionAction` message to reposition context menu without any attempt to close the context menu. The solution is to hide the context menu before the repositioning of the caret occurs such that the `updateposition` event doesn't trigger a redisplay. I tried several versions of this trying to trigger the menu close from inside `GeckoViewSelectionActionDelegate`, including making the call to `docShell.doCommand("cmd_paste");` asynchronous and firing `ACTION_HIDE` before firing `ACTION_PASTE`, but the result is always the same - one of the events is processed before the other and so the second event is rejected as a stale response. Therefore we are firing the `pagehide` from inside the code that performs the paste. This has to be done before the paste occurs otherwise the `updateposition` event is fired before the page hide event and the context menu redisplays before being hidden. Differential Revision: https://phabricator.services.mozilla.com/D29665
mobile/android/chrome/geckoview/GeckoViewSelectionActionChild.js
--- a/mobile/android/chrome/geckoview/GeckoViewSelectionActionChild.js
+++ b/mobile/android/chrome/geckoview/GeckoViewSelectionActionChild.js
@@ -29,17 +29,17 @@ class GeckoViewSelectionActionChild exte
       id: "org.mozilla.geckoview.COPY",
       predicate: e => !e.collapsed && !this._isPasswordField(e),
       perform: _ => docShell.doCommand("cmd_copy"),
     }, {
       id: "org.mozilla.geckoview.PASTE",
       predicate: e => e.selectionEditable &&
                       Services.clipboard.hasDataMatchingFlavors(
                           ["text/unicode"], 1, Ci.nsIClipboard.kGlobalClipboard),
-      perform: _ => docShell.doCommand("cmd_paste"),
+      perform: _ => this._performPaste(),
     }, {
       id: "org.mozilla.geckoview.DELETE",
       predicate: e => !e.collapsed && e.selectionEditable,
       perform: _ => docShell.doCommand("cmd_delete"),
     }, {
       id: "org.mozilla.geckoview.COLLAPSE_TO_START",
       predicate: e => !e.collapsed && e.selectionEditable,
       perform: e => docShell.doCommand("cmd_moveLeft"),
@@ -53,16 +53,21 @@ class GeckoViewSelectionActionChild exte
       perform: e => docShell.doCommand("cmd_selectNone"),
     }, {
       id: "org.mozilla.geckoview.SELECT_ALL",
       predicate: e => e.reason !== "longpressonemptycontent",
       perform: e => docShell.doCommand("cmd_selectAll"),
     }];
   }
 
+  _performPaste() {
+    this.handleEvent({type: "pagehide"});
+    docShell.doCommand("cmd_paste");
+  }
+
   _isPasswordField(aEvent) {
     if (!aEvent.selectionEditable) {
       return false;
     }
 
     const win = aEvent.target.defaultView;
     const focus = aEvent.target.activeElement;
     return win && win.HTMLInputElement &&
@@ -185,18 +190,16 @@ class GeckoViewSelectionActionChild exte
         // Don't call again if we're already active and things haven't changed.
         return;
       }
 
       msg.seqNo = ++this._seqNo;
       this._isActive = true;
       this._previousMessage = JSON.stringify(msg);
 
-      // This event goes to GeckoViewSelectionAction.jsm, where the data is
-      // further transformed and then sent to GeckoSession.
       this.eventDispatcher.sendRequest(msg, {
         onSuccess: response => {
           if (response.seqNo !== this._seqNo) {
             // Stale action.
             warn `Stale response ${response.id}`;
             return;
           }
           let action = actions.find(action => action.id === response.id);