diff options
Diffstat (limited to 'dom/html/htmlMenuBuilder.js')
-rw-r--r-- | dom/html/htmlMenuBuilder.js | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/dom/html/htmlMenuBuilder.js b/dom/html/htmlMenuBuilder.js new file mode 100644 index 000000000..863fc4d74 --- /dev/null +++ b/dom/html/htmlMenuBuilder.js @@ -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. + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const Cc = Components.classes; +const Ci = Components.interfaces; + +// 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: XPCOMUtils.generateQI([Ci.nsIMenuBuilder]), + + currentNode: null, + root: null, + items: {}, + nestedStack: [], + + toJSONString: function() { + return JSON.stringify(this.root); + }, + + openContainer: function(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: function(aElement, aCanLoadIcon) { + 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: function() { + if (!("children" in this.currentNode)) { + return; + } + + this.currentNode.children.push({ type: "separator"}); + }, + + undoAddSeparator: function() { + if (!("children" in this.currentNode)) { + return; + } + + let children = this.currentNode.children; + if (children.length && children[children.length - 1].type == "separator") { + children.pop(); + } + }, + + closeContainer: function() { + this.currentNode = this.nestedStack.length ? this.nestedStack.pop() : this.root; + }, + + click: function(id) { + let item = this.items[id]; + if (item) { + item.click(); + } + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HTMLMenuBuilder]); |