Bug 1456852 - Programatically create edit menu in the browser console input draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Mon, 09 Jul 2018 13:00:46 -0700
changeset 815726 9333edb4223bd272b64b7b4e19fca1e5575ed139
parent 815724 6999a8bdf7cda86d5d73214847a780dbc7147ca2
push id115627
push userbgrinstead@mozilla.com
push dateMon, 09 Jul 2018 20:01:08 +0000
bugs1456852
milestone63.0a1
Bug 1456852 - Programatically create edit menu in the browser console input MozReview-Commit-ID: FIcT0MTjaiO
devtools/client/framework/menu-item.js
devtools/client/framework/menu.js
devtools/client/webconsole/browserconsole.xul
devtools/client/webconsole/components/JSTerm.js
devtools/client/webconsole/index.html
--- a/devtools/client/framework/menu-item.js
+++ b/devtools/client/framework/menu-item.js
@@ -46,28 +46,30 @@
  *      the type: 'submenu' can be omitted. If the value is not a Menu then it
  *      will be automatically converted to one using Menu.buildFromTemplate.
  *    Boolean visible
  *      If false, the menu item will be entirely hidden.
  */
 function MenuItem({
     accelerator = null,
     accesskey = null,
+    ftl = null,
     checked = false,
     click = () => {},
     disabled = false,
     hover = () => {},
     id = null,
     label = "",
     submenu = null,
     type = "normal",
     visible = true,
 } = { }) {
   this.accelerator = accelerator;
   this.accesskey = accesskey;
+  this.ftl = ftl;
   this.checked = checked;
   this.click = click;
   this.disabled = disabled;
   this.hover = hover;
   this.id = id;
   this.label = label;
   this.submenu = submenu;
   this.type = type;
--- a/devtools/client/framework/menu.js
+++ b/devtools/client/framework/menu.js
@@ -131,36 +131,51 @@ Menu.prototype._createMenuItems = functi
     }
 
     if (item.submenu) {
       const menupopup = doc.createElementNS(XUL_NS, "menupopup");
       item.submenu._createMenuItems(menupopup);
 
       const menu = doc.createElementNS(XUL_NS, "menu");
       menu.appendChild(menupopup);
-      menu.setAttribute("label", item.label);
+      if (item.ftl) {
+        menu.setAttribute("data-l10n-id", item.ftl);
+      } else {
+        menu.setAttribute("label", item.label);
+        if (item.accelerator) {
+          menu.setAttribute("acceltext", item.accelerator);
+        }
+        if (item.accesskey) {
+          menu.setAttribute("accesskey", item.accesskey);
+        }
+      }
       if (item.disabled) {
         menu.setAttribute("disabled", "true");
       }
-      if (item.accelerator) {
-        menu.setAttribute("acceltext", item.accelerator);
-      }
-      if (item.accesskey) {
-        menu.setAttribute("accesskey", item.accesskey);
-      }
       if (item.id) {
         menu.id = item.id;
       }
       parent.appendChild(menu);
     } else if (item.type === "separator") {
       const menusep = doc.createElementNS(XUL_NS, "menuseparator");
       parent.appendChild(menusep);
     } else {
       const menuitem = doc.createElementNS(XUL_NS, "menuitem");
-      menuitem.setAttribute("label", item.label);
+
+      if (item.ftl) {
+        menuitem.setAttribute("data-l10n-id", item.ftl);
+      } else {
+        menuitem.setAttribute("label", item.label);
+        if (item.accelerator) {
+          menuitem.setAttribute("acceltext", item.accelerator);
+        }
+        if (item.accesskey) {
+          menuitem.setAttribute("accesskey", item.accesskey);
+        }
+      }
       menuitem.addEventListener("command", () => {
         item.click();
       });
       menuitem.addEventListener("DOMMenuItemActive", () => {
         item.hover();
       });
 
       if (item.type === "checkbox") {
@@ -170,22 +185,16 @@ Menu.prototype._createMenuItems = functi
         menuitem.setAttribute("type", "radio");
       }
       if (item.disabled) {
         menuitem.setAttribute("disabled", "true");
       }
       if (item.checked) {
         menuitem.setAttribute("checked", "true");
       }
-      if (item.accelerator) {
-        menuitem.setAttribute("acceltext", item.accelerator);
-      }
-      if (item.accesskey) {
-        menuitem.setAttribute("accesskey", item.accesskey);
-      }
       if (item.id) {
         menuitem.id = item.id;
       }
 
       parent.appendChild(menuitem);
     }
   });
 };
--- a/devtools/client/webconsole/browserconsole.xul
+++ b/devtools/client/webconsole/browserconsole.xul
@@ -6,11 +6,13 @@
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="devtools-webconsole"
         macanimationtype="document"
         fullscreenbutton="true"
         title=""
         windowtype="devtools:webconsole"
         width="900" height="350"
         persist="screenX screenY width height sizemode">
