summaryrefslogtreecommitdiffstats
path: root/dom/html/htmlMenuBuilder.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/html/htmlMenuBuilder.js')
-rw-r--r--dom/html/htmlMenuBuilder.js132
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]);