htmlMenuBuilder.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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/. */
  4. // This component is used to build the menus for the HTML contextmenu attribute.
  5. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  6. const Cc = Components.classes;
  7. const Ci = Components.interfaces;
  8. // A global value that is used to identify each menu item. It is
  9. // incremented with each one that is found.
  10. var gGeneratedId = 1;
  11. function HTMLMenuBuilder() {
  12. this.currentNode = null;
  13. this.root = null;
  14. this.items = {};
  15. this.nestedStack = [];
  16. };
  17. // Building is done in two steps:
  18. // The first generates a hierarchical JS object that contains the menu structure.
  19. // This object is returned by toJSONString.
  20. //
  21. // The second step can take this structure and generate a XUL menu hierarchy or
  22. // other UI from this object. The default UI is done in PageMenu.jsm.
  23. //
  24. // When a multi-process browser is used, the first step is performed by the child
  25. // process and the second step is performed by the parent process.
  26. HTMLMenuBuilder.prototype =
  27. {
  28. classID: Components.ID("{51c65f5d-0de5-4edc-9058-60e50cef77f8}"),
  29. QueryInterface: XPCOMUtils.generateQI([Ci.nsIMenuBuilder]),
  30. currentNode: null,
  31. root: null,
  32. items: {},
  33. nestedStack: [],
  34. toJSONString: function() {
  35. return JSON.stringify(this.root);
  36. },
  37. openContainer: function(aLabel) {
  38. if (!this.currentNode) {
  39. this.root = {
  40. type: "menu",
  41. children: []
  42. };
  43. this.currentNode = this.root;
  44. }
  45. else {
  46. let parent = this.currentNode;
  47. this.currentNode = {
  48. type: "menu",
  49. label: aLabel,
  50. children: []
  51. };
  52. parent.children.push(this.currentNode);
  53. this.nestedStack.push(parent);
  54. }
  55. },
  56. addItemFor: function(aElement, aCanLoadIcon) {
  57. if (!("children" in this.currentNode)) {
  58. return;
  59. }
  60. let item = {
  61. type: "menuitem",
  62. label: aElement.label
  63. };
  64. let elementType = aElement.type;
  65. if (elementType == "checkbox" || elementType == "radio") {
  66. item.checkbox = true;
  67. if (aElement.checked) {
  68. item.checked = true;
  69. }
  70. }
  71. let icon = aElement.icon;
  72. if (icon.length > 0 && aCanLoadIcon) {
  73. item.icon = icon;
  74. }
  75. if (aElement.disabled) {
  76. item.disabled = true;
  77. }
  78. item.id = gGeneratedId++;
  79. this.currentNode.children.push(item);
  80. this.items[item.id] = aElement;
  81. },
  82. addSeparator: function() {
  83. if (!("children" in this.currentNode)) {
  84. return;
  85. }
  86. this.currentNode.children.push({ type: "separator"});
  87. },
  88. undoAddSeparator: function() {
  89. if (!("children" in this.currentNode)) {
  90. return;
  91. }
  92. let children = this.currentNode.children;
  93. if (children.length && children[children.length - 1].type == "separator") {
  94. children.pop();
  95. }
  96. },
  97. closeContainer: function() {
  98. this.currentNode = this.nestedStack.length ? this.nestedStack.pop() : this.root;
  99. },
  100. click: function(id) {
  101. let item = this.items[id];
  102. if (item) {
  103. item.click();
  104. }
  105. }
  106. };
  107. this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HTMLMenuBuilder]);