+  <link rel="localization" href="toolkit/main-window/editmenu.ftl"/>
+  <script type="text/javascript" src="chrome://global/content/l10n.js"></script>
   <popupset></popupset>
   <iframe src="index.html" flex="1"></iframe>
 </window>
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -16,16 +16,19 @@ loader.lazyRequireGetter(this, "EventEmi
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup");
 loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "KeyCodes", "devtools/client/shared/keycodes", true);
 loader.lazyRequireGetter(this, "Editor", "devtools/client/sourceeditor/editor");
 loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
 loader.lazyRequireGetter(this, "processScreenshot", "devtools/shared/webconsole/screenshot-helper");
 
+const Menu = require("devtools/client/framework/menu");
+const MenuItem = require("devtools/client/framework/menu-item");
+
 const l10n = require("devtools/client/webconsole/webconsole-l10n");
 
 const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";
 
 function gSequenceId() {
   return gSequenceId.n++;
 }
 gSequenceId.n = 0;
@@ -1414,16 +1417,81 @@ class JSTerm extends Component {
           tabIndex: "0",
           rows: "1",
           "aria-autocomplete": "list",
           ref: node => {
             this.inputNode = node;
           },
           onPaste: onPaste,
           onDrop: onPaste,
+          onContextMenu: function(e) {
+            const docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIWebNavigation)
+                                   .QueryInterface(Ci.nsIDocShell);
+
+            const menu = new Menu({
+              id: "webconsole-menu"
+            });
+
+            menu.append(new MenuItem({
+              id: "console-menu-undo",
+              ftl: "undo-cmd",
+              disabled: !docshell.isCommandEnabled("cmd_undo"),
+              click: () => {
+                docshell.doCommand("cmd_undo");
+              },
+            }));
+            menu.append(new MenuItem({
+              type: "separator"
+            }));
+            menu.append(new MenuItem({
+              id: "console-menu-cut",
+              ftl: "cut-cmd",
+              disabled: !docshell.isCommandEnabled("cmd_cut"),
+              click: () => {
+                docshell.doCommand("cmd_cut");
+              },
+            }));
+            menu.append(new MenuItem({
+              id: "console-menu-copy",
+              ftl: "copy-cmd",
+              disabled: !docshell.isCommandEnabled("cmd_copy"),
+              click: () => {
+                docshell.doCommand("cmd_copy");
+              },
+            }));
+            menu.append(new MenuItem({
+              id: "console-menu-paste",
+              ftl: "paste-cmd",
+              disabled: !docshell.isCommandEnabled("cmd_paste"),
+              click: () => {
+                docshell.doCommand("cmd_paste");
+              },
+            }));
+            menu.append(new MenuItem({
+              id: "console-menu-delete",
+              ftl: "delete-cmd",
+              disabled: !docshell.isCommandEnabled("cmd_delete"),
+              click: () => {
+                docshell.doCommand("cmd_delete");
+              },
+            }));
+            menu.append(new MenuItem({
+              type: "separator"
+            }));
+            menu.append(new MenuItem({
+              id: "console-menu-selectAll",
+              ftl: "select-all-cmd",
+              disabled: !docshell.isCommandEnabled("cmd_selectAll"),
+              click: () => {
+                docshell.doCommand("cmd_selectAll");
+              },
+            }));
+            menu.popup(e.screenX, e.screenY, { doc: window.top.document });
+          }
         })
       )
     );
   }
 }
 
 // Redux connect
 
--- a/devtools/client/webconsole/index.html
+++ b/devtools/client/webconsole/index.html
@@ -4,25 +4,28 @@
 <!DOCTYPE html>
 <html dir=""
       id="devtools-webconsole"
       windowtype="devtools:webconsole"
       width="900" height="350"
       persist="screenX screenY width height sizemode">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+    <link rel="localization" href="toolkit/main-window/editmenu.ftl"/>
+    <link rel="stylesheet" href="chrome://global/skin/"/>
     <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/webconsole.css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css"/>
     <link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/>
     <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>
     <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/TabBar.css"/>
     <link rel="stylesheet" href="resource://devtools/client/shared/components/NotificationBox.css"/>
     <link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/httpi.css"/>
 
+    <script type="text/javascript" src="chrome://global/content/l10n.js"></script>
     <script src="chrome://devtools/content/shared/theme-switching.js"></script>
     <script type="application/javascript"
             src="resource://devtools/client/webconsole/main.js"></script>
   </head>
   <body class="theme-sidebar" role="application">
     <div id="app-wrapper" class="theme-body">
       <div id="output-container" role="document" aria-live="polite"></div>
     </div>