Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sources / 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.runtimeModel.createRemoteObjectFromPrimitiveValue(""));
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         WebInspector.targetManager.targets().forEach(function(target) {target.runtimeAgent().releaseObjectGroup(this._watchObjectGroupId)}, this);
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         var currentExecutionContext = WebInspector.context.flavor(WebInspector.ExecutionContext);
207         if (currentExecutionContext) {
208             for (var i = 0; i < this.watchExpressions.length; ++i) {
209                 var expression = this.watchExpressions[i];
210                 if (!expression)
211                     continue;
212
213                 currentExecutionContext.evaluate(expression, this._watchObjectGroupId, false, true, false, false, appendResult.bind(this, expression, i));
214             }
215         }
216
217         if (!propertyCount) {
218             if (!this.emptyElement.parentNode)
219                 this.element.appendChild(this.emptyElement);
220         } else {
221             if (this.emptyElement.parentNode)
222                 this.element.removeChild(this.emptyElement);
223         }
224
225         // note this is setting the expansion of the tree, not the section;
226         // with no expressions, and expanded tree, we get some extra vertical
227         // white space
228         this.expanded = (propertyCount != 0);
229     },
230
231     addExpression: function(expression)
232     {
233         this.watchExpressions.push(expression);
234         this.saveExpressions();
235         this.update();
236     },
237
238     addNewExpressionAndEdit: function()
239     {
240         this._newExpressionAdded = true;
241         this.watchExpressions.push(WebInspector.WatchExpressionsSection.NewWatchExpression);
242         this.update();
243     },
244
245     _sectionDoubleClick: function(event)
246     {
247         if (event.target !== this.element && event.target !== this.propertiesElement && event.target !== this.emptyElement)
248             return;
249         event.consume();
250         this.addNewExpressionAndEdit();
251     },
252
253     updateExpression: function(element, value)
254     {
255         if (value === null) {
256             var index = element.property.watchIndex;
257             this.watchExpressions.splice(index, 1);
258         }
259         else
260             this.watchExpressions[element.property.watchIndex] = value;
261         this.saveExpressions();
262         this.update();
263     },
264
265     _deleteAllExpressions: function()
266     {
267         this.watchExpressions = [];
268         this.saveExpressions();
269         this.update();
270     },
271
272     /**
273      * @return {?TreeElement}
274      */
275     findAddedTreeElement: function()
276     {
277         var children = this.propertiesTreeOutline.children;
278         for (var i = 0; i < children.length; ++i) {
279             if (children[i].property.name === WebInspector.WatchExpressionsSection.NewWatchExpression)
280                 return children[i];
281         }
282         return null;
283     },
284
285     /**
286      * @return {number}
287      */
288     saveExpressions: function()
289     {
290         var toSave = [];
291         for (var i = 0; i < this.watchExpressions.length; i++)
292             if (this.watchExpressions[i])
293                 toSave.push(this.watchExpressions[i]);
294
295         WebInspector.settings.watchExpressions.set(toSave);
296         return toSave.length;
297     },
298
299     _mouseMove: function(e)
300     {
301         if (this.propertiesElement.firstChild)
302             this._updateHoveredElement(e.pageY);
303     },
304
305     _mouseOut: function()
306     {
307         if (this._hoveredElement) {
308             this._hoveredElement.classList.remove("hovered");
309             delete this._hoveredElement;
310         }
311         delete this._lastMouseMovePageY;
312     },
313
314     _updateHoveredElement: function(pageY)
315     {
316         var candidateElement = this.propertiesElement.firstChild;
317         while (true) {
318             var next = candidateElement.nextSibling;
319             while (next && !next.clientHeight)
320                 next = next.nextSibling;
321             if (!next || next.totalOffsetTop() > pageY)
322                 break;
323             candidateElement = next;
324         }
325
326         if (this._hoveredElement !== candidateElement) {
327             if (this._hoveredElement)
328                 this._hoveredElement.classList.remove("hovered");
329             if (candidateElement)
330                 candidateElement.classList.add("hovered");
331             this._hoveredElement = candidateElement;
332         }
333
334         this._lastMouseMovePageY = pageY;
335     },
336
337     _emptyElementContextMenu: function(event)
338     {
339         var contextMenu = new WebInspector.ContextMenu(event);
340         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add watch expression" : "Add Watch Expression"), this.addNewExpressionAndEdit.bind(this));
341         contextMenu.show();
342     },
343
344     __proto__: WebInspector.ObjectPropertiesSection.prototype
345 }
346
347 WebInspector.WatchExpressionsSection.CompareProperties = function(propertyA, propertyB)
348 {
349     if (propertyA.watchIndex == propertyB.watchIndex)
350         return 0;
351     else if (propertyA.watchIndex < propertyB.watchIndex)
352         return -1;
353     else
354         return 1;
355 }
356
357 /**
358  * @constructor
359  * @extends {WebInspector.ObjectPropertyTreeElement}
360  * @param {!WebInspector.RemoteObjectProperty} property
361  */
362 WebInspector.WatchExpressionTreeElement = function(property)
363 {
364     WebInspector.ObjectPropertyTreeElement.call(this, property);
365 }
366
367 WebInspector.WatchExpressionTreeElement.prototype = {
368     onexpand: function()
369     {
370         WebInspector.ObjectPropertyTreeElement.prototype.onexpand.call(this);
371         this.treeOutline.section._expandedExpressions[this._expression()] = true;
372     },
373
374     oncollapse: function()
375     {
376         WebInspector.ObjectPropertyTreeElement.prototype.oncollapse.call(this);
377         delete this.treeOutline.section._expandedExpressions[this._expression()];
378     },
379
380     onattach: function()
381     {
382         WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
383         if (this.treeOutline.section._expandedExpressions[this._expression()])
384             this.expanded = true;
385     },
386
387     _expression: function()
388     {
389         return this.property.name;
390     },
391
392     update: function()
393     {
394         WebInspector.ObjectPropertyTreeElement.prototype.update.call(this);
395
396         if (this.property.wasThrown) {
397             this.valueElement.textContent = WebInspector.UIString("<not available>");
398             this.listItemElement.classList.add("dimmed");
399         } else
400             this.listItemElement.classList.remove("dimmed");
401
402         var deleteButton = document.createElement("input");
403         deleteButton.type = "button";
404         deleteButton.title = WebInspector.UIString("Delete watch expression.");
405         deleteButton.classList.add("enabled-button");
406         deleteButton.classList.add("delete-button");
407         deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this), false);
408         this.listItemElement.addEventListener("contextmenu", this._contextMenu.bind(this), false);
409         this.listItemElement.insertBefore(deleteButton, this.listItemElement.firstChild);
410     },
411
412     /**
413      * @param {!WebInspector.ContextMenu} contextMenu
414      * @override
415      */
416     populateContextMenu: function(contextMenu)
417     {
418         if (!this.isEditing()) {
419             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Add watch expression" : "Add Watch Expression"), this.treeOutline.section.addNewExpressionAndEdit.bind(this.treeOutline.section));
420             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete watch expression" : "Delete Watch Expression"), this._deleteButtonClicked.bind(this));
421         }
422         if (this.treeOutline.section.watchExpressions.length > 1)
423             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Delete all watch expressions" : "Delete All Watch Expressions"), this._deleteAllButtonClicked.bind(this));
424     },
425
426     _contextMenu: function(event)
427     {
428         var contextMenu = new WebInspector.ContextMenu(event);
429         this.populateContextMenu(contextMenu);
430         contextMenu.show();
431     },
432
433     _deleteAllButtonClicked: function()
434     {
435         this.treeOutline.section._deleteAllExpressions();
436     },
437
438     _deleteButtonClicked: function()
439     {
440         this.treeOutline.section.updateExpression(this, null);
441     },
442
443     /**
444      * @return {boolean}
445      */
446     renderPromptAsBlock: function()
447     {
448         return true;
449     },
450
451     /**
452      * @param {!Event=} event
453      * @return {!Array.<!Element|string>}
454      */
455     elementAndValueToEdit: function(event)
456     {
457         return [this.nameElement, this.property.name.trim()];
458     },
459
460     editingCancelled: function(element, context)
461     {
462         if (!context.elementToEdit.textContent)
463             this.treeOutline.section.updateExpression(this, null);
464
465         WebInspector.ObjectPropertyTreeElement.prototype.editingCancelled.call(this, element, context);
466     },
467
468     applyExpression: function(expression, updateInterface)
469     {
470         expression = expression.trim();
471
472         if (!expression)
473             expression = null;
474
475         this.property.name = expression;
476         this.treeOutline.section.updateExpression(this, expression);
477     },
478
479     __proto__: WebInspector.ObjectPropertyTreeElement.prototype
480 }
481
482
483 /**
484  * @constructor
485  * @extends {WebInspector.ObjectPropertyTreeElement}
486  * @param {!WebInspector.RemoteObjectProperty} property
487  */
488 WebInspector.WatchedPropertyTreeElement = function(property)
489 {
490     WebInspector.ObjectPropertyTreeElement.call(this, property);
491 }
492
493 WebInspector.WatchedPropertyTreeElement.prototype = {
494     onattach: function()
495     {
496         WebInspector.ObjectPropertyTreeElement.prototype.onattach.call(this);
497         if (this.hasChildren && this.propertyPath() in this.treeOutline.section._expandedProperties)
498             this.expand();
499     },
500
501     onexpand: function()
502     {
503         WebInspector.ObjectPropertyTreeElement.prototype.onexpand.call(this);
504         this.treeOutline.section._expandedProperties[this.propertyPath()] = true;
505     },
506
507     oncollapse: function()
508     {
509         WebInspector.ObjectPropertyTreeElement.prototype.oncollapse.call(this);
510         delete this.treeOutline.section._expandedProperties[this.propertyPath()];
511     },
512
513     __proto__: WebInspector.ObjectPropertyTreeElement.prototype
514 }