function* testSubmenu(toolbox) { let clickFired = false; let menu = new Menu({ id: "menu-popup", }); let submenu = new Menu({ id: "submenu-popup", }); submenu.append(new MenuItem({ label: "Submenu item", click: () => { info("Click callback has fired for submenu item"); clickFired = true; }, })); menu.append(new MenuItem({ label: "Submenu parent", submenu: submenu, })); menu.popup(0, 0, toolbox); ok(toolbox.doc.querySelector("#menu-popup"), "A popup is in the DOM"); is(toolbox.doc.querySelectorAll("#menu-popup > menuitem").length, 0, "No menuitem children"); let menus = toolbox.doc.querySelectorAll("#menu-popup > menu"); is(menus.length, 1, "Correct number of menus"); is(menus[0].getAttribute("label"), "Submenu parent", "Correct label for menus"); let subMenuItems = menus[0].querySelectorAll("menupopup > menuitem"); is(subMenuItems.length, 1, "Correct number of submenu items"); is(subMenuItems[0].getAttribute("label"), "Submenu item", "Correct label"); yield once(menu, "open"); let closed = once(menu, "close"); info("Using keyboard navigation to open, close, and reopen the submenu"); let shown = once(menus[0], "popupshown"); EventUtils.synthesizeKey("VK_DOWN", {}); EventUtils.synthesizeKey("VK_RIGHT", {}); yield shown; let hidden = once(menus[0], "popuphidden"); EventUtils.synthesizeKey("VK_LEFT", {}); yield hidden; shown = once(menus[0], "popupshown"); EventUtils.synthesizeKey("VK_RIGHT", {}); yield shown; info("Clicking the submenu item"); EventUtils.synthesizeMouseAtCenter(subMenuItems[0], {}, toolbox.doc.defaultView); yield closed; ok(clickFired, "Click has fired"); }
function testMenuItems() { const menu = new Menu(); const menuItem1 = new MenuItem(); const menuItem2 = new MenuItem(); menu.append(menuItem1); menu.append(menuItem2); is(menu.items.length, 2, "Correct number of 'items'"); is(menu.items[0], menuItem1, "Correct reference to MenuItem"); is(menu.items[1], menuItem2, "Correct reference to MenuItem"); }
this.state.tabs.forEach(tab => { menu.append(new MenuItem({ label: tab.title, type: "checkbox", checked: this.getCurrentTabId() == tab.id, click: () => this.select(tab.id), })); });
panelDefinitions.forEach(({id, label}) => { menu.append(new MenuItem({ checked: currentToolId === id, click: () => { selectTool(id); }, id: "all-tools-menupopup-" + id, label, type: "checkbox", })); });
panelDefinitions.forEach(({id, label}) => { if (this.state.overflowedTabIds.includes(id)) { menu.append(new MenuItem({ click: () => { selectTool(id, "tab_switch"); }, id: "tools-chevron-menupopup-" + id, label, type: "checkbox", })); } });
/** * Display the "..." menu (affectionately known as the meatball menu). * * @param {Object} menuButton * The <button> element from which the menu should pop out. The geometry * of this element is used to position the menu. * @param {Object} props * Properties as described below. * @param {string} props.currentToolId * The id of the currently selected tool. * @param {Object[]} props.hostTypes * Array of host type objects. * This array will be empty if we shouldn't shouldn't show any dock * options. * @param {string} props.hostTypes[].position * Position name. * @param {Function} props.hostTypes[].switchHost * Function to switch the host. * @param {string} props.currentHostType * The current docking configuration. * @param {boolean} isSplitConsoleActive * Is the split console currently visible? * @param {boolean|undefined} disableAutohide * Are we disabling the behavior where pop-ups are automatically * closed when clicking outside them. * (Only defined for the browser toolbox.) * @param {Function} selectTool * Function to select a tool based on its id. * @param {Function} toggleOptions * Function to turn the options panel on / off. * @param {Function} toggleSplitConsole * Function to turn the split console on / off. * @param {Function} toggleNoAutohide * Function to turn the disable pop-up autohide behavior on / off. * @param {Object} props.L10N * Localization interface. * @param {Object} props.toolbox * The devtools toolbox. Used by the Menu component to determine which * document to use. */ function showMeatballMenu( menuButton, { currentToolId, hostTypes, currentHostType, isSplitConsoleActive, disableAutohide, toggleOptions, toggleSplitConsole, toggleNoAutohide, L10N, toolbox, } ) { const menu = new Menu({ id: "toolbox-meatball-menu" }); // Dock options for (const hostType of hostTypes) { const l10nkey = hostType.position === "window" ? "separateWindow" : hostType.position; menu.append( new MenuItem({ id: `toolbox-meatball-menu-dock-${hostType.position}`, label: L10N.getStr(`toolbox.meatballMenu.dock.${l10nkey}.label`), click: () => hostType.switchHost(), type: "checkbox", checked: hostType.position === currentHostType, }) ); } if (menu.items.length) { menu.append(new MenuItem({ type: "separator" })); } // Split console if (currentToolId !== "webconsole") { menu.append(new MenuItem({ id: "toolbox-meatball-menu-splitconsole", label: L10N.getStr( `toolbox.meatballMenu.${ isSplitConsoleActive ? "hideconsole" : "splitconsole" }.label` ), accelerator: "Esc", click: toggleSplitConsole, })); } // Disable pop-up autohide // // If |disableAutohide| is undefined, it means this feature is not available // in this context. if (typeof disableAutohide !== "undefined") { menu.append(new MenuItem({ id: "toolbox-meatball-menu-noautohide", label: L10N.getStr("toolbox.meatballMenu.noautohide.label"), type: "checkbox", checked: disableAutohide, click: toggleNoAutohide, })); } // Settings menu.append(new MenuItem({ id: "toolbox-meatball-menu-settings", label: L10N.getStr("toolbox.meatballMenu.settings.label"), accelerator: L10N.getStr("toolbox.help.key"), click: () => toggleOptions(), })); if (menu.items.length) { menu.append(new MenuItem({ type: "separator" })); } // Getting started menu.append(new MenuItem({ id: "toolbox-meatball-menu-documentation", label: L10N.getStr("toolbox.meatballMenu.documentation.label"), click: () => { openWebLink( "https://developer.mozilla.org/docs/Tools?utm_source=devtools&utm_medium=tabbar-menu"); }, })); // Give feedback menu.append(new MenuItem({ id: "toolbox-meatball-menu-community", label: L10N.getStr("toolbox.meatballMenu.community.label"), click: () => { openWebLink( "https://discourse.mozilla.org/c/devtools?utm_source=devtools&utm_medium=tabbar-menu"); }, })); const rect = menuButton.getBoundingClientRect(); const screenX = menuButton.ownerDocument.defaultView.mozInnerScreenX; const screenY = menuButton.ownerDocument.defaultView.mozInnerScreenY; // Display the popup below the button. menu.popupWithZoom(rect.left + screenX, rect.bottom + screenY, toolbox); }
async function testMenuPopup(toolbox) { let clickFired = false; const menu = new Menu({ id: "menu-popup", }); menu.append(new MenuItem({ type: "separator" })); const MENU_ITEMS = [ new MenuItem({ id: "menu-item-1", label: "Normal Item", click: () => { info("Click callback has fired for menu item"); clickFired = true; }, }), new MenuItem({ label: "Checked Item", type: "checkbox", checked: true, }), new MenuItem({ label: "Radio Item", type: "radio", }), new MenuItem({ label: "Disabled Item", disabled: true, }), new MenuItem({ l10nID: "editmenu-undo", }), ]; for (const item of MENU_ITEMS) { menu.append(item); } // Append an invisible MenuItem, which shouldn't show up in the DOM menu.append(new MenuItem({ label: "Invisible", visible: false, })); menu.popup(0, 0, toolbox.doc); ok(toolbox.topDoc.querySelector("#menu-popup"), "A popup is in the DOM"); const menuSeparators = toolbox.topDoc.querySelectorAll("#menu-popup > menuseparator"); is(menuSeparators.length, 1, "A separator is in the menu"); const menuItems = toolbox.topDoc.querySelectorAll("#menu-popup > menuitem"); is(menuItems.length, MENU_ITEMS.length, "Correct number of menuitems"); is(menuItems[0].id, MENU_ITEMS[0].id, "Correct id for menuitem"); is(menuItems[0].getAttribute("label"), MENU_ITEMS[0].label, "Correct label"); is(menuItems[1].getAttribute("label"), MENU_ITEMS[1].label, "Correct label"); is(menuItems[1].getAttribute("type"), "checkbox", "Correct type attr"); is(menuItems[1].getAttribute("checked"), "true", "Has checked attr"); is(menuItems[2].getAttribute("label"), MENU_ITEMS[2].label, "Correct label"); is(menuItems[2].getAttribute("type"), "radio", "Correct type attr"); ok(!menuItems[2].hasAttribute("checked"), "Doesn't have checked attr"); is(menuItems[3].getAttribute("label"), MENU_ITEMS[3].label, "Correct label"); is(menuItems[3].getAttribute("disabled"), "true", "disabled attr menuitem"); is(menuItems[4].getAttribute("data-l10n-id"), MENU_ITEMS[4].l10nID, "Correct localization attribute"); await once(menu, "open"); const closed = once(menu, "close"); EventUtils.synthesizeMouseAtCenter(menuItems[0], {}, toolbox.topWindow); await closed; ok(clickFired, "Click has fired"); ok(!toolbox.topDoc.querySelector("#menu-popup"), "Popup removed from the DOM"); }
async function testSubmenu(toolbox) { let clickFired = false; const menu = new Menu({ id: "menu-popup", }); const submenu = new Menu({ id: "submenu-popup", }); submenu.append(new MenuItem({ label: "Submenu item", click: () => { info("Click callback has fired for submenu item"); clickFired = true; }, })); menu.append(new MenuItem({ l10nID: "editmenu-copy", submenu: submenu, })); menu.append(new MenuItem({ label: "Submenu parent with attributes", id: "submenu-parent-with-attrs", submenu: submenu, accesskey: "A", disabled: true, })); menu.popup(0, 0, toolbox.doc); ok(toolbox.topDoc.querySelector("#menu-popup"), "A popup is in the DOM"); is(toolbox.topDoc.querySelectorAll("#menu-popup > menuitem").length, 0, "No menuitem children"); const menus = toolbox.topDoc.querySelectorAll("#menu-popup > menu"); is(menus.length, 2, "Correct number of menus"); ok(!menus[0].hasAttribute("label"), "No label: should be set by localization"); ok(!menus[0].hasAttribute("disabled"), "Correct disabled state"); is(menus[0].getAttribute("data-l10n-id"), "editmenu-copy", "Correct localization attribute"); is(menus[1].getAttribute("accesskey"), "A", "Correct accesskey"); ok(menus[1].hasAttribute("disabled"), "Correct disabled state"); is(menus[1].id, "submenu-parent-with-attrs", "Correct id"); const subMenuItems = menus[0].querySelectorAll("menupopup > menuitem"); is(subMenuItems.length, 1, "Correct number of submenu items"); is(subMenuItems[0].getAttribute("label"), "Submenu item", "Correct label"); await once(menu, "open"); const closed = once(menu, "close"); info("Using keyboard navigation to open, close, and reopen the submenu"); let shown = once(menus[0], "popupshown"); EventUtils.synthesizeKey("KEY_ArrowDown"); EventUtils.synthesizeKey("KEY_ArrowRight"); await shown; const hidden = once(menus[0], "popuphidden"); EventUtils.synthesizeKey("KEY_ArrowLeft"); await hidden; shown = once(menus[0], "popupshown"); EventUtils.synthesizeKey("KEY_ArrowRight"); await shown; info("Clicking the submenu item"); EventUtils.synthesizeMouseAtCenter(subMenuItems[0], {}, toolbox.topWindow); await closed; ok(clickFired, "Click has fired"); }
return NetMonitorView.RequestsMenu.items; }, /** * Handle the context menu opening. Hide items if no request is selected. * Since visible attribute only accept boolean value but the method call may * return undefined, we use !! to force convert any object to boolean */ open({ screenX = 0, screenY = 0 } = {}) { let selectedItem = this.selectedItem; let menu = new Menu(); menu.append(new MenuItem({ id: "request-menu-context-copy-url", label: L10N.getStr("netmonitor.context.copyUrl"), accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"), visible: !!selectedItem, click: () => this.copyUrl(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-url-params", label: L10N.getStr("netmonitor.context.copyUrlParams"), accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"), visible: !!(selectedItem && NetworkHelper.nsIURL(selectedItem.attachment.url).query), click: () => this.copyUrlParams(), })); menu.append(new MenuItem({ id: "request-menu-context-copy-post-data",
/** * Create a Menu instance for the webconsole. * * @param {Object} hud * The webConsoleFrame. * @param {Element} parentNode * The container of the new console frontend output wrapper. * @param {Object} options * - {String} actor (optional) actor id to use for context menu actions * - {String} clipboardText (optional) text to "Copy" if no selection is available * - {String} variableText (optional) which is the textual frontend * representation of the variable * - {Object} message (optional) message object containing metadata such as: * - {String} source * - {String} request * - {Function} openSidebar (optional) function that will open the object * inspector sidebar * - {String} rootActorId (optional) actor id for the root object being clicked on */ function createContextMenu(hud, parentNode, { actor, clipboardText, variableText, message, serviceContainer, openSidebar, rootActorId, }) { let win = parentNode.ownerDocument.defaultView; let selection = win.getSelection(); let { source, request } = message || {}; let menu = new Menu({ id: "webconsole-menu" }); // Copy URL for a network request. menu.append(new MenuItem({ id: "console-menu-copy-url", label: l10n.getStr("webconsole.menu.copyURL.label"), accesskey: l10n.getStr("webconsole.menu.copyURL.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => { if (!request) { return; } clipboardHelper.copyString(request.url); }, })); // Open Network message in the Network panel. if (serviceContainer.openNetworkPanel && request) { menu.append(new MenuItem({ id: "console-menu-open-in-network-panel", label: l10n.getStr("webconsole.menu.openInNetworkPanel.label"), accesskey: l10n.getStr("webconsole.menu.openInNetworkPanel.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => serviceContainer.openNetworkPanel(message.messageId) })); } // Open URL in a new tab for a network request. menu.append(new MenuItem({ id: "console-menu-open-url", label: l10n.getStr("webconsole.menu.openURL.label"), accesskey: l10n.getStr("webconsole.menu.openURL.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => { if (!request) { return; } let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); mainWindow.openWebLinkIn(request.url, "tab", { triggeringPrincipal: mainWindow.document.nodePrincipal, }); }, })); // Store as global variable. menu.append(new MenuItem({ id: "console-menu-store", label: l10n.getStr("webconsole.menu.storeAsGlobalVar.label"), accesskey: l10n.getStr("webconsole.menu.storeAsGlobalVar.accesskey"), disabled: !actor, click: () => { let evalString = `{ let i = 0; while (this.hasOwnProperty("temp" + i) && i < 1000) { i++; } this["temp" + i] = _self; "temp" + i; }`; let options = { selectedObjectActor: actor, }; hud.jsterm.requestEvaluation(evalString, options).then((res) => { hud.jsterm.focus(); hud.jsterm.setInputValue(res.result); }); }, })); // Copy message or grip. menu.append(new MenuItem({ id: "console-menu-copy", label: l10n.getStr("webconsole.menu.copyMessage.label"), accesskey: l10n.getStr("webconsole.menu.copyMessage.accesskey"), // Disabled if there is no selection and no message element available to copy. disabled: selection.isCollapsed && !clipboardText, click: () => { if (selection.isCollapsed) { // If the selection is empty/collapsed, copy the text content of the // message for which the context menu was opened. clipboardHelper.copyString(clipboardText); } else { clipboardHelper.copyString(selection.toString()); } }, })); // Copy message object. menu.append(new MenuItem({ id: "console-menu-copy-object", label: l10n.getStr("webconsole.menu.copyObject.label"), accesskey: l10n.getStr("webconsole.menu.copyObject.accesskey"), // Disabled if there is no actor and no variable text associated. disabled: (!actor && !variableText), click: () => { if (actor) { // The Debugger.Object of the OA will be bound to |_self| during evaluation, hud.jsterm.copyObject(`_self`, { selectedObjectActor: actor }).then((res) => { clipboardHelper.copyString(res.helperResult.value); }); } else { clipboardHelper.copyString(variableText); } }, })); // Select all. menu.append(new MenuItem({ id: "console-menu-select", label: l10n.getStr("webconsole.menu.selectAll.label"), accesskey: l10n.getStr("webconsole.menu.selectAll.accesskey"), disabled: false, click: () => { let webconsoleOutput = parentNode.querySelector(".webconsole-output"); selection.selectAllChildren(webconsoleOutput); }, })); // Open object in sidebar. if (openSidebar) { menu.append(new MenuItem({ id: "console-menu-open-sidebar", label: l10n.getStr("webconsole.menu.openInSidebar.label"), accesskey: l10n.getStr("webconsole.menu.openInSidebar.accesskey"), disabled: !rootActorId, click: () => openSidebar(message.messageId), })); } return menu; }
/** * Return an 'edit' menu for a input field. This integrates directly * with docshell commands to provide the right enabled state and editor * functionality. * * You'll need to call menu.popup() yourself, this just returns the Menu instance. * * @returns {Menu} */ function createEditContextMenu() { const docshell = window.docShell; const menu = new Menu({ id: "webconsole-menu" }); menu.append(new MenuItem({ id: "editmenu-undo", l10nID: "editmenu-undo", disabled: !docshell.isCommandEnabled("cmd_undo"), click: () => { docshell.doCommand("cmd_undo"); }, })); menu.append(new MenuItem({ type: "separator" })); menu.append(new MenuItem({ id: "editmenu-cut", l10nID: "editmenu-cut", disabled: !docshell.isCommandEnabled("cmd_cut"), click: () => { docshell.doCommand("cmd_cut"); }, })); menu.append(new MenuItem({ id: "editmenu-copy", l10nID: "editmenu-copy", disabled: !docshell.isCommandEnabled("cmd_copy"), click: () => { docshell.doCommand("cmd_copy"); }, })); menu.append(new MenuItem({ id: "editmenu-paste", l10nID: "editmenu-paste", disabled: !docshell.isCommandEnabled("cmd_paste"), click: () => { docshell.doCommand("cmd_paste"); }, })); menu.append(new MenuItem({ id: "editmenu-delete", l10nID: "editmenu-delete", disabled: !docshell.isCommandEnabled("cmd_delete"), click: () => { docshell.doCommand("cmd_delete"); }, })); menu.append(new MenuItem({ type: "separator" })); menu.append(new MenuItem({ id: "editmenu-selectAll", l10nID: "editmenu-select-all", disabled: !docshell.isCommandEnabled("cmd_selectAll"), click: () => { docshell.doCommand("cmd_selectAll"); }, })); return menu; }
_openMenu: function ({ target, screenX = 0, screenY = 0 } = { }) { // In the sidebar we do not have this.styleDocument.popupNode // so we need to save the node ourselves. this.styleDocument.popupNode = target; this.styleWindow.focus(); let menu = new Menu(); let menuitemCopy = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy"), accesskey: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy.accessKey"), click: () => { this._onCopy(); }, disabled: !this._hasTextSelected(), }); let menuitemCopyLocation = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyLocation"), click: () => { this._onCopyLocation(); }, visible: false, }); let menuitemCopyRule = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyRule"), click: () => { this._onCopyRule(); }, visible: this.isRuleView, }); let copyColorAccessKey = "styleinspector.contextmenu.copyColor.accessKey"; let menuitemCopyColor = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyColor"), accesskey: STYLE_INSPECTOR_L10N.getStr(copyColorAccessKey), click: () => { this._onCopyColor(); }, visible: this._isColorPopup(), }); let copyUrlAccessKey = "styleinspector.contextmenu.copyUrl.accessKey"; let menuitemCopyUrl = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyUrl"), accesskey: STYLE_INSPECTOR_L10N.getStr(copyUrlAccessKey), click: () => { this._onCopyUrl(); }, visible: this._isImageUrl(), }); let copyImageAccessKey = "styleinspector.contextmenu.copyImageDataUrl.accessKey"; let menuitemCopyImageDataUrl = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyImageDataUrl"), accesskey: STYLE_INSPECTOR_L10N.getStr(copyImageAccessKey), click: () => { this._onCopyImageDataUrl(); }, visible: this._isImageUrl(), }); let copyPropDeclarationLabel = "styleinspector.contextmenu.copyPropertyDeclaration"; let menuitemCopyPropertyDeclaration = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr(copyPropDeclarationLabel), click: () => { this._onCopyPropertyDeclaration(); }, visible: false, }); let menuitemCopyPropertyName = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyName"), click: () => { this._onCopyPropertyName(); }, visible: false, }); let menuitemCopyPropertyValue = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyValue"), click: () => { this._onCopyPropertyValue(); }, visible: false, }); let menuitemCopySelector = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copySelector"), click: () => { this._onCopySelector(); }, visible: false, }); this._clickedNodeInfo = this._getClickedNodeInfo(); if (this.isRuleView && this._clickedNodeInfo) { switch (this._clickedNodeInfo.type) { case VIEW_NODE_PROPERTY_TYPE : menuitemCopyPropertyDeclaration.visible = true; menuitemCopyPropertyName.visible = true; break; case VIEW_NODE_VALUE_TYPE : menuitemCopyPropertyDeclaration.visible = true; menuitemCopyPropertyValue.visible = true; break; case VIEW_NODE_SELECTOR_TYPE : menuitemCopySelector.visible = true; break; case VIEW_NODE_LOCATION_TYPE : menuitemCopyLocation.visible = true; break; } } menu.append(menuitemCopy); menu.append(menuitemCopyLocation); menu.append(menuitemCopyRule); menu.append(menuitemCopyColor); menu.append(menuitemCopyUrl); menu.append(menuitemCopyImageDataUrl); menu.append(menuitemCopyPropertyDeclaration); menu.append(menuitemCopyPropertyName); menu.append(menuitemCopyPropertyValue); menu.append(menuitemCopySelector); menu.append(new MenuItem({ type: "separator", })); // Select All let selectAllAccessKey = "styleinspector.contextmenu.selectAll.accessKey"; let menuitemSelectAll = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.selectAll"), accesskey: STYLE_INSPECTOR_L10N.getStr(selectAllAccessKey), click: () => { this._onSelectAll(); }, }); menu.append(menuitemSelectAll); menu.append(new MenuItem({ type: "separator", })); // Add new rule let addRuleAccessKey = "styleinspector.contextmenu.addNewRule.accessKey"; let menuitemAddRule = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.addNewRule"), accesskey: STYLE_INSPECTOR_L10N.getStr(addRuleAccessKey), click: () => { this._onAddNewRule(); }, visible: this.isRuleView, disabled: !this.isRuleView || this.inspector.selection.isAnonymousNode(), }); menu.append(menuitemAddRule); // Show MDN Docs let mdnDocsAccessKey = "styleinspector.contextmenu.showMdnDocs.accessKey"; let menuitemShowMdnDocs = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.showMdnDocs"), accesskey: STYLE_INSPECTOR_L10N.getStr(mdnDocsAccessKey), click: () => { this._onShowMdnDocs(); }, visible: (Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP) && this._isPropertyName()), }); menu.append(menuitemShowMdnDocs); // Show Original Sources let sourcesAccessKey = "styleinspector.contextmenu.toggleOrigSources.accessKey"; let menuitemSources = new MenuItem({ label: STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.toggleOrigSources"), accesskey: STYLE_INSPECTOR_L10N.getStr(sourcesAccessKey), click: () => { this._onToggleOrigSources(); }, type: "checkbox", checked: Services.prefs.getBoolPref(PREF_ORIG_SOURCES), }); menu.append(menuitemSources); menu.popup(screenX, screenY, this.inspector._toolbox); return menu; },
/** * Create a Menu instance for the webconsole. * * @param {WebConsoleUI} webConsoleUI * The webConsoleUI instance. * @param {Element} parentNode * The container of the new console frontend output wrapper. * @param {Object} options * - {String} actor (optional) actor id to use for context menu actions * - {String} clipboardText (optional) text to "Copy" if no selection is available * - {String} variableText (optional) which is the textual frontend * representation of the variable * - {Object} message (optional) message object containing metadata such as: * - {String} source * - {String} request * - {Function} openSidebar (optional) function that will open the object * inspector sidebar * - {String} rootActorId (optional) actor id for the root object being clicked on * - {Object} executionPoint (optional) when replaying, the execution point where * this message was logged */ function createContextMenu(webConsoleUI, parentNode, { actor, clipboardText, variableText, message, serviceContainer, openSidebar, rootActorId, executionPoint, toolbox, url, }) { const win = parentNode.ownerDocument.defaultView; const selection = win.getSelection(); const { source, request } = message || {}; const menu = new Menu({ id: "webconsole-menu", }); // Copy URL for a network request. menu.append(new MenuItem({ id: "console-menu-copy-url", label: l10n.getStr("webconsole.menu.copyURL.label"), accesskey: l10n.getStr("webconsole.menu.copyURL.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => { if (!request) { return; } clipboardHelper.copyString(request.url); }, })); // Open Network message in the Network panel. if (serviceContainer.openNetworkPanel && request) { menu.append(new MenuItem({ id: "console-menu-open-in-network-panel", label: l10n.getStr("webconsole.menu.openInNetworkPanel.label"), accesskey: l10n.getStr("webconsole.menu.openInNetworkPanel.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => serviceContainer.openNetworkPanel(message.messageId), })); } // Resend Network message. if (serviceContainer.resendNetworkRequest && request) { menu.append(new MenuItem({ id: "console-menu-resend-network-request", label: l10n.getStr("webconsole.menu.resendNetworkRequest.label"), accesskey: l10n.getStr("webconsole.menu.resendNetworkRequest.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => serviceContainer.resendNetworkRequest(message.messageId), })); } // Open URL in a new tab for a network request. menu.append(new MenuItem({ id: "console-menu-open-url", label: l10n.getStr("webconsole.menu.openURL.label"), accesskey: l10n.getStr("webconsole.menu.openURL.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => { if (!request) { return; } openContentLink(request.url); }, })); // Store as global variable. menu.append(new MenuItem({ id: "console-menu-store", label: l10n.getStr("webconsole.menu.storeAsGlobalVar.label"), accesskey: l10n.getStr("webconsole.menu.storeAsGlobalVar.accesskey"), disabled: !actor, click: () => { const evalString = `{ let i = 0; while (this.hasOwnProperty("temp" + i) && i < 1000) { i++; } this["temp" + i] = _self; "temp" + i; }`; const options = { selectedObjectActor: actor, }; webConsoleUI.jsterm.requestEvaluation(evalString, options).then((res) => { webConsoleUI.jsterm.focus(); webConsoleUI.hud.setInputValue(res.result); }); }, })); // Copy message or grip. menu.append(new MenuItem({ id: "console-menu-copy", label: l10n.getStr("webconsole.menu.copyMessage.label"), accesskey: l10n.getStr("webconsole.menu.copyMessage.accesskey"), // Disabled if there is no selection and no message element available to copy. disabled: selection.isCollapsed && !clipboardText, click: () => { if (selection.isCollapsed) { // If the selection is empty/collapsed, copy the text content of the // message for which the context menu was opened. clipboardHelper.copyString(clipboardText); } else { clipboardHelper.copyString(selection.toString()); } }, })); // Copy message object. menu.append(new MenuItem({ id: "console-menu-copy-object", label: l10n.getStr("webconsole.menu.copyObject.label"), accesskey: l10n.getStr("webconsole.menu.copyObject.accesskey"), // Disabled if there is no actor and no variable text associated. disabled: (!actor && !variableText), click: () => { if (actor) { // The Debugger.Object of the OA will be bound to |_self| during evaluation, webConsoleUI.jsterm.copyObject(`_self`, { selectedObjectActor: actor }) .then((res) => { clipboardHelper.copyString(res.helperResult.value); }); } else { clipboardHelper.copyString(variableText); } }, })); // Select all. menu.append(new MenuItem({ id: "console-menu-select", label: l10n.getStr("webconsole.menu.selectAll.label"), accesskey: l10n.getStr("webconsole.menu.selectAll.accesskey"), disabled: false, click: () => { const webconsoleOutput = parentNode.querySelector(".webconsole-output"); selection.selectAllChildren(webconsoleOutput); }, })); // Export to clipboard menu.append(new MenuItem({ id: "console-menu-export-clipboard", label: l10n.getStr("webconsole.menu.exportClipboard.label"), disabled: false, click: () => { const webconsoleOutput = parentNode.querySelector(".webconsole-output"); clipboardHelper.copyString(getElementText(webconsoleOutput)); }, })); // Open object in sidebar. if (openSidebar) { menu.append(new MenuItem({ id: "console-menu-open-sidebar", label: l10n.getStr("webconsole.menu.openInSidebar.label"), accesskey: l10n.getStr("webconsole.menu.openInSidebar.accesskey"), disabled: !rootActorId, click: () => openSidebar(message.messageId), })); } // Add time warp option if available. if (executionPoint) { menu.append(new MenuItem({ id: "console-menu-time-warp", label: l10n.getStr("webconsole.menu.timeWarp.label"), disabled: false, click: () => { const threadClient = toolbox.threadClient; threadClient.timeWarp(executionPoint); }, })); } if (url) { menu.append(new MenuItem({ id: "console-menu-copy-url", label: l10n.getStr("webconsole.menu.copyURL.label"), accesskey: l10n.getStr("webconsole.menu.copyURL.accesskey"), click: () => clipboardHelper.copyString(url), })); } return menu; }
/** * Return an 'edit' menu for a input field. This integrates directly * with docshell commands to provide the right enabled state and editor * functionality. * * You'll need to call menu.popup() yourself, this just returns the Menu instance. * * @param {Window} win parent window reference * @param {String} id menu ID * * @returns {Menu} */ function createEditContextMenu(win, id) { // Localized strings for the menu are loaded lazily. loadEditMenuStrings(win); const docshell = win.docShell; const menu = new Menu({id}); menu.append(new MenuItem({ id: "editmenu-undo", l10nID: "editmenu-undo", disabled: !docshell.isCommandEnabled("cmd_undo"), click: () => { docshell.doCommand("cmd_undo"); }, })); menu.append(new MenuItem({ type: "separator", })); menu.append(new MenuItem({ id: "editmenu-cut", l10nID: "editmenu-cut", disabled: !docshell.isCommandEnabled("cmd_cut"), click: () => { docshell.doCommand("cmd_cut"); }, })); menu.append(new MenuItem({ id: "editmenu-copy", l10nID: "editmenu-copy", disabled: !docshell.isCommandEnabled("cmd_copy"), click: () => { docshell.doCommand("cmd_copy"); }, })); menu.append(new MenuItem({ id: "editmenu-paste", l10nID: "editmenu-paste", disabled: !docshell.isCommandEnabled("cmd_paste"), click: () => { docshell.doCommand("cmd_paste"); }, })); menu.append(new MenuItem({ id: "editmenu-delete", l10nID: "editmenu-delete", disabled: !docshell.isCommandEnabled("cmd_delete"), click: () => { docshell.doCommand("cmd_delete"); }, })); menu.append(new MenuItem({ type: "separator", })); menu.append(new MenuItem({ id: "editmenu-selectAll", l10nID: "editmenu-select-all", disabled: !docshell.isCommandEnabled("cmd_selectAll"), click: () => { docshell.doCommand("cmd_selectAll"); }, })); return menu; }
function* testMenuPopup(toolbox) { let clickFired = false; let menu = new Menu({ id: "menu-popup", }); menu.append(new MenuItem({ type: "separator" })); let MENU_ITEMS = [ new MenuItem({ id: "menu-item-1", label: "Normal Item", click: () => { info("Click callback has fired for menu item"); clickFired = true; }, }), new MenuItem({ label: "Checked Item", type: "checkbox", checked: true, }), new MenuItem({ label: "Radio Item", type: "radio", }), new MenuItem({ label: "Disabled Item", disabled: true, }), ]; for (let item of MENU_ITEMS) { menu.append(item); } menu.popup(0, 0, toolbox); ok(toolbox.doc.querySelector("#menu-popup"), "A popup is in the DOM"); let menuSeparators = toolbox.doc.querySelectorAll("#menu-popup > menuseparator"); is(menuSeparators.length, 1, "A separator is in the menu"); let menuItems = toolbox.doc.querySelectorAll("#menu-popup > menuitem"); is(menuItems.length, MENU_ITEMS.length, "Correct number of menuitems"); is(menuItems[0].id, MENU_ITEMS[0].id, "Correct id for menuitem"); is(menuItems[0].getAttribute("label"), MENU_ITEMS[0].label, "Correct label"); is(menuItems[1].getAttribute("label"), MENU_ITEMS[1].label, "Correct label"); is(menuItems[1].getAttribute("type"), "checkbox", "Correct type attribute"); is(menuItems[1].getAttribute("checked"), "true", "Has checked attribute"); is(menuItems[2].getAttribute("label"), MENU_ITEMS[2].label, "Correct label"); is(menuItems[2].getAttribute("type"), "radio", "Correct type attribute"); ok(!menuItems[2].hasAttribute("checked"), "Doesn't have checked attribute"); is(menuItems[3].getAttribute("label"), MENU_ITEMS[3].label, "Correct label"); is(menuItems[3].getAttribute("disabled"), "true", "disabled attribute menuitem"); yield once(menu, "open"); let closed = once(menu, "close"); EventUtils.synthesizeMouseAtCenter(menuItems[0], {}, toolbox.doc.defaultView); yield closed; ok(clickFired, "Click has fired"); ok(!toolbox.doc.querySelector("#menu-popup"), "The popup is removed from the DOM"); }
/** * Create a Menu instance for the webconsole. * * @param {Object} jsterm * The JSTerm instance used by the webconsole. * @param {Element} parentNode * The container of the new console frontend output wrapper. * @param {Object} options * - {String} actor (optional) actor id to use for context menu actions * - {String} clipboardText (optional) text to "Copy" if no selection is available * - {Object} message (optional) message object containing metadata such as: * - {String} source * - {String} request */ function createContextMenu(jsterm, parentNode, { actor, clipboardText, message }) { let win = parentNode.ownerDocument.defaultView; let selection = win.getSelection(); let { source, request } = message || {}; let menu = new Menu({ id: "webconsole-menu" }); // Copy URL for a network request. menu.append(new MenuItem({ id: "console-menu-copy-url", label: l10n.getStr("webconsole.menu.copyURL.label"), accesskey: l10n.getStr("webconsole.menu.copyURL.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => { if (!request) { return; } clipboardHelper.copyString(request.url); }, })); // Open URL in a new tab for a network request. menu.append(new MenuItem({ id: "console-menu-open-url", label: l10n.getStr("webconsole.menu.openURL.label"), accesskey: l10n.getStr("webconsole.menu.openURL.accesskey"), visible: source === MESSAGE_SOURCE.NETWORK, click: () => { if (!request) { return; } let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType); mainWindow.openUILinkIn(request.url, "tab"); }, })); // Open in variables view. menu.append(new MenuItem({ id: "console-menu-open", label: l10n.getStr("webconsole.menu.openInVarView.label"), accesskey: l10n.getStr("webconsole.menu.openInVarView.accesskey"), disabled: !actor, click: () => { openVariablesView(actor); }, })); // Store as global variable. menu.append(new MenuItem({ id: "console-menu-store", label: l10n.getStr("webconsole.menu.storeAsGlobalVar.label"), accesskey: l10n.getStr("webconsole.menu.storeAsGlobalVar.accesskey"), disabled: !actor, click: () => { let evalString = `{ let i = 0; while (this.hasOwnProperty("temp" + i) && i < 1000) { i++; } this["temp" + i] = _self; "temp" + i; }`; let options = { selectedObjectActor: actor, }; jsterm.requestEvaluation(evalString, options).then((res) => { jsterm.focus(); jsterm.setInputValue(res.result); }); }, })); // Copy message or grip. menu.append(new MenuItem({ id: "console-menu-copy", label: l10n.getStr("webconsole.menu.copy.label"), accesskey: l10n.getStr("webconsole.menu.copy.accesskey"), // Disabled if there is no selection and no message element available to copy. disabled: selection.isCollapsed && !clipboardText, click: () => { if (selection.isCollapsed) { // If the selection is empty/collapsed, copy the text content of the // message for which the context menu was opened. clipboardHelper.copyString(clipboardText); } else { clipboardHelper.copyString(selection.toString()); } }, })); // Select all. menu.append(new MenuItem({ id: "console-menu-select", label: l10n.getStr("webconsole.menu.selectAll.label"), accesskey: l10n.getStr("webconsole.menu.selectAll.accesskey"), disabled: false, click: () => { let webconsoleOutput = parentNode.querySelector(".webconsole-output"); selection.selectAllChildren(webconsoleOutput); }, })); return menu; }