Bug 1219794 - Show the bookmark popup for new bookmarks and autoclose if no interaction. r=mak
☠☠ backed out by 7edccea56247 ☠ ☠
authorJared Wein <jwein@mozilla.com>
Thu, 18 Feb 2016 15:36:22 -0500
changeset 321033 b7450f64aa8754db12c5db879c809b6a13270015
parent 321032 0c1987d10ab8a89ebbfa7a7e6c2ed116eeef7d08
child 321034 6226f2e8a41b637c428862cf6931462bf59938ff
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1219794
milestone47.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 1219794 - Show the bookmark popup for new bookmarks and autoclose if no interaction. r=mak MozReview-Commit-ID: FSx0bB4KqpQ
browser/base/content/browser-places.js
browser/base/content/browser.xul
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_bookmark_popup.js
browser/locales/en-US/chrome/browser/browser.dtd
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -4,30 +4,35 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 //// StarUI
 
 var StarUI = {
   _itemId: -1,
   uri: null,
   _batching: false,
+  _isNewBookmark: false,
+  _autoCloseTimer: 0,
 
   _element: function(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, false);
+    element.addEventListener("mouseout", this, false);
+    element.addEventListener("mouseover", this, false);
     element.addEventListener("popuphidden", this, false);
-    element.addEventListener("keypress", this, false);
+    element.addEventListener("popupshown", this, false);
     return this.panel = element;
   },
 
   // Array of command elements to disable when the panel is opened.
   get _blockedCommands() {
     delete this._blockedCommands;
     return this._blockedCommands =
       ["cmd_close", "cmd_closeWindow"].map(id => this._element(id));
@@ -53,87 +58,110 @@ var StarUI = {
         elt.removeAttribute("disabled");
       elt.removeAttribute("wasDisabled");
     });
   },
 
   // nsIDOMEventListener
   handleEvent(aEvent) {
     switch (aEvent.type) {
+      case "mouseover":
+        clearTimeout(this._autoCloseTimer);
+        break;
       case "popuphidden":
+        clearTimeout(this._autoCloseTimer);
         if (aEvent.originalTarget == this.panel) {
           if (!this._element("editBookmarkPanelContent").hidden)
             this.quitEditMode();
 
           if (this._anchorToolbarButton) {
             this._anchorToolbarButton.removeAttribute("open");
             this._anchorToolbarButton = null;
           }
           this._restoreCommandsState();
           this._itemId = -1;
           if (this._batching)
             this.endBatch();
 
-          switch (this._actionOnHide) {
-            case "cancel": {
-              if (!PlacesUIUtils.useAsyncTransactions) {
+          if (this._uriForRemoval) {
+            if (this._isNewBookmark) {
+              if (!PlacesUtils.useAsyncTransactions) {
                 PlacesUtils.transactionManager.undoTransaction();
                 break;
               }
-              PlacesTransactions.undo().catch(Cu.reportError);
+              PlacesTransactions().undo().catch(Cu.reportError);
               break;
             }
-            case "remove": {
-              // Remove all bookmarks for the bookmark's url, this also removes
-              // the tags for the url.
-              if (!PlacesUIUtils.useAsyncTransactions) {
-                let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval);
-                for (let itemId of itemIds) {
-                  let txn = new PlacesRemoveItemTransaction(itemId);
-                  PlacesUtils.transactionManager.doTransaction(txn);
-                }
-                break;
+            // Remove all bookmarks for the bookmark's url, this also removes
+            // the tags for the url.
+            if (!PlacesUIUtils.useAsyncTransactions) {
+              let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval);
+              for (let itemId of itemIds) {
+                let txn = new PlacesRemoveItemTransaction(itemId);
+                PlacesUtils.transactionManager.doTransaction(txn);
               }
-
-              PlacesTransactions.RemoveBookmarksForUrls(this._uriForRemoval)
-                                .transact().catch(Cu.reportError);
               break;
             }
+
+            PlacesTransactions.RemoveBookmarksForUrls([this._uriForRemoval])
+                              .transact().catch(Cu.reportError);
           }
-          this._actionOnHide = "";
         }
         break;
       case "keypress":
+        clearTimeout(this._autoCloseTimer);
+
         if (aEvent.defaultPrevented) {
           // The event has already been consumed inside of the panel.
           break;
         }
+
         switch (aEvent.keyCode) {
           case KeyEvent.DOM_VK_ESCAPE:
-            if (!this._element("editBookmarkPanelContent").hidden)
-              this.cancelButtonOnCommand();
+            this.panel.hidePopup();
             break;
           case KeyEvent.DOM_VK_RETURN:
             if (aEvent.target.classList.contains("expander-up") ||
                 aEvent.target.classList.contains("expander-down") ||
                 aEvent.target.id == "editBMPanel_newFolderButton")  {
               //XXX Why is this necessary? The defaultPrevented check should
               //    be enough.
               break;
             }
             this.panel.hidePopup();
             break;
         }
         break;
+      case "mouseout": {
+        // Don't handle events for descendent elements.
+        if (aEvent.target != aEvent.currentTarget) {
+          break;
+        }
+        // Explicit fall-through
+      }
+      case "popupshown":
+        // auto-close if new and not interacted with
+        if (this._isNewBookmark) {
+          // 3500ms matches the timeout that Pocket uses in
+          // browser/extensions/pocket/content/panels/js/saved.js
+          let delay = 3500;
+          if (this._closePanelQuickForTesting) {
+            delay /= 10;
+          }
+          this._autoCloseTimer = setTimeout(() => this.panel.hidePopup(), delay, this);
+        }
+        break;
     }
   },
 
   _overlayLoaded: false,
   _overlayLoading: false,
-  showEditBookmarkPopup: Task.async(function* (aNode, aAnchorElement, aPosition) {
+  showEditBookmarkPopup: Task.async(function* (aNode, aAnchorElement, aPosition, aIsNewBookmark) {
+    this._isNewBookmark = aIsNewBookmark;
+    this._uriForRemoval = "";
     // TODO: Deprecate this once async transactions are enabled and the legacy
     // transactions code is gone (bug 1131491) - we don't want addons to to use
     // the  completeNodeLikeObjectForItemId, so it's better if they keep passing
     // the item-id for now).
     if (typeof(aNode) == "number") {
       let itemId = aNode;
       if (PlacesUIUtils.useAsyncTransactions) {
         let guid = yield PlacesUtils.promiseItemGuid(itemId);
@@ -172,36 +200,28 @@ var StarUI = {
       }).bind(this)
     );
   }),
 
   _doShowEditBookmarkPanel: Task.async(function* (aNode, aAnchorElement, aPosition) {
     if (this.panel.state != "closed")
       return;
 
-    this._blockCommands(); // un-done in the popuphiding handler
+    this._blockCommands(); // un-done in the popuphidden handler
 
-    // Set panel title:
-    // if we are batching, i.e. the bookmark has been added now,
-    // then show Page Bookmarked, else if the bookmark did already exist,
-    // we are about editing it, then use Edit This Bookmark.
     this._element("editBookmarkPanelTitle").value =
-      this._batching ?
+      this._isNewBookmark ?
         gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
         gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle");
 
-    // No description; show the Done, Cancel;
+    // No description; show the Done, Remove;
     this._element("editBookmarkPanelDescription").textContent = "";
     this._element("editBookmarkPanelBottomButtons").hidden = false;
     this._element("editBookmarkPanelContent").hidden = false;
 
-    // The remove button is shown only if we're not already batching, i.e.
-    // if the cancel button/ESC does not remove the bookmark.
-    this._element("editBookmarkPanelRemoveButton").hidden = this._batching;
-
     // The label of the remove button differs if the URI is bookmarked
     // multiple times.
     let bookmarks = PlacesUtils.getBookmarksForURI(gBrowser.currentURI);
     let forms = gNavigatorBundle.getString("editBookmark.removeBookmarks.label");
     let label = PluralForm.get(bookmarks.length, forms).replace("#1", bookmarks.length);
     this._element("editBookmarkPanelRemoveButton").label = label;
 
     // unset the unstarred state, if set
@@ -245,24 +265,18 @@ var StarUI = {
   },
 
   quitEditMode: function SU_quitEditMode() {
     this._element("editBookmarkPanelContent").hidden = true;
     this._element("editBookmarkPanelBottomButtons").hidden = true;
     gEditItemOverlay.uninitPanel(true);
   },
 
-  cancelButtonOnCommand: function SU_cancelButtonOnCommand() {
-    this._actionOnHide = "cancel";
-    this.panel.hidePopup(true);
-  },
-
   removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
     this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
-    this._actionOnHide = "remove";
     this.panel.hidePopup();
   },
 
   // Matching the way it is used in the Library, editBookmarkOverlay implements
   // an instant-apply UI, having no batched-Undo/Redo support.
   // However, in this context (the Star UI) we have a Cancel button whose
   // expected behavior is to undo all the operations done in the panel.
   // Sometime in the future this needs to be reimplemented using a
@@ -320,37 +334,38 @@ var PlacesCommandHook = {
   bookmarkPage: Task.async(function* (aBrowser, aParent, aShowEditUI) {
     if (PlacesUIUtils.useAsyncTransactions) {
       yield this._bookmarkPagePT(aBrowser, aParent, aShowEditUI);
       return;
     }
 
     var uri = aBrowser.currentURI;
     var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
-    if (itemId == -1) {
+    let isNewBookmark = itemId == -1;
+    if (isNewBookmark) {
       // Bug 1148838 - Make this code work for full page plugins.
       var title;
       var description;
       var charset;
 
       let docInfo = yield this._getPageDetails(aBrowser);
 
       try {
         title = docInfo.isErrorPage ? PlacesUtils.history.getPageTitle(uri)
                                     : aBrowser.contentTitle;
         title = title || uri.spec;
         description = docInfo.description;
         charset = aBrowser.characterSet;
       }
       catch (e) { }
 
-      if (aShowEditUI) {
-        // If we bookmark the page here (i.e. page was not "starred" already)
-        // but open right into the "edit" state, start batching here, so
-        // "Cancel" in that state removes the bookmark.
+      if (aShowEditUI && isNewBookmark) {
+        // If we bookmark the page here but open right into a cancelable
+        // state (i.e. new bookmark in Library), start batching here so
+        // all of the actions can be undone in a single undo step.
         StarUI.beginBatch();
       }
 
       var parent = aParent !== undefined ?
                    aParent : PlacesUtils.unfiledBookmarksFolderId;
       var descAnno = { name: PlacesUIUtils.DESCRIPTION_ANNO, value: description };
       var txn = new PlacesCreateBookmarkTransaction(uri, parent,
                                                     PlacesUtils.bookmarks.DEFAULT_INDEX,
@@ -371,34 +386,35 @@ var PlacesCommandHook = {
       return;
 
     // Try to dock the panel to:
     // 1. the bookmarks menu button
     // 2. the identity icon
     // 3. the content area
     if (BookmarkingUI.anchor) {
       StarUI.showEditBookmarkPopup(itemId, BookmarkingUI.anchor,
-                                   "bottomcenter topright");
+                                   "bottomcenter topright", isNewBookmark);
       return;
     }
 
     let identityIcon = document.getElementById("identity-icon");
     if (isElementVisible(identityIcon)) {
       StarUI.showEditBookmarkPopup(itemId, identityIcon,
-                                   "bottomcenter topright");
+                                   "bottomcenter topright", isNewBookmark);
     } else {
-      StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
+      StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap", isNewBookmark);
     }
   }),
 
   // TODO: Replace bookmarkPage code with this function once legacy
   // transactions are removed.
   _bookmarkPagePT: Task.async(function* (aBrowser, aParentId, aShowEditUI) {
     let url = new URL(aBrowser.currentURI.spec);
     let info = yield PlacesUtils.bookmarks.fetch({ url });
+    let isNewBookmark = !info;
     if (!info) {
       let parentGuid = aParentId !== undefined ?
                          yield PlacesUtils.promiseItemGuid(aParentId) :
                          PlacesUtils.bookmarks.unfiledGuid;
       info = { url, parentGuid };
       // Bug 1148838 - Make this code work for full page plugins.
       let description = null;
       let charset = null;
@@ -412,20 +428,20 @@ var PlacesCommandHook = {
         info.title = info.title || url.href;
         description = docInfo.description;
         charset = aBrowser.characterSet;
       }
       catch (e) {
         Components.utils.reportError(e);
       }
 
-      if (aShowEditUI) {
-        // If we bookmark the page here (i.e. page was not "starred" already)
-        // but open right into the "edit" state, start batching here, so
-        // "Cancel" in that state removes the bookmark.
+      if (aShowEditUI && isNewBookmark) {
+        // If we bookmark the page here but open right into a cancelable
+        // state (i.e. new bookmark in Library), start batching here so
+        // all of the actions can be undone in a single undo step.
         StarUI.beginBatch();
       }
 
       if (description) {
         info.annotations = [{ name: PlacesUIUtils.DESCRIPTION_ANNO
                             , value: description }];
       }
 
@@ -447,26 +463,26 @@ var PlacesCommandHook = {
     let node = yield PlacesUIUtils.promiseNodeLikeFromFetchInfo(info);
 
     // Try to dock the panel to:
     // 1. the bookmarks menu button
     // 2. the identity icon
     // 3. the content area
     if (BookmarkingUI.anchor) {
       StarUI.showEditBookmarkPopup(node, BookmarkingUI.anchor,
-                                   "bottomcenter topright");
+                                   "bottomcenter topright", isNewBookmark);
       return;
     }
 
     let identityIcon = document.getElementById("identity-icon");
     if (isElementVisible(identityIcon)) {
       StarUI.showEditBookmarkPopup(node, identityIcon,
-                                   "bottomcenter topright");
+                                   "bottomcenter topright", isNewBookmark);
     } else {
-      StarUI.showEditBookmarkPopup(node, aBrowser, "overlap");
+      StarUI.showEditBookmarkPopup(node, aBrowser, "overlap", isNewBookmark);
     }
   }),
 
   _getPageDetails(browser) {
     return new Promise(resolve => {
       let mm = browser.messageManager;
       mm.addMessageListener("Bookmarks:GetPageDetails:Result", function listener(msg) {
         mm.removeMessageListener("Bookmarks:GetPageDetails:Result", listener);
@@ -1698,29 +1714,25 @@ var BookmarkingUI = {
 
     if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) {
       this._showSubview();
       return;
     }
     let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
                                .forWindow(window);
     if (widget.overflowed) {
-      // Allow to close the panel if the page is already bookmarked, cause
-      // we are going to open the edit bookmark panel.
-      if (isBookmarked)
-        widget.node.removeAttribute("closemenu");
-      else
-        widget.node.setAttribute("closemenu", "none");
+      // Close the overflow panel because the Edit Bookmark panel will appear.
+      widget.node.removeAttribute("closemenu");
     }
 
     // Ignore clicks on the star if we are updating its state.
     if (!this._pendingStmt) {
       if (!isBookmarked)
         this._showBookmarkedNotification();
-      PlacesCommandHook.bookmarkCurrentPage(isBookmarked);
+      PlacesCommandHook.bookmarkCurrentPage(true);
     }
   },
 
   onCurrentPageContextPopupShowing() {
     this._updateBookmarkPageMenuItem();
   },
 
   handleEvent: function BUI_handleEvent(aEvent) {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -175,41 +175,35 @@
            aria-labelledby="editBookmarkPanelTitle">
       <row id="editBookmarkPanelHeader" align="center" hidden="true">
         <vbox align="center">
           <image id="editBookmarkPanelStarIcon"/>
         </vbox>
         <vbox>
           <label id="editBookmarkPanelTitle"/>
           <description id="editBookmarkPanelDescription"/>
-          <hbox>
-            <button id="editBookmarkPanelRemoveButton"
-                    class="editBookmarkPanelHeaderButton"
-                    oncommand="StarUI.removeBookmarkButtonCommand();"
-                    accesskey="&editBookmark.removeBookmark.accessKey;"/>
-          </hbox>
         </vbox>
       </row>
       <vbox id="editBookmarkPanelContent" flex="1" hidden="true"/>
       <hbox id="editBookmarkPanelBottomButtons" pack="end">
 #ifndef XP_UNIX
         <button id="editBookmarkPanelDoneButton"
                 class="editBookmarkPanelBottomButton"
                 label="&editBookmark.done.label;"
                 default="true"
                 oncommand="StarUI.panel.hidePopup();"/>
-        <button id="editBookmarkPanelDeleteButton"
+        <button id="editBookmarkPanelRemoveButton"
                 class="editBookmarkPanelBottomButton"
-                label="&editBookmark.cancel.label;"
-                oncommand="StarUI.cancelButtonOnCommand();"/>
+                oncommand="StarUI.removeBookmarkButtonCommand();"
+                accesskey="&editBookmark.removeBookmark.accessKey;"/>
 #else
-        <button id="editBookmarkPanelDeleteButton"
+        <button id="editBookmarkPanelRemoveButton"
                 class="editBookmarkPanelBottomButton"
-                label="&editBookmark.cancel.label;"
-                oncommand="StarUI.cancelButtonOnCommand();"/>
+                oncommand="StarUI.removeBookmarkButtonCommand();"
+                accesskey="&editBookmark.removeBookmark.accessKey;"/>
         <button id="editBookmarkPanelDoneButton"
                 class="editBookmarkPanelBottomButton"
                 label="&editBookmark.done.label;"
                 default="true"
                 oncommand="StarUI.panel.hidePopup();"/>
 #endif
       </hbox>
     </panel>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -146,16 +146,17 @@ skip-if = e10s # Bug 1101993 - times out
 [browser_autocomplete_no_title.js]
 [browser_autocomplete_autoselect.js]
 [browser_autocomplete_oldschool_wrap.js]
 [browser_autocomplete_tag_star_visibility.js]
 [browser_backButtonFitts.js]
 skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
 [browser_beforeunload_duplicate_dialogs.js]
 [browser_blob-channelname.js]
+[browser_bookmark_popup.js]
 [browser_bookmark_titles.js]
 skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
 [browser_bug304198.js]
 [browser_bug321000.js]
 skip-if = true # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
 [browser_bug329212.js]
 skip-if = e10s # Bug 1236991 - Update or remove tests that use fillInPageTooltip
 [browser_bug331772_xul_tooltiptext_in_html.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -0,0 +1,246 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Test opening and closing the bookmarks panel.
+ */
+
+let bookmarkPanel = document.getElementById("editBookmarkPanel");
+let bookmarkStar = document.getElementById("bookmarks-menu-button");
+let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
+
+StarUI._closePanelQuickForTesting = true;
+Services.prefs.setBoolPref("browser.bookmarks.closePanelQuickForTesting", true);
+
+function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
+                                shouldAutoClose, popupHideFn, isBookmarkRemoved}) {
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home");
+  try {
+    if (!isNewBookmark) {
+      yield PlacesUtils.bookmarks.insert({
+        parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+        url: "about:home",
+        title: "Home Page"
+      });
+    }
+
+    is(bookmarkStar.hasAttribute("starred"), !isNewBookmark,
+       "Page should only be starred prior to popupshown if editing bookmark");
+    let shownPromise = promisePopupShown(bookmarkPanel);
+    yield popupShowFn(tab.linkedBrowser);
+    yield shownPromise;
+
+    if (popupEditFn) {
+      yield popupEditFn();
+    }
+    let bookmarks = [];
+    yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm));
+    is(bookmarks.length, 1, "Only one bookmark should exist");
+    is(bookmarkStar.getAttribute("starred"), "true", "Page is starred");
+    is(bookmarkPanel.state, "open", "Check that panel state is 'open'");
+    is(bookmarkPanelTitle.value,
+      isNewBookmark ?
+        gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
+        gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"),
+      "title should match isEditingBookmark state");
+
+    if (!shouldAutoClose) {
+      yield new Promise(resolve => setTimeout(resolve, 400));
+    }
+
+    let hiddenPromise = promisePopupHidden(bookmarkPanel);
+    if (popupHideFn) {
+      yield popupHideFn();
+    }
+    yield hiddenPromise;
+    is(bookmarkStar.hasAttribute("starred"), !isBookmarkRemoved,
+       "Page is starred after closing");
+  } finally {
+    let bookmark = yield PlacesUtils.bookmarks.fetch({url: "about:home"});
+    is(!!bookmark, !isBookmarkRemoved,
+       "bookmark should not be present if a panel action should've removed it");
+    if (bookmark) {
+      yield PlacesUtils.bookmarks.remove(bookmark);
+    }
+    gBrowser.removeTab(tab);
+  }
+}
+
+add_task(function* panel_shown_for_new_bookmarks_and_autocloses() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_once_for_doubleclick_on_new_bookmark_star_and_autocloses() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, { clickCount: 2 },
+                                 window);
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_once_for_slow_doubleclick_on_new_bookmark_star_and_autocloses() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    *popupShowFn() {
+      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
+      yield new Promise(resolve => setTimeout(resolve, 500));
+      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_keyboardshortcut_on_new_bookmark_star_and_autocloses() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      EventUtils.synthesizeKey("D", {accelKey: true}, window);
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmarks_mouseover_mouseout() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    *popupEditFn() {
+      yield new Promise(resolve => {
+        EventUtils.synthesizeNativeMouseMove(bookmarkPanel, 0, 0, resolve, window);
+      });
+      yield new Promise(resolve => setTimeout(resolve, 400));
+      is(bookmarkPanel.state, "open", "Panel should still be open on mouseover");
+      yield new Promise(resolve => {
+        EventUtils.synthesizeNativeMouseMove(bookmarkStar, 0, 0, resolve, window);
+      });
+      info("Should autoclose now on mouseout");
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmark_no_autoclose_close_with_ESC() {
+  yield test_bookmarks_popup({
+    isNewBookmark: false,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_editing_no_autoclose_close_with_ESC() {
+  yield test_bookmarks_popup({
+    isNewBookmark: false,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmark_keypress_no_autoclose() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    popupEditFn() {
+      EventUtils.sendChar("VK_TAB", window);
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      bookmarkPanel.hidePopup();
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* contextmenu_new_bookmark_click_no_autoclose() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    *popupShowFn(browser) {
+      let contextMenu = document.getElementById("contentAreaContextMenu");
+      let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu,
+                                                          "popupshown");
+      let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu,
+                                                           "popuphidden");
+      yield BrowserTestUtils.synthesizeMouseAtCenter("body", {
+        type: "contextmenu",
+        button: 2
+      }, browser);
+      yield awaitPopupShown;
+      document.getElementById("context-bookmarkpage").click();
+      contextMenu.hidePopup();
+      yield awaitPopupHidden;
+    },
+    popupEditFn() {
+      bookmarkPanelTitle.click();
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      bookmarkPanel.hidePopup();
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* bookmarks_menu_new_bookmark_remove_bookmark() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn(browser) {
+      document.getElementById("menu_bookmarkThisPage").doCommand();
+    },
+    shouldAutoClose: true,
+    popupHideFn() {
+      document.getElementById("editBookmarkPanelRemoveButton").click();
+    },
+    isBookmarkRemoved: true,
+  });
+});
+
+add_task(function* ctrl_d_edit_bookmark_remove_bookmark() {
+  yield test_bookmarks_popup({
+    isNewBookmark: false,
+    popupShowFn(browser) {
+      EventUtils.synthesizeKey("D", {accelKey: true}, window);
+    },
+    shouldAutoClose: true,
+    popupHideFn() {
+      document.getElementById("editBookmarkPanelRemoveButton").click();
+    },
+    isBookmarkRemoved: true,
+  });
+});
+
+registerCleanupFunction(function() {
+  Services.prefs.clearUserPref("browser.bookmarks.closePanelQuickForTesting");
+  delete StarUI._closePanelQuickForTesting;
+})
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -739,17 +739,16 @@ you can use these alternative items. Oth
 <!ENTITY findAgainCmd.commandkey "g">
 <!ENTITY findAgainCmd.commandkey2 "VK_F3">
 <!ENTITY findSelectionCmd.commandkey "e">
 
 <!ENTITY spellAddDictionaries.label "Add Dictionaries…">
 <!ENTITY spellAddDictionaries.accesskey "A">
 
 <!ENTITY editBookmark.done.label                     "Done">
-<!ENTITY editBookmark.cancel.label                   "Cancel">
 <!ENTITY editBookmark.removeBookmark.accessKey       "R">
 
 <!ENTITY identity.connectionSecure "Secure Connection">
 <!ENTITY identity.connectionNotSecure "Connection is Not Secure">
 <!ENTITY identity.connectionFile "This page is stored on your computer.">
 <!ENTITY identity.connectionVerified1 "You are securely connected to this site, run by:">
 <!ENTITY identity.connectionInternal "This is a secure &brandShortName; page.">
 <!ENTITY identity.insecureLoginForms2 "Logins entered on this page could be compromised.">