diff options
Diffstat (limited to '')
-rw-r--r-- | dom/html/HTMLMenuBuilder.jsm | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/dom/html/HTMLMenuBuilder.jsm b/dom/html/HTMLMenuBuilder.jsm new file mode 100644 index 0000000000..6b8da6a1cc --- /dev/null +++ b/dom/html/HTMLMenuBuilder.jsm @@ -0,0 +1,132 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This component is used to build the menus for the HTML contextmenu attribute. + +// A global value that is used to identify each menu item. It is +// incremented with each one that is found. +var gGeneratedId = 1; + +function HTMLMenuBuilder() { + this.currentNode = null; + this.root = null; + this.items = {}; + this.nestedStack = []; +} + +// Building is done in two steps: +// The first generates a hierarchical JS object that contains the menu structure. +// This object is returned by toJSONString. +// +// The second step can take this structure and generate a XUL menu hierarchy or +// other UI from this object. The default UI is done in PageMenu.jsm. +// +// When a multi-process browser is used, the first step is performed by the child +// process and the second step is performed by the parent process. + +HTMLMenuBuilder.prototype = { + classID: Components.ID("{51c65f5d-0de5-4edc-9058-60e50cef77f8}"), + QueryInterface: ChromeUtils.generateQI(["nsIMenuBuilder"]), + + currentNode: null, + root: null, + items: {}, + nestedStack: [], + + toJSONString() { + return JSON.stringify(this.root); + }, + + openContainer(aLabel) { + if (!this.currentNode) { + this.root = { + type: "menu", + children: [], + }; + this.currentNode = this.root; + } else { + let parent = this.currentNode; + this.currentNode = { + type: "menu", + label: aLabel, + children: [], + }; + parent.children.push(this.currentNode); + this.nestedStack.push(parent); + } + }, + + addItemFor(aElement, aCanLoadIcon) { + // Since we no longer type check this at the IDL level, make sure we've got + // the right element type here. + if (ChromeUtils.getClassName(aElement) !== "HTMLMenuItemElement") { + return; + } + if (!("children" in this.currentNode)) { + return; + } + + let item = { + type: "menuitem", + label: aElement.label, + }; + + let elementType = aElement.type; + if (elementType == "checkbox" || elementType == "radio") { + item.checkbox = true; + + if (aElement.checked) { + item.checked = true; + } + } + + let icon = aElement.icon; + if (icon.length > 0 && aCanLoadIcon) { + item.icon = icon; + } + + if (aElement.disabled) { + item.disabled = true; + } + + item.id = gGeneratedId++; + this.currentNode.children.push(item); + + this.items[item.id] = aElement; + }, + + addSeparator() { + if (!("children" in this.currentNode)) { + return; + } + + this.currentNode.children.push({ type: "separator" }); + }, + + undoAddSeparator() { + if (!("children" in this.currentNode)) { + return; + } + + let children = this.currentNode.children; + if (children.length && children[children.length - 1].type == "separator") { + children.pop(); + } + }, + + closeContainer() { + this.currentNode = this.nestedStack.length + ? this.nestedStack.pop() + : this.root; + }, + + click(id) { + let item = this.items[id]; + if (item) { + item.click(); + } + }, +}; + +var EXPORTED_SYMBOLS = ["HTMLMenuBuilder"]; |