Web Inspector: Fix new JS compiler warnings.
[WebKit-https.git] / Source / WebCore / inspector / front-end / DOMBreakpointsSidebarPane.js
1 /*
2  * Copyright (C) 2011 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  * @extends {WebInspector.NativeBreakpointsSidebarPane}
34  */
35 WebInspector.DOMBreakpointsSidebarPane = function()
36 {
37     WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("DOM Breakpoints"));
38
39     this._breakpointElements = {};
40
41     this._breakpointTypes = {
42         SubtreeModified: "subtree-modified",
43         AttributeModified: "attribute-modified",
44         NodeRemoved: "node-removed"
45     };
46     this._breakpointTypeLabels = {};
47     this._breakpointTypeLabels[this._breakpointTypes.SubtreeModified] = WebInspector.UIString("Subtree Modified");
48     this._breakpointTypeLabels[this._breakpointTypes.AttributeModified] = WebInspector.UIString("Attribute Modified");
49     this._breakpointTypeLabels[this._breakpointTypes.NodeRemoved] = WebInspector.UIString("Node Removed");
50
51     this._contextMenuLabels = {};
52     this._contextMenuLabels[this._breakpointTypes.SubtreeModified] = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Subtree modifications" : "Subtree Modifications");
53     this._contextMenuLabels[this._breakpointTypes.AttributeModified] = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Attributes modifications" : "Attributes Modifications");
54     this._contextMenuLabels[this._breakpointTypes.NodeRemoved] = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Node removal" : "Node Removal");
55
56     WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.InspectedURLChanged, this._inspectedURLChanged, this);
57     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.NodeRemoved, this._nodeRemoved, this);
58 }
59
60 WebInspector.DOMBreakpointsSidebarPane.prototype = {
61     _inspectedURLChanged: function(event)
62     {
63         this._breakpointElements = {};
64         this._reset();
65         var url = event.data;
66         this._inspectedURL = url.removeURLFragment();
67     },
68
69     populateNodeContextMenu: function(node, contextMenu)
70     {
71         var nodeBreakpoints = {};
72         for (var id in this._breakpointElements) {
73             var element = this._breakpointElements[id];
74             if (element._node === node)
75                 nodeBreakpoints[element._type] = true;
76         }
77
78         function toggleBreakpoint(type)
79         {
80             if (!nodeBreakpoints[type])
81                 this._setBreakpoint(node, type, true);
82             else
83                 this._removeBreakpoint(node, type);
84             this._saveBreakpoints();
85         }
86
87         var breakPointSubMenu = contextMenu.appendSubMenuItem(WebInspector.UIString("Break on..."));
88         for (var key in this._breakpointTypes) {
89             var type = this._breakpointTypes[key];
90             var label = this._contextMenuLabels[type];
91             breakPointSubMenu.appendCheckboxItem(label, toggleBreakpoint.bind(this, type), nodeBreakpoints[type]);
92         }
93     },
94
95     createBreakpointHitStatusMessage: function(auxData, callback)
96     {
97         if (auxData.type === this._breakpointTypes.SubtreeModified) {
98             var targetNodeObject = WebInspector.RemoteObject.fromPayload(auxData["targetNode"]);
99             function didPushNodeToFrontend(targetNodeId)
100             {
101                 if (targetNodeId)
102                     targetNodeObject.release();
103                 this._doCreateBreakpointHitStatusMessage(auxData, targetNodeId, callback);
104             }
105             targetNodeObject.pushNodeToFrontend(didPushNodeToFrontend.bind(this));
106         } else
107             this._doCreateBreakpointHitStatusMessage(auxData, null, callback);
108     },
109
110     _doCreateBreakpointHitStatusMessage: function (auxData, targetNodeId, callback)
111     {
112         var message;
113         var typeLabel = this._breakpointTypeLabels[auxData.type];
114         var linkifiedNode = WebInspector.DOMPresentationUtils.linkifyNodeById(auxData.nodeId);
115         var substitutions = [typeLabel, linkifiedNode];
116         var targetNode = "";
117         if (targetNodeId)
118             targetNode = WebInspector.DOMPresentationUtils.linkifyNodeById(targetNodeId);
119
120         if (auxData.type === this._breakpointTypes.SubtreeModified) {
121             if (auxData.insertion) {
122                 if (targetNodeId !== auxData.nodeId) {
123                     message = "Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.";
124                     substitutions.push(targetNode);
125                 } else
126                     message = "Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.";
127             } else {
128                 message = "Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.";
129                 substitutions.push(targetNode);
130             }
131         } else
132             message = "Paused on a \"%s\" breakpoint set on %s.";
133
134         var element = document.createElement("span");
135         var formatters = {
136             s: function(substitution)
137             {
138                 return substitution;
139             }
140         };
141         function append(a, b)
142         {
143             if (typeof b === "string")
144                 b = document.createTextNode(b);
145             element.appendChild(b);
146         }
147         WebInspector.formatLocalized(message, substitutions, formatters, "", append);
148
149         callback(element);
150     },
151
152     _nodeRemoved: function(event)
153     {
154         var node = event.data.node;
155         this._removeBreakpointsForNode(event.data.node);
156         if (!node.children)
157             return;
158         for (var i = 0; i < node.children.length; ++i)
159             this._removeBreakpointsForNode(node.children[i]);
160         this._saveBreakpoints();
161     },
162
163     _removeBreakpointsForNode: function(node)
164     {
165         for (var id in this._breakpointElements) {
166             var element = this._breakpointElements[id];
167             if (element._node === node)
168                 this._removeBreakpoint(element._node, element._type);
169         }
170     },
171
172     _setBreakpoint: function(node, type, enabled)
173     {
174         var breakpointId = this._createBreakpointId(node.id, type);
175         if (breakpointId in this._breakpointElements)
176             return;
177
178         var element = document.createElement("li");
179         element._node = node;
180         element._type = type;
181         element.addEventListener("contextmenu", this._contextMenu.bind(this, node, type), true);
182
183         var checkboxElement = document.createElement("input");
184         checkboxElement.className = "checkbox-elem";
185         checkboxElement.type = "checkbox";
186         checkboxElement.checked = enabled;
187         checkboxElement.addEventListener("click", this._checkboxClicked.bind(this, node, type), false);
188         element._checkboxElement = checkboxElement;
189         element.appendChild(checkboxElement);
190
191         var labelElement = document.createElement("span");
192         element.appendChild(labelElement);
193
194         var linkifiedNode = WebInspector.DOMPresentationUtils.linkifyNodeById(node.id);
195         linkifiedNode.addStyleClass("monospace");
196         labelElement.appendChild(linkifiedNode);
197
198         var description = document.createElement("div");
199         description.className = "source-text";
200         description.textContent = this._breakpointTypeLabels[type];
201         labelElement.appendChild(description);
202
203         var currentElement = this.listElement.firstChild;
204         while (currentElement) {
205             if (currentElement._type && currentElement._type < element._type)
206                 break;
207             currentElement = currentElement.nextSibling;
208         }
209         this._addListElement(element, currentElement);
210         this._breakpointElements[breakpointId] = element;
211         if (enabled)
212             DOMDebuggerAgent.setDOMBreakpoint(node.id, type);
213     },
214
215     _removeAllBreakpoints: function()
216     {
217         for (var id in this._breakpointElements) {
218             var element = this._breakpointElements[id];
219             this._removeBreakpoint(element._node, element._type);
220         }
221         this._saveBreakpoints();
222     },
223
224     _removeBreakpoint: function(node, type)
225     {
226         var breakpointId = this._createBreakpointId(node.id, type);
227         var element = this._breakpointElements[breakpointId];
228         if (!element)
229             return;
230
231         this._removeListElement(element);
232         delete this._breakpointElements[breakpointId];
233         if (element._checkboxElement.checked)
234             DOMDebuggerAgent.removeDOMBreakpoint(node.id, type);
235     },
236
237     _contextMenu: function(node, type, event)
238     {
239         var contextMenu = new WebInspector.ContextMenu(event);
240         function removeBreakpoint()
241         {
242             this._removeBreakpoint(node, type);
243             this._saveBreakpoints();
244         }
245         contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeBreakpoint.bind(this));
246         contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Remove all DOM breakpoints" : "Remove All DOM Breakpoints"), this._removeAllBreakpoints.bind(this));
247         contextMenu.show();
248     },
249
250     _checkboxClicked: function(node, type, event)
251     {
252         if (event.target.checked)
253             DOMDebuggerAgent.setDOMBreakpoint(node.id, type);
254         else
255             DOMDebuggerAgent.removeDOMBreakpoint(node.id, type);
256         this._saveBreakpoints();
257     },
258
259     highlightBreakpoint: function(auxData)
260     {
261         var breakpointId = this._createBreakpointId(auxData.nodeId, auxData.type);
262         var element = this._breakpointElements[breakpointId];
263         if (!element)
264             return;
265         this.expanded = true;
266         element.addStyleClass("breakpoint-hit");
267         this._highlightedElement = element;
268     },
269
270     clearBreakpointHighlight: function()
271     {
272         if (this._highlightedElement) {
273             this._highlightedElement.removeStyleClass("breakpoint-hit");
274             delete this._highlightedElement;
275         }
276     },
277
278     _createBreakpointId: function(nodeId, type)
279     {
280         return nodeId + ":" + type;
281     },
282
283     _saveBreakpoints: function()
284     {
285         var breakpoints = [];
286         var storedBreakpoints = WebInspector.settings.domBreakpoints.get();
287         for (var i = 0; i < storedBreakpoints.length; ++i) {
288             var breakpoint = storedBreakpoints[i];
289             if (breakpoint.url !== this._inspectedURL)
290                 breakpoints.push(breakpoint);
291         }
292         for (var id in this._breakpointElements) {
293             var element = this._breakpointElements[id];
294             breakpoints.push({ url: this._inspectedURL, path: element._node.path(), type: element._type, enabled: element._checkboxElement.checked });
295         }
296         WebInspector.settings.domBreakpoints.set(breakpoints);
297     },
298
299     restoreBreakpoints: function()
300     {
301         var pathToBreakpoints = {};
302
303         function didPushNodeByPathToFrontend(path, nodeId)
304         {
305             var node = WebInspector.domAgent.nodeForId(nodeId);
306             if (!node)
307                 return;
308
309             var breakpoints = pathToBreakpoints[path];
310             for (var i = 0; i < breakpoints.length; ++i)
311                 this._setBreakpoint(node, breakpoints[i].type, breakpoints[i].enabled);
312         }
313
314         var breakpoints = WebInspector.settings.domBreakpoints.get();
315         for (var i = 0; i < breakpoints.length; ++i) {
316             var breakpoint = breakpoints[i];
317             if (breakpoint.url !== this._inspectedURL)
318                 continue;
319             var path = breakpoint.path;
320             if (!pathToBreakpoints[path]) {
321                 pathToBreakpoints[path] = [];
322                 WebInspector.domAgent.pushNodeByPathToFrontend(path, didPushNodeByPathToFrontend.bind(this, path));
323             }
324             pathToBreakpoints[path].push(breakpoint);
325         }
326     },
327
328     __proto__: WebInspector.NativeBreakpointsSidebarPane.prototype
329 }
330
331 /**
332  * @type {?WebInspector.DOMBreakpointsSidebarPane}
333  */
334 WebInspector.domBreakpointsSidebarPane = null;