Bug 1294799 - Disable the autoclosing of the bookmark popup if there is any interaction with it such as mousedown or keypress. r=mak
authorJared Wein <jwein@mozilla.com>
Fri, 10 Feb 2017 11:11:57 -0500
changeset 342254 6ab46d343895f817a251cbe8e1d271df5ccf47af
parent 342253 45e8e39788c822f4c4ade9352a380f72e3e4b9f1
child 342255 2e61f82dd80d8d88a8512889c80b7123b48762eb
push id31346
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 22:33:24 +0000
treeherdermozilla-central@7b9d9e4a82a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1294799
milestone54.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 1294799 - Disable the autoclosing of the bookmark popup if there is any interaction with it such as mousedown or keypress. r=mak MozReview-Commit-ID: JRo5ZVu0uFD
browser/base/content/browser-places.js
browser/base/content/test/general/browser_bookmark_popup.js
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -4,29 +4,34 @@
 
 var StarUI = {
   _itemId: -1,
   uri: null,
   _batching: false,
   _isNewBookmark: false,
   _isComposing: false,
   _autoCloseTimer: 0,
+  // The autoclose timer is diasbled if the user interacts with the
+  // popup, such as making a change through typing or clicking on
+  // the popup.
+  _autoCloseTimerEnabled: true,
 
   _element(aID) {
     return document.getElementById(aID);
   },
 
   // Edit-bookmark panel
   get panel() {
     delete this.panel;
     var element = this._element("editBookmarkPanel");
     // initially the panel is hidden
     // to avoid impacting startup / new window performance
     element.hidden = false;
     element.addEventListener("keypress", this);
+    element.addEventListener("mousedown", this);
     element.addEventListener("mouseout", this);
     element.addEventListener("mousemove", this);
     element.addEventListener("compositionstart", this);
     element.addEventListener("compositionend", this);
     element.addEventListener("input", this);
     element.addEventListener("popuphidden", this);
     element.addEventListener("popupshown", this);
     return this.panel = element;
@@ -61,16 +66,18 @@ var StarUI = {
     });
   },
 
   // nsIDOMEventListener
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "mousemove":
         clearTimeout(this._autoCloseTimer);
+        // The autoclose timer is not disabled on generic mouseout
+        // because the user may not have actually interacted with the popup.
         break;
       case "popuphidden":
         clearTimeout(this._autoCloseTimer);
         if (aEvent.originalTarget == this.panel) {
           if (!this._element("editBookmarkPanelContent").hidden)
             this.quitEditMode();
 
           if (this._anchorToolbarButton) {
@@ -104,16 +111,17 @@ var StarUI = {
 
             PlacesTransactions.RemoveBookmarksForUrls([this._uriForRemoval])
                               .transact().catch(Cu.reportError);
           }
         }
         break;
       case "keypress":
         clearTimeout(this._autoCloseTimer);
+        this._autoCloseTimerEnabled = false;
 
         if (aEvent.defaultPrevented) {
           // The event has already been consumed inside of the panel.
           break;
         }
 
         switch (aEvent.keyCode) {
           case KeyEvent.DOM_VK_ESCAPE:
@@ -134,36 +142,42 @@ var StarUI = {
           case 0:
             let accessKey = document.getElementById("key_close");
             if (eventMatchesKey(aEvent, accessKey)) {
                 this.panel.hidePopup();
             }
             break;
         }
         break;
+      case "compositionend":
+        // After composition is committed, "mouseout" or something can set
+        // auto close timer.
+        this._isComposing = false;
+        break;
       case "compositionstart":
         if (aEvent.defaultPrevented) {
           // If the composition was canceled, nothing to do here.
           break;
         }
-        // During composition, panel shouldn't be hidden automatically.
+        this._isComposing = true;
+        // Explicit fall-through, during composition, panel shouldn't be
+        // hidden automatically.
+      case "input":
+        // Might have edited some text without keyboard events nor composition
+        // events. Fall-through to cancel auto close in such case.
+      case "mousedown":
         clearTimeout(this._autoCloseTimer);
-        this._isComposing = true;
-        break;
-      case "compositionend":
-        // After composition is committed, "mouseout" or something can set
-        // auto close timer.
-        this._isComposing = false;
-        break;
-      case "input":
-        // Might be edited some text without keyboard events nor composition
-        // events. Let's cancel auto close in such case.
-        clearTimeout(this._autoCloseTimer);
+        this._autoCloseTimerEnabled = false;
         break;
       case "mouseout":
+        if (!this._autoCloseTimerEnabled) {
+          // Don't autoclose the popup if the user has made a selection
+          // or keypress and then subsequently mouseout.
+          break;
+        }
         // Explicit fall-through
       case "popupshown":
         // Don't handle events for descendent elements.
         if (aEvent.target != aEvent.currentTarget) {
           break;
         }
         // auto-close if new and not interacted with
         if (this._isNewBookmark && !this._isComposing) {
@@ -174,16 +188,17 @@ var StarUI = {
             delay /= 10;
           }
           clearTimeout(this._autoCloseTimer);
           this._autoCloseTimer = setTimeout(() => {
             if (!this.panel.mozMatchesSelector(":hover")) {
               this.panel.hidePopup();
             }
           }, delay);
+          this._autoCloseTimerEnabled = true;
         }
         break;
     }
   },
 
   _overlayLoaded: false,
   _overlayLoading: false,
   showEditBookmarkPopup: Task.async(function* (aNode, aAnchorElement, aPosition, aIsNewBookmark) {
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -258,38 +258,33 @@ add_task(function* panel_shown_for_new_b
     popupHideFn() {
       EventUtils.synthesizeComposition({ type: "compositioncommitasis" });
       bookmarkPanel.hidePopup();
     },
     isBookmarkRemoved: false,
   });
 });
 
-add_task(function* panel_shown_for_new_bookmark_compositionend_mouseout_autoclose() {
+add_task(function* panel_shown_for_new_bookmark_compositionend_no_autoclose() {
   yield test_bookmarks_popup({
     isNewBookmark: true,
     popupShowFn() {
       bookmarkStar.click();
     },
     *popupEditFn() {
       let mouseMovePromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mousemove");
       EventUtils.synthesizeMouseAtCenter(bookmarkPanel, {type: "mousemove"});
       info("Waiting for mousemove event");
       yield mouseMovePromise;
       info("Got mousemove event");
 
       EventUtils.synthesizeComposition({ type: "compositioncommit", data: "committed text" });
     },
-    *popupHideFn() {
-      let mouseOutPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mouseout");
-      EventUtils.synthesizeMouse(bookmarkPanel, 0, 0, {type: "mouseout"});
-      EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
-      info("Waiting for mouseout event");
-      yield mouseOutPromise;
-      info("Got mouseout event, should autoclose now");
+    popupHideFn() {
+      bookmarkPanel.hidePopup();
     },
     shouldAutoClose: false,
     isBookmarkRemoved: false,
   });
 });
 
 add_task(function* contextmenu_new_bookmark_keypress_no_autoclose() {
   yield test_bookmarks_popup({
@@ -393,11 +388,43 @@ add_task(function* mouse_hovering_panel_
     shouldAutoClose: false,
     popupHideFn() {
       document.getElementById("editBookmarkPanelRemoveButton").click();
     },
     isBookmarkRemoved: true,
   });
 });
 
+add_task(function* ctrl_d_new_bookmark_mousedown_mouseout_no_autoclose() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn(browser) {
+      EventUtils.synthesizeKey("D", {accelKey: true}, window);
+    },
+    *popupEditFn() {
+      let mouseMovePromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mousemove");
+      EventUtils.synthesizeMouseAtCenter(bookmarkPanel, {type: "mousemove"});
+      info("Waiting for mousemove event");
+      yield mouseMovePromise;
+      info("Got mousemove event");
+
+      yield new Promise(resolve => setTimeout(resolve, 400));
+      is(bookmarkPanel.state, "open", "Panel should still be open on mousemove");
+
+      EventUtils.synthesizeMouseAtCenter(bookmarkPanelTitle, {button: 1, type: "mousedown"});
+
+      let mouseOutPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mouseout");
+      EventUtils.synthesizeMouse(bookmarkPanel, 0, 0, {type: "mouseout"});
+      EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
+      info("Waiting for mouseout event");
+      yield mouseOutPromise;
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      document.getElementById("editBookmarkPanelRemoveButton").click();
+    },
+    isBookmarkRemoved: true,
+  });
+});
+
 registerCleanupFunction(function() {
   delete StarUI._closePanelQuickForTesting;
-})
+});