Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / WatchExpressionsSidebarPane.js
1 /*
2  * Copyright (C) IBM Corp. 2009  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 IBM Corp. 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  * @extends {WebInspector.SidebarPane}
34  */
35 WebInspector.WatchExpressionsSidebarPane = function()
36 {
37     WebInspector.SidebarPane.call(this, WebInspector.UIString("Watch Expressions"));
38
39     this.section = new WebInspector.WatchExpressionsSection();
40     this.bodyElement.appendChild(this.section.element);
41
42     var refreshButton = document.createElement("button");
43     refreshButton.className = "pane-title-button refresh";
44     refreshButton.addEventListener("click", this._refreshButtonClicked.bind(this), false);
45     refreshButton.title = WebInspector.UIString("Refresh");
46     this.titleElement.appendChild(refreshButton);
47
48     var addButton = document.createElement("button");
49     addButton.className = "pane-title-button add";
50     addButton.addEventListener("click", this._addButtonClicked.bind(this), false);
51     this.titleElement.appendChild(addButton);
52     addButton.title = WebInspector.UIString("Add watch expression");
53
54     this._requiresUpdate = true;
55 }
56
57 WebInspector.WatchExpressionsSidebarPane.prototype = {
58     wasShown: function()
59     {
60         this._refreshExpressionsIfNeeded();
61     },
62
63     reset: function()
64     {
65         this.refreshExpressions();
66     },
67
68     refreshExpressions: function()
69     {
70         this._requiresUpdate = true;
71         this._refreshExpressionsIfNeeded();
72     },
73
74     addExpression: function(expression)
75     {
76         this.section.addExpression(expression);
77         this.expand();
78     },
79
80     _refreshExpressionsIfNeeded: function()
81     {
82         if (this._requiresUpdate && this.isShowing()) {
83             this.section.update();
84             delete this._requiresUpdate;
85         } else
86             this._requiresUpdate = true;
87     },
88
89     _addButtonClicked: function(event)
90     {
91         event.consume();
92         this.expand();
93         this.section.addNewExpressionAndEdit();
94     },
95
96     _refreshButtonClicked: function(event)
97     {
98         event.consume();
99         this.refreshExpressions();
100     },
101
102     __proto__: WebInspector.SidebarPane.prototype
103 }
104
105 /**
106  * @constructor
107  * @extends {WebInspector.ObjectPropertiesSection}
108  */
109 WebInspector.WatchExpressionsSection = function()
110 {
111     this._watchObjectGroupId = "watch-group";
112
113     WebInspector.ObjectPropertiesSection.call(this, WebInspector.RemoteObject.fromPrimitiveValue(""));
114
115     this.treeElementConstructor = WebInspector.WatchedPropertyTreeElement;
116     this._expandedExpressions = {};
117     this._expandedProperties = {};
118
119     this.emptyElement = document.createElement("div");
120     this.emptyElement.className = "info";
121     this.emptyElement.textContent = WebInspector.UIString("No Watch Expressions");
122
123     this.watchExpressions = WebInspector.settings.watchExpressions.get();
124
125     this.headerElement.className = "hidden";
126     this.editable = true;
127     this.expanded = true;
128     this.propertiesElement.classList.add("watch-expressions");
129
130     this.element.addEventListener("mousemove", this._mouseMove.bind(this), true);
131     this.element.addEventListener("mouseout", this._mouseOut.bind(this), true);
132     this.element.addEventListener("dblclick", this._sectionDoubleClick.bind(this), false);
133     this.emptyElement.addEventListener("contextmenu", this._emptyElementContextMenu.bind(this), false);
134 }
135
136 WebInspector.WatchExpressionsSection.NewWatchExpression = "\xA0";
137
138 WebInspector.WatchExpressionsSection.prototype = {
139     /**
140      * @param {?Event=} e
141      */
142     update: function(e)
143     {
144         if (e)
145             e.consume();
146
147         /***
148          * @param {string} expression
149          * @param {number} watchIndex
150          * @param {?WebInspector.RemoteObject} result
151          * @param {boolean} wasThrown
152          * @this {WebInspector.WatchExpressionsSection}
153          */
154         function appendResult(expression, watchIndex, result, wasThrown)
155         {
156             if (!result)
157                 return;
158
159             var property = new WebInspector.RemoteObjectProperty(expression, result);
160             property.watchIndex = watchIndex;
161             property.wasThrown = wasThrown;
162
163             // To clarify what's going on here:
164             // In the outer function, we calculate the number of properties
165             // that we're going to be updating, and set that in the
166             // propertyCount variable.
167             // In this function, we test to see when we are processing the
168             // last property, and then call the superclass's updateProperties()
169             // method to get all the properties refreshed at once.
170             properties.push(property);
171
172             if (properties.length == propertyCount) {
173                 this.updateProperties(properties, [], WebInspector.WatchExpressionTreeElement, WebInspector.WatchExpressionsSection.CompareProperties);
174
175                 // check to see if we just added a new watch expression,
176                 // which will always be the last property
177                 if (this._newExpressionAdded) {
178                     delete this._newExpressionAdded;
179
180                     var treeElement = this.findAddedTreeElement();
181                     if (treeElement)
182                         treeElement.startEditing();
183                 }
184
185                 // Force displaying delete button for hovered element.
186                 if (this._lastMouseMovePageY)
187                     this._updateHoveredElement(this._lastMouseMovePageY);
188             }
189         }
190
191         // TODO: pass exact injected script id.
192         RuntimeAgent.releaseObjectGroup(this._watchObjectGroupId)
193         var properties = [];
194
195         // Count the properties, so we known when to call this.updateProperties()
196         // in appendResult()
197         var propertyCount = 0;
198         for (var i = 0; i < this.watchExpressions.length; ++i) {
199             if (!this.watchExpressions[i])
200                 continue;
201             ++propertyCount;
202         }
203
204         // Now process all the expressions, since we have the actual count,
205         // which is checked in the appendResult inner function.
206         for (var i = 0; i < this.watchExpressions.length; ++i) {
207             var expression = this.watchExpressions[i];
208             if (!expression)
209                 continue;
210
211             WebInspector.runtimeModel.evaluate(expression, this._watchObjectGroupId, false, true, false, false, appendResult.bind(this, expression, i));
212         }
213
214         if (!propertyCount) {
215             if (!this.emptyElement.parentNode)
216                 this.element.appendChild(this.emptyElement);
217         } else {
218             if (this.emptyElement.parentNode)
219                 this.element.removeChild(this.emptyElement);
220         }
221
222         // note this is setting the expansion of the tree, not the section;
223         // with no expressions, and expanded tree, we get some extra vertical
224         // white space
225         this.expanded = (propertyCount != 0);
226     },
227
228     addExpression: function(expression)
229     {
230         this.watchExpressions.push(expression);
231         this.saveExpressions();
232         this.update();
233     },
234
235     addNewExpressionAndEdit: function()
236     {
237         this._newExpressionAdded = true;
238         this.watchExpressions.push(WebInspector.WatchExpressionsSection.NewWatchExpression);
239         this.update();
240     },
241
242     _sectionDoubleClick: function(event)
243     {
244         if (event.target !== this.element && event.target !== this.propertiesElement && event.target !== this.emptyElement)
245             return;
246         event.consume();
247         this.addNewExpressionAndEdit();
248     },
249
250     updateExpression: function(element, value)
251     {
252         if (value === null) {
253             var index = element.property.watchIndex;
254             this.watchExpressions.splice(index, 1);
255         }
256         else
257             this.watchExpressions[element.property.watchIndex] = value;
258         this.saveExpressions();
259         this.update();
260     },
261     
262     _deleteAllExpressions: function()
263     {
264         this.watchExpressions = [];
265         this.saveExpressions();
266         this.update();
267     },
268
269     /**
270      * @return {?TreeElement}
271      */
272     findAddedTreeElement: function()
273     {
274         var children = this.propertiesTreeOutline.children;
275         for (var i = 0; i < children.length; ++i) {
276             if (children[i].property.name === WebInspector.WatchExpressionsSection.NewWatchExpression)
277                 return children[i];
278         }
279         return null;
280     },
281
282     /**
283      * @return {number}
284      */
285     saveExpressions: function()
286     {
287         var toSave = [];
288         for (var i = 0; i < this.watchExpressions.length; i++)
289             if (this.watchExpressions[i])
290                 toSave.push(this.watchExpressions[i]);
291
292         WebInspector.settings.watchExpressions.set(toSave);
293         return toSave.length;
294     },
295
296     _mouseMove: function(e)
297     {
298         if (this.propertiesElement.firstChild)
299             this._updateHoveredElement(e.pageY);
300     },
301
302     _mouseOut: function()
303     {
304         if (this._hoveredElement) {
305             this._hoveredElement.classList.remove("hovered");
306             delete this._hoveredElement;
307         }
308         delete this._lastMouseMovePageY;
309     },
310
311     _updateHoveredElement: function(pageY)
312     {
313         var candidateElement = this.propertiesElement.firstChild;
314         while (true) {
315             var next = candidateElement.nextSibling;
316             while (next && !next.clientHeight)
317                 next = next.nextSibling;
318             if (!next || next.totalOffsetTop() > pageY)
319                 break;
320             candidateElement = next;
321         }
322
323         if (this._hoveredElement !== candidateElement) {
324             if (this._hoveredElement)
325                 this._hoveredElement.classList.remove("hovered");
326             if (candidateElement)
327                 candidateElement.classList.add("hovered");
328             this._hoveredElement = candidateElement;
329         }
330
331         this._lastMouseMovePageY = pageY;
332     },
333
334     _emptyElementContextMenu: function(event)
335     {
336         var contextMenu = new WebInspector.ContextMenu(event);
337         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add watch expression" : "Add Watch Expression"), this.addNewExpressionAndEdit.bind(this));
338         contextMenu.show();
339     },
340
341     __proto__: WebInspector.ObjectPropertiesSection.prototype
342 }
343
344 WebInspector.WatchExpressionsSection.CompareProperties = function(propertyA, propertyB)
345 {
346     if (propertyA.watchIndex == propertyB.watchIndex)
347         return 0;
348     else if (propertyA.watchIndex < propertyB.watchIndex)
349         return -1;
350     else
351         return 1;
352 }
353
354 /**
355  * @constructor
356  * @extends {WebInspector.ObjectPropertyTreeElement}
357  * @param {!WebInspector.RemoteObjectProperty} property
358  */
359 WebInspector.WatchExpressionTreeElement = function(property)
360 {
361     WebInspector.ObjectPropertyTreeElement.call(this, property);
362 }
363
364 WebInspector.WatchExpressionTreeElement.prototype = {
365     onexpand: function()
366     {
367         WebInspector.ObjectPropertyTreeElement.prototype.onexpand.call(this);
368         this.treeOutline.section._expandedExpressions[this._expression()] = true;
369     },
370
371     oncollapse: function()
372     {
373         WebInspector.ObjectPropertyTreeElement.prototype.oncollapse.call(this);
374         delete this.treeOutline.section._expandedExpressions[this._expression()];
375     },
376
377     onattach: function()
378     {
379         WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
380         if (this.treeOutline.section._expandedExpressions[this._expression()])
381             this.expanded = true;
382     },
383
384     _expression: function()
385     {
386         return this.property.name;
387     },
388
389     update: function()
390     {
391         WebInspector.ObjectPropertyTreeElement.prototype.update.call(this);
392
393         if (this.property.wasThrown) {
394             this.valueElement.textContent = WebInspector.UIString("<not available>");
395             this.listItemElement.classList.add("dimmed");
396         } else
397             this.listItemElement.classList.remove("dimmed");
398
399         var deleteButton = document.createElement("input");
400         deleteButton.type = "button";
401         deleteButton.title = WebInspector.UIString("Delete watch expression.");
402         deleteButton.classList.add("enabled-button");
403         deleteButton.classList.add("delete-button");
404         deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this), false);
405         this.listItemElement.addEventListener("contextmenu", this._contextMenu.bind(this), false);
406         this.listItemElement.insertBefore(deleteButton, this.listItemElement.firstChild);
407     },
408
409     /**
410      * @param {!WebInspector.ContextMenu} contextMenu
411      * @override
412      */
413     populateContextMenu: function(contextMenu)
414     {
415         if (!this.isEditing()) {
416             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add watch expression" : "Add Watch Expression"), this.treeOutline.section.addNewExpressionAndEdit.bind(this.treeOutline.section));
417             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete watch expression" : "Delete Watch Expression"), this._deleteButtonClicked.bind(this));
418         }
419         if (this.treeOutline.section.watchExpressions.length > 1)
420             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete all watch expressions" : "Delete All Watch Expressions"), this._deleteAllButtonClicked.bind(this));
421     },
422
423     _contextMenu: function(event)
424     {
425         var contextMenu = new WebInspector.ContextMenu(event);
426         this.populateContextMenu(contextMenu);
427         contextMenu.show();
428     },
429
430     _deleteAllButtonClicked: function()
431     {
432         this.treeOutline.section._deleteAllExpressions();
433     },
434
435     _deleteButtonClicked: function()
436     {
437         this.treeOutline.section.updateExpression(this, null);
438     },
439
440     /**
441      * @return {boolean}
442      */
443     renderPromptAsBlock: function()
444     {
445         return true;
446     },
447
448     /**
449      * @param {!Event=} event
450      * @return {!Array.<!Element|string>}
451      */
452     elementAndValueToEdit: function(event)
453     {
454         return [this.nameElement, this.property.name.trim()];
455     },
456
457     editingCancelled: function(element, context)
458     {
459         if (!context.elementToEdit.textContent)
460             this.treeOutline.section.updateExpression(this, null);
461
462         WebInspector.ObjectPropertyTreeElement.prototype.editingCancelled.call(this, element, context);
463     },
464
465     applyExpression: function(expression, updateInterface)
466     {
467         expression = expression.trim();
468
469         if (!expression)
470             expression = null;
471
472         this.property.name = expression;
473         this.treeOutline.section.updateExpression(this, expression);
474     },
475
476     __proto__: WebInspector.ObjectPropertyTreeElement.prototype
477 }
478
479
480 /**
481  * @constructor
482  * @extends {WebInspector.ObjectPropertyTreeElement}
483  * @param {!WebInspector.RemoteObjectProperty} property
484  */
485 WebInspector.WatchedPropertyTreeElement = function(property)
486 {
487     WebInspector.ObjectPropertyTreeElement.call(this, property);
488 }
489
490 WebInspector.WatchedPropertyTreeElement.prototype = {
491     onattach: function()
492     {
493         WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
494         if (this.hasChildren && this.propertyPath() in this.treeOutline.section._expandedProperties)
495             this.expand();
496     },
497
498     onexpand: function()
499     {
500         WebInspector.ObjectPropertyTreeElement.prototype.onexpand.call(this);
501         this.treeOutline.section._expandedProperties[this.propertyPath()] = true;
502     },
503
504     oncollapse: function()
505     {
506         WebInspector.ObjectPropertyTreeElement.prototype.oncollapse.call(this);
507         delete this.treeOutline.section._expandedProperties[this.propertyPath()];
508     },
509
510     __proto__: WebInspector.ObjectPropertyTreeElement.prototype
511 }