Bug 1294799 - Disable the autoclosing of the bookmark popup if there is any interaction with it such as mousedown or keypress. r?mak draft
authorJared Wein <jwein@mozilla.com>
Fri, 10 Feb 2017 11:11:57 -0500
changeset 481881 db8124bf692345cb5f3c1d52353859f1c5f32d4b
parent 481880 511093a0d82882d4b20f91b0287ad4b610c5225f
child 545304 4e37c3a035649c384ff5e91830a172169685f7f3
push id44937
push userjwein@mozilla.com
push dateFri, 10 Feb 2017 16:13:19 +0000
reviewersmak
bugs1294799
milestone54.0a1
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;
-})
+});