Bug 1359855 - Prevent loading any DevTools module until users interact with any devtool entrypoint. r=jdescottes
☠☠ backed out by a6b198e415f3 ☠ ☠
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 18 Jul 2017 11:05:47 +0200
changeset 419237 a46ee323e1e75963076316ecd8fa14f4b763a5c2
parent 419236 059f64f6bc238321b9f98711a7eb9939586276ae
child 419238 44726932185aa82dbab1cea9a4b4bdc727f8fd5e
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1359855
milestone56.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 1359855 - Prevent loading any DevTools module until users interact with any devtool entrypoint. r=jdescottes MozReview-Commit-ID: 4rORySoFRQY
devtools/client/definitions.js
devtools/client/devtools-startup.js
devtools/client/framework/browser-menus.js
devtools/client/framework/devtools-browser.js
devtools/client/locales/en-US/key-shortcuts.properties
devtools/client/locales/en-US/menus.properties
devtools/client/locales/en-US/startup.properties
devtools/client/menus.js
devtools/shared/tests/browser/browser_l10n_localizeMarkup.js
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -25,18 +25,21 @@ loader.lazyGetter(this, "ScratchpadPanel
 loader.lazyGetter(this, "DomPanel", () => require("devtools/client/dom/dom-panel").DomPanel);
 
 // Other dependencies
 loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "CommandState", "devtools/shared/gcli/command-state", true);
 loader.lazyImporter(this, "ResponsiveUIManager", "resource://devtools/client/responsivedesign/responsivedesign.jsm");
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
-const {LocalizationHelper} = require("devtools/shared/l10n");
-const L10N = new LocalizationHelper("devtools/client/locales/startup.properties");
+const {MultiLocalizationHelper} = require("devtools/shared/l10n");
+const L10N = new MultiLocalizationHelper(
+  "devtools/client/locales/startup.properties",
+  "devtools/client/locales/key-shortcuts.properties"
+);
 
 var Tools = {};
 exports.Tools = Tools;
 
 // Definitions
 Tools.options = {
   id: "options",
   ordinal: 0,
@@ -57,27 +60,26 @@ Tools.options = {
   build: function (iframeWindow, toolbox) {
     return new OptionsPanel(iframeWindow, toolbox);
   }
 };
 
 Tools.inspector = {
   id: "inspector",
   accesskey: l10n("inspector.accesskey"),
-  key: l10n("inspector.commandkey"),
   ordinal: 1,
-  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
   icon: "chrome://devtools/skin/images/tool-inspector.svg",
   invertIconForDarkTheme: true,
   url: "chrome://devtools/content/inspector/inspector.xhtml",
   label: l10n("inspector.label"),
   panelLabel: l10n("inspector.panelLabel"),
   get tooltip() {
     return l10n("inspector.tooltip2",
-    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key);
+    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
+    l10n("inspector.commandkey"));
   },
   inMenu: true,
   commands: [
     "devtools/client/responsivedesign/resize-commands",
     "devtools/client/inspector/inspector-commands"
   ],
 
   preventClosingOnKey: true,
@@ -90,30 +92,29 @@ Tools.inspector = {
   },
 
   build: function (iframeWindow, toolbox) {
     return new InspectorPanel(iframeWindow, toolbox);
   }
 };
 Tools.webConsole = {
   id: "webconsole",
-  key: l10n("cmd.commandkey"),
   accesskey: l10n("webConsoleCmd.accesskey"),
-  modifiers: Services.appinfo.OS == "Darwin" ? "accel,alt" : "accel,shift",
   ordinal: 2,
   oldWebConsoleURL: "chrome://devtools/content/webconsole/webconsole.xul",
   newWebConsoleURL: "chrome://devtools/content/webconsole/webconsole.xhtml",
   icon: "chrome://devtools/skin/images/tool-webconsole.svg",
   invertIconForDarkTheme: true,
   label: l10n("ToolboxTabWebconsole.label"),
   menuLabel: l10n("MenuWebconsole.label"),
   panelLabel: l10n("ToolboxWebConsole.panelLabel"),
   get tooltip() {
     return l10n("ToolboxWebconsole.tooltip2",
-    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key);
+    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
+    l10n("webconsole.commandkey"));
   },
   inMenu: true,
   commands: "devtools/client/webconsole/console-commands",
 
   preventClosingOnKey: true,
   onkey: function (panel, toolbox) {
     if (toolbox.splitConsole) {
       return toolbox.focusConsoleInput();
@@ -141,29 +142,28 @@ switchWebconsole();
 
 Services.prefs.addObserver(
   "devtools.webconsole.new-frontend-enabled",
   { observe: switchWebconsole }
 );
 
 Tools.jsdebugger = {
   id: "jsdebugger",
-  key: l10n("debuggerMenu.commandkey"),
   accesskey: l10n("debuggerMenu.accesskey"),
-  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
   ordinal: 3,
   icon: "chrome://devtools/skin/images/tool-debugger.svg",
   invertIconForDarkTheme: true,
   highlightedicon: "chrome://devtools/skin/images/tool-debugger-paused.svg",
   url: "chrome://devtools/content/debugger/debugger.xul",
   label: l10n("ToolboxDebugger.label"),
   panelLabel: l10n("ToolboxDebugger.panelLabel"),
   get tooltip() {
     return l10n("ToolboxDebugger.tooltip2",
-    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key);
+    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
+    l10n("debugger.commandkey"));
   },
   inMenu: true,
   commands: "devtools/client/debugger/debugger-commands",
 
   isTargetSupported: function () {
     return true;
   },
 
@@ -189,29 +189,27 @@ switchDebugger();
 
 Services.prefs.addObserver(
   "devtools.debugger.new-debugger-frontend",
   { observe: switchDebugger }
 );
 
 Tools.styleEditor = {
   id: "styleeditor",
-  key: l10n("open.commandkey"),
   ordinal: 4,
   visibilityswitch: "devtools.styleeditor.enabled",
   accesskey: l10n("open.accesskey"),
-  modifiers: "shift",
   icon: "chrome://devtools/skin/images/tool-styleeditor.svg",
   invertIconForDarkTheme: true,
   url: "chrome://devtools/content/styleeditor/styleeditor.xul",
   label: l10n("ToolboxStyleEditor.label"),
   panelLabel: l10n("ToolboxStyleEditor.panelLabel"),
   get tooltip() {
     return l10n("ToolboxStyleEditor.tooltip3",
-    "Shift+" + functionkey(this.key));
+    "Shift+" + functionkey(l10n("styleeditor.commandkey")));
   },
   inMenu: true,
   commands: "devtools/client/styleeditor/styleeditor-commands",
 
   isTargetSupported: function (target) {
     return target.hasActor("styleEditor") || target.hasActor("styleSheets");
   },
 
@@ -268,21 +266,20 @@ Tools.performance = {
   icon: "chrome://devtools/skin/images/tool-profiler.svg",
   invertIconForDarkTheme: true,
   highlightedicon: "chrome://devtools/skin/images/tool-profiler-active.svg",
   url: "chrome://devtools/content/performance/performance.xul",
   visibilityswitch: "devtools.performance.enabled",
   label: l10n("performance.label"),
   panelLabel: l10n("performance.panelLabel"),
   get tooltip() {
-    return l10n("performance.tooltip", "Shift+" + functionkey(this.key));
+    return l10n("performance.tooltip", "Shift+" +
+    functionkey(l10n("performance.commandkey")));
   },
   accesskey: l10n("performance.accesskey"),
-  key: l10n("performance.commandkey"),
-  modifiers: "shift",
   inMenu: true,
 
   isTargetSupported: function (target) {
     return target.hasActor("profiler");
   },
 
   build: function (frame, target) {
     return new PerformancePanel(frame, target);
@@ -308,55 +305,53 @@ Tools.memory = {
   build: function (frame, target) {
     return new MemoryPanel(frame, target);
   }
 };
 
 Tools.netMonitor = {
   id: "netmonitor",
   accesskey: l10n("netmonitor.accesskey"),
-  key: l10n("netmonitor.commandkey2"),
   ordinal: 9,
-  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
   visibilityswitch: "devtools.netmonitor.enabled",
   icon: "chrome://devtools/skin/images/tool-network.svg",
   invertIconForDarkTheme: true,
   url: "chrome://devtools/content/netmonitor/index.html",
   label: l10n("netmonitor.label"),
   panelLabel: l10n("netmonitor.panelLabel"),
   get tooltip() {
     return l10n("netmonitor.tooltip2",
-    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key);
+    (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
+    l10n("netmonitor.commandkey"));
   },
   inMenu: true,
 
   isTargetSupported: function (target) {
     return target.getTrait("networkMonitor");
   },
 
   build: function (iframeWindow, toolbox) {
     return new NetMonitorPanel(iframeWindow, toolbox);
   }
 };
 
 Tools.storage = {
   id: "storage",
-  key: l10n("storage.commandkey"),
   ordinal: 10,
   accesskey: l10n("storage.accesskey"),
-  modifiers: "shift",
   visibilityswitch: "devtools.storage.enabled",
   icon: "chrome://devtools/skin/images/tool-storage.svg",
   invertIconForDarkTheme: true,
   url: "chrome://devtools/content/storage/storage.xul",
   label: l10n("storage.label"),
   menuLabel: l10n("storage.menuLabel"),
   panelLabel: l10n("storage.panelLabel"),
   get tooltip() {
-    return l10n("storage.tooltip3", "Shift+" + functionkey(this.key));
+    return l10n("storage.tooltip3", "Shift+" +
+    functionkey(l10n("storage.commandkey")));
   },
   inMenu: true,
 
   isTargetSupported: function (target) {
     return target.isLocalTab ||
            (target.hasActor("storage") && target.getTrait("storageInspector"));
   },
 
@@ -405,28 +400,27 @@ Tools.scratchpad = {
   build: function (iframeWindow, toolbox) {
     return new ScratchpadPanel(iframeWindow, toolbox);
   }
 };
 
 Tools.dom = {
   id: "dom",
   accesskey: l10n("dom.accesskey"),
-  key: l10n("dom.commandkey"),
   ordinal: 13,
-  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
   visibilityswitch: "devtools.dom.enabled",
   icon: "chrome://devtools/skin/images/tool-dom.svg",
   invertIconForDarkTheme: true,
   url: "chrome://devtools/content/dom/dom.html",
   label: l10n("dom.label"),
   panelLabel: l10n("dom.panelLabel"),
   get tooltip() {
     return l10n("dom.tooltip",
-      (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key);
+      (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
+      l10n("dom.commandkey"));
   },
   inMenu: true,
 
   isTargetSupported: function (target) {
     return target.getTrait("webConsoleCommands");
   },
 
   build: function (iframeWindow, toolbox) {
--- a/devtools/client/devtools-startup.js
+++ b/devtools/client/devtools-startup.js
@@ -1,30 +1,162 @@
 /* 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/. */
 
 /**
  * This XPCOM component is loaded very early.
- * It handles command line arguments like -jsconsole, but also ensures starting
+ * Be careful to lazy load dependencies as much as possible.
+ *
+ * It manages all the possible entry points for DevTools:
+ * - Handles command line arguments like -jsconsole,
+ * - Register all key shortcuts,
+ * - Listen for "Web Developer" system menu opening, under "Tools",
+ * - Inject the wrench icon in toolbar customization, which is used
+ *   by the "Web Developer" list displayed in the hamburger menu,
+ * - Register the JSON Viewer protocol handler.
+ *
+ * Only once any of these entry point is fired, this module ensures starting
  * core modules like 'devtools-browser.js' that hooks the browser windows
  * and ensure setting up tools.
- *
- * Be careful to lazy load dependencies as much as possible.
  **/
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 const kDebuggerPrefs = [
   "devtools.debugger.remote-enabled",
   "devtools.chrome.enabled"
 ];
 const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
-XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+                                  "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "Bundle", function () {
+  const kUrl = "chrome://devtools/locale/key-shortcuts.properties";
+  return Services.strings.createBundle(kUrl);
+});
+
+XPCOMUtils.defineLazyGetter(this, "KeyShortcuts", function () {
+  const isMac = AppConstants.platform == "macosx";
+
+  // Common modifier shared by most key shortcuts
+  const modifiers = isMac ? "accel,alt" : "accel,shift";
+
+  // List of all key shortcuts triggering installation UI
+  // `id` should match tool's id from client/definitions.js
+  return [
+    // The following keys are also registered in /client/menus.js
+    // And should be synced.
+
+    // Both are toggling the toolbox on the last selected panel
+    // or the default one.
+    {
+      id: "toggleToolbox",
+      shortcut: Bundle.GetStringFromName("toggleToolbox.commandkey"),
+      modifiers
+    },
+    // All locales are using F12
+    {
+      id: "toggleToolboxF12",
+      shortcut: Bundle.GetStringFromName("toggleToolboxF12.commandkey"),
+      modifiers: "" // F12 is the only one without modifiers
+    },
+    // Toggle the visibility of the Developer Toolbar (=gcli)
+    {
+      id: "toggleToolbar",
+      shortcut: Bundle.GetStringFromName("toggleToolbar.commandkey"),
+      modifiers: "shift"
+    },
+    // Open WebIDE window
+    {
+      id: "webide",
+      shortcut: Bundle.GetStringFromName("webide.commandkey"),
+      modifiers: "shift"
+    },
+    // Open the Browser Toolbox
+    {
+      id: "browserToolbox",
+      shortcut: Bundle.GetStringFromName("browserToolbox.commandkey"),
+      modifiers: "accel,alt,shift"
+    },
+    // Open the Browser Console
+    {
+      id: "browserConsole",
+      shortcut: Bundle.GetStringFromName("browserConsole.commandkey"),
+      modifiers: "accel,shift"
+    },
+    // Toggle the Responsive Design Mode
+    {
+      id: "responsiveDesignMode",
+      shortcut: Bundle.GetStringFromName("responsiveDesignMode.commandkey"),
+      modifiers
+    },
+    // Open ScratchPad window
+    {
+      id: "scratchpad",
+      shortcut: Bundle.GetStringFromName("scratchpad.commandkey"),
+      modifiers: "shift"
+    },
+
+    // The following keys are also registered in /client/definitions.js
+    // and should be synced.
+
+    // Key for opening the Inspector
+    {
+      toolId: "inspector",
+      shortcut: Bundle.GetStringFromName("inspector.commandkey"),
+      modifiers
+    },
+    // Key for opening the Web Console
+    {
+      toolId: "webconsole",
+      shortcut: Bundle.GetStringFromName("webconsole.commandkey"),
+      modifiers
+    },
+    // Key for opening the Debugger
+    {
+      toolId: "jsdebugger",
+      shortcut: Bundle.GetStringFromName("debugger.commandkey"),
+      modifiers
+    },
+    // Key for opening the Network Monitor
+    {
+      toolId: "netmonitor",
+      shortcut: Bundle.GetStringFromName("netmonitor.commandkey"),
+      modifiers
+    },
+    // Key for opening the Style Editor
+    {
+      toolId: "styleeditor",
+      shortcut: Bundle.GetStringFromName("styleeditor.commandkey"),
+      modifiers: "shift"
+    },
+    // Key for opening the Performance Panel
+    {
+      toolId: "performance",
+      shortcut: Bundle.GetStringFromName("performance.commandkey"),
+      modifiers: "shift"
+    },
+    // Key for opening the Storage Panel
+    {
+      toolId: "storage",
+      shortcut: Bundle.GetStringFromName("storage.commandkey"),
+      modifiers: "shift"
+    },
+    // Key for opening the DOM Panel
+    {
+      toolId: "dom",
+      shortcut: Bundle.GetStringFromName("dom.commandkey"),
+      modifiers
+    },
+  ];
+});
 
 function DevToolsStartup() {}
 
 DevToolsStartup.prototype = {
   handle: function (cmdLine) {
     let consoleFlag = cmdLine.handleFlag("jsconsole", false);
     let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
     let devtoolsFlag = cmdLine.handleFlag("devtools", false);
@@ -43,34 +175,114 @@ DevToolsStartup.prototype = {
       // We get an error if the option is given but not followed by a value.
       // By catching and trying again, the value is effectively optional.
       debuggerServerFlag = cmdLine.handleFlag("start-debugger-server", false);
     }
     if (debuggerServerFlag) {
       this.handleDebuggerServerFlag(cmdLine, debuggerServerFlag);
     }
 
-    let onStartup = window => {
-      Services.obs.removeObserver(onStartup,
-                                  "browser-delayed-startup-finished");
-      // Ensure loading core module once firefox is ready
-      this.initDevTools();
+    let onWindowReady = window => {
+      this.hookWindow(window);
 
       if (devtoolsFlag) {
         this.handleDevToolsFlag(window);
+        // This listener is called for all Firefox windows, but we want to execute
+        // that command only once
+        devtoolsFlag = false;
       }
     };
-    Services.obs.addObserver(onStartup, "browser-delayed-startup-finished");
+    Services.obs.addObserver(onWindowReady, "browser-delayed-startup-finished");
+  },
+
+  /**
+   * Register listeners to all possible entry points for Developer Tools.
+   * But instead of implementing the actual actions, defer to DevTools codebase.
+   * In most cases, it only needs to call this.initDevTools which handles the rest.
+   * We do that to prevent loading any DevTools module until the user intent to use them.
+   */
+  hookWindow(window) {
+    this.hookKeyShortcuts(window);
+
+    // All the other hooks are only necessary if the tools aren't loaded yet.
+    if (this.initialized) {
+      return;
+    }
+
+    this.hookWebDeveloperMenu(window);
+  },
+
+  /**
+   * We listen to the "Web Developer" system menu, which is under "Tools" main item.
+   * This menu item is hardcoded empty in Firefox UI. We listen for its opening to
+   * populate it lazily. Loading main DevTools module is going to populate it.
+   */
+  hookWebDeveloperMenu(window) {
+    let menu = window.document.getElementById("webDeveloperMenu");
+    menu.addEventListener("popupshowing", () => this.initDevTools(), { once: true });
   },
 
+  hookKeyShortcuts(window) {
+    let doc = window.document;
+    let keyset = doc.createElement("keyset");
+    keyset.setAttribute("id", "devtoolsKeyset");
+
+    for (let key of KeyShortcuts) {
+      let xulKey = this.createKey(doc, key, () => this.onKey(window, key));
+      keyset.appendChild(xulKey);
+    }
+
+    // Appending a <key> element is not always enough. The <keyset> needs
+    // to be detached and reattached to make sure the <key> is taken into
+    // account (see bug 832984).
+    let mainKeyset = doc.getElementById("mainKeyset");
+    mainKeyset.parentNode.insertBefore(keyset, mainKeyset);
+  },
+
+  onKey(window, key) {
+    let require = this.initDevTools();
+    let { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
+    gDevToolsBrowser.onKeyShortcut(window, key);
+  },
+
+  // Create a <xul:key> DOM Element
+  createKey(doc, { id, toolId, shortcut, modifiers: mod }, oncommand) {
+    let k = doc.createElement("key");
+    k.id = "key_" + (id || toolId);
+
+    if (shortcut.startsWith("VK_")) {
+      k.setAttribute("keycode", shortcut);
+    } else {
+      k.setAttribute("key", shortcut);
+    }
+
+    if (mod) {
+      k.setAttribute("modifiers", mod);
+    }
+
+    // Bug 371900: command event is fired only if "oncommand" attribute is set.
+    k.setAttribute("oncommand", ";");
+    k.addEventListener("command", oncommand);
+
+    return k;
+  },
+
+  /**
+   * Boolean flag to check if DevTools have been already initialized or not.
+   * By initialized, we mean that its main modules are loaded.
+   */
+  initialized: false,
+
   initDevTools: function () {
-    let { loader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    this.initialized = true;
+    let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     // Ensure loading main devtools module that hooks up into browser UI
     // and initialize all devtools machinery.
-    loader.require("devtools/client/framework/devtools-browser");
+    require("devtools/client/framework/devtools-browser");
+    return require;
   },
 
   handleConsoleFlag: function (cmdLine) {
     let window = Services.wm.getMostRecentWindow("devtools:webconsole");
     if (!window) {
       this.initDevTools();
 
       let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
@@ -84,17 +296,17 @@ DevToolsStartup.prototype = {
 
     if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
       cmdLine.preventDefault = true;
     }
   },
 
   // Open the toolbox on the selected tab once the browser starts up.
   handleDevToolsFlag: function (window) {
-    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const require = this.initDevTools();
     const {gDevTools} = require("devtools/client/framework/devtools");
     const {TargetFactory} = require("devtools/client/framework/target");
     let target = TargetFactory.forTab(window.gBrowser.selectedTab);
     gDevTools.showToolbox(target);
   },
 
   _isRemoteDebuggingEnabled() {
     let remoteDebuggingEnabled = false;
--- a/devtools/client/framework/browser-menus.js
+++ b/devtools/client/framework/browser-menus.js
@@ -1,18 +1,18 @@
 /* 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";
 
 /**
- * This module inject dynamically menu items and key shortcuts into browser UI.
+ * This module inject dynamically menu items into browser UI.
  *
- * Menu and shortcut definitions are fetched from:
+ * Menu definitions are fetched from:
  * - devtools/client/menus for top level entires
  * - devtools/client/definitions for tool-specifics entries
  */
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const MENUS_L10N = new LocalizationHelper("devtools/client/locales/menus.properties");
 
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
@@ -22,63 +22,20 @@ loader.lazyRequireGetter(this, "gDevTool
 // Maps browser xul document => list of DOM Elements
 const FragmentsCache = new Map();
 
 function l10n(key) {
   return MENUS_L10N.getStr(key);
 }
 
 /**
- * Create a xul:key element
- *
- * @param {XULDocument} doc
- *        The document to which keys are to be added.
- * @param {String} id
- *        key's id, automatically prefixed with "key_".
- * @param {String} shortcut
- *        The key shortcut value.
- * @param {String} keytext
- *        If `shortcut` refers to a function key, refers to the localized
- *        string to describe a non-character shortcut.
- * @param {String} modifiers
- *        Space separated list of modifier names.
- * @param {Function} oncommand
- *        The function to call when the shortcut is pressed.
- *
- * @return XULKeyElement
- */
-function createKey({ doc, id, shortcut, keytext, modifiers, oncommand }) {
-  let k = doc.createElement("key");
-  k.id = "key_" + id;
-
-  if (shortcut.startsWith("VK_")) {
-    k.setAttribute("keycode", shortcut);
-    if (keytext) {
-      k.setAttribute("keytext", keytext);
-    }
-  } else {
-    k.setAttribute("key", shortcut);
-  }
-
-  if (modifiers) {
-    k.setAttribute("modifiers", modifiers);
-  }
-
-  // Bug 371900: command event is fired only if "oncommand" attribute is set.
-  k.setAttribute("oncommand", ";");
-  k.addEventListener("command", oncommand);
-
-  return k;
-}
-
-/**
  * Create a xul:menuitem element
  *
  * @param {XULDocument} doc
- *        The document to which keys are to be added.
+ *        The document to which menus are to be added.
  * @param {String} id
  *        Element id.
  * @param {String} label
  *        Menu label.
  * @param {String} accesskey (optional)
  *        Access key of the menuitem, used as shortcut while opening the menu.
  * @param {Boolean} isCheckbox (optional)
  *        If true, the menuitem will act as a checkbox and have an optional
@@ -96,39 +53,16 @@ function createMenuItem({ doc, id, label
   if (isCheckbox) {
     menuitem.setAttribute("type", "checkbox");
     menuitem.setAttribute("autocheck", "false");
   }
   return menuitem;
 }
 
 /**
- * Add a <key> to <keyset id="devtoolsKeyset">.
- * Appending a <key> element is not always enough. The <keyset> needs
- * to be detached and reattached to make sure the <key> is taken into
- * account (see bug 832984).
- *
- * @param {XULDocument} doc
- *        The document to which keys are to be added
- * @param {XULElement} or {DocumentFragment} keys
- *        Keys to add
- */
-function attachKeybindingsToBrowser(doc, keys) {
-  let devtoolsKeyset = doc.getElementById("devtoolsKeyset");
-
-  if (!devtoolsKeyset) {
-    devtoolsKeyset = doc.createElement("keyset");
-    devtoolsKeyset.setAttribute("id", "devtoolsKeyset");
-  }
-  devtoolsKeyset.appendChild(keys);
-  let mainKeyset = doc.getElementById("mainKeyset");
-  mainKeyset.parentNode.insertBefore(devtoolsKeyset, mainKeyset);
-}
-
-/**
  * Add a menu entry for a tool definition
  *
  * @param {Object} toolDefinition
  *        Tool definition of the tool to add a menu entry.
  * @param {XULDocument} doc
  *        The document to which the tool menu item is to be added.
  */
 function createToolMenuElements(toolDefinition, doc) {
@@ -140,62 +74,45 @@ function createToolMenuElements(toolDefi
     return;
   }
 
   let oncommand = function (id, event) {
     let window = event.target.ownerDocument.defaultView;
     gDevToolsBrowser.selectToolCommand(window.gBrowser, id);
   }.bind(null, id);
 
-  let key = null;
-  if (toolDefinition.key) {
-    key = createKey({
-      doc,
-      id,
-      shortcut: toolDefinition.key,
-      modifiers: toolDefinition.modifiers,
-      oncommand: oncommand
-    });
-  }
-
   let menuitem = createMenuItem({
     doc,
     id: "menuitem_" + id,
     label: toolDefinition.menuLabel || toolDefinition.label,
     accesskey: toolDefinition.accesskey
   });
-  if (key) {
-    // Refer to the key in order to display the key shortcut at menu ends
-    menuitem.setAttribute("key", key.id);
-  }
+  // Refer to the key in order to display the key shortcut at menu ends
+  // This <key> element is being created by devtools/client/devtools-startup.js
+  menuitem.setAttribute("key", "key_" + id);
   menuitem.addEventListener("command", oncommand);
 
   return {
-    key,
     menuitem
   };
 }
 
 /**
  * Create xul menuitem, key elements for a given tool.
  * And then insert them into browser DOM.
  *
  * @param {XULDocument} doc
  *        The document to which the tool is to be registered.
  * @param {Object} toolDefinition
  *        Tool definition of the tool to register.
  * @param {Object} prevDef
  *        The tool definition after which the tool menu item is to be added.
  */
 function insertToolMenuElements(doc, toolDefinition, prevDef) {
-  let { key, menuitem } = createToolMenuElements(toolDefinition, doc);
-
-  if (key) {
-    attachKeybindingsToBrowser(doc, key);
-  }
+  let { menuitem } = createToolMenuElements(toolDefinition, doc);
 
   let ref;
   if (prevDef) {
     let menuitem = doc.getElementById("menuitem_" + prevDef.id);
     ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null;
   } else {
     ref = doc.getElementById("menu_devtools_separator");
   }
@@ -249,32 +166,29 @@ function addAllToolsToMenu(doc) {
     }
 
     if (elements.key) {
       fragKeys.appendChild(elements.key);
     }
     fragMenuItems.appendChild(elements.menuitem);
   }
 
-  attachKeybindingsToBrowser(doc, fragKeys);
-
   let mps = doc.getElementById("menu_devtools_separator");
   if (mps) {
     mps.parentNode.insertBefore(fragMenuItems, mps);
   }
 }
 
 /**
- * Add global menus and shortcuts that are not panel specific.
+ * Add global menus that are not panel specific.
  *
  * @param {XULDocument} doc
- *        The document to which keys and menus are to be added.
+ *        The document to which menus are to be added.
  */
 function addTopLevelItems(doc) {
-  let keys = doc.createDocumentFragment();
   let menuItems = doc.createDocumentFragment();
 
   let { menuitems } = require("../menus");
   for (let item of menuitems) {
     if (item.separator) {
       let separator = doc.createElement("menuseparator");
       separator.id = item.id;
       menuItems.appendChild(separator);
@@ -287,104 +201,72 @@ function addTopLevelItems(doc) {
         id,
         label: l10n(l10nKey + ".label"),
         accesskey: l10n(l10nKey + ".accesskey"),
         isCheckbox: item.checkbox
       });
       menuitem.addEventListener("command", item.oncommand);
       menuItems.appendChild(menuitem);
 
-      if (item.key && l10nKey) {
-        // Create a <key>
-        let shortcut = l10n(l10nKey + ".key");
-        let key = createKey({
-          doc,
-          id: item.key.id,
-          shortcut: shortcut,
-          keytext: shortcut.startsWith("VK_") ? l10n(l10nKey + ".keytext") : null,
-          modifiers: item.key.modifiers,
-          oncommand: item.oncommand
-        });
-        // Refer to the key in order to display the key shortcut at menu ends
-        menuitem.setAttribute("key", key.id);
-        keys.appendChild(key);
-      }
-      if (item.additionalKeys) {
-        // Create additional <key>
-        for (let key of item.additionalKeys) {
-          let shortcut = l10n(key.l10nKey + ".key");
-          let node = createKey({
-            doc,
-            id: key.id,
-            shortcut: shortcut,
-            keytext: shortcut.startsWith("VK_") ? l10n(key.l10nKey + ".keytext") : null,
-            modifiers: key.modifiers,
-            oncommand: item.oncommand
-          });
-          keys.appendChild(node);
-        }
+      if (item.keyId) {
+        menuitem.setAttribute("key", "key_" + item.keyId);
       }
     }
   }
 
   // Cache all nodes before insertion to be able to remove them on unload
   let nodes = [];
-  for (let node of keys.children) {
-    nodes.push(node);
-  }
   for (let node of menuItems.children) {
     nodes.push(node);
   }
   FragmentsCache.set(doc, nodes);
 
-  attachKeybindingsToBrowser(doc, keys);
-
   let menu = doc.getElementById("menuWebDeveloperPopup");
   menu.appendChild(menuItems);
 
   // There is still "Page Source" menuitem hardcoded into browser.xul. Instead
   // of manually inserting everything around it, move it to the expected
   // position.
   let pageSource = doc.getElementById("menu_pageSource");
   let endSeparator = doc.getElementById("devToolsEndSeparator");
   menu.insertBefore(pageSource, endSeparator);
 }
 
 /**
- * Remove global menus and shortcuts that are not panel specific.
+ * Remove global menus that are not panel specific.
  *
  * @param {XULDocument} doc
- *        The document to which keys and menus are to be added.
+ *        The document to which menus are to be added.
  */
 function removeTopLevelItems(doc) {
   let nodes = FragmentsCache.get(doc);
   if (!nodes) {
     return;
   }
   FragmentsCache.delete(doc);
   for (let node of nodes) {
     node.remove();
   }
 }
 
 /**
- * Add menus and shortcuts to a browser document
+ * Add menus to a browser document
  *
  * @param {XULDocument} doc
- *        The document to which keys and menus are to be added.
+ *        The document to which menus are to be added.
  */
 exports.addMenus = function (doc) {
   addTopLevelItems(doc);
 
   addAllToolsToMenu(doc);
 };
 
 /**
- * Remove menus and shortcuts from a browser document
+ * Remove menus from a browser document
  *
  * @param {XULDocument} doc
- *        The document to which keys and menus are to be removed.
+ *        The document to which menus are to be removed.
  */
 exports.removeMenus = function (doc) {
   // We only remove top level entries. Per-tool entries are removed while
   // unregistering each tool.
   removeTopLevelItems(doc);
 };
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -20,16 +20,19 @@ const {gDevTools} = require("./devtools"
 // Load target and toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "BrowserMenus", "devtools/client/framework/browser-menus");
 loader.lazyRequireGetter(this, "appendStyleSheet", "devtools/client/shared/stylesheet-utils", true);
 loader.lazyRequireGetter(this, "DeveloperToolbar", "devtools/client/shared/developer-toolbar", true);
+loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");
+loader.lazyImporter(this, "ResponsiveUIManager", "resource://devtools/client/responsivedesign/responsivedesign.jsm");
+loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 loader.lazyImporter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
 loader.lazyImporter(this, "CustomizableWidgets", "resource:///modules/CustomizableWidgets.jsm");
 loader.lazyImporter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
 loader.lazyImporter(this, "LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
@@ -270,16 +273,63 @@ var gDevToolsBrowser = exports.gDevTools
       gDevTools.showToolbox(target, toolId).then(newToolbox => {
         newToolbox.fireCustomKey(toolId);
         gDevTools.emit("select-tool-command", toolId);
       });
     }
   },
 
   /**
+   * Called by devtools/client/devtools-startup.js when a key shortcut is pressed
+   *
+   * @param  {Window} window
+   *         The top level browser window from which the key shortcut is pressed.
+   * @param  {Object} key
+   *         Key object describing the key shortcut being pressed. It comes
+   *         from devtools-startup.js's KeyShortcuts array. The useful fields here
+   *         are:
+   *         - `toolId` used to identify a toolbox's panel like inspector or webconsole,
+   *         - `id` used to identify any other key shortcuts like scratchpad or
+   *         about:debugging
+   */
+  onKeyShortcut(window, key) {
+    // If this is a toolbox's panel key shortcut, delegate to selectToolCommand
+    if (key.toolId) {
+      gDevToolsBrowser.selectToolCommand(window.gBrowser, key.toolId);
+      return;
+    }
+    // Otherwise implement all other key shortcuts individually here
+    switch (key.id) {
+      case "toggleToolbox":
+      case "toggleToolboxF12":
+        gDevToolsBrowser.toggleToolboxCommand(window.gBrowser);
+        break;
+      case "toggleToolbar":
+        window.DeveloperToolbar.focusToggle();
+        break;
+      case "webide":
+        gDevToolsBrowser.openWebIDE();
+        break;
+      case "browserToolbox":
+        BrowserToolboxProcess.init();
+        break;
+      case "browserConsole":
+        let HUDService = require("devtools/client/webconsole/hudservice");
+        HUDService.openBrowserConsoleOrFocus();
+        break;
+      case "responsiveDesignMode":
+        ResponsiveUIManager.toggle(window, window.gBrowser.selectedTab);
+        break;
+      case "scratchpad":
+        ScratchpadManager.openScratchpad();
+        break;
+    }
+  },
+
+  /**
    * Open a tab on "about:debugging", optionally pre-select a given tab.
    */
    // Used by browser-sets.inc, command
   openAboutDebugging(gBrowser, hash) {
     let url = "about:debugging" + (hash ? "#" + hash : "");
     gBrowser.selectedTab = gBrowser.addTab(url);
   },
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/locales/en-US/key-shortcuts.properties
@@ -0,0 +1,67 @@
+# 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/.
+
+# LOCALIZATION NOTE (toogleToolbox.commandkey):
+# Key pressed to open a toolbox with the default panel selected
+toggleToolbox.commandkey=I
+
+# LOCALIZATION NOTE (toogleToolboxF12.commandkey):
+# Alternative key pressed to open a toolbox with the default panel selected
+toggleToolboxF12.commandkey=VK_F12
+
+# LOCALIZATION NOTE (toogleToolbar.commandkey):
+# Key pressed to open the Developer Toolbar (a.k.a gcli)
+toggleToolbar.commandkey=VK_F2
+
+# LOCALIZATION NOTE (webide.commandkey):
+# Key pressed to open the Web IDE window
+webide.commandkey=VK_F8
+
+# LOCALIZATION NOTE (browserToolbox.commandkey):
+# Key pressed to open the Browser Toolbox, used for debugging Firefox itself
+browserToolbox.commandkey=I
+
+# LOCALIZATION NOTE (browserConsole.commandkey):
+# Key pressed to open the Browser Console, used for debugging Firefox itself
+browserConsole.commandkey=J
+
+# LOCALIZATION NOTE (responsiveDesignMode.commandkey):
+# Key pressed to toggle on the Responsive Design Mode
+responsiveDesignMode.commandkey=M
+
+# LOCALIZATION NOTE (scratchpad.commandkey):
+# Key pressed to open the Scratchpad in its own window
+scratchpad.commandkey=VK_F4
+
+# LOCALIZATION NOTE (inspector.commandkey):
+# Key pressed to open a toolbox with the inspector panel selected
+inspector.commandkey=C
+
+# LOCALIZATION NOTE (webconsole.commandkey):
+# Key pressed to open a toolbox with the web console panel selected
+webconsole.commandkey=K
+
+# LOCALIZATION NOTE (debugger.commandkey):
+# Key pressed to open a toolbox with the debugger panel selected
+debugger.commandkey=S
+
+# LOCALIZATION NOTE (netmonitor.commandkey):
+# Key pressed to open a toolbox with the network monitor panel selected
+netmonitor.commandkey=E
+
+# LOCALIZATION NOTE (styleeditor.commandkey):
+# Key pressed to open a toolbox with the style editor panel selected
+styleeditor.commandkey=VK_F7
+
+# LOCALIZATION NOTE (performance.commandkey):
+# Key pressed to open a toolbox with the performance panel selected
+performance.commandkey=VK_F5
+
+# LOCALIZATION NOTE (storage.commandkey):
+# Key pressed to open a toolbox with the storage panel selected
+storage.commandkey=VK_F9
+
+# LOCALIZATION NOTE (dom.commandkey):
+# Key pressed to open a toolbox with the DOM panel selected
+dom.commandkey=W
--- a/devtools/client/locales/en-US/menus.properties
+++ b/devtools/client/locales/en-US/menus.properties
@@ -1,67 +1,54 @@
 # 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/.
 
-devToolsCmd.key = VK_F12
-devToolsCmd.keytext = F12
-
 devtoolsServiceWorkers.label = Service Workers
 devtoolsServiceWorkers.accesskey = k
 
 devtoolsConnect.label = Connect…
 devtoolsConnect.accesskey = C
 
 browserConsoleCmd.label = Browser Console
 browserConsoleCmd.accesskey = B
-browserConsoleCmd.key = j
 
 responsiveDesignMode.label = Responsive Design Mode
 responsiveDesignMode.accesskey = R
-responsiveDesignMode.key = M
 
 eyedropper.label = Eyedropper
 eyedropper.accesskey = Y
 
 # LOCALIZATION NOTE (scratchpad.label): This menu item label appears
 # in the Tools menu. See bug 653093.
 # The Scratchpad is intended to provide a simple text editor for creating
 # and evaluating bits of JavaScript code for the purposes of function
 # prototyping, experimentation and convenient scripting.
 #
 # It's quite possible that you won't have a good analogue for the word
 # "Scratchpad" in your locale. You should feel free to find a close
 # approximation to it or choose a word (or words) that means
 # "simple discardable text editor".
 scratchpad.label = Scratchpad
 scratchpad.accesskey = s
-scratchpad.key = VK_F4
-scratchpad.keytext = F4
 
 # LOCALIZATION NOTE (browserToolboxMenu.label): This is the label for the
 # application menu item that opens the browser toolbox UI in the Tools menu.
 browserToolboxMenu.label = Browser Toolbox
 browserToolboxMenu.accesskey = e
-browserToolboxMenu.key = i
 
 # LOCALIZATION NOTE (browserContentToolboxMenu.label): This is the label for the
 # application menu item that opens the browser content toolbox UI in the Tools menu.
 # This toolbox allows to debug the chrome of the content process in multiprocess builds.
 browserContentToolboxMenu.label = Browser Content Toolbox
 browserContentToolboxMenu.accesskey = x
 
 devToolbarMenu.label = Developer Toolbar
 devToolbarMenu.accesskey = v
-devToolbarMenu.key = VK_F2
-devToolbarMenu.keytext = F2
 
 webide.label = WebIDE
 webide.accesskey = W
-webide.key = VK_F8
-webide.keytext = F8
 
 devToolboxMenuItem.label = Toggle Tools
 devToolboxMenuItem.accesskey = T
-devToolboxMenuItem.key = I
 
 getMoreDevtoolsCmd.label = Get More Tools
 getMoreDevtoolsCmd.accesskey = M
--- a/devtools/client/locales/en-US/startup.properties
+++ b/devtools/client/locales/en-US/startup.properties
@@ -30,19 +30,18 @@ options.firebugTheme.label2=Firebug
 # This string is displayed in the title of the tab when the profiler is
 # displayed inside the developer tools window and in the Developer Tools Menu.
 performance.label=Performance
 
 # LOCALIZATION NOTE (performance.panelLabel):
 # This is used as the label for the toolbox panel.
 performance.panelLabel=Performance Panel
 
-# LOCALIZATION NOTE (performance.commandkey, performance.accesskey)
+# LOCALIZATION NOTE (performance.accesskey)
 # Used for the menuitem in the tool menu
-performance.commandkey=VK_F5
 performance.accesskey=P
 
 # LOCALIZATION NOTE (performance.tooltip):
 # This string is displayed in the tooltip of the tab when the profiler is
 # displayed inside the developer tools window.
 # Keyboard shortcut for Performance Tools will be shown inside brackets.
 performance.tooltip=Performance (%S)
 
@@ -59,17 +58,16 @@ ToolboxTabWebconsole.label=Console
 ToolboxWebConsole.panelLabel=Console Panel
 
 # LOCALIZATION NOTE (ToolboxWebconsole.tooltip2): the string displayed in the
 # tooltip of the tab when the Web Console is displayed inside the developer
 # tools window.
 # Keyboard shortcut for Console will be shown inside the brackets.
 ToolboxWebconsole.tooltip2=Web Console (%S)
 
-cmd.commandkey=K
 webConsoleCmd.accesskey=W
 
 # LOCALIZATION NOTE (ToolboxDebugger.label):
 # This string is displayed in the title of the tab when the debugger is
 # displayed inside the developer tools window and in the Developer Tools Menu.
 ToolboxDebugger.label=Debugger
 
 # LOCALIZATION NOTE (ToolboxDebugger.panelLabel):
@@ -77,19 +75,18 @@ ToolboxDebugger.label=Debugger
 ToolboxDebugger.panelLabel=Debugger Panel
 
 # LOCALIZATION NOTE (ToolboxDebugger.tooltip2):
 # This string is displayed in the tooltip of the tab when the debugger is
 # displayed inside the developer tools window..
 # A keyboard shortcut for JS Debugger will be shown inside brackets.
 ToolboxDebugger.tooltip2=JavaScript Debugger (%S)
 
-# LOCALIZATION NOTE (debuggerMenu.commandkey, debuggerMenu.accesskey)
+# LOCALIZATION NOTE (debuggerMenu.accesskey)
 # Used for the menuitem in the tool menu
-debuggerMenu.commandkey=S
 debuggerMenu.accesskey=D
 
 # LOCALIZATION NOTE (ToolboxStyleEditor.label):
 # This string is displayed in the title of the tab when the style editor is
 # displayed inside the developer tools window and in the Developer Tools Menu.
 ToolboxStyleEditor.label=Style Editor
 
 # LOCALIZATION NOTE (ToolboxStyleEditor.panelLabel):
@@ -97,20 +94,16 @@ ToolboxStyleEditor.label=Style Editor
 ToolboxStyleEditor.panelLabel=Style Editor Panel
 
 # LOCALIZATION NOTE (ToolboxStyleEditor.tooltip3):
 # This string is displayed in the tooltip of the tab when the style editor is
 # displayed inside the developer tools window.
 # A keyboard shortcut for Stylesheet Editor will be shown inside the latter pair of brackets.
 ToolboxStyleEditor.tooltip3=Stylesheet Editor (CSS) (%S)
 
-# LOCALIZATION NOTE  (open.commandkey): This the key to use in
-# conjunction with shift to open the style editor
-open.commandkey=VK_F7
-
 # LOCALIZATION NOTE (open.accesskey): The access key used to open the style
 # editor.
 open.accesskey=l
 
 # LOCALIZATION NOTE (ToolboxShaderEditor.label):
 # This string is displayed in the title of the tab when the Shader Editor is
 # displayed inside the developer tools window and in the Developer Tools Menu.
 ToolboxShaderEditor.label=Shader Editor
@@ -150,17 +143,16 @@ ToolboxWebAudioEditor1.panelLabel=Web Au
 # LOCALIZATION NOTE (ToolboxWebAudioEditor1.tooltip):
 # This string is displayed in the tooltip of the tab when the Web Audio Editor is
 # displayed inside the developer tools window.
 ToolboxWebAudioEditor1.tooltip=Web Audio context visualizer and audio node inspector
 
 # LOCALIZATION NOTE (inspector.*)
 # Used for the menuitem in the tool menu
 inspector.label=Inspector
-inspector.commandkey=C
 inspector.accesskey=I
 
 # LOCALIZATION NOTE (inspector.panelLabel)
 # Labels applied to the panel and views within the panel in the toolbox
 inspector.panelLabel=Inspector Panel
 
 # LOCALIZATION NOTE (inspector.tooltip2)
 # Keyboard shortcut for DOM and Style Inspector will be shown inside brackets.
@@ -170,31 +162,26 @@ inspector.tooltip2=DOM and Style Inspect
 # This string is displayed in the title of the tab when the Network Monitor is
 # displayed inside the developer tools window and in the Developer Tools Menu.
 netmonitor.label=Network
 
 # LOCALIZATION NOTE (netmonitor.panelLabel):
 # This is used as the label for the toolbox panel.
 netmonitor.panelLabel=Network Panel
 
-# LOCALIZATION NOTE (netmonitor.commandkey2, netmonitor.accesskey)
+# LOCALIZATION NOTE (netmonitor.accesskey)
 # Used for the menuitem in the tool menu
-netmonitor.commandkey2=E
 netmonitor.accesskey=N
 
 # LOCALIZATION NOTE (netmonitor.tooltip2):
 # This string is displayed in the tooltip of the tab when the Network Monitor is
 # displayed inside the developer tools window.
 # Keyboard shortcut for Network Monitor will be shown inside the brackets.
 netmonitor.tooltip2=Network Monitor (%S)
 
-# LOCALIZATION NOTE  (storage.commandkey): This the key to use in
-# conjunction with shift to open the storage editor
-storage.commandkey=VK_F9
-
 # LOCALIZATION NOTE (storage.accesskey): The access key used to open the storage
 # editor.
 storage.accesskey=a
 
 # LOCALIZATION NOTE (storage.label):
 # This string is displayed as the label of the tab in the developer tools window
 storage.label=Storage
 
@@ -245,19 +232,18 @@ memory.tooltip=Memory
 # This string is displayed in the title of the tab when the DOM panel is
 # displayed inside the developer tools window and in the Developer Tools Menu.
 dom.label=DOM
 
 # LOCALIZATION NOTE (dom.panelLabel):
 # This is used as the label for the toolbox panel.
 dom.panelLabel=DOM Panel
 
-# LOCALIZATION NOTE (dom.commandkey, dom.accesskey)
+# LOCALIZATION NOTE (dom.accesskey)
 # Used for the menuitem in the tool menu
-dom.commandkey=W
 dom.accesskey=D
 
 # LOCALIZATION NOTE (dom.tooltip):
 # This string is displayed in the tooltip of the tab when the DOM is
 # displayed inside the developer tools window.
 # Keyboard shortcut for DOM panel will be shown inside the brackets.
 dom.tooltip=DOM (%S)
 
--- a/devtools/client/menus.js
+++ b/devtools/client/menus.js
@@ -11,62 +11,44 @@
  *
  * Various fields are necessary for historical compatiblity with XUL/addons:
  * - id:
  *   used as <xul:menuitem> id attribute
  * - l10nKey:
  *   prefix used to locale localization strings from menus.properties
  * - oncommand:
  *   function called when the menu item or key shortcut are fired
- * - key:
- *    - id:
- *      prefixed by 'key_' to compute <xul:key> id attribute
- *    - modifiers:
- *      optional modifiers for the key shortcut
- *    - keytext:
- *      boolean, to set to true for key shortcut using regular character
- * - additionalKeys:
- *   Array of additional keys, see `key` definition.
+ * - keyId:
+ *   Identifier used in devtools/client/devtools-startup.js
+ *   Helps figuring out the DOM id for the related <xul:key>
+ *   in order to have the key text displayed in menus.
  * - disabled:
  *   If true, the menuitem and key shortcut are going to be hidden and disabled
  *   on startup, until some runtime code eventually enable them.
  * - checkbox:
  *   If true, the menuitem is prefixed by a checkbox and runtime code can
  *   toggle it.
  */
 
-const Services = require("Services");
-const isMac = Services.appinfo.OS === "Darwin";
-
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 
 loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");
 loader.lazyImporter(this, "ResponsiveUIManager", "resource://devtools/client/responsivedesign/responsivedesign.jsm");
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 exports.menuitems = [
   { id: "menu_devToolbox",
     l10nKey: "devToolboxMenuItem",
     oncommand(event) {
       let window = event.target.ownerDocument.defaultView;
       gDevToolsBrowser.toggleToolboxCommand(window.gBrowser);
     },
-    key: {
-      id: "devToolboxMenuItem",
-      modifiers: isMac ? "accel,alt" : "accel,shift",
-      // This is the only one with a letter key
-      // and needs to be translated differently
-      keytext: true,
-    },
-    additionalKeys: [{
-      id: "devToolboxMenuItemF12",
-      l10nKey: "devToolsCmd",
-    }],
+    keyId: "toggleToolbox",
     checkbox: true
   },
   { id: "menu_devtools_separator",
     separator: true },
   { id: "menu_devToolbar",
     l10nKey: "devToolbarMenu",
     disabled: true,
     oncommand(event) {
@@ -75,76 +57,58 @@ exports.menuitems = [
       // or close the toolbar and when hitting the key shortcut where we just
       // focus the toolbar if it doesn't already has it.
       if (event.target.tagName.toLowerCase() == "menuitem") {
         gDevToolsBrowser.getDeveloperToolbar(window).toggle();
       } else {
         gDevToolsBrowser.getDeveloperToolbar(window).focusToggle();
       }
     },
-    key: {
-      id: "devToolbar",
-      modifiers: "shift"
-    },
+    keyId: "toggleToolbar",
     checkbox: true
   },
   { id: "menu_webide",
     l10nKey: "webide",
     disabled: true,
     oncommand() {
       gDevToolsBrowser.openWebIDE();
     },
-    key: {
-      id: "webide",
-      modifiers: "shift"
-    }
+    keyId: "webide",
   },
   { id: "menu_browserToolbox",
     l10nKey: "browserToolboxMenu",
     disabled: true,
     oncommand() {
       BrowserToolboxProcess.init();
     },
-    key: {
-      id: "browserToolbox",
-      modifiers: "accel,alt,shift",
-      keytext: true
-    }
+    keyId: "browserToolbox",
   },
   { id: "menu_browserContentToolbox",
     l10nKey: "browserContentToolboxMenu",
     disabled: true,
     oncommand(event) {
       let window = event.target.ownerDocument.defaultView;
       gDevToolsBrowser.openContentProcessToolbox(window.gBrowser);
     }
   },
   { id: "menu_browserConsole",
     l10nKey: "browserConsoleCmd",
     oncommand() {
       let HUDService = require("devtools/client/webconsole/hudservice");
       HUDService.openBrowserConsoleOrFocus();
     },
-    key: {
-      id: "browserConsole",
-      modifiers: "accel,shift",
-      keytext: true
-    }
+    keyId: "browserConsole",
   },
   { id: "menu_responsiveUI",
     l10nKey: "responsiveDesignMode",
     oncommand(event) {
       let window = event.target.ownerDocument.defaultView;
       ResponsiveUIManager.toggle(window, window.gBrowser.selectedTab);
     },
-    key: {
-      id: "responsiveUI",
-      modifiers: isMac ? "accel,alt" : "accel,shift",
-      keytext: true
-    },
+    keyId: "responsiveDesignMode",
     checkbox: true
   },
   { id: "menu_eyedropper",
     l10nKey: "eyedropper",
     oncommand(event) {
       let window = event.target.ownerDocument.defaultView;
       let target = TargetFactory.forTab(window.gBrowser.selectedTab);
 
@@ -152,20 +116,17 @@ exports.menuitems = [
     },
     checkbox: true
   },
   { id: "menu_scratchpad",
     l10nKey: "scratchpad",
     oncommand() {
       ScratchpadManager.openScratchpad();
     },
-    key: {
-      id: "scratchpad",
-      modifiers: "shift"
-    }
+    keyId: "scratchpad",
   },
   { id: "menu_devtools_serviceworkers",
     l10nKey: "devtoolsServiceWorkers",
     disabled: true,
     oncommand(event) {
       let window = event.target.ownerDocument.defaultView;
       gDevToolsBrowser.openAboutDebugging(window.gBrowser, "workers");
     }
--- a/devtools/shared/tests/browser/browser_l10n_localizeMarkup.js
+++ b/devtools/shared/tests/browser/browser_l10n_localizeMarkup.js
@@ -7,31 +7,31 @@
 
 const { localizeMarkup, LocalizationHelper } = require("devtools/shared/l10n");
 
 add_task(function* () {
   info("Check that the strings used for this test are still valid");
   let STARTUP_L10N = new LocalizationHelper("devtools/client/locales/startup.properties");
   let TOOLBOX_L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
   let str1 = STARTUP_L10N.getStr("inspector.label");
-  let str2 = STARTUP_L10N.getStr("inspector.commandkey");
+  let str2 = STARTUP_L10N.getStr("inspector.accesskey");
   let str3 = TOOLBOX_L10N.getStr("toolbox.defaultTitle");
   ok(str1 && str2 && str3, "If this failed, strings should be updated in the test");
 
   info("Create the test markup");
   let div = document.createElement("div");
   div.innerHTML =
   `<div data-localization-bundle="devtools/client/locales/startup.properties">
      <div id="d0" data-localization="content=inspector.someInvalidKey"></div>
      <div id="d1" data-localization="content=inspector.label">Text will disappear</div>
-     <div id="d2" data-localization="content=inspector.label;title=inspector.commandkey">
+     <div id="d2" data-localization="content=inspector.label;title=inspector.accesskey">
      </div>
      <!-- keep the following data-localization on two separate lines -->
      <div id="d3" data-localization="content=inspector.label;
-                                     title=inspector.commandkey"></div>
+                                     title=inspector.accesskey"></div>
      <div id="d4" data-localization="aria-label=inspector.label">Some content</div>
      <div data-localization-bundle="devtools/client/locales/toolbox.properties">
        <div id="d5" data-localization="content=toolbox.defaultTitle"></div>
      </div>
    </div>
   `;
 
   info("Use localization helper to localize the test markup");