Bug 1258987 - Stop generating useless xul:command and xul:broadcaster. r=jryans

This commit is contained in:
Alexandre Poirot
2016-04-05 07:16:48 -07:00
parent 1dd77421e8
commit 14bde395e1
2 changed files with 81 additions and 161 deletions

View File

@@ -16,6 +16,7 @@ const Services = require("Services");
const MenuStrings = Services.strings.createBundle("chrome://devtools/locale/menus.properties");
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
// Keep list of inserted DOM Elements in order to remove them on unload
// Maps browser xul document => list of DOM Elements
@@ -30,40 +31,41 @@ function l10n(key) {
*
* @param {XULDocument} doc
* The document to which keys are to be added.
* @param {String} l10nKey
* Prefix of the properties entry to look for key shortcut in
* localization file. We will look for {property}.key and
* {property}.keytext for non-character shortcuts like F12.
* @param {String} command
* Id of the xul:command to map to.
* @param {Object} key definition dictionnary
* Definition with following attributes:
* - {String} id
* xul:key's id, automatically prefixed with "key_",
* - {String} modifiers
* Space separater list of modifier names,
* - {Boolean} keytext
* If true, consider the shortcut as a characther one,
* otherwise a non-character one like F12.
* @param {String} id
* key's id, automatically prefixed with "key_".
* @param {String} shortcut
* The key shortcut value.
* @param {String} keytext
* If `shortcut` refers to a function key, refers to the localized
* string to describe a non-character shortcut.
* @param {String} modifiers
* Space separated list of modifier names.
* @param {Function} oncommand
* The function to call when the shortcut is pressed.
*
* @return XULKeyElement
*/
function createKey(doc, l10nKey, command, key) {
function createKey({ doc, id, shortcut, keytext, modifiers, oncommand }) {
let k = doc.createElement("key");
k.id = "key_" + key.id;
let shortcut = l10n(l10nKey + ".key");
k.id = "key_" + id;
if (shortcut.startsWith("VK_")) {
k.setAttribute("keycode", shortcut);
k.setAttribute("keytext", l10n(l10nKey + ".keytext"));
if (keytext) {
k.setAttribute("keytext", keytext);
}
} else {
k.setAttribute("key", shortcut);
}
if (command) {
k.setAttribute("command", command);
}
if (key.modifiers) {
k.setAttribute("modifiers", key.modifiers);
if (modifiers) {
k.setAttribute("modifiers", modifiers);
}
// Bug 371900: command event is fired only if "oncommand" attribute is set.
k.setAttribute("oncommand", ";");
k.addEventListener("command", oncommand);
return k;
}
@@ -76,25 +78,18 @@ function createKey(doc, l10nKey, command, key) {
* Element id.
* @param {String} label
* Menu label.
* @param {String} broadcasterId (optional)
* Id of the xul:broadcaster to map to.
* @param {String} accesskey (optional)
* Access key of the menuitem, used as shortcut while opening the menu.
* @param {Boolean} isCheckbox
* @param {Boolean} isCheckbox (optional)
* If true, the menuitem will act as a checkbox and have an optional
* tick on its left.
*
* @return XULMenuItemElement
*/
function createMenuItem({ doc, id, label, broadcasterId, accesskey, isCheckbox }) {
function createMenuItem({ doc, id, label, accesskey, isCheckbox }) {
let menuitem = doc.createElement("menuitem");
menuitem.id = id;
if (label) {
menuitem.setAttribute("label", label);
}
if (broadcasterId) {
menuitem.setAttribute("observes", broadcasterId);
}
menuitem.setAttribute("label", label);
if (accesskey) {
menuitem.setAttribute("accesskey", accesskey);
}
@@ -105,56 +100,6 @@ function createMenuItem({ doc, id, label, broadcasterId, accesskey, isCheckbox }
return menuitem;
}
/**
* Create a xul:broadcaster element
*
* @param {XULDocument} doc
* The document to which keys are to be added.
* @param {String} id
* Element id.
* @param {String} label
* Broadcaster label.
* @param {Boolean} isCheckbox
* If true, the broadcaster is a checkbox one.
*
* @return XULMenuItemElement
*/
function createBroadcaster({ doc, id, label, isCheckbox }) {
let broadcaster = doc.createElement("broadcaster");
broadcaster.id = id;
broadcaster.setAttribute("label", label);
if (isCheckbox) {
broadcaster.setAttribute("type", "checkbox");
broadcaster.setAttribute("autocheck", "false");
}
return broadcaster;
}
/**
* Create a xul:command element
*
* @param {XULDocument} doc
* The document to which keys are to be added.
* @param {String} id
* Element id.
* @param {String} oncommand
* JS String to run when the command is fired.
* @param {Boolean} disabled
* If true, the command is disabled and hidden.
*
* @return XULCommandElement
*/
function createCommand({ doc, id, oncommand, disabled }) {
let command = doc.createElement("command");
command.id = id;
command.setAttribute("oncommand", oncommand);
if (disabled) {
command.setAttribute("disabled", "true");
command.setAttribute("hidden", "true");
}
return command;
}
/**
* Add a <key> to <keyset id="devtoolsKeyset">.
* Appending a <key> element is not always enough. The <keyset> needs
@@ -188,61 +133,49 @@ function attachKeybindingsToBrowser(doc, keys) {
*/
function createToolMenuElements(toolDefinition, doc) {
let id = toolDefinition.id;
let menuId = "menuitem_" + id;
// Prevent multiple entries for the same tool.
if (doc.getElementById("Tools:" + id)) {
if (doc.getElementById(menuId)) {
return;
}
let cmd = createCommand({
doc,
id: "Tools:" + id,
oncommand: 'gDevToolsBrowser.selectToolCommand(gBrowser, "' + id + '");',
});
let oncommand = function (id, event) {
let window = event.target.ownerDocument.defaultView;
gDevToolsBrowser.selectToolCommand(window.gBrowser, id);
}.bind(null, id);
let key = null;
if (toolDefinition.key) {
key = doc.createElement("key");
key.id = "key_" + id;
if (toolDefinition.key.startsWith("VK_")) {
key.setAttribute("keycode", toolDefinition.key);
} else {
key.setAttribute("key", toolDefinition.key);
}
key.setAttribute("command", cmd.id);
key.setAttribute("modifiers", toolDefinition.modifiers);
}
let bc = createBroadcaster({
doc,
id: "devtoolsMenuBroadcaster_" + id,
label: toolDefinition.menuLabel || toolDefinition.label
});
bc.setAttribute("command", cmd.id);
if (key) {
bc.setAttribute("key", "key_" + id);
key = createKey({
doc,
id,
shortcut: toolDefinition.key,
modifiers: toolDefinition.modifiers,
oncommand: oncommand
});
}
let menuitem = createMenuItem({
doc,
id: "menuitem_" + id,
broadcasterId: "devtoolsMenuBroadcaster_" + id,
label: toolDefinition.menuLabel || toolDefinition.label,
accesskey: toolDefinition.accesskey
});
if (key) {
// Refer to the key in order to display the key shortcut at menu ends
menuitem.setAttribute("key", key.id);
}
menuitem.addEventListener("command", oncommand);
return {
cmd: cmd,
key: key,
bc: bc,
menuitem: menuitem
key,
menuitem
};
}
/**
* Create xul menuitem, command, broadcaster and key elements for a given tool.
* Create xul menuitem, key elements for a given tool.
* And then insert them into browser DOM.
*
* @param {XULDocument} doc
@@ -253,16 +186,12 @@ function createToolMenuElements(toolDefinition, doc) {
* The tool definition after which the tool menu item is to be added.
*/
function insertToolMenuElements(doc, toolDefinition, prevDef) {
let elements = createToolMenuElements(toolDefinition, doc);
let { key, menuitem } = createToolMenuElements(toolDefinition, doc);
doc.getElementById("mainCommandSet").appendChild(elements.cmd);
if (elements.key) {
attachKeybindingsToBrowser(doc, elements.key);
if (key) {
attachKeybindingsToBrowser(doc, key);
}
doc.getElementById("mainBroadcasterSet").appendChild(elements.bc);
let ref;
if (prevDef) {
let menuitem = doc.getElementById("menuitem_" + prevDef.id);
@@ -272,7 +201,7 @@ function insertToolMenuElements(doc, toolDefinition, prevDef) {
}
if (ref) {
ref.parentNode.insertBefore(elements.menuitem, ref);
ref.parentNode.insertBefore(menuitem, ref);
}
}
exports.insertToolMenuElements = insertToolMenuElements;
@@ -286,24 +215,14 @@ exports.insertToolMenuElements = insertToolMenuElements;
* The document to which the tool menu item is to be removed from
*/
function removeToolFromMenu(toolId, doc) {
let command = doc.getElementById("Tools:" + toolId);
if (command) {
command.parentNode.removeChild(command);
}
let key = doc.getElementById("key_" + toolId);
if (key) {
key.parentNode.removeChild(key);
}
let bc = doc.getElementById("devtoolsMenuBroadcaster_" + toolId);
if (bc) {
bc.parentNode.removeChild(bc);
key.remove();
}
let menuitem = doc.getElementById("menuitem_" + toolId);
if (menuitem) {
menuitem.parentNode.removeChild(menuitem);
menuitem.remove();
}
}
exports.removeToolFromMenu = removeToolFromMenu;
@@ -315,9 +234,7 @@ exports.removeToolFromMenu = removeToolFromMenu;
* The document to which the tool items are to be added.
*/
function addAllToolsToMenu(doc) {
let fragCommands = doc.createDocumentFragment();
let fragKeys = doc.createDocumentFragment();
let fragBroadcasters = doc.createDocumentFragment();
let fragMenuItems = doc.createDocumentFragment();
for (let toolDefinition of gDevTools.getToolDefinitionArray()) {
@@ -331,22 +248,14 @@ function addAllToolsToMenu(doc) {
continue;
}
fragCommands.appendChild(elements.cmd);
if (elements.key) {
fragKeys.appendChild(elements.key);
}
fragBroadcasters.appendChild(elements.bc);
fragMenuItems.appendChild(elements.menuitem);
}
let mcs = doc.getElementById("mainCommandSet");
mcs.appendChild(fragCommands);
attachKeybindingsToBrowser(doc, fragKeys);
let mbs = doc.getElementById("mainBroadcasterSet");
mbs.appendChild(fragBroadcasters);
let mps = doc.getElementById("menu_devtools_separator");
if (mps) {
mps.parentNode.insertBefore(fragMenuItems, mps);
@@ -385,10 +294,15 @@ function addTopLevelItems(doc) {
if (item.key && l10nKey) {
// Create a <key>
let key = createKey(doc, l10nKey, null, item.key);
// Bug 371900: command event is fired only if "oncommand" attribute is set.
key.setAttribute("oncommand", ";");
key.addEventListener("command", item.oncommand);
let shortcut = l10n(l10nKey + ".key");
let key = createKey({
doc,
id: item.key.id,
shortcut: shortcut,
keytext: shortcut.startsWith("VK_") ? l10n(l10nKey + ".keytext") : null,
modifiers: item.key.modifiers,
oncommand: item.oncommand
});
// Refer to the key in order to display the key shortcut at menu ends
menuitem.setAttribute("key", key.id);
keys.appendChild(key);
@@ -396,10 +310,15 @@ function addTopLevelItems(doc) {
if (item.additionalKeys) {
// Create additional <key>
for (let key of item.additionalKeys) {
let node = createKey(doc, key.l10nKey, null, key);
// Bug 371900: command event is fired only if "oncommand" attribute is set.
node.setAttribute("oncommand", ";");
node.addEventListener("command", item.oncommand);
let shortcut = l10n(key.l10nKey + ".key");
let node = createKey({
doc,
id: key.id,
shortcut: shortcut,
keytext: shortcut.startsWith("VK_") ? l10n(key.l10nKey + ".keytext") : null,
modifiers: key.modifiers,
oncommand: item.oncommand
});
keys.appendChild(node);
}
}