Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / components / DOMBreakpointsSidebarPane.js
blob65e6d856106e13322644f62b5071434436a699aa
1 /*
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
6 * met:
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.
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.
31 /**
32 * @constructor
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 = {};
68 this.reset();
69 var url = /** @type {string} */ (event.data);
70 this._inspectedURL = url.removeURLFragment();
73 /**
74 * @param {!WebInspector.DOMNode} node
75 * @param {!WebInspector.ContextMenu} contextMenu
76 * @param {boolean} createSubMenu
78 populateNodeContextMenu: function(node, contextMenu, createSubMenu)
80 if (node.pseudoType())
81 return;
83 var nodeBreakpoints = this._nodeBreakpoints(node);
85 /**
86 * @param {!DOMDebuggerAgent.DOMBreakpointType} type
87 * @this {WebInspector.DOMBreakpointsSidebarPane}
89 function toggleBreakpoint(type)
91 if (!nodeBreakpoints[type])
92 this._setBreakpoint(node, type, true);
93 else
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
123 * @return {boolean}
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)
130 return true;
132 return false;
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());
143 if (!domModel)
144 return;
145 if (auxData.type === this._breakpointTypes.SubtreeModified) {
146 var targetNodeObject = details.target().runtimeModel.createRemoteObject(auxData["targetNode"]);
147 domModel.pushObjectAsNodeToFrontend(targetNodeObject, didPushNodeToFrontend.bind(this));
148 } else {
149 this._doCreateBreakpointHitStatusMessage(auxData, domModel.nodeForId(auxData.nodeId), null, callback);
153 * @param {?WebInspector.DOMNode} targetNode
154 * @this {WebInspector.DOMBreakpointsSidebarPane}
156 function didPushNodeToFrontend(targetNode)
158 if (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)
172 var message;
173 var typeLabel = this._breakpointTypeLabels[auxData.type];
174 var linkifiedNode = WebInspector.DOMPresentationUtils.linkifyNodeReference(node);
175 var substitutions = [typeLabel, linkifiedNode];
176 var targetNodeLink = "";
177 if (targetNode)
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);
185 } else
186 message = "Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node.";
187 } else {
188 message = "Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed.";
189 substitutions.push(targetNodeLink);
191 } else
192 message = "Paused on a \"%s\" breakpoint set on %s.";
194 var element = WebInspector.formatLocalized(message, substitutions, "");
196 callback(element);
199 _nodeRemoved: function(event)
201 var node = event.data.node;
202 this._removeBreakpointsForNode(event.data.node);
203 var children = node.children();
204 if (!children)
205 return;
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;
235 } else {
236 breakpointElement._checkboxElement.checked = enabled;
238 if (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)
275 break;
276 currentElement = currentElement.nextSibling;
278 this.addListElement(element, currentElement);
279 return element;
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];
299 if (!element)
300 return;
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));
328 contextMenu.show();
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);
340 else
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];
349 if (!element)
350 return;
351 this.expand();
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;
404 if (!node)
405 return;
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)
416 continue;
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);
433 if (!this._proxies)
434 this._proxies = [];
435 this._proxies.push(proxy);
436 return proxy;
439 onContentReady: function()
441 for (var i = 0; i != this._proxies.length; i++)
442 this._proxies[i].onContentReady();
445 __proto__: WebInspector.BreakpointsSidebarPaneBase.prototype
449 * @constructor
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;
460 this._panel = panel;
463 WebInspector.DOMBreakpointsSidebarPane.Proxy.prototype = {
464 expand: function()
466 this._wrappedPane.expand();
469 onContentReady: function()
471 if (this._panel.isShowing())
472 this._reattachBody();
474 WebInspector.SidebarPane.prototype.onContentReady.call(this);
477 wasShown: function()
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
493 * @constructor
494 * @implements {WebInspector.DOMPresentationUtils.MarkerDecorator}
496 WebInspector.DOMBreakpointsSidebarPane.MarkerDecorator = function()
500 WebInspector.DOMBreakpointsSidebarPane.MarkerDecorator.prototype = {
502 * @override
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;