Bug 1331798 Bookmark panel for new bookmark shouldn't be closed automatically during composition and after text input without keyboard events nor composition events r?jaws draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 19 Jan 2017 15:04:56 +0900
changeset 463456 a250bc2a641ed37b3f9377cdcd41a2d5c7c070ea
parent 463342 96cb95af530477edb66ae48d98c18533476e57bb
child 542685 091c658227d1a554bbe3a200b180819ec3fcd530
push id42075
push usermasayuki@d-toybox.com
push dateThu, 19 Jan 2017 06:08:03 +0000
reviewersjaws
bugs1331798
milestone53.0a1
Bug 1331798 Bookmark panel for new bookmark shouldn't be closed automatically during composition and after text input without keyboard events nor composition events r?jaws Bookmark panel for a new bookmark shouldn't be closed automatically during composition even if mouse cursor is moved outside the panel because user may click in IME's UI. Additionally, editor might be modified without keyboard events nor composition events. In such case, the bookmark panel shouldn't be closed automatically too. MozReview-Commit-ID: 1FAUmkXjid
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
@@ -2,32 +2,36 @@
  * 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/. */
 
 var StarUI = {
   _itemId: -1,
   uri: null,
   _batching: false,
   _isNewBookmark: false,
+  _isComposing: false,
   _autoCloseTimer: 0,
 
   _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("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;
   },
 
   // Array of command elements to disable when the panel is opened.
   get _blockedCommands() {
     delete this._blockedCommands;
@@ -130,25 +134,44 @@ var StarUI = {
           case 0:
             let accessKey = document.getElementById("key_close");
             if (eventMatchesKey(aEvent, accessKey)) {
                 this.panel.hidePopup();
             }
             break;
         }
         break;
+      case "compositionstart":
+        if (aEvent.defaultPrevented) {
+          // If the composition was canceled, nothing to do here.
+          break;
+        }
+        // During composition, panel shouldn't be hidden automatically.
+        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);
+        break;
       case "mouseout":
         // 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) {
+        if (this._isNewBookmark && !this._isComposing) {
           // 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();
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -191,16 +191,102 @@ add_task(function* panel_shown_for_new_b
     shouldAutoClose: false,
     popupHideFn() {
       bookmarkPanel.hidePopup();
     },
     isBookmarkRemoved: false,
   });
 });
 
+
+add_task(function* panel_shown_for_new_bookmark_compositionstart_no_autoclose() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    *popupEditFn() {
+      let compositionStartPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "compositionstart");
+      EventUtils.synthesizeComposition({ type: "compositionstart" }, window);
+      info("Waiting for compositionstart event");
+      yield compositionStartPromise;
+      info("Got compositionstart event");
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      EventUtils.synthesizeComposition({ type: "compositioncommitasis" });
+      bookmarkPanel.hidePopup();
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmark_compositionstart_mouseout_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");
+
+      let compositionStartPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "compositionstart");
+      EventUtils.synthesizeComposition({ type: "compositionstart" }, window);
+      info("Waiting for compositionstart event");
+      yield compositionStartPromise;
+      info("Got compositionstart event");
+
+      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, but shouldn't run autoclose");
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      EventUtils.synthesizeComposition({ type: "compositioncommitasis" });
+      bookmarkPanel.hidePopup();
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmark_compositionend_mouseout_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");
+    },
+    shouldAutoClose: false,
+    isBookmarkRemoved: false,
+  });
+});
+
 add_task(function* contextmenu_new_bookmark_keypress_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,