2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 * @extends {WebInspector.BreakpointsSidebarPaneBase}
35 WebInspector
.DOMBreakpointsSidebarPane = function()
37 WebInspector
.BreakpointsSidebarPaneBase
.call(this, WebInspector
.UIString("DOM Breakpoints"));
38 this._domBreakpointsSetting
= WebInspector
.settings
.createLocalSetting("domBreakpoints", []);
39 this.listElement
.classList
.add("dom-breakpoints-list");
41 this._breakpointElements
= {};
43 this._breakpointTypes
= {
44 SubtreeModified
: "subtree-modified",
45 AttributeModified
: "attribute-modified",
46 NodeRemoved
: "node-removed"
48 this._breakpointTypeLabels
= {};
49 this._breakpointTypeLabels
[this._breakpointTypes
.SubtreeModified
] = WebInspector
.UIString("Subtree Modified");
50 this._breakpointTypeLabels
[this._breakpointTypes
.AttributeModified
] = WebInspector
.UIString("Attribute Modified");
51 this._breakpointTypeLabels
[this._breakpointTypes
.NodeRemoved
] = WebInspector
.UIString("Node Removed");
53 this._contextMenuLabels
= {};
54 this._contextMenuLabels
[this._breakpointTypes
.SubtreeModified
] = WebInspector
.UIString
.capitalize("Subtree ^modifications");
55 this._contextMenuLabels
[this._breakpointTypes
.AttributeModified
] = WebInspector
.UIString
.capitalize("Attributes ^modifications");
56 this._contextMenuLabels
[this._breakpointTypes
.NodeRemoved
] = WebInspector
.UIString
.capitalize("Node ^removal");
58 WebInspector
.targetManager
.addEventListener(WebInspector
.TargetManager
.Events
.InspectedURLChanged
, this._inspectedURLChanged
, this);
59 WebInspector
.targetManager
.addModelListener(WebInspector
.DOMModel
, WebInspector
.DOMModel
.Events
.NodeRemoved
, this._nodeRemoved
, this);
62 WebInspector
.DOMBreakpointsSidebarPane
.Marker
= "breakpoint-marker";
64 WebInspector
.DOMBreakpointsSidebarPane
.prototype = {
65 _inspectedURLChanged: function(event
)
67 this._breakpointElements
= {};
69 var url
= /** @type {string} */ (event
.data
);
70 this._inspectedURL
= url
.removeURLFragment();
74 * @param {!WebInspector.DOMNode} node
75 * @param {!WebInspector.ContextMenu} contextMenu
76 * @param {boolean} createSubMenu
78 populateNodeContextMenu: function(node
, contextMenu
, createSubMenu
)
80 if (node
.pseudoType())
83 var nodeBreakpoints
= this._nodeBreakpoints(node
);
86 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
87 * @this {WebInspector.DOMBreakpointsSidebarPane}
89 function toggleBreakpoint(type
)
91 if (!nodeBreakpoints
[type
])
92 this._setBreakpoint(node
, type
, true);
94 this._removeBreakpoint(node
, type
);
95 this._saveBreakpoints();
98 var breakpointsMenu
= createSubMenu
? contextMenu
.appendSubMenuItem(WebInspector
.UIString("Break on...")) : contextMenu
;
99 for (var key
in this._breakpointTypes
) {
100 var type
= this._breakpointTypes
[key
];
101 var label
= this._contextMenuLabels
[type
];
102 breakpointsMenu
.appendCheckboxItem(label
, toggleBreakpoint
.bind(this, type
), nodeBreakpoints
[type
]);
107 * @param {!WebInspector.DOMNode} node
108 * @return {!Object<string, boolean>}
110 _nodeBreakpoints: function(node
)
112 var nodeBreakpoints
= {};
113 for (var id
in this._breakpointElements
) {
114 var element
= this._breakpointElements
[id
];
115 if (element
._node
=== node
&& element
._checkboxElement
.checked
)
116 nodeBreakpoints
[element
._type
] = true;
118 return nodeBreakpoints
;
122 * @param {!WebInspector.DOMNode} node
125 hasBreakpoints: function(node
)
127 for (var id
in this._breakpointElements
) {
128 var element
= this._breakpointElements
[id
];
129 if (element
._node
=== node
&& element
._checkboxElement
.checked
)
136 * @param {!WebInspector.DebuggerPausedDetails} details
137 * @param {function(!Element)} callback
139 createBreakpointHitStatusMessage: function(details
, callback
)
141 var auxData
= /** @type {!Object} */ (details
.auxData
);
142 var domModel
= WebInspector
.DOMModel
.fromTarget(details
.target());
145 if (auxData
.type
=== this._breakpointTypes
.SubtreeModified
) {
146 var targetNodeObject
= details
.target().runtimeModel
.createRemoteObject(auxData
["targetNode"]);
147 domModel
.pushObjectAsNodeToFrontend(targetNodeObject
, didPushNodeToFrontend
.bind(this));
149 this._doCreateBreakpointHitStatusMessage(auxData
, domModel
.nodeForId(auxData
.nodeId
), null, callback
);
153 * @param {?WebInspector.DOMNode} targetNode
154 * @this {WebInspector.DOMBreakpointsSidebarPane}
156 function didPushNodeToFrontend(targetNode
)
159 targetNodeObject
.release();
160 this._doCreateBreakpointHitStatusMessage(auxData
, domModel
.nodeForId(auxData
.nodeId
), targetNode
, callback
);
165 * @param {!Object} auxData
166 * @param {?WebInspector.DOMNode} node
167 * @param {?WebInspector.DOMNode} targetNode
168 * @param {function(!Element)} callback
170 _doCreateBreakpointHitStatusMessage: function(auxData
, node
, targetNode
, callback
)
173 var typeLabel
= this._breakpointTypeLabels
[auxData
.type
];
174 var linkifiedNode
= WebInspector
.DOMPresentationUtils
.linkifyNodeReference(node
);
175 var substitutions
= [typeLabel
, linkifiedNode
];
176 var targetNodeLink
= "";
178 targetNodeLink
= WebInspector
.DOMPresentationUtils
.linkifyNodeReference(targetNode
);
180 if (auxData
.type
=== this._breakpointTypes
.SubtreeModified
) {
181 if (auxData
.insertion
) {
182 if (targetNode
!== node
) {
183 message
= "Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s.";
184 substitutions
.push(targetNodeLink
);
186 message
= "Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.";
188 message
= "Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.";
189 substitutions
.push(targetNodeLink
);
192 message
= "Paused on a \"%s\" breakpoint set on %s.";
194 var element
= WebInspector
.formatLocalized(message
, substitutions
, "");
199 _nodeRemoved: function(event
)
201 var node
= event
.data
.node
;
202 this._removeBreakpointsForNode(event
.data
.node
);
203 var children
= node
.children();
206 for (var i
= 0; i
< children
.length
; ++i
)
207 this._removeBreakpointsForNode(children
[i
]);
208 this._saveBreakpoints();
212 * @param {!WebInspector.DOMNode} node
214 _removeBreakpointsForNode: function(node
)
216 for (var id
in this._breakpointElements
) {
217 var element
= this._breakpointElements
[id
];
218 if (element
._node
=== node
)
219 this._removeBreakpoint(element
._node
, element
._type
);
224 * @param {!WebInspector.DOMNode} node
225 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
226 * @param {boolean} enabled
228 _setBreakpoint: function(node
, type
, enabled
)
230 var breakpointId
= this._createBreakpointId(node
.id
, type
);
231 var breakpointElement
= this._breakpointElements
[breakpointId
];
232 if (!breakpointElement
) {
233 breakpointElement
= this._createBreakpointElement(node
, type
, enabled
);
234 this._breakpointElements
[breakpointId
] = breakpointElement
;
236 breakpointElement
._checkboxElement
.checked
= enabled
;
239 node
.target().domdebuggerAgent().setDOMBreakpoint(node
.id
, type
);
240 node
.setMarker(WebInspector
.DOMBreakpointsSidebarPane
.Marker
, true);
244 * @param {!WebInspector.DOMNode} node
245 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
246 * @param {boolean} enabled
248 _createBreakpointElement: function(node
, type
, enabled
)
250 var element
= createElement("li");
251 element
._node
= node
;
252 element
._type
= type
;
253 element
.addEventListener("contextmenu", this._contextMenu
.bind(this, node
, type
), true);
255 var checkboxLabel
= createCheckboxLabel("", enabled
);
256 checkboxLabel
.addEventListener("click", this._checkboxClicked
.bind(this, node
, type
), false);
257 element
._checkboxElement
= checkboxLabel
.checkboxElement
;
258 element
.appendChild(checkboxLabel
);
260 var labelElement
= createElementWithClass("div", "dom-breakpoint");
261 element
.appendChild(labelElement
);
263 var linkifiedNode
= WebInspector
.DOMPresentationUtils
.linkifyNodeReference(node
);
264 linkifiedNode
.classList
.add("monospace");
265 linkifiedNode
.style
.display
= "block";
266 labelElement
.appendChild(linkifiedNode
);
268 var description
= createElement("div");
269 description
.textContent
= this._breakpointTypeLabels
[type
];
270 labelElement
.appendChild(description
);
272 var currentElement
= this.listElement
.firstChild
;
273 while (currentElement
) {
274 if (currentElement
._type
&& currentElement
._type
< element
._type
)
276 currentElement
= currentElement
.nextSibling
;
278 this.addListElement(element
, currentElement
);
282 _removeAllBreakpoints: function()
284 for (var id
in this._breakpointElements
) {
285 var element
= this._breakpointElements
[id
];
286 this._removeBreakpoint(element
._node
, element
._type
);
288 this._saveBreakpoints();
292 * @param {!WebInspector.DOMNode} node
293 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
295 _removeBreakpoint: function(node
, type
)
297 var breakpointId
= this._createBreakpointId(node
.id
, type
);
298 var element
= this._breakpointElements
[breakpointId
];
302 this.removeListElement(element
);
303 delete this._breakpointElements
[breakpointId
];
304 if (element
._checkboxElement
.checked
)
305 node
.target().domdebuggerAgent().removeDOMBreakpoint(node
.id
, type
);
306 node
.setMarker(WebInspector
.DOMBreakpointsSidebarPane
.Marker
, this.hasBreakpoints(node
) ? true : null);
310 * @param {!WebInspector.DOMNode} node
311 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
312 * @param {!Event} event
314 _contextMenu: function(node
, type
, event
)
316 var contextMenu
= new WebInspector
.ContextMenu(event
);
319 * @this {WebInspector.DOMBreakpointsSidebarPane}
321 function removeBreakpoint()
323 this._removeBreakpoint(node
, type
);
324 this._saveBreakpoints();
326 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Remove ^breakpoint"), removeBreakpoint
.bind(this));
327 contextMenu
.appendItem(WebInspector
.UIString
.capitalize("Remove ^all DOM breakpoints"), this._removeAllBreakpoints
.bind(this));
332 * @param {!WebInspector.DOMNode} node
333 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
334 * @param {!Event} event
336 _checkboxClicked: function(node
, type
, event
)
338 if (event
.target
.checked
)
339 node
.target().domdebuggerAgent().setDOMBreakpoint(node
.id
, type
);
341 node
.target().domdebuggerAgent().removeDOMBreakpoint(node
.id
, type
);
342 this._saveBreakpoints();
345 highlightBreakpoint: function(auxData
)
347 var breakpointId
= this._createBreakpointId(auxData
.nodeId
, auxData
.type
);
348 var element
= this._breakpointElements
[breakpointId
];
352 element
.classList
.add("breakpoint-hit");
353 this._highlightedElement
= element
;
356 clearBreakpointHighlight: function()
358 if (this._highlightedElement
) {
359 this._highlightedElement
.classList
.remove("breakpoint-hit");
360 delete this._highlightedElement
;
365 * @param {number} nodeId
366 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
368 _createBreakpointId: function(nodeId
, type
)
370 return nodeId
+ ":" + type
;
373 _saveBreakpoints: function()
375 var breakpoints
= [];
376 var storedBreakpoints
= this._domBreakpointsSetting
.get();
377 for (var i
= 0; i
< storedBreakpoints
.length
; ++i
) {
378 var breakpoint
= storedBreakpoints
[i
];
379 if (breakpoint
.url
!== this._inspectedURL
)
380 breakpoints
.push(breakpoint
);
382 for (var id
in this._breakpointElements
) {
383 var element
= this._breakpointElements
[id
];
384 breakpoints
.push({ url
: this._inspectedURL
, path
: element
._node
.path(), type
: element
._type
, enabled
: element
._checkboxElement
.checked
});
386 this._domBreakpointsSetting
.set(breakpoints
);
390 * @param {!WebInspector.DOMModel} domModel
392 restoreBreakpoints: function(domModel
)
394 var pathToBreakpoints
= {};
397 * @param {string} path
398 * @param {?DOMAgent.NodeId} nodeId
399 * @this {WebInspector.DOMBreakpointsSidebarPane}
401 function didPushNodeByPathToFrontend(path
, nodeId
)
403 var node
= nodeId
? domModel
.nodeForId(nodeId
) : null;
407 var breakpoints
= pathToBreakpoints
[path
];
408 for (var i
= 0; i
< breakpoints
.length
; ++i
)
409 this._setBreakpoint(node
, breakpoints
[i
].type
, breakpoints
[i
].enabled
);
412 var breakpoints
= this._domBreakpointsSetting
.get();
413 for (var i
= 0; i
< breakpoints
.length
; ++i
) {
414 var breakpoint
= breakpoints
[i
];
415 if (breakpoint
.url
!== this._inspectedURL
)
417 var path
= breakpoint
.path
;
418 if (!pathToBreakpoints
[path
]) {
419 pathToBreakpoints
[path
] = [];
420 domModel
.pushNodeByPathToFrontend(path
, didPushNodeByPathToFrontend
.bind(this, path
));
422 pathToBreakpoints
[path
].push(breakpoint
);
427 * @param {!WebInspector.Panel} panel
428 * @return {!WebInspector.DOMBreakpointsSidebarPane.Proxy}
430 createProxy: function(panel
)
432 var proxy
= new WebInspector
.DOMBreakpointsSidebarPane
.Proxy(this, panel
);
435 this._proxies
.push(proxy
);
439 onContentReady: function()
441 for (var i
= 0; i
!= this._proxies
.length
; i
++)
442 this._proxies
[i
].onContentReady();
445 __proto__
: WebInspector
.BreakpointsSidebarPaneBase
.prototype
450 * @extends {WebInspector.SidebarPane}
451 * @param {!WebInspector.DOMBreakpointsSidebarPane} pane
452 * @param {!WebInspector.Panel} panel
454 WebInspector
.DOMBreakpointsSidebarPane
.Proxy = function(pane
, panel
)
456 WebInspector
.SidebarPane
.call(this, pane
.title());
457 this.registerRequiredCSS("components/breakpointsList.css");
459 this._wrappedPane
= pane
;
463 WebInspector
.DOMBreakpointsSidebarPane
.Proxy
.prototype = {
466 this._wrappedPane
.expand();
469 onContentReady: function()
471 if (this._panel
.isShowing())
472 this._reattachBody();
474 WebInspector
.SidebarPane
.prototype.onContentReady
.call(this);
479 WebInspector
.SidebarPane
.prototype.wasShown
.call(this);
480 this._reattachBody();
483 _reattachBody: function()
485 if (this._wrappedPane
.element
.parentNode
!== this.element
)
486 this._wrappedPane
.show(this.element
);
489 __proto__
: WebInspector
.SidebarPane
.prototype
494 * @implements {WebInspector.DOMPresentationUtils.MarkerDecorator}
496 WebInspector
.DOMBreakpointsSidebarPane
.MarkerDecorator = function()
500 WebInspector
.DOMBreakpointsSidebarPane
.MarkerDecorator
.prototype = {
503 * @param {!WebInspector.DOMNode} node
504 * @return {?{title: string, color: string}}
506 decorate: function(node
)
508 return { title
: WebInspector
.UIString("DOM Breakpoint"), color
: "rgb(105, 140, 254)" };
513 * @type {!WebInspector.DOMBreakpointsSidebarPane}
515 WebInspector
.domBreakpointsSidebarPane
;