2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @param {!WebInspector.ContextMenu} topLevelMenu
34 * @param {string} type
35 * @param {string=} label
36 * @param {boolean=} disabled
37 * @param {boolean=} checked
39 WebInspector.ContextMenuItem = function(topLevelMenu, type, label, disabled, checked)
43 this._disabled = disabled;
44 this._checked = checked;
45 this._contextMenu = topLevelMenu;
46 if (type === "item" || type === "checkbox")
47 this._id = topLevelMenu.nextId();
50 WebInspector.ContextMenuItem.prototype = {
72 return !this._disabled;
76 * @param {boolean} enabled
78 setEnabled: function(enabled)
80 this._disabled = !enabled;
84 * @return {!InspectorFrontendHostAPI.ContextMenuDescriptor}
86 _buildDescriptor: function()
90 return { type: "item", id: this._id, label: this._label, enabled: !this._disabled };
92 return { type: "separator" };
94 return { type: "checkbox", id: this._id, label: this._label, checked: !!this._checked, enabled: !this._disabled };
96 throw new Error("Invalid item type:" + this._type);
102 * @extends {WebInspector.ContextMenuItem}
103 * @param {!WebInspector.ContextMenu} topLevelMenu
104 * @param {string=} label
105 * @param {boolean=} disabled
107 WebInspector.ContextSubMenuItem = function(topLevelMenu, label, disabled)
109 WebInspector.ContextMenuItem.call(this, topLevelMenu, "subMenu", label, disabled);
110 /** @type {!Array.<!WebInspector.ContextMenuItem>} */
114 WebInspector.ContextSubMenuItem.prototype = {
116 * @param {string} label
117 * @param {function(?)} handler
118 * @param {boolean=} disabled
119 * @return {!WebInspector.ContextMenuItem}
121 appendItem: function(label, handler, disabled)
123 var item = new WebInspector.ContextMenuItem(this._contextMenu, "item", label, disabled);
124 this._pushItem(item);
125 this._contextMenu._setHandler(item.id(), handler);
130 * @param {string} label
131 * @param {boolean=} disabled
132 * @return {!WebInspector.ContextSubMenuItem}
134 appendSubMenuItem: function(label, disabled)
136 var item = new WebInspector.ContextSubMenuItem(this._contextMenu, label, disabled);
137 this._pushItem(item);
142 * @param {string} label
143 * @param {function()} handler
144 * @param {boolean=} checked
145 * @param {boolean=} disabled
146 * @return {!WebInspector.ContextMenuItem}
148 appendCheckboxItem: function(label, handler, checked, disabled)
150 var item = new WebInspector.ContextMenuItem(this._contextMenu, "checkbox", label, disabled, checked);
151 this._pushItem(item);
152 this._contextMenu._setHandler(item.id(), handler);
156 appendSeparator: function()
158 if (this._items.length)
159 this._pendingSeparator = true;
163 * @param {!WebInspector.ContextMenuItem} item
165 _pushItem: function(item)
167 if (this._pendingSeparator) {
168 this._items.push(new WebInspector.ContextMenuItem(this._contextMenu, "separator"));
169 delete this._pendingSeparator;
171 this._items.push(item);
179 return !this._items.length;
183 * @return {!InspectorFrontendHostAPI.ContextMenuDescriptor}
185 _buildDescriptor: function()
187 var result = { type: "subMenu", label: this._label, enabled: !this._disabled, subItems: [] };
188 for (var i = 0; i < this._items.length; ++i)
189 result.subItems.push(this._items[i]._buildDescriptor());
193 __proto__: WebInspector.ContextMenuItem.prototype
198 * @extends {WebInspector.ContextSubMenuItem}
199 * @param {!Event} event
201 WebInspector.ContextMenu = function(event)
203 WebInspector.ContextSubMenuItem.call(this, this, "");
204 /** @type {!Array.<!Promise.<!Array.<!WebInspector.ContextMenu.Provider>>>} */
205 this._pendingPromises = [];
206 /** @type {!Array.<!Promise.<!Object>>} */
207 this._pendingTargets = [];
215 WebInspector.ContextMenu.initialize = function()
217 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.SetUseSoftMenu, setUseSoftMenu);
219 * @param {!WebInspector.Event} event
221 function setUseSoftMenu(event)
223 WebInspector.ContextMenu._useSoftMenu = /** @type {boolean} */ (event.data);
228 * @param {!Document} doc
230 WebInspector.ContextMenu.installHandler = function(doc)
232 doc.body.addEventListener("contextmenu", handler, false);
235 * @param {!Event} event
237 function handler(event)
239 var contextMenu = new WebInspector.ContextMenu(event);
240 contextMenu.appendApplicableItems(/** @type {!Object} */ (event.target));
245 WebInspector.ContextMenu.prototype = {
256 Promise.all(this._pendingPromises).then(populateAndShow.bind(this)).done();
257 WebInspector.ContextMenu._pendingMenu = this;
260 * @param {!Array.<!Array.<!WebInspector.ContextMenu.Provider>>} appendCallResults
261 * @this {WebInspector.ContextMenu}
263 function populateAndShow(appendCallResults)
265 if (WebInspector.ContextMenu._pendingMenu !== this)
267 delete WebInspector.ContextMenu._pendingMenu;
269 for (var i = 0; i < appendCallResults.length; ++i) {
270 var providers = appendCallResults[i];
271 var target = this._pendingTargets[i];
273 for (var j = 0; j < providers.length; ++j) {
274 var provider = /** @type {!WebInspector.ContextMenu.Provider} */ (providers[j]);
275 this.appendSeparator();
276 provider.appendApplicableItems(this._event, this, target);
277 this.appendSeparator();
281 this._pendingPromises = [];
282 this._pendingTargets = [];
286 this._event.consume(true);
289 _innerShow: function()
291 var menuObject = this._buildDescriptor();
293 WebInspector._contextMenu = this;
294 if (WebInspector.ContextMenu._useSoftMenu || InspectorFrontendHost.isHostedMode()) {
295 var softMenu = new WebInspector.SoftContextMenu(menuObject, this._itemSelected.bind(this));
296 softMenu.show(this._event.target.ownerDocument, this._x, this._y);
298 InspectorFrontendHost.showContextMenuAtPoint(this._x, this._y, menuObject, this._event.target.ownerDocument);
299 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
300 InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
306 * @param {function(?)} handler
308 _setHandler: function(id, handler)
311 this._handlers[id] = handler;
315 * @return {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>}
317 _buildDescriptor: function()
320 for (var i = 0; i < this._items.length; ++i)
321 result.push(this._items[i]._buildDescriptor());
326 * @param {!WebInspector.Event} event
328 _onItemSelected: function(event)
330 this._itemSelected(/** @type {string} */ (event.data));
336 _itemSelected: function(id)
338 if (this._handlers[id])
339 this._handlers[id].call(this);
343 _menuCleared: function()
345 InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
346 InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
350 * @param {!Object} target
352 appendApplicableItems: function(target)
354 this._pendingPromises.push(self.runtime.instancesPromise(WebInspector.ContextMenu.Provider, target));
355 this._pendingTargets.push(target);
358 __proto__: WebInspector.ContextSubMenuItem.prototype
364 WebInspector.ContextMenu.Provider = function() {
367 WebInspector.ContextMenu.Provider.prototype = {
369 * @param {!Event} event
370 * @param {!WebInspector.ContextMenu} contextMenu
371 * @param {!Object} target
373 appendApplicableItems: function(event, contextMenu, target) { }