Bug 1290227 - add replacement module for DOM_VK_ constants; r?bgrins draft
authorTom Tromey <tom@tromey.com>
Mon, 08 Aug 2016 15:41:37 -0600
changeset 399967 d3e488b3c21cbd3a85bd2a701ad2edb51f286324
parent 399322 4caf18e4aaca92756f79b2cba666073fe76eabb7
child 528115 2dabd9c43e1b97001ba75bcfee87d2762a6fff5f
push id26050
push userbmo:ttromey@mozilla.com
push dateFri, 12 Aug 2016 13:54:34 +0000
reviewersbgrins
bugs1290227
milestone51.0a1
Bug 1290227 - add replacement module for DOM_VK_ constants; r?bgrins MozReview-Commit-ID: INmMxfP8ZuL
devtools/client/animationinspector/animation-panel.js
devtools/client/debugger/content/views/sources-view.js
devtools/client/debugger/views/filter-view.js
devtools/client/debugger/views/watch-expressions-view.js
devtools/client/framework/toolbox.js
devtools/client/inspector/inspector-search.js
devtools/client/inspector/markup/markup.js
devtools/client/inspector/shared/utils.js
devtools/client/shared/SplitView.jsm
devtools/client/shared/inplace-editor.js
devtools/client/shared/key-shortcuts.js
devtools/client/shared/keycodes.js
devtools/client/shared/moz.build
devtools/client/shared/test/browser.ini
devtools/client/shared/test/browser_flame-graph-05.js
devtools/client/shared/test/browser_keycodes.js
devtools/client/shared/widgets/AbstractTreeItem.jsm
devtools/client/shared/widgets/TableWidget.js
devtools/client/shared/widgets/Tooltip.js
devtools/client/shared/widgets/TreeWidget.js
devtools/client/shared/widgets/VariablesView.jsm
devtools/client/shared/widgets/view-helpers.js
devtools/client/sourceeditor/autocomplete.js
devtools/client/storage/ui.js
devtools/client/styleeditor/StyleEditorUI.jsm
devtools/client/webconsole/jsterm.js
devtools/shared/gcli/source/lib/gcli/util/util.js
--- a/devtools/client/animationinspector/animation-panel.js
+++ b/devtools/client/animationinspector/animation-panel.js
@@ -7,16 +7,17 @@
 /* import-globals-from animation-controller.js */
 /* globals document */
 
 "use strict";
 
 const {AnimationsTimeline} = require("devtools/client/animationinspector/components/animation-timeline");
 const {RateSelector} = require("devtools/client/animationinspector/components/rate-selector");
 const {formatStopwatchTime} = require("devtools/client/animationinspector/utils");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 var $ = (selector, target = document) => target.querySelector(selector);
 
 /**
  * The main animations panel UI.
  */
 var AnimationsPanel = {
   UI_UPDATED_EVENT: "ui-updated",
@@ -157,22 +158,20 @@ var AnimationsPanel = {
       this.onTimelineDataChanged);
 
     if (this.rateSelectorComponent) {
       this.rateSelectorComponent.off("rate-changed", this.onRateChanged);
     }
   },
 
   onKeyDown: function (event) {
-    let keyEvent = Ci.nsIDOMKeyEvent;
-
     // If the space key is pressed, it should toggle the play state of
     // the animations displayed in the panel, or of all the animations on
     // the page if the selected node does not have any animation on it.
-    if (event.keyCode === keyEvent.DOM_VK_SPACE) {
+    if (event.keyCode === KeyCodes.DOM_VK_SPACE) {
       if (AnimationsController.animationPlayers.length > 0) {
         this.playPauseTimeline().catch(ex => console.error(ex));
       } else {
         this.toggleAll().catch(ex => console.error(ex));
       }
       event.preventDefault();
     }
   },
--- a/devtools/client/debugger/content/views/sources-view.js
+++ b/devtools/client/debugger/content/views/sources-view.js
@@ -23,16 +23,17 @@ const { bindActionCreators } = require("
 const {
   Heritage,
   WidgetMethods,
   setNamedTimeout
 } = require("devtools/client/shared/widgets/view-helpers");
 const { Task } = require("devtools/shared/task");
 const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 const { gDevTools } = require("devtools/client/framework/devtools");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
 const FUNCTION_SEARCH_POPUP_POSITION = "topcenter bottomleft";
 const BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH = 1000; // chars
 const BREAKPOINT_CONDITIONAL_POPUP_POSITION = "before_start";
 const BREAKPOINT_CONDITIONAL_POPUP_OFFSET_X = 7; // px
 const BREAKPOINT_CONDITIONAL_POPUP_OFFSET_Y = -3; // px
 
@@ -1201,17 +1202,17 @@ SourcesView.prototype = Heritage.extend(
     this._cbPanel.hidden = true;
     window.emit(EVENTS.CONDITIONAL_BREAKPOINT_POPUP_HIDDEN);
   },
 
   /**
    * The keypress listener for the breakpoints conditional expression textbox.
    */
   _onConditionalTextboxKeyPress: function (e) {
-    if (e.keyCode == e.DOM_VK_RETURN) {
+    if (e.keyCode == KeyCodes.DOM_VK_RETURN) {
       this._hideConditionalPopup();
     }
   },
 
   /**
    * Called when the add breakpoint key sequence was pressed.
    */
   _onCmdAddBreakpoint: function (e) {
--- a/devtools/client/debugger/views/filter-view.js
+++ b/devtools/client/debugger/views/filter-view.js
@@ -4,17 +4,16 @@
  * 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/. */
 /* import-globals-from ../debugger-controller.js */
 /* import-globals-from ../debugger-view.js */
 /* import-globals-from ../utils.js */
 /* globals document, window */
 "use strict";
 
-
 /**
  * Functions handling the filtering UI.
  */
 function FilterView(DebuggerController, DebuggerView) {
   dumpn("FilterView was instantiated");
 
   this.Parser = DebuggerController.Parser;
 
@@ -316,25 +315,25 @@ FilterView.prototype = {
     }
     // Meta+Shift+G and Ctrl+P focus previous matches.
     else if ((e.char == "G" && e.metaKey) || e.char == "p" && e.ctrlKey) {
       actionToPerform = "selectPrev";
     }
     // Return, enter, down and up keys focus next or previous matches, while
     // the escape key switches focus from the search container.
     else switch (e.keyCode) {
-      case e.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_RETURN:
         var isReturnKey = true;
         // If the shift key is pressed, focus on the previous result
         actionToPerform = e.shiftKey ? "selectPrev" : "selectNext";
         break;
-      case e.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_DOWN:
         actionToPerform = "selectNext";
         break;
-      case e.DOM_VK_UP:
+      case KeyCodes.DOM_VK_UP:
         actionToPerform = "selectPrev";
         break;
     }
 
     // If there's no action to perform, or no operator, file line or token
     // were specified, then this is either a broken or empty search.
     if (!actionToPerform || (!operator && !args.length)) {
       this.DebuggerView.editor.dropSelection();
--- a/devtools/client/debugger/views/watch-expressions-view.js
+++ b/devtools/client/debugger/views/watch-expressions-view.js
@@ -4,16 +4,18 @@
  * 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/. */
 /* import-globals-from ../debugger-controller.js */
 /* import-globals-from ../debugger-view.js */
 /* import-globals-from ../utils.js */
 /* globals document */
 "use strict";
 
+const {KeyCodes} = require("devtools/client/shared/keycodes");
+
 /**
  * Functions handling the watch expressions UI.
  */
 function WatchExpressionsView(DebuggerController, DebuggerView) {
   dumpn("WatchExpressionsView was instantiated");
 
   this.StackFrames = DebuggerController.StackFrames;
   this.DebuggerView = DebuggerView;
@@ -284,18 +286,18 @@ WatchExpressionsView.prototype = Heritag
     this.StackFrames.syncWatchExpressions();
   },
 
   /**
    * The keypress listener for a watch expression's textbox.
    */
   _onKeyPress: function (e) {
     switch (e.keyCode) {
-      case e.DOM_VK_RETURN:
-      case e.DOM_VK_ESCAPE:
+      case KeyCodes.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_ESCAPE:
         e.stopPropagation();
         this.DebuggerView.editor.focus();
     }
   }
 });
 
 DebuggerView.WatchExpressions = new WatchExpressionsView(DebuggerController,
                                                          DebuggerView);
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -22,16 +22,17 @@ var {gDevTools} = require("devtools/clie
 var EventEmitter = require("devtools/shared/event-emitter");
 var Telemetry = require("devtools/client/shared/telemetry");
 var HUDService = require("devtools/client/webconsole/hudservice");
 var viewSource = require("devtools/client/shared/view-source");
 var { attachThread, detachThread } = require("./attach-thread");
 var Menu = require("devtools/client/framework/menu");
 var MenuItem = require("devtools/client/framework/menu-item");
 var { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
+const { KeyCodes } = require("devtools/client/shared/keycodes");
 
 const { BrowserLoader } =
   Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 
 loader.lazyGetter(this, "toolboxStrings", () => {
   const properties = "chrome://devtools/locale/toolbox.properties";
   const bundle = Services.strings.createBundle(properties);
   return (name, ...args) => {
@@ -544,17 +545,17 @@ Toolbox.prototype = {
       // Prevent the opening of bookmarks window on toolbox.options.key
       event.preventDefault();
     };
     shortcuts.on(toolboxStrings("toolbox.options.key"), selectOptions);
     shortcuts.on(toolboxStrings("toolbox.help.key"), selectOptions);
   },
 
   _splitConsoleOnKeypress: function (e) {
-    if (e.keyCode === e.DOM_VK_ESCAPE) {
+    if (e.keyCode === KeyCodes.DOM_VK_ESCAPE) {
       this.toggleSplitConsole();
       // If the debugger is paused, don't let the ESC key stop any pending
       // navigation.
       let jsdebugger = this.getPanel("jsdebugger");
       if (jsdebugger && jsdebugger.panelWin.gThreadClient.state == "paused") {
         e.preventDefault();
       }
     }
--- a/devtools/client/inspector/inspector-search.js
+++ b/devtools/client/inspector/inspector-search.js
@@ -1,16 +1,17 @@
 /* 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";
 
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const system = require("devtools/shared/system");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
 
 // Maximum number of selector suggestions shown in the panel.
 const MAX_SUGGESTIONS = 15;
 
@@ -114,23 +115,23 @@ InspectorSearch.prototype = {
   _onKeyDown: function (event) {
     if (this.searchBox.value.length === 0) {
       this.searchClearButton.hidden = true;
       this.searchBox.removeAttribute("filled");
     } else {
       this.searchClearButton.hidden = false;
       this.searchBox.setAttribute("filled", true);
     }
-    if (event.keyCode === event.DOM_VK_RETURN) {
+    if (event.keyCode === KeyCodes.DOM_VK_RETURN) {
       this._onSearch(event.shiftKey);
     }
 
     const modifierKey = system.constants.platform === "macosx"
                         ? event.metaKey : event.ctrlKey;
-    if (event.keyCode === event.DOM_VK_G && modifierKey) {
+    if (event.keyCode === KeyCodes.DOM_VK_G && modifierKey) {
       this._onSearch(event.shiftKey);
       event.preventDefault();
     }
   },
 
   _onClearSearch: function () {
     this.searchBox.value = "";
     this.searchClearButton.hidden = true;
@@ -324,55 +325,55 @@ SelectorAutocompleter.prototype = {
 
   /**
    * Handles keypresses inside the input box.
    */
   _onSearchKeypress: function (event) {
     let popup = this.searchPopup;
 
     switch (event.keyCode) {
-      case event.DOM_VK_RETURN:
-      case event.DOM_VK_TAB:
+      case KeyCodes.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_TAB:
         if (popup.isOpen) {
           if (popup.selectedItem) {
             this.searchBox.value = popup.selectedItem.label;
           }
           this.hidePopup();
         } else if (!popup.isOpen) {
           // When tab is pressed with focus on searchbox and closed popup,
           // do not prevent the default to avoid a keyboard trap and move focus
           // to next/previous element.
           this.emit("processing-done");
           return;
         }
         break;
 
-      case event.DOM_VK_UP:
+      case KeyCodes.DOM_VK_UP:
         if (popup.isOpen && popup.itemCount > 0) {
           if (popup.selectedIndex === 0) {
             popup.selectedIndex = popup.itemCount - 1;
           } else {
             popup.selectedIndex--;
           }
           this.searchBox.value = popup.selectedItem.label;
         }
         break;
 
-      case event.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_DOWN:
         if (popup.isOpen && popup.itemCount > 0) {
           if (popup.selectedIndex === popup.itemCount - 1) {
             popup.selectedIndex = 0;
           } else {
             popup.selectedIndex++;
           }
           this.searchBox.value = popup.selectedItem.label;
         }
         break;
 
-      case event.DOM_VK_ESCAPE:
+      case KeyCodes.DOM_VK_ESCAPE:
         if (popup.isOpen) {
           this.hidePopup();
         }
         break;
 
       default:
         return;
     }
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -55,16 +55,17 @@ const {PrefObserver} = require("devtools
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 const {template} = require("devtools/shared/gcli/templater");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
 /* eslint-disable mozilla/reject-some-requires */
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 /* eslint-enable mozilla/reject-some-requires */
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
 /**
  * Vocabulary for the purposes of this file:
@@ -2268,22 +2269,22 @@ MarkupContainer.prototype = {
   },
 
   _onKeyDown: function (event) {
     let {target, keyCode, shiftKey} = event;
     let isInput = this.markup._isInputOrTextarea(target);
 
     // Ignore all keystrokes that originated in editors except for when 'Tab' is
     // pressed.
-    if (isInput && keyCode !== event.DOM_VK_TAB) {
+    if (isInput && keyCode !== KeyCodes.DOM_VK_TAB) {
       return;
     }
 
     switch (keyCode) {
-      case event.DOM_VK_TAB:
+      case KeyCodes.DOM_VK_TAB:
         // Only handle 'Tab' if tabbable element is on the edge (first or last).
         if (isInput) {
           // Corresponding tabbable element is editor's next sibling.
           let next = this._wrapMoveFocus(target.nextSibling, shiftKey);
           if (next) {
             event.preventDefault();
             // Keep the editing state if possible.
             if (next._editable) {
@@ -2294,17 +2295,17 @@ MarkupContainer.prototype = {
           }
         } else {
           let next = this._wrapMoveFocus(target, shiftKey);
           if (next) {
             event.preventDefault();
           }
         }
         break;
-      case event.DOM_VK_ESCAPE:
+      case KeyCodes.DOM_VK_ESCAPE:
         this.clearFocus();
         this.markup.getContainer(this.markup._rootNode).elt.focus();
         if (this.isDragging) {
           // Escape when dragging is handled by markup view itself.
           return;
         }
         event.preventDefault();
         break;
--- a/devtools/client/inspector/shared/utils.js
+++ b/devtools/client/inspector/shared/utils.js
@@ -1,22 +1,20 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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";
 
-/* eslint-disable mozilla/reject-some-requires */
-const {Ci} = require("chrome");
-/* eslint-enable mozilla/reject-some-requires */
 const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
 const promise = require("promise");
 const {getCSSLexer} = require("devtools/shared/css-lexer");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 /**
  * Create a child element with a set of attributes.
  *
  * @param {Element} parent
  *        The parent node.
@@ -70,17 +68,17 @@ exports.appendText = appendText;
  *        Current text editor value.
  * @param {number} insertionPoint
  *        The index of the insertion point.
  * @return {Boolean} True if the focus should advance; false if
  *        the character should be inserted.
  */
 function advanceValidate(keyCode, value, insertionPoint) {
   // Only ";" has special handling here.
-  if (keyCode !== Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON) {
+  if (keyCode !== KeyCodes.DOM_VK_SEMICOLON) {
     return false;
   }
 
   // Insert the character provisionally and see what happens.  If we
   // end up with a ";" symbol token, then the semicolon terminates the
   // value.  Otherwise it's been inserted in some spot where it has a
   // valid meaning, like a comment or string.
   value = value.slice(0, insertionPoint) + ";" + value.slice(insertionPoint);
--- a/devtools/client/shared/SplitView.jsm
+++ b/devtools/client/shared/SplitView.jsm
@@ -1,15 +1,19 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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";
 
+const Cu = Components.utils;
+const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const {KeyCodes} = require("devtools/client/shared/keycodes");
+
 this.EXPORTED_SYMBOLS = ["SplitView"];
 
 /* this must be kept in sync with CSS (ie. splitview.css) */
 const LANDSCAPE_MEDIA_QUERY = "(min-width: 701px)";
 
 var bindings = new WeakMap();
 
 /**
@@ -51,26 +55,26 @@ this.SplitView = function SplitView(aRoo
         aEvent.target.tagName == "textbox" ||
         aEvent.target.tagName == "textarea" ||
         aEvent.target.classList.contains("textbox")) {
       return false;
     }
 
     // handle keyboard navigation within the items list
     let newFocusOrdinal;
-    if (aEvent.keyCode == aEvent.DOM_VK_PAGE_UP ||
-        aEvent.keyCode == aEvent.DOM_VK_HOME) {
+    if (aEvent.keyCode == KeyCodes.DOM_VK_PAGE_UP ||
+        aEvent.keyCode == KeyCodes.DOM_VK_HOME) {
       newFocusOrdinal = 0;
-    } else if (aEvent.keyCode == aEvent.DOM_VK_PAGE_DOWN ||
-               aEvent.keyCode == aEvent.DOM_VK_END) {
+    } else if (aEvent.keyCode == KeyCodes.DOM_VK_PAGE_DOWN ||
+               aEvent.keyCode == KeyCodes.DOM_VK_END) {
       newFocusOrdinal = this._nav.childNodes.length - 1;
-    } else if (aEvent.keyCode == aEvent.DOM_VK_UP) {
+    } else if (aEvent.keyCode == KeyCodes.DOM_VK_UP) {
       newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
       newFocusOrdinal--;
-    } else if (aEvent.keyCode == aEvent.DOM_VK_DOWN) {
+    } else if (aEvent.keyCode == KeyCodes.DOM_VK_DOWN) {
       newFocusOrdinal = getFocusedItemWithin(this._nav).getAttribute("data-ordinal");
       newFocusOrdinal++;
     }
     if (newFocusOrdinal !== undefined) {
       aEvent.stopPropagation();
       let el = this.getSummaryElementByOrdinal(newFocusOrdinal);
       if (el) {
         el.focus();
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -21,16 +21,17 @@
  * See editableField() for more options.
  */
 
 "use strict";
 
 const {Ci, Cc} = require("chrome");
 const Services = require("Services");
 const focusManager = Services.focus;
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const CONTENT_TYPES = {
   PLAIN_TEXT: 0,
   CSS_VALUE: 1,
   CSS_MIXED: 2,
   CSS_PROPERTY: 3,
 };
@@ -43,27 +44,27 @@ const FOCUS_FORWARD = focusManager.MOVEF
 const FOCUS_BACKWARD = focusManager.MOVEFOCUS_BACKWARD;
 
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
 
 /**
  * Helper to check if the provided key matches one of the expected keys.
- * Keys will be prefixed with DOM_VK_ and should match a key in nsIDOMKeyEvent.
+ * Keys will be prefixed with DOM_VK_ and should match a key in KeyCodes.
  *
  * @param {String} key
  *        the key to check (can be a keyCode).
  * @param {...String} keys
  *        list of possible keys allowed.
  * @return {Boolean} true if the key matches one of the keys.
  */
 function isKeyIn(key, ...keys) {
   return keys.some(expectedKey => {
-    return key === Ci.nsIDOMKeyEvent["DOM_VK_" + expectedKey];
+    return key === KeyCodes["DOM_VK_" + expectedKey];
   });
 }
 
 /**
  * Mark a span editable.  |editableField| will listen for the span to
  * be focused and create an InlineEditor to handle text input.
  * Changes will be committed when the InlineEditor's input is blurred
  * or dropped when the user presses escape.
--- a/devtools/client/shared/key-shortcuts.js
+++ b/devtools/client/shared/key-shortcuts.js
@@ -2,16 +2,17 @@
  * 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";
 
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const isOSX = Services.appinfo.OS === "Darwin";
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 // List of electron keys mapped to DOM API (DOM_VK_*) key code
 const ElectronKeysMapping = {
   "F1": "DOM_VK_F1",
   "F2": "DOM_VK_F2",
   "F3": "DOM_VK_F3",
   "F4": "DOM_VK_F4",
   "F5": "DOM_VK_F5",
@@ -134,17 +135,17 @@ KeyShortcuts.parseElectronKey = function
   }
 
   if (typeof key === "string" && key.length === 1) {
     // Match any single character
     shortcut.key = key.toLowerCase();
   } else if (key in ElectronKeysMapping) {
     // Maps the others manually to DOM API DOM_VK_*
     key = ElectronKeysMapping[key];
-    shortcut.keyCode = window.KeyboardEvent[key];
+    shortcut.keyCode = KeyCodes[key];
     // Used only to stringify the shortcut
     shortcut.keyCodeString = key;
   } else {
     console.error("Unsupported key:", key);
     return null;
   }
 
   return shortcut;
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/keycodes.js
@@ -0,0 +1,146 @@
+/* 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 was copied (and slightly modified) from
+// devtools/shared/gcli/source/lib/gcli/util/util.js, which in turn
+// says:
+
+/**
+ * Keyboard handling is a mess. http://unixpapa.com/js/key.html
+ * It would be good to use DOM L3 Keyboard events,
+ * http://www.w3.org/TR/2010/WD-DOM-Level-3-Events-20100907/#events-keyboardevents
+ * however only Webkit supports them, and there isn't a shim on Modernizr:
+ * https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
+ * and when the code that uses this KeyEvent was written, nothing was clear,
+ * so instead, we're using this unmodern shim:
+ * http://stackoverflow.com/questions/5681146/chrome-10-keyevent-or-something-similar-to-firefoxs-keyevent
+ * See BUG 664991: GCLI's keyboard handling should be updated to use DOM-L3
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
+ */
+
+exports.KeyCodes = {
+  DOM_VK_CANCEL: 3,
+  DOM_VK_HELP: 6,
+  DOM_VK_BACK_SPACE: 8,
+  DOM_VK_TAB: 9,
+  DOM_VK_CLEAR: 12,
+  DOM_VK_RETURN: 13,
+  DOM_VK_SHIFT: 16,
+  DOM_VK_CONTROL: 17,
+  DOM_VK_ALT: 18,
+  DOM_VK_PAUSE: 19,
+  DOM_VK_CAPS_LOCK: 20,
+  DOM_VK_ESCAPE: 27,
+  DOM_VK_SPACE: 32,
+  DOM_VK_PAGE_UP: 33,
+  DOM_VK_PAGE_DOWN: 34,
+  DOM_VK_END: 35,
+  DOM_VK_HOME: 36,
+  DOM_VK_LEFT: 37,
+  DOM_VK_UP: 38,
+  DOM_VK_RIGHT: 39,
+  DOM_VK_DOWN: 40,
+  DOM_VK_PRINTSCREEN: 44,
+  DOM_VK_INSERT: 45,
+  DOM_VK_DELETE: 46,
+  DOM_VK_0: 48,
+  DOM_VK_1: 49,
+  DOM_VK_2: 50,
+  DOM_VK_3: 51,
+  DOM_VK_4: 52,
+  DOM_VK_5: 53,
+  DOM_VK_6: 54,
+  DOM_VK_7: 55,
+  DOM_VK_8: 56,
+  DOM_VK_9: 57,
+  DOM_VK_SEMICOLON: 59,
+  DOM_VK_EQUALS: 61,
+  DOM_VK_A: 65,
+  DOM_VK_B: 66,
+  DOM_VK_C: 67,
+  DOM_VK_D: 68,
+  DOM_VK_E: 69,
+  DOM_VK_F: 70,
+  DOM_VK_G: 71,
+  DOM_VK_H: 72,
+  DOM_VK_I: 73,
+  DOM_VK_J: 74,
+  DOM_VK_K: 75,
+  DOM_VK_L: 76,
+  DOM_VK_M: 77,
+  DOM_VK_N: 78,
+  DOM_VK_O: 79,
+  DOM_VK_P: 80,
+  DOM_VK_Q: 81,
+  DOM_VK_R: 82,
+  DOM_VK_S: 83,
+  DOM_VK_T: 84,
+  DOM_VK_U: 85,
+  DOM_VK_V: 86,
+  DOM_VK_W: 87,
+  DOM_VK_X: 88,
+  DOM_VK_Y: 89,
+  DOM_VK_Z: 90,
+  DOM_VK_CONTEXT_MENU: 93,
+  DOM_VK_NUMPAD0: 96,
+  DOM_VK_NUMPAD1: 97,
+  DOM_VK_NUMPAD2: 98,
+  DOM_VK_NUMPAD3: 99,
+  DOM_VK_NUMPAD4: 100,
+  DOM_VK_NUMPAD5: 101,
+  DOM_VK_NUMPAD6: 102,
+  DOM_VK_NUMPAD7: 103,
+  DOM_VK_NUMPAD8: 104,
+  DOM_VK_NUMPAD9: 105,
+  DOM_VK_MULTIPLY: 106,
+  DOM_VK_ADD: 107,
+  DOM_VK_SEPARATOR: 108,
+  DOM_VK_SUBTRACT: 109,
+  DOM_VK_DECIMAL: 110,
+  DOM_VK_DIVIDE: 111,
+  DOM_VK_F1: 112,
+  DOM_VK_F2: 113,
+  DOM_VK_F3: 114,
+  DOM_VK_F4: 115,
+  DOM_VK_F5: 116,
+  DOM_VK_F6: 117,
+  DOM_VK_F7: 118,
+  DOM_VK_F8: 119,
+  DOM_VK_F9: 120,
+  DOM_VK_F10: 121,
+  DOM_VK_F11: 122,
+  DOM_VK_F12: 123,
+  DOM_VK_F13: 124,
+  DOM_VK_F14: 125,
+  DOM_VK_F15: 126,
+  DOM_VK_F16: 127,
+  DOM_VK_F17: 128,
+  DOM_VK_F18: 129,
+  DOM_VK_F19: 130,
+  DOM_VK_F20: 131,
+  DOM_VK_F21: 132,
+  DOM_VK_F22: 133,
+  DOM_VK_F23: 134,
+  DOM_VK_F24: 135,
+  DOM_VK_NUM_LOCK: 144,
+  DOM_VK_SCROLL_LOCK: 145,
+  DOM_VK_COMMA: 188,
+  DOM_VK_PERIOD: 190,
+  DOM_VK_SLASH: 191,
+  DOM_VK_BACK_QUOTE: 192,
+  DOM_VK_OPEN_BRACKET: 219,
+  DOM_VK_BACK_SLASH: 220,
+  DOM_VK_CLOSE_BRACKET: 221,
+  DOM_VK_QUOTE: 222,
+  DOM_VK_META: 224,
+
+  // A few that did not appear in gcli, but that are apparently used
+  // in devtools.
+  DOM_VK_COLON: 58,
+  DOM_VK_VOLUME_MUTE: 181,
+  DOM_VK_VOLUME_DOWN: 182,
+  DOM_VK_VOLUME_UP: 183,
+};
--- a/devtools/client/shared/moz.build
+++ b/devtools/client/shared/moz.build
@@ -30,16 +30,17 @@ DevToolsModules(
     'doorhanger.js',
     'file-watcher-worker.js',
     'file-watcher.js',
     'frame-script-utils.js',
     'getjson.js',
     'inplace-editor.js',
     'Jsbeautify.jsm',
     'key-shortcuts.js',
+    'keycodes.js',
     'l10n.js',
     'node-attribute-parser.js',
     'options-view.js',
     'output-parser.js',
     'poller.js',
     'prefs.js',
     'source-utils.js',
     'SplitView.jsm',
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -127,16 +127,17 @@ skip-if = e10s # Bug 1221911, bug 122228
 [browser_html_tooltip_width-auto.js]
 [browser_html_tooltip_xul-wrapper.js]
 [browser_inplace-editor-01.js]
 [browser_inplace-editor-02.js]
 [browser_inplace-editor_autocomplete_01.js]
 [browser_inplace-editor_autocomplete_02.js]
 [browser_inplace-editor_autocomplete_offset.js]
 [browser_inplace-editor_maxwidth.js]
+[browser_keycodes.js]
 [browser_key_shortcuts.js]
 [browser_layoutHelpers.js]
 skip-if = e10s # Layouthelpers test should not run in a content page.
 [browser_layoutHelpers-getBoxQuads.js]
 skip-if = e10s # Layouthelpers test should not run in a content page.
 [browser_mdn-docs-01.js]
 [browser_mdn-docs-02.js]
 [browser_mdn-docs-03.js]
--- a/devtools/client/shared/test/browser_flame-graph-05.js
+++ b/devtools/client/shared/test/browser_flame-graph-05.js
@@ -75,17 +75,25 @@ function* testGraph(host, graph) {
 
   ok(Math.abs(distanceRight - distanceLeft) < 0.1,
     "The graph zoomed correctly towards the center point.");
 }
 
 function pressKeyForTime(graph, keyCode, ms) {
   let deferred = defer();
 
-  graph._onKeyDown({ keyCode });
+  graph._onKeyDown({
+    keyCode,
+    preventDefault: () => { },
+    stopPropagation: () => { },
+  });
 
   setTimeout(() => {
-    graph._onKeyUp({ keyCode });
+    graph._onKeyUp({
+      keyCode,
+      preventDefault: () => { },
+      stopPropagation: () => { },
+    });
     deferred.resolve();
   }, ms);
 
   return deferred.promise;
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/browser_keycodes.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {KeyCodes} = require("devtools/client/shared/keycodes");
+
+add_task(function* () {
+  for (let key in KeyCodes) {
+    is(KeyCodes[key], Ci.nsIDOMKeyEvent[key], "checking value for " + key);
+  }
+});
--- a/devtools/client/shared/widgets/AbstractTreeItem.jsm
+++ b/devtools/client/shared/widgets/AbstractTreeItem.jsm
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { interfaces: Ci, utils: Cu } = Components;
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
+const { KeyCodes } = require("devtools/client/shared/keycodes");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
   "resource://devtools/shared/event-emitter.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
   "resource://gre/modules/Console.jsm");
 
 this.EXPORTED_SYMBOLS = ["AbstractTreeItem"];
@@ -583,67 +584,67 @@ AbstractTreeItem.prototype = {
   /**
    * Handler for the "keypress" event on the element displaying this tree item.
    */
   _onKeyPress: function (e) {
     // Prevent scrolling when pressing navigation keys.
     ViewHelpers.preventScrolling(e);
 
     switch (e.keyCode) {
-      case e.DOM_VK_UP:
+      case KeyCodes.DOM_VK_UP:
         this._focusPrevNode();
         return;
 
-      case e.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_DOWN:
         this._focusNextNode();
         return;
 
-      case e.DOM_VK_LEFT:
+      case KeyCodes.DOM_VK_LEFT:
         if (this._expanded && this._populated) {
           this.collapse();
         } else {
           this._focusParentNode();
         }
         return;
 
-      case e.DOM_VK_RIGHT:
+      case KeyCodes.DOM_VK_RIGHT:
         if (!this._expanded) {
           this.expand();
         } else {
           this._focusNextNode();
         }
         return;
 
-      case e.DOM_VK_PAGE_UP:
+      case KeyCodes.DOM_VK_PAGE_UP:
         let pageUpElement =
           this._getSiblingAtDelta(-this._getNodesPerPageSize());
         // There's a chance that the root node is hidden. In this case, its
         // height will be 0.
         if (pageUpElement && this._getHeight(pageUpElement)) {
           pageUpElement.focus();
         } else {
           this._focusFirstNode();
         }
         return;
 
-      case e.DOM_VK_PAGE_DOWN:
+      case KeyCodes.DOM_VK_PAGE_DOWN:
         let pageDownElement =
           this._getSiblingAtDelta(this._getNodesPerPageSize());
         if (pageDownElement) {
           pageDownElement.focus();
         } else {
           this._focusLastNode();
         }
         return;
 
-      case e.DOM_VK_HOME:
+      case KeyCodes.DOM_VK_HOME:
         this._focusFirstNode();
         return;
 
-      case e.DOM_VK_END:
+      case KeyCodes.DOM_VK_END:
         this._focusLastNode();
         return;
     }
   },
 
   /**
    * Handler for the "focus" event on the element displaying this tree item.
    */
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -4,16 +4,17 @@
 "use strict";
 
 const {Ci} = require("chrome");
 const EventEmitter = require("devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "setNamedTimeout",
   "devtools/client/shared/widgets/view-helpers", true);
 loader.lazyRequireGetter(this, "clearNamedTimeout",
   "devtools/client/shared/widgets/view-helpers", true);
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const AFTER_SCROLL_DELAY = 100;
 
 // Different types of events emitted by the Various components of the
 // TableWidget.
 const EVENTS = {
@@ -465,17 +466,17 @@ TableWidget.prototype = {
 
     let colName;
     let column;
     let visibleCells;
     let index;
     let cell;
 
     switch (event.keyCode) {
-      case event.DOM_VK_UP:
+      case KeyCodes.DOM_VK_UP:
         event.preventDefault();
 
         colName = selectedCell.parentNode.id;
         column = this.columns.get(colName);
         visibleCells = column.visibleCellNodes;
         index = visibleCells.indexOf(selectedCell);
 
         if (index > 0) {
@@ -483,17 +484,17 @@ TableWidget.prototype = {
         } else {
           index = visibleCells.length - 1;
         }
 
         cell = visibleCells[index];
 
         this.emit(EVENTS.ROW_SELECTED, cell.getAttribute("data-id"));
         break;
-      case event.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_DOWN:
         event.preventDefault();
 
         colName = selectedCell.parentNode.id;
         column = this.columns.get(colName);
         visibleCells = column.visibleCellNodes;
         index = visibleCells.indexOf(selectedCell);
 
         if (index === visibleCells.length - 1) {
@@ -1660,24 +1661,24 @@ EditableFieldsEngine.prototype = {
    *         The calling event.
    */
   onKeydown: function (event) {
     if (!this.textbox) {
       return;
     }
 
     switch (event.keyCode) {
-      case event.DOM_VK_ESCAPE:
+      case KeyCodes.DOM_VK_ESCAPE:
         this.cancelEdit();
         event.preventDefault();
         break;
-      case event.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_RETURN:
         this.completeEdit();
         break;
-      case event.DOM_VK_TAB:
+      case KeyCodes.DOM_VK_TAB:
         if (this.onTab) {
           this.onTab(event);
         }
         break;
     }
   },
 
   /**
--- a/devtools/client/shared/widgets/Tooltip.js
+++ b/devtools/client/shared/widgets/Tooltip.js
@@ -1,41 +1,41 @@
 /* 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";
 
-const {Ci} = require("chrome");
 const defer = require("devtools/shared/defer");
 const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
 const {CubicBezierWidget} =
       require("devtools/client/shared/widgets/CubicBezierWidget");
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
 const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {colorUtils} = require("devtools/shared/css-color");
 const Heritage = require("sdk/core/heritage");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 const {Task} = require("devtools/shared/task");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 loader.lazyRequireGetter(this, "beautify", "devtools/shared/jsbeautify/beautify");
 loader.lazyRequireGetter(this, "setNamedTimeout", "devtools/client/shared/widgets/view-helpers", true);
 loader.lazyRequireGetter(this, "clearNamedTimeout", "devtools/client/shared/widgets/view-helpers", true);
 loader.lazyRequireGetter(this, "setNamedTimeout", "devtools/client/shared/widgets/view-helpers", true);
 
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
   "resource://devtools/client/shared/widgets/VariablesView.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
   "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
-const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE;
+const ESCAPE_KEYCODE = KeyCodes.DOM_VK_ESCAPE;
 const POPUP_EVENTS = ["shown", "hidden", "showing", "hiding"];
 
 /**
  * Tooltip widget.
  *
  * This widget is intended at any tool that may need to show rich content in the
  * form of floating panels.
  * A common use case is image previewing in the CSS rule view, but more complex
--- a/devtools/client/shared/widgets/TreeWidget.js
+++ b/devtools/client/shared/widgets/TreeWidget.js
@@ -3,16 +3,17 @@
 /* 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";
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 const EventEmitter = require("devtools/shared/event-emitter");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 /**
  * A tree widget with keyboard navigation and collapsable structure.
  *
  * @param {nsIDOMNode} node
  *        The container element for the tree widget.
  * @param {Object} options
  *        - emptyText {string}: text to display when no entries in the table.
@@ -340,33 +341,33 @@ TreeWidget.prototype = {
   },
 
   /**
    * Keypress handler for this tree. Used to select next and previous visible
    * items, as well as collapsing and expanding any item.
    */
   onKeypress: function (event) {
     switch (event.keyCode) {
-      case event.DOM_VK_UP:
+      case KeyCodes.DOM_VK_UP:
         this.selectPreviousItem();
         break;
 
-      case event.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_DOWN:
         this.selectNextItem();
         break;
 
-      case event.DOM_VK_RIGHT:
+      case KeyCodes.DOM_VK_RIGHT:
         if (this._selectedLabel.hasAttribute("expanded")) {
           this.selectNextItem();
         } else {
           this._selectedLabel.setAttribute("expanded", "true");
         }
         break;
 
-      case event.DOM_VK_LEFT:
+      case KeyCodes.DOM_VK_LEFT:
         if (this._selectedLabel.hasAttribute("expanded") &&
             !this._selectedLabel.hasAttribute("empty")) {
           this._selectedLabel.removeAttribute("expanded");
         } else {
           this.selectPreviousItem();
         }
         break;
 
--- a/devtools/client/shared/widgets/VariablesView.jsm
+++ b/devtools/client/shared/widgets/VariablesView.jsm
@@ -23,16 +23,17 @@ const DevToolsUtils = require("devtools/
 const Services = require("Services");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const { Heritage, ViewHelpers, setNamedTimeout } =
   require("devtools/client/shared/widgets/view-helpers");
 const { Task } = require("devtools/shared/task");
 const nodeConstants = require("devtools/shared/dom-node-constants");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1",
   "nsIClipboardHelper");
 
@@ -508,20 +509,20 @@ VariablesView.prototype = {
     this.scheduleSearch(this._searchboxNode.value);
   },
 
   /**
    * Listener handling the searchbox key press event.
    */
   _onSearchboxKeyPress: function (e) {
     switch (e.keyCode) {
-      case e.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_RETURN:
         this._onSearchboxInput();
         return;
-      case e.DOM_VK_ESCAPE:
+      case KeyCodes.DOM_VK_ESCAPE:
         this._searchboxNode.value = "";
         this._onSearchboxInput();
         return;
     }
   },
 
   /**
    * Schedules searching for variables or properties matching the query.
@@ -821,100 +822,100 @@ VariablesView.prototype = {
    */
   _onViewKeyPress: function (e) {
     let item = this.getFocusedItem();
 
     // Prevent scrolling when pressing navigation keys.
     ViewHelpers.preventScrolling(e);
 
     switch (e.keyCode) {
-      case e.DOM_VK_UP:
+      case KeyCodes.DOM_VK_UP:
         // Always rewind focus.
         this.focusPrevItem(true);
         return;
 
-      case e.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_DOWN:
         // Always advance focus.
         this.focusNextItem(true);
         return;
 
-      case e.DOM_VK_LEFT:
+      case KeyCodes.DOM_VK_LEFT:
         // Collapse scopes, variables and properties before rewinding focus.
         if (item._isExpanded && item._isArrowVisible) {
           item.collapse();
         } else {
           this._focusItem(item.ownerView);
         }
         return;
 
-      case e.DOM_VK_RIGHT:
+      case KeyCodes.DOM_VK_RIGHT:
         // Nothing to do here if this item never expands.
         if (!item._isArrowVisible) {
           return;
         }
         // Expand scopes, variables and properties before advancing focus.
         if (!item._isExpanded) {
           item.expand();
         } else {
           this.focusNextItem(true);
         }
         return;
 
-      case e.DOM_VK_PAGE_UP:
+      case KeyCodes.DOM_VK_PAGE_UP:
         // Rewind a certain number of elements based on the container height.
         this.focusItemAtDelta(-(this.scrollPageSize || Math.min(Math.floor(this._list.scrollHeight /
           PAGE_SIZE_SCROLL_HEIGHT_RATIO),
           PAGE_SIZE_MAX_JUMPS)));
         return;
 
-      case e.DOM_VK_PAGE_DOWN:
+      case KeyCodes.DOM_VK_PAGE_DOWN:
         // Advance a certain number of elements based on the container height.
         this.focusItemAtDelta(+(this.scrollPageSize || Math.min(Math.floor(this._list.scrollHeight /
           PAGE_SIZE_SCROLL_HEIGHT_RATIO),
           PAGE_SIZE_MAX_JUMPS)));
         return;
 
-      case e.DOM_VK_HOME:
+      case KeyCodes.DOM_VK_HOME:
         this.focusFirstVisibleItem();
         return;
 
-      case e.DOM_VK_END:
+      case KeyCodes.DOM_VK_END:
         this.focusLastVisibleItem();
         return;
 
-      case e.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_RETURN:
         // Start editing the value or name of the Variable or Property.
         if (item instanceof Variable) {
           if (e.metaKey || e.altKey || e.shiftKey) {
             item._activateNameInput();
           } else {
             item._activateValueInput();
           }
         }
         return;
 
-      case e.DOM_VK_DELETE:
-      case e.DOM_VK_BACK_SPACE:
+      case KeyCodes.DOM_VK_DELETE:
+      case KeyCodes.DOM_VK_BACK_SPACE:
         // Delete the Variable or Property if allowed.
         if (item instanceof Variable) {
           item._onDelete(e);
         }
         return;
 
-      case e.DOM_VK_INSERT:
+      case KeyCodes.DOM_VK_INSERT:
         item._onAddProperty(e);
         return;
     }
   },
 
   /**
    * Listener handling a key down event on the view.
    */
   _onViewKeyDown: function (e) {
-    if (e.keyCode == e.DOM_VK_C) {
+    if (e.keyCode == KeyCodes.DOM_VK_C) {
       // Copy current selection to clipboard.
       if (e.ctrlKey || e.metaKey) {
         let item = this.getFocusedItem();
         clipboardHelper.copyString(
           item._nameString + item.separatorStr + item._valueString
         );
       }
     }
@@ -4077,23 +4078,23 @@ Editable.prototype = {
 
   /**
    * Event handler for when the input receives a key press.
    */
   _onKeypress: function (e) {
     e.stopPropagation();
 
     switch (e.keyCode) {
-      case e.DOM_VK_TAB:
+      case KeyCodes.DOM_VK_TAB:
         this._next();
         break;
-      case e.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_RETURN:
         this._save();
         break;
-      case e.DOM_VK_ESCAPE:
+      case KeyCodes.DOM_VK_ESCAPE:
         this._reset();
         break;
     }
   },
 };
 
 
 /**
--- a/devtools/client/shared/widgets/view-helpers.js
+++ b/devtools/client/shared/widgets/view-helpers.js
@@ -1,16 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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";
 
 const { Ci } = require("chrome");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const PANE_APPEARANCE_DELAY = 50;
 const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
 const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
 
 /**
  * Inheritance helpers from the addon SDK's core/heritage.
  * Remove these when all devtools are loadered.
@@ -198,38 +199,38 @@ const ViewHelpers = exports.ViewHelpers 
   /**
    * Prevents event propagation when navigation keys are pressed.
    *
    * @param Event e
    *        The event to be prevented.
    */
   preventScrolling: function (e) {
     switch (e.keyCode) {
-      case e.DOM_VK_UP:
-      case e.DOM_VK_DOWN:
-      case e.DOM_VK_LEFT:
-      case e.DOM_VK_RIGHT:
-      case e.DOM_VK_PAGE_UP:
-      case e.DOM_VK_PAGE_DOWN:
-      case e.DOM_VK_HOME:
-      case e.DOM_VK_END:
+      case KeyCodes.DOM_VK_UP:
+      case KeyCodes.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_LEFT:
+      case KeyCodes.DOM_VK_RIGHT:
+      case KeyCodes.DOM_VK_PAGE_UP:
+      case KeyCodes.DOM_VK_PAGE_DOWN:
+      case KeyCodes.DOM_VK_HOME:
+      case KeyCodes.DOM_VK_END:
         e.preventDefault();
         e.stopPropagation();
     }
   },
 
   /**
    * Check if the enter key or space was pressed
    *
    * @param event event
    *        The event triggered by a keypress on an element
    */
   isSpaceOrReturn: function (event) {
-    return event.keyCode === event.DOM_VK_SPACE ||
-          event.keyCode === event.DOM_VK_RETURN;
+    return event.keyCode === KeyCodes.DOM_VK_SPACE ||
+          event.keyCode === KeyCodes.DOM_VK_RETURN;
   },
 
   /**
    * Sets a toggled pane hidden or visible. The pane can either be displayed on
    * the side (right or left depending on the locale) or at the bottom.
    *
    * @param object flags
    *        An object containing some of the following properties:
@@ -1498,36 +1499,36 @@ const WidgetMethods = exports.WidgetMeth
    * @param string name
    * @param KeyboardEvent event
    */
   _onWidgetKeyPress: function (name, event) {
     // Prevent scrolling when pressing navigation keys.
     ViewHelpers.preventScrolling(event);
 
     switch (event.keyCode) {
-      case event.DOM_VK_UP:
-      case event.DOM_VK_LEFT:
+      case KeyCodes.DOM_VK_UP:
+      case KeyCodes.DOM_VK_LEFT:
         this.focusPrevItem();
         return;
-      case event.DOM_VK_DOWN:
-      case event.DOM_VK_RIGHT:
+      case KeyCodes.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_RIGHT:
         this.focusNextItem();
         return;
-      case event.DOM_VK_PAGE_UP:
+      case KeyCodes.DOM_VK_PAGE_UP:
         this.focusItemAtDelta(-(this.pageSize ||
                                (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
         return;
-      case event.DOM_VK_PAGE_DOWN:
+      case KeyCodes.DOM_VK_PAGE_DOWN:
         this.focusItemAtDelta(+(this.pageSize ||
                                (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
         return;
-      case event.DOM_VK_HOME:
+      case KeyCodes.DOM_VK_HOME:
         this.focusFirstVisibleItem();
         return;
-      case event.DOM_VK_END:
+      case KeyCodes.DOM_VK_END:
         this.focusLastVisibleItem();
         return;
     }
   },
 
   /**
    * The mousePress event listener for this container.
    * @param string name
--- a/devtools/client/sourceeditor/autocomplete.js
+++ b/devtools/client/sourceeditor/autocomplete.js
@@ -2,16 +2,17 @@
  * 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";
 
 const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
 const { AutocompletePopup } = require("devtools/client/shared/autocomplete-popup");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const CM_TERN_SCRIPTS = [
   "chrome://devtools/content/sourceeditor/codemirror/addon/tern/tern.js",
   "chrome://devtools/content/sourceeditor/codemirror/addon/hint/show-hint.js"
 ];
 
 const autocompleteMap = new WeakMap();
 
@@ -311,49 +312,49 @@ function onEditorKeypress({ ed, Editor }
 
   // Do not try to autocomplete with multiple selections.
   if (ed.hasMultipleSelections()) {
     autocompleteOpts.doNotAutocomplete = true;
     autocompleteOpts.popup.hidePopup();
     return;
   }
 
-  if ((event.ctrlKey || event.metaKey) && event.keyCode == event.DOM_VK_SPACE) {
+  if ((event.ctrlKey || event.metaKey) && event.keyCode == KeyCodes.DOM_VK_SPACE) {
     // When Ctrl/Cmd + Space is pressed, two simultaneous keypresses are emitted
     // first one for just the Ctrl/Cmd and second one for combo. The first one
     // leave the autocompleteOpts.doNotAutocomplete as true, so we have to make
     // it false
     autocompleteOpts.doNotAutocomplete = false;
     return;
   }
 
   if (event.ctrlKey || event.metaKey || event.altKey) {
     autocompleteOpts.doNotAutocomplete = true;
     autocompleteOpts.popup.hidePopup();
     return;
   }
 
   switch (event.keyCode) {
-    case event.DOM_VK_RETURN:
+    case KeyCodes.DOM_VK_RETURN:
       autocompleteOpts.doNotAutocomplete = true;
       break;
-    case event.DOM_VK_ESCAPE:
+    case KeyCodes.DOM_VK_ESCAPE:
       if (autocompleteOpts.popup.isOpen) {
         event.preventDefault();
       }
       break;
-    case event.DOM_VK_LEFT:
-    case event.DOM_VK_RIGHT:
-    case event.DOM_VK_HOME:
-    case event.DOM_VK_END:
+    case KeyCodes.DOM_VK_LEFT:
+    case KeyCodes.DOM_VK_RIGHT:
+    case KeyCodes.DOM_VK_HOME:
+    case KeyCodes.DOM_VK_END:
       autocompleteOpts.doNotAutocomplete = true;
       autocompleteOpts.popup.hidePopup();
       break;
-    case event.DOM_VK_BACK_SPACE:
-    case event.DOM_VK_DELETE:
+    case KeyCodes.DOM_VK_BACK_SPACE:
+    case KeyCodes.DOM_VK_DELETE:
       if (ed.config.mode == Editor.modes.css) {
         autocompleteOpts.completer.invalidateCache(ed.getCursor().line);
       }
       autocompleteOpts.doNotAutocomplete = true;
       autocompleteOpts.popup.hidePopup();
       break;
     default:
       autocompleteOpts.doNotAutocomplete = false;
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -5,16 +5,17 @@
 
 "use strict";
 
 const {Task} = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {LocalizationHelper} = require("devtools/client/shared/l10n");
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 const JSOL = require("devtools/client/shared/vendor/jsol");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 loader.lazyRequireGetter(this, "TreeWidget",
                          "devtools/client/shared/widgets/TreeWidget", true);
 loader.lazyRequireGetter(this, "TableWidget",
                          "devtools/client/shared/widgets/TableWidget", true);
 loader.lazyRequireGetter(this, "ViewHelpers",
                          "devtools/client/shared/widgets/view-helpers");
 loader.lazyImporter(this, "VariablesView",
@@ -840,17 +841,17 @@ StorageUI.prototype = {
 
   /**
    * Handles keypress event on the body table to close the sidebar when open
    *
    * @param {DOMEvent} event
    *        The event passed by the keypress event.
    */
   handleKeypress: function (event) {
-    if (event.keyCode == event.DOM_VK_ESCAPE && !this.sidebar.hidden) {
+    if (event.keyCode == KeyCodes.DOM_VK_ESCAPE && !this.sidebar.hidden) {
       // Stop Propagation to prevent opening up of split console
       this.hideSidebar();
       event.stopPropagation();
       event.preventDefault();
     }
   },
 
   /**
--- a/devtools/client/styleeditor/StyleEditorUI.jsm
+++ b/devtools/client/styleeditor/StyleEditorUI.jsm
@@ -29,16 +29,17 @@ loader.lazyImporter(this, "PluralForm", 
 const {PrefObserver, PREF_ORIG_SOURCES} =
       require("devtools/client/styleeditor/utils");
 const csscoverage = require("devtools/shared/fronts/csscoverage");
 const {console} = require("resource://gre/modules/Console.jsm");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const {ResponsiveUIManager} =
   require("resource://devtools/client/responsivedesign/responsivedesign.jsm");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const LOAD_ERROR = "error-load";
 const STYLE_EDITOR_TEMPLATE = "stylesheet";
 const SELECTOR_HIGHLIGHTER_TYPE = "SelectorHighlighter";
 const PREF_MEDIA_SIDEBAR = "devtools.styleeditor.showMediaSidebar";
 const PREF_SIDEBAR_WIDTH = "devtools.styleeditor.mediaSidebarWidth";
 const PREF_NAV_WIDTH = "devtools.styleeditor.navSidebarWidth";
 
@@ -527,17 +528,17 @@ StyleEditorUI.prototype = {
           event.target.blur();
 
           createdEditor.toggleDisabled();
         });
 
         wire(summary, ".stylesheet-name", {
           events: {
             "keypress": (event) => {
-              if (event.keyCode == event.DOM_VK_RETURN) {
+              if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
                 this._view.activeSummary = summary;
               }
             }
           }
         });
 
         wire(summary, ".stylesheet-saveButton", function onSaveButton(event) {
           event.stopPropagation();
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -1,23 +1,22 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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";
 
-const {Ci} = require("chrome");
-
 const {Utils: WebConsoleUtils} =
   require("devtools/client/webconsole/utils");
 const promise = require("promise");
 const Debugger = require("Debugger");
 const Services = require("Services");
+const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
 loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
@@ -685,17 +684,17 @@ JSTerm.prototype = {
    * is used for removing the sidebar when Escape is pressed.
    *
    * @private
    * @param nsIDOMEvent event
    *        The keypress DOM event object.
    */
   _onKeypressInVariablesView: function (event) {
     let tag = event.target.nodeName;
-    if (event.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE || event.shiftKey ||
+    if (event.keyCode != KeyCodes.DOM_VK_ESCAPE || event.shiftKey ||
         event.altKey || event.ctrlKey || event.metaKey ||
         ["input", "textarea", "select", "textbox"].indexOf(tag) > -1) {
       return;
     }
 
     this._sidebarDestroy();
     this.focus();
     event.stopPropagation();
@@ -1114,139 +1113,139 @@ JSTerm.prototype = {
             this.focus();
           }
           this.clearCompletion();
           break;
         default:
           break;
       }
       return;
-    } else if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
+    } else if (event.keyCode == KeyCodes.DOM_VK_RETURN) {
       let autoMultiline = Services.prefs.getBoolPref(PREF_AUTO_MULTILINE);
       if (event.shiftKey ||
           (!Debugger.isCompilableUnit(inputNode.value) && autoMultiline)) {
         // shift return or incomplete statement
         return;
       }
     }
 
     switch (event.keyCode) {
-      case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE:
+      case KeyCodes.DOM_VK_ESCAPE:
         if (this.autocompletePopup.isOpen) {
           this.clearCompletion();
           event.preventDefault();
           event.stopPropagation();
         } else if (this.sidebar) {
           this._sidebarDestroy();
           event.preventDefault();
           event.stopPropagation();
         }
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_RETURN:
+      case KeyCodes.DOM_VK_RETURN:
         if (this._autocompletePopupNavigated &&
             this.autocompletePopup.isOpen &&
             this.autocompletePopup.selectedIndex > -1) {
           this.acceptProposedCompletion();
         } else {
           this.execute();
           this._inputChanged = false;
         }
         event.preventDefault();
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_UP:
+      case KeyCodes.DOM_VK_UP:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_BACKWARD);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         } else if (this.canCaretGoPrevious()) {
           inputUpdated = this.historyPeruse(HISTORY_BACK);
         }
         if (inputUpdated) {
           event.preventDefault();
         }
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_DOWN:
+      case KeyCodes.DOM_VK_DOWN:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_FORWARD);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         } else if (this.canCaretGoNext()) {
           inputUpdated = this.historyPeruse(HISTORY_FORWARD);
         }
         if (inputUpdated) {
           event.preventDefault();
         }
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP:
+      case KeyCodes.DOM_VK_PAGE_UP:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_PAGEUP);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         } else {
           this.hud.outputWrapper.scrollTop =
             Math.max(0,
               this.hud.outputWrapper.scrollTop -
               this.hud.outputWrapper.clientHeight
             );
         }
         event.preventDefault();
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN:
+      case KeyCodes.DOM_VK_PAGE_DOWN:
         if (this.autocompletePopup.isOpen) {
           inputUpdated = this.complete(this.COMPLETE_PAGEDOWN);
           if (inputUpdated) {
             this._autocompletePopupNavigated = true;
           }
         } else {
           this.hud.outputWrapper.scrollTop =
             Math.min(this.hud.outputWrapper.scrollHeight,
               this.hud.outputWrapper.scrollTop +
               this.hud.outputWrapper.clientHeight
             );
         }
         event.preventDefault();
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_HOME:
+      case KeyCodes.DOM_VK_HOME:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectedIndex = 0;
           event.preventDefault();
         } else if (inputValue.length <= 0) {
           this.hud.outputWrapper.scrollTop = 0;
           event.preventDefault();
         }
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_END:
+      case KeyCodes.DOM_VK_END:
         if (this.autocompletePopup.isOpen) {
           this.autocompletePopup.selectedIndex =
             this.autocompletePopup.itemCount - 1;
           event.preventDefault();
         } else if (inputValue.length <= 0) {
           this.hud.outputWrapper.scrollTop =
             this.hud.outputWrapper.scrollHeight;
           event.preventDefault();
         }
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
+      case KeyCodes.DOM_VK_LEFT:
         if (this.autocompletePopup.isOpen || this.lastCompletion.value) {
           this.clearCompletion();
         }
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_RIGHT:
+      case KeyCodes.DOM_VK_RIGHT:
         let cursorAtTheEnd = this.inputNode.selectionStart ==
                              this.inputNode.selectionEnd &&
                              this.inputNode.selectionStart ==
                              inputValue.length;
         let haveSuggestion = this.autocompletePopup.isOpen ||
                              this.lastCompletion.value;
         let useCompletion = cursorAtTheEnd || this._autocompletePopupNavigated;
         if (haveSuggestion && useCompletion &&
@@ -1255,17 +1254,17 @@ JSTerm.prototype = {
             this.acceptProposedCompletion()) {
           event.preventDefault();
         }
         if (this.autocompletePopup.isOpen) {
           this.clearCompletion();
         }
         break;
 
-      case Ci.nsIDOMKeyEvent.DOM_VK_TAB:
+      case KeyCodes.DOM_VK_TAB:
         // Generate a completion and accept the first proposed value.
         if (this.complete(this.COMPLETE_HINT_ONLY) &&
             this.lastCompletion &&
             this.acceptProposedCompletion()) {
           event.preventDefault();
         } else if (this._inputChanged) {
           this.updateCompleteNode(l10n.getStr("Autocomplete.blank"));
           event.preventDefault();
--- a/devtools/shared/gcli/source/lib/gcli/util/util.js
+++ b/devtools/shared/gcli/source/lib/gcli/util/util.js
@@ -554,17 +554,17 @@ exports.createEmptyNodeList = function(d
 };
 
 //------------------------------------------------------------------------------
 
 /**
  * Keyboard handling is a mess. http://unixpapa.com/js/key.html
  * It would be good to use DOM L3 Keyboard events,
  * http://www.w3.org/TR/2010/WD-DOM-Level-3-Events-20100907/#events-keyboardevents
- * however only Webkit supports them, and there isn't a shim on Monernizr:
+ * however only Webkit supports them, and there isn't a shim on Modernizr:
  * https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
  * and when the code that uses this KeyEvent was written, nothing was clear,
  * so instead, we're using this unmodern shim:
  * http://stackoverflow.com/questions/5681146/chrome-10-keyevent-or-something-similar-to-firefoxs-keyevent
  * See BUG 664991: GCLI's keyboard handling should be updated to use DOM-L3
  * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
  */
 if (typeof 'KeyEvent' === 'undefined') {