Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / ui / ContextMenu.js
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30
31 /**
32  * @constructor
33  * @param {!WebInspector.ContextMenu} topLevelMenu
34  * @param {string} type
35  * @param {string=} label
36  * @param {boolean=} disabled
37  * @param {boolean=} checked
38  */
39 WebInspector.ContextMenuItem = function(topLevelMenu, type, label, disabled, checked)
40 {
41     this._type = type;
42     this._label = label;
43     this._disabled = disabled;
44     this._checked = checked;
45     this._contextMenu = topLevelMenu;
46     if (type === "item" || type === "checkbox")
47         this._id = topLevelMenu.nextId();
48 }
49
50 WebInspector.ContextMenuItem.prototype = {
51     /**
52      * @return {number}
53      */
54     id: function()
55     {
56         return this._id;
57     },
58
59     /**
60      * @return {string}
61      */
62     type: function()
63     {
64         return this._type;
65     },
66
67     /**
68      * @return {boolean}
69      */
70     isEnabled: function()
71     {
72         return !this._disabled;
73     },
74
75     /**
76      * @param {boolean} enabled
77      */
78     setEnabled: function(enabled)
79     {
80         this._disabled = !enabled;
81     },
82
83     /**
84      * @return {!InspectorFrontendHostAPI.ContextMenuDescriptor}
85      */
86     _buildDescriptor: function()
87     {
88         switch (this._type) {
89         case "item":
90             return { type: "item", id: this._id, label: this._label, enabled: !this._disabled };
91         case "separator":
92             return { type: "separator" };
93         case "checkbox":
94             return { type: "checkbox", id: this._id, label: this._label, checked: !!this._checked, enabled: !this._disabled };
95         }
96         throw new Error("Invalid item type:"  + this._type);
97     }
98 }
99
100 /**
101  * @constructor
102  * @extends {WebInspector.ContextMenuItem}
103  * @param {!WebInspector.ContextMenu} topLevelMenu
104  * @param {string=} label
105  * @param {boolean=} disabled
106  */
107 WebInspector.ContextSubMenuItem = function(topLevelMenu, label, disabled)
108 {
109     WebInspector.ContextMenuItem.call(this, topLevelMenu, "subMenu", label, disabled);
110     /** @type {!Array.<!WebInspector.ContextMenuItem>} */
111     this._items = [];
112 }
113
114 WebInspector.ContextSubMenuItem.prototype = {
115     /**
116      * @param {string} label
117      * @param {function(?)} handler
118      * @param {boolean=} disabled
119      * @return {!WebInspector.ContextMenuItem}
120      */
121     appendItem: function(label, handler, disabled)
122     {
123         var item = new WebInspector.ContextMenuItem(this._contextMenu, "item", label, disabled);
124         this._pushItem(item);
125         this._contextMenu._setHandler(item.id(), handler);
126         return item;
127     },
128
129     /**
130      * @param {string} label
131      * @param {boolean=} disabled
132      * @return {!WebInspector.ContextSubMenuItem}
133      */
134     appendSubMenuItem: function(label, disabled)
135     {
136         var item = new WebInspector.ContextSubMenuItem(this._contextMenu, label, disabled);
137         this._pushItem(item);
138         return item;
139     },
140
141     /**
142      * @param {string} label
143      * @param {function()} handler
144      * @param {boolean=} checked
145      * @param {boolean=} disabled
146      * @return {!WebInspector.ContextMenuItem}
147      */
148     appendCheckboxItem: function(label, handler, checked, disabled)
149     {
150         var item = new WebInspector.ContextMenuItem(this._contextMenu, "checkbox", label, disabled, checked);
151         this._pushItem(item);
152         this._contextMenu._setHandler(item.id(), handler);
153         return item;
154     },
155
156     appendSeparator: function()
157     {
158         if (this._items.length)
159             this._pendingSeparator = true;
160     },
161
162     /**
163      * @param {!WebInspector.ContextMenuItem} item
164      */
165     _pushItem: function(item)
166     {
167         if (this._pendingSeparator) {
168             this._items.push(new WebInspector.ContextMenuItem(this._contextMenu, "separator"));
169             delete this._pendingSeparator;
170         }
171         this._items.push(item);
172     },
173
174     /**
175      * @return {boolean}
176      */
177     isEmpty: function()
178     {
179         return !this._items.length;
180     },
181
182     /**
183      * @return {!InspectorFrontendHostAPI.ContextMenuDescriptor}
184      */
185     _buildDescriptor: function()
186     {
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());
190         return result;
191     },
192
193     __proto__: WebInspector.ContextMenuItem.prototype
194 }
195
196 /**
197  * @constructor
198  * @extends {WebInspector.ContextSubMenuItem}
199  * @param {!Event} event
200  */
201 WebInspector.ContextMenu = function(event)
202 {
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 = [];
208     this._event = event;
209     this._x = event.x;
210     this._y = event.y;
211     this._handlers = {};
212     this._id = 0;
213 }
214
215 WebInspector.ContextMenu.initialize = function()
216 {
217     InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.SetUseSoftMenu, setUseSoftMenu);
218     /**
219      * @param {!WebInspector.Event} event
220      */
221     function setUseSoftMenu(event)
222     {
223         WebInspector.ContextMenu._useSoftMenu = /** @type {boolean} */ (event.data);
224     }
225 }
226
227 /**
228  * @param {!Document} doc
229  */
230 WebInspector.ContextMenu.installHandler = function(doc)
231 {
232     doc.body.addEventListener("contextmenu", handler, false);
233
234     /**
235      * @param {!Event} event
236      */
237     function handler(event)
238     {
239         var contextMenu = new WebInspector.ContextMenu(event);
240         contextMenu.appendApplicableItems(/** @type {!Object} */ (event.target));
241         contextMenu.show();
242     }
243 }
244
245 WebInspector.ContextMenu.prototype = {
246     /**
247      * @return {number}
248      */
249     nextId: function()
250     {
251         return this._id++;
252     },
253
254     show: function()
255     {
256         Promise.all(this._pendingPromises).then(populateAndShow.bind(this)).done();
257         WebInspector.ContextMenu._pendingMenu = this;
258
259         /**
260          * @param {!Array.<!Array.<!WebInspector.ContextMenu.Provider>>} appendCallResults
261          * @this {WebInspector.ContextMenu}
262          */
263         function populateAndShow(appendCallResults)
264         {
265             if (WebInspector.ContextMenu._pendingMenu !== this)
266                 return;
267             delete WebInspector.ContextMenu._pendingMenu;
268
269             for (var i = 0; i < appendCallResults.length; ++i) {
270                 var providers = appendCallResults[i];
271                 var target = this._pendingTargets[i];
272
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();
278                 }
279             }
280
281             this._pendingPromises = [];
282             this._pendingTargets = [];
283             this._innerShow();
284         }
285
286         this._event.consume(true);
287     },
288
289     _innerShow: function()
290     {
291         var menuObject = this._buildDescriptor();
292
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);
297         } else {
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);
301         }
302     },
303
304     /**
305      * @param {number} id
306      * @param {function(?)} handler
307      */
308     _setHandler: function(id, handler)
309     {
310         if (handler)
311             this._handlers[id] = handler;
312     },
313
314     /**
315      * @return {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>}
316      */
317     _buildDescriptor: function()
318     {
319         var result = [];
320         for (var i = 0; i < this._items.length; ++i)
321             result.push(this._items[i]._buildDescriptor());
322         return result;
323     },
324
325     /**
326      * @param {!WebInspector.Event} event
327      */
328     _onItemSelected: function(event)
329     {
330         this._itemSelected(/** @type {string} */ (event.data));
331     },
332
333     /**
334      * @param {string} id
335      */
336     _itemSelected: function(id)
337     {
338         if (this._handlers[id])
339             this._handlers[id].call(this);
340         this._menuCleared();
341     },
342
343     _menuCleared: function()
344     {
345         InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
346         InspectorFrontendHost.events.removeEventListener(InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
347     },
348
349     /**
350      * @param {!Object} target
351      */
352     appendApplicableItems: function(target)
353     {
354         this._pendingPromises.push(self.runtime.instancesPromise(WebInspector.ContextMenu.Provider, target));
355         this._pendingTargets.push(target);
356     },
357
358     __proto__: WebInspector.ContextSubMenuItem.prototype
359 }
360
361 /**
362  * @interface
363  */
364 WebInspector.ContextMenu.Provider = function() {
365 }
366
367 WebInspector.ContextMenu.Provider.prototype = {
368     /**
369      * @param {!Event} event
370      * @param {!WebInspector.ContextMenu} contextMenu
371      * @param {!Object} target
372      */
373     appendApplicableItems: function(event, contextMenu, target) { }
374 }