Bug 52821 - Add a pref to disable the quit application shortcut. r=Gijs
authorTom Schuster <evilpies@gmail.com>
Wed, 10 Feb 2021 17:02:57 +0000
changeset 566856 4ffa8b3b68e8a87658c036f948b30b66c18a3d98
parent 566855 d94de995a62b8d4f65c1a8f759320cf6f6bf30bf
child 566857 a71cb2eafb03c63d8357fc2f3dbaba4bf14561fb
push id38191
push userbtara@mozilla.com
push dateThu, 11 Feb 2021 05:02:45 +0000
treeherdermozilla-central@5cbcb80f72bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs52821
milestone87.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 52821 - Add a pref to disable the quit application shortcut. r=Gijs browser.quitShortcut.disabled=true will disable the Ctrl + Q shortcut on all platforms. Differential Revision: https://phabricator.services.mozilla.com/D104189
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/nonbrowser-mac.js
browser/components/tests/browser/browser.ini
browser/components/tests/browser/browser_quit_disabled.js
browser/modules/BrowserUIUtils.jsm
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -950,16 +950,19 @@ pref("browser.sessionstore.debug.no_auto
 // Forget closed windows/tabs after two weeks
 pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
 // Amount of failed SessionFile writes until we restart the worker.
 pref("browser.sessionstore.max_write_failures", 5);
 
 // Whether to warn the user when quitting, even though their tabs will be restored.
 pref("browser.sessionstore.warnOnQuit", false);
 
+// Don't quit the browser when Ctrl + Q is pressed.
+pref("browser.quitShortcut.disabled", false);
+
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
 
 // Whether history is enabled or not.
 pref("places.history.enabled", true);
 
 // Whether or not diacritics must match in history text searches.
 pref("places.search.matchDiacritics", false);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1874,16 +1874,25 @@ var gBrowserInit = {
 
     if (!PrivateBrowsingUtils.enabled) {
       document.getElementById("Tools:PrivateBrowsing").hidden = true;
       // Setting disabled doesn't disable the shortcut, so we just remove
       // the keybinding.
       document.getElementById("key_privatebrowsing").remove();
     }
 
+    if (BrowserUIUtils.quitShortcutDisabled) {
+      document.getElementById("key_quitApplication").remove();
+      document.getElementById("menu_FileQuitItem").removeAttribute("key");
+      PanelMultiView.getViewNode(
+        document,
+        "appMenu-quit-button"
+      )?.removeAttribute("key");
+    }
+
     this._loadHandled = true;
   },
 
   _cancelDelayedStartup() {
     window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
     this._boundDelayedStartup = null;
   },
 
--- a/browser/base/content/nonbrowser-mac.js
+++ b/browser/base/content/nonbrowser-mac.js
@@ -105,16 +105,20 @@ function nonBrowserWindowStartup() {
 
     // Hide menuitems that don't apply to private contexts.
     if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
       document.getElementById("macDockMenuNewWindow").hidden = true;
     }
     if (!PrivateBrowsingUtils.enabled) {
       document.getElementById("macDockMenuNewPrivateWindow").hidden = true;
     }
+    if (BrowserUIUtils.quitShortcutDisabled) {
+      document.getElementById("key_quitApplication").remove();
+      document.getElementById("menu_FileQuitItem").removeAttribute("key");
+    }
   }
 
   delayedStartupTimeoutId = setTimeout(nonBrowserWindowDelayedStartup, 0);
 }
 
 function nonBrowserWindowDelayedStartup() {
   delayedStartupTimeoutId = null;
 
--- a/browser/components/tests/browser/browser.ini
+++ b/browser/components/tests/browser/browser.ini
@@ -2,8 +2,9 @@
 
 [browser_bug538331.js]
 skip-if = !updater
 reason = test depends on update channel
 [browser_contentpermissionprompt.js]
 [browser_default_bookmark_toolbar_visibility.js]
 [browser_initial_tab_remoteType.js]
 [browser_startup_homepage.js]
+[browser_quit_disabled.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/tests/browser/browser_quit_disabled.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function test_appMenu_quit_disabled() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.quitShortcut.disabled", true]],
+  });
+
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  let doc = win.document;
+
+  let menuButton = doc.getElementById("PanelUI-menu-button");
+  menuButton.click();
+  await BrowserTestUtils.waitForEvent(win.PanelUI.mainView, "ViewShown");
+
+  let quitButton = doc.querySelector(`[key="key_quitApplication"]`);
+  is(quitButton, null, "No quit button with shortcut key");
+
+  await BrowserTestUtils.closeWindow(win);
+
+  await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_quit_shortcut_disabled() {
+  async function testQuitShortcut(shouldQuit) {
+    let win = await BrowserTestUtils.openNewBrowserWindow();
+
+    let observer = {
+      observe(subject, topic, data) {
+        is(topic, "quit-application-requested", "Right observer topic");
+        ok(shouldQuit, "Quit shortcut should NOT have worked");
+
+        // Don't actually quit the browser when testing.
+        let cancelQuit = subject.QueryInterface(Ci.nsISupportsPRBool);
+        cancelQuit.data = true;
+      },
+    };
+    Services.obs.addObserver(observer, "quit-application-requested");
+
+    let modifiers = { accelKey: true };
+    if (AppConstants.platform == "win") {
+      modifiers.shiftKey = true;
+    }
+    EventUtils.synthesizeKey("q", modifiers, win);
+
+    await BrowserTestUtils.closeWindow(win);
+
+    Services.obs.removeObserver(observer, "quit-application-requested");
+  }
+
+  // Quit shortcut should work when pref is not set.
+  await testQuitShortcut(true);
+
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.quitShortcut.disabled", true]],
+  });
+  await testQuitShortcut(false);
+});
--- a/browser/modules/BrowserUIUtils.jsm
+++ b/browser/modules/BrowserUIUtils.jsm
@@ -1,15 +1,19 @@
 /* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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";
 
+var { XPCOMUtils } = ChromeUtils.import(
+  "resource://gre/modules/XPCOMUtils.jsm"
+);
+
 var EXPORTED_SYMBOLS = ["BrowserUIUtils"];
 
 var BrowserUIUtils = {
   /**
    * Check whether a page can be considered as 'empty', that its URI
    * reflects its origin, and that if it's loaded in a tab, that tab
    * could be considered 'empty' (e.g. like the result of opening
    * a 'blank' new tab).
@@ -197,8 +201,15 @@ var BrowserUIUtils = {
   trimURL(aURL) {
     let url = this.removeSingleTrailingSlashFromURL(aURL);
     // Remove "http://" prefix.
     return url.startsWith(this.trimURLProtocol)
       ? url.substring(this.trimURLProtocol.length)
       : url;
   },
 };
+
+XPCOMUtils.defineLazyPreferenceGetter(
+  BrowserUIUtils,
+  "quitShortcutDisabled",
+  "browser.quitShortcut.disabled",
+  false
+);