Bumping manifests a=b2g-bump
[gecko.git] / toolkit / modules / PageMenu.jsm
blob7d5823f8557bceb5fb5d8b216b9e18dd1b095c39
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 this.EXPORTED_SYMBOLS = ["PageMenu"];
7 this.PageMenu = function PageMenu() {
10 PageMenu.prototype = {
11   PAGEMENU_ATTR: "pagemenu",
12   GENERATEDITEMID_ATTR: "generateditemid",
14   popup: null,
15   builder: null,
17   maybeBuildAndAttachMenu: function(aTarget, aPopup) {
18     var pageMenu = null;
19     var target = aTarget;
20     while (target) {
21       var contextMenu = target.contextMenu;
22       if (contextMenu) {
23         pageMenu = contextMenu;
24         break;
25       }
26       target = target.parentNode;
27     }
29     if (!pageMenu) {
30       return false;
31     }
33     var insertionPoint = this.getInsertionPoint(aPopup);
34     if (!insertionPoint) {
35       return false;
36     }
38     pageMenu.QueryInterface(Components.interfaces.nsIHTMLMenu);
39     pageMenu.sendShowEvent();
40     // the show event is not cancelable, so no need to check a result here
42     var fragment = aPopup.ownerDocument.createDocumentFragment();
44     var builder = pageMenu.createBuilder();
45     if (!builder) {
46       return false;
47     }
48     builder.QueryInterface(Components.interfaces.nsIXULContextMenuBuilder);
49     builder.init(fragment, this.GENERATEDITEMID_ATTR);
51     pageMenu.build(builder);
53     var pos = insertionPoint.getAttribute(this.PAGEMENU_ATTR);
54     if (pos == "start") {
55       insertionPoint.insertBefore(fragment,
56                                   insertionPoint.firstChild);
57     } else if (pos.startsWith("#")) {
58       insertionPoint.insertBefore(fragment, insertionPoint.querySelector(pos));
59     } else {
60       insertionPoint.appendChild(fragment);
61     }
63     this.builder = builder;
64     this.popup = aPopup;
66     this.popup.addEventListener("command", this);
67     this.popup.addEventListener("popuphidden", this);
69     return true;
70   },
72   handleEvent: function(event) {
73     var type = event.type;
74     var target = event.target;
75     if (type == "command" && target.hasAttribute(this.GENERATEDITEMID_ATTR)) {
76       this.builder.click(target.getAttribute(this.GENERATEDITEMID_ATTR));
77     } else if (type == "popuphidden" && this.popup == target) {
78       this.removeGeneratedContent(this.popup);
80       this.popup.removeEventListener("popuphidden", this);
81       this.popup.removeEventListener("command", this);
83       this.popup = null;
84       this.builder = null;
85     }
86   },
88   getImmediateChild: function(element, tag) {
89     var child = element.firstChild;
90     while (child) {
91       if (child.localName == tag) {
92         return child;
93       }
94       child = child.nextSibling;
95     }
96     return null;
97   },
99   getInsertionPoint: function(aPopup) {
100     if (aPopup.hasAttribute(this.PAGEMENU_ATTR))
101       return aPopup;
103     var element = aPopup.firstChild;
104     while (element) {
105       if (element.localName == "menu") {
106         var popup = this.getImmediateChild(element, "menupopup");
107         if (popup) {
108           var result = this.getInsertionPoint(popup);
109           if (result) {
110             return result;
111           }
112         }
113       }
114       element = element.nextSibling;
115     }
117     return null;
118   },
120   removeGeneratedContent: function(aPopup) {
121     var ungenerated = [];
122     ungenerated.push(aPopup);
124     var count;
125     while (0 != (count = ungenerated.length)) {
126       var last = count - 1;
127       var element = ungenerated[last];
128       ungenerated.splice(last, 1);
130       var i = element.childNodes.length;
131       while (i-- > 0) {
132         var child = element.childNodes[i];
133         if (!child.hasAttribute(this.GENERATEDITEMID_ATTR)) {
134           ungenerated.push(child);
135           continue;
136         }
137         element.removeChild(child);
138       }
139     }
140   }