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