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