PageRenderTime 13ms CodeModel.GetById 2ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/toolkit/content/PageMenu.jsm

http://github.com/zpao/v8monkey
Unknown | 170 lines | 145 code | 25 blank | 0 comment | 0 complexity | db3c56cbe3556f6983825cf27bf775e9 MD5 | raw file
  1# ***** BEGIN LICENSE BLOCK *****
  2# Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3#
  4# The contents of this file are subject to the Mozilla Public License Version
  5# 1.1 (the "License"); you may not use this file except in compliance with
  6# the License. You may obtain a copy of the License at
  7# http://www.mozilla.org/MPL/
  8#
  9# Software distributed under the License is distributed on an "AS IS" basis,
 10# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11# for the specific language governing rights and limitations under the
 12# License.
 13#
 14# The Original Code is Mozilla code.
 15#
 16# The Initial Developer of the Original Code is Mozilla Foundation
 17# Portions created by the Initial Developer are Copyright (C) 2011
 18# the Initial Developer. All Rights Reserved.
 19#
 20# Contributor(s):
 21#
 22# Alternatively, the contents of this file may be used under the terms of
 23# either the GNU General Public License Version 2 or later (the "GPL"), or
 24# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 25# in which case the provisions of the GPL or the LGPL are applicable instead
 26# of those above. If you wish to allow use of your version of this file only
 27# under the terms of either the GPL or the LGPL, and not to allow others to
 28# use your version of this file under the terms of the MPL, indicate your
 29# decision by deleting the provisions above and replace them with the notice
 30# and other provisions required by the LGPL or the GPL. If you do not delete
 31# the provisions above, a recipient may use your version of this file under
 32# the terms of any one of the MPL, the GPL or the LGPL.
 33#
 34# ***** END LICENSE BLOCK ***** -->
 35
 36let EXPORTED_SYMBOLS = ["PageMenu"];
 37
 38function PageMenu() {
 39}
 40
 41PageMenu.prototype = {
 42  PAGEMENU_ATTR: "pagemenu",
 43  GENERATEDITEMID_ATTR: "generateditemid",
 44
 45  popup: null,
 46  builder: null,
 47
 48  maybeBuildAndAttachMenu: function(aTarget, aPopup) {
 49    var pageMenu = null;
 50    var target = aTarget;
 51    while (target) {
 52      var contextMenu = target.contextMenu;
 53      if (contextMenu) {
 54        pageMenu = contextMenu;
 55        break;
 56      }
 57      target = target.parentNode;
 58    }
 59
 60    if (!pageMenu) {
 61      return false;
 62    }
 63
 64    var insertionPoint = this.getInsertionPoint(aPopup);
 65    if (!insertionPoint) {
 66      return false;
 67    }
 68
 69    pageMenu.QueryInterface(Components.interfaces.nsIHTMLMenu);
 70    pageMenu.sendShowEvent();
 71    // the show event is not cancelable, so no need to check a result here
 72
 73    var fragment = aPopup.ownerDocument.createDocumentFragment();
 74
 75    var builder = pageMenu.createBuilder();
 76    if (!builder) {
 77      return false;
 78    }
 79    builder.QueryInterface(Components.interfaces.nsIXULContextMenuBuilder);
 80    builder.init(fragment, this.GENERATEDITEMID_ATTR);
 81
 82    pageMenu.build(builder);
 83
 84    var pos = insertionPoint.getAttribute(this.PAGEMENU_ATTR);
 85    if (pos == "start") {
 86      insertionPoint.insertBefore(fragment,
 87                                  insertionPoint.firstChild);
 88    } else {
 89      insertionPoint.appendChild(fragment);
 90    }
 91
 92    this.builder = builder;
 93    this.popup = aPopup;
 94
 95    this.popup.addEventListener("command", this);
 96    this.popup.addEventListener("popuphidden", this);
 97
 98    return true;
 99  },
100
101  handleEvent: function(event) {
102    var type = event.type;
103    var target = event.target;
104    if (type == "command" && target.hasAttribute(this.GENERATEDITEMID_ATTR)) {
105      this.builder.click(target.getAttribute(this.GENERATEDITEMID_ATTR));
106    } else if (type == "popuphidden" && this.popup == target) {
107      this.removeGeneratedContent(this.popup);
108
109      this.popup.removeEventListener("popuphidden", this);
110      this.popup.removeEventListener("command", this);
111
112      this.popup = null;
113      this.builder = null;
114    }
115  },
116
117  getImmediateChild: function(element, tag) {
118    var child = element.firstChild;
119    while (child) {
120      if (child.localName == tag) {
121        return child;
122      }
123      child = child.nextSibling;
124    }
125    return null;
126  },
127
128  getInsertionPoint: function(aPopup) {
129    if (aPopup.hasAttribute(this.PAGEMENU_ATTR))
130      return aPopup;
131
132    var element = aPopup.firstChild;
133    while (element) {
134      if (element.localName == "menu") {
135        var popup = this.getImmediateChild(element, "menupopup");
136        if (popup) {
137          var result = this.getInsertionPoint(popup);
138          if (result) {
139            return result;
140          }
141        }
142      }
143      element = element.nextSibling;
144    }
145
146    return null;
147  },
148
149  removeGeneratedContent: function(aPopup) {
150    var ungenerated = [];
151    ungenerated.push(aPopup);
152
153    var count;
154    while (0 != (count = ungenerated.length)) {
155      var last = count - 1;
156      var element = ungenerated[last];
157      ungenerated.splice(last, 1);
158
159      var i = element.childNodes.length;
160      while (i-- > 0) {
161        var child = element.childNodes[i];
162        if (!child.hasAttribute(this.GENERATEDITEMID_ATTR)) {
163          ungenerated.push(child);
164          continue;
165        }
166        element.removeChild(child);
167      }
168    }
169  }
170}