Bug 1358645 - Close the preference dialog when the user clicks the overlay background. r?jaws draft
authorChris Peterson <cpeterson@mozilla.com>
Sat, 29 Apr 2017 19:43:50 -0700
changeset 575203 78388b34c4a994fb1d32ff7ff906896cd7b78fc1
parent 575133 120d8562d4a53e4f78bd86c6f5076f6db265e5a3
child 627860 8889bd41615f7cebb0755df9d9ce02e8a5651bf3
push id57995
push usercpeterson@mozilla.com
push dateWed, 10 May 2017 04:24:47 +0000
reviewersjaws
bugs1358645
milestone55.0a1
Bug 1358645 - Close the preference dialog when the user clicks the overlay background. r?jaws We close the dialog using the same code path as when the user presses the ESC key. MozReview-Commit-ID: LNMDTgMl2L
browser/components/preferences/in-content-old/subdialogs.js
browser/components/preferences/in-content-old/tests/browser_subdialogs.js
browser/components/preferences/in-content/subdialogs.js
browser/components/preferences/in-content/tests/browser_subdialogs.js
--- a/browser/components/preferences/in-content-old/subdialogs.js
+++ b/browser/components/preferences/in-content-old/subdialogs.js
@@ -123,16 +123,23 @@ var gSubDialog = {
       };
       this._frame.addEventListener("load", onBlankLoad);
       this._frame.loadURI("about:blank");
     }, 0);
   },
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
+      case "click":
+        // Close the dialog if the user clicked the overlay background, just
+        // like when the user presses the ESC key (case "command" below).
+        if (aEvent.target === this._overlay) {
+          this._frame.contentWindow.close();
+        }
+        break;
       case "command":
         this._frame.contentWindow.close();
         break;
       case "dialogclosing":
         this._onDialogClosing(aEvent);
         break;
       case "DOMTitleChanged":
         this.updateTitle(aEvent);
@@ -385,33 +392,39 @@ var gSubDialog = {
     // Similarly DOMFrameContentLoaded only fires on the top window
     window.addEventListener("DOMFrameContentLoaded", this, true);
 
     // Wait for the stylesheets injected during DOMContentLoaded to load before showing the dialog
     // otherwise there is a flicker of the stylesheet applying.
     this._frame.addEventListener("load", this);
 
     chromeBrowser.addEventListener("unload", this, true);
+
     // Ensure we get <esc> keypresses even if nothing in the subdialog is focusable
     // (happens on OS X when only text inputs and lists are focusable, and
     //  the subdialog only has checkboxes/radiobuttons/buttons)
     window.addEventListener("keydown", this, true);
+
+    this._overlay.addEventListener("click", this, true);
   },
 
   _removeDialogEventListeners() {
     let chromeBrowser = this._getBrowser();
     chromeBrowser.removeEventListener("DOMTitleChanged", this, true);
     chromeBrowser.removeEventListener("unload", this, true);
 
     this._closeButton.removeEventListener("command", this);
 
     window.removeEventListener("DOMFrameContentLoaded", this, true);
     this._frame.removeEventListener("load", this);
     this._frame.contentWindow.removeEventListener("dialogclosing", this);
     window.removeEventListener("keydown", this, true);
+
+    this._overlay.removeEventListener("click", this, true);
+
     if (this._resizeObserver) {
       this._resizeObserver.disconnect();
       this._resizeObserver = null;
     }
     this._untrapFocus();
   },
 
   _trapFocus() {
--- a/browser/components/preferences/in-content-old/tests/browser_subdialogs.js
+++ b/browser/components/preferences/in-content-old/tests/browser_subdialogs.js
@@ -165,16 +165,34 @@ add_task(function* click_close_button_on
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
 
   info("canceling the dialog");
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { return BrowserTestUtils.synthesizeMouseAtCenter("#dialogClose", {}, tab.linkedBrowser); },
     null, 0, {runClosingFnOutsideOfContentTask: true});
 });
 
+add_task(function* background_click_should_close_dialog() {
+  yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
+
+  // Clicking on an inactive part of dialog itself should not close the dialog.
+  // Click the dialog title bar here to make sure nothing happens.
+  info("clicking the dialog title bar");
+  BrowserTestUtils.synthesizeMouseAtCenter("#dialogTitle", {}, tab.linkedBrowser);
+
+  // Close the dialog by clicking on the overlay background. Simulate a click
+  // at point (2,2) instead of (0,0) so we are sure we're clicking on the
+  // overlay background instead of some boundary condition that a real user
+  // would never click.
+  info("clicking the overlay background");
+  yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
+    function() { return BrowserTestUtils.synthesizeMouseAtPoint(2, 2, {}, tab.linkedBrowser); },
+    null, 0, {runClosingFnOutsideOfContentTask: true});
+});
+
 add_task(function* back_navigation_on_subdialog_should_close_dialog() {
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
 
   info("canceling the dialog");
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { content.window.gSubDialog._frame.goBack(); },
     null, undefined);
 });
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -123,16 +123,23 @@ var gSubDialog = {
       };
       this._frame.addEventListener("load", onBlankLoad);
       this._frame.loadURI("about:blank");
     }, 0);
   },
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
+      case "click":
+        // Close the dialog if the user clicked the overlay background, just
+        // like when the user presses the ESC key (case "command" below).
+        if (aEvent.target === this._overlay) {
+          this._frame.contentWindow.close();
+        }
+        break;
       case "command":
         this._frame.contentWindow.close();
         break;
       case "dialogclosing":
         this._onDialogClosing(aEvent);
         break;
       case "DOMTitleChanged":
         this.updateTitle(aEvent);
@@ -385,33 +392,39 @@ var gSubDialog = {
     // Similarly DOMFrameContentLoaded only fires on the top window
     window.addEventListener("DOMFrameContentLoaded", this, true);
 
     // Wait for the stylesheets injected during DOMContentLoaded to load before showing the dialog
     // otherwise there is a flicker of the stylesheet applying.
     this._frame.addEventListener("load", this);
 
     chromeBrowser.addEventListener("unload", this, true);
+
     // Ensure we get <esc> keypresses even if nothing in the subdialog is focusable
     // (happens on OS X when only text inputs and lists are focusable, and
     //  the subdialog only has checkboxes/radiobuttons/buttons)
     window.addEventListener("keydown", this, true);
+
+    this._overlay.addEventListener("click", this, true);
   },
 
   _removeDialogEventListeners() {
     let chromeBrowser = this._getBrowser();
     chromeBrowser.removeEventListener("DOMTitleChanged", this, true);
     chromeBrowser.removeEventListener("unload", this, true);
 
     this._closeButton.removeEventListener("command", this);
 
     window.removeEventListener("DOMFrameContentLoaded", this, true);
     this._frame.removeEventListener("load", this);
     this._frame.contentWindow.removeEventListener("dialogclosing", this);
     window.removeEventListener("keydown", this, true);
+
+    this._overlay.removeEventListener("click", this, true);
+
     if (this._resizeObserver) {
       this._resizeObserver.disconnect();
       this._resizeObserver = null;
     }
     this._untrapFocus();
   },
 
   _trapFocus() {
--- a/browser/components/preferences/in-content/tests/browser_subdialogs.js
+++ b/browser/components/preferences/in-content/tests/browser_subdialogs.js
@@ -165,16 +165,34 @@ add_task(function* click_close_button_on
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
 
   info("canceling the dialog");
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { return BrowserTestUtils.synthesizeMouseAtCenter("#dialogClose", {}, tab.linkedBrowser); },
     null, 0, {runClosingFnOutsideOfContentTask: true});
 });
 
+add_task(function* background_click_should_close_dialog() {
+  yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
+
+  // Clicking on an inactive part of dialog itself should not close the dialog.
+  // Click the dialog title bar here to make sure nothing happens.
+  info("clicking the dialog title bar");
+  BrowserTestUtils.synthesizeMouseAtCenter("#dialogTitle", {}, tab.linkedBrowser);
+
+  // Close the dialog by clicking on the overlay background. Simulate a click
+  // at point (2,2) instead of (0,0) so we are sure we're clicking on the
+  // overlay background instead of some boundary condition that a real user
+  // would never click.
+  info("clicking the overlay background");
+  yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
+    function() { return BrowserTestUtils.synthesizeMouseAtPoint(2, 2, {}, tab.linkedBrowser); },
+    null, 0, {runClosingFnOutsideOfContentTask: true});
+});
+
 add_task(function* back_navigation_on_subdialog_should_close_dialog() {
   yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
 
   info("canceling the dialog");
   yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
     function() { content.window.gSubDialog._frame.goBack(); },
     null, undefined);
 });