/* * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ WebInspector.DOMBreakpointsSidebarPane = function() { WebInspector.NativeBreakpointsSidebarPane.call(this, WebInspector.UIString("DOM Breakpoints")); this._breakpointElements = {}; this._breakpointTypes = { SubtreeModified: 0, AttributeModified: 1, NodeRemoved: 2 }; this._breakpointTypeLabels = [ WebInspector.UIString("Subtree Modified"), WebInspector.UIString("Attribute Modified"), WebInspector.UIString("Node Removed") ]; this._contextMenuLabels = [ WebInspector.UIString("Break on Subtree Modifications"), WebInspector.UIString("Break on Attributes Modifications"), WebInspector.UIString("Break on Node Removal") ]; } WebInspector.DOMBreakpointsSidebarPane.prototype = { setInspectedURL: function(url) { this._reset(); this._inspectedURL = url.removeURLFragment(); }, populateNodeContextMenu: function(node, contextMenu) { var nodeBreakpoints = {}; for (var id in this._breakpointElements) { var element = this._breakpointElements[id]; if (element._node === node) nodeBreakpoints[element._type] = true; } function toggleBreakpoint(type) { if (!nodeBreakpoints[type]) this._setBreakpoint(node, type, true); else this._removeBreakpoint(node, type); this._saveBreakpoints(); } for (var type = 0; type < 3; ++type) { var label = this._contextMenuLabels[type]; contextMenu.appendCheckboxItem(label, toggleBreakpoint.bind(this, type), nodeBreakpoints[type]); } }, createBreakpointHitStatusMessage: function(eventData, callback) { if (eventData.type === this._breakpointTypes.SubtreeModified) { var targetNodeObject = WebInspector.RemoteObject.fromPayload(eventData.targetNode); function didPushNodeToFrontend(targetNodeId) { if (targetNodeId) targetNodeObject.release(); this._doCreateBreakpointHitStatusMessage(eventData, targetNodeId, callback); } targetNodeObject.pushNodeToFrontend(didPushNodeToFrontend.bind(this)); } else this._doCreateBreakpointHitStatusMessage(eventData, null, callback); }, _doCreateBreakpointHitStatusMessage: function (eventData, targetNodeId, callback) { var message; var typeLabel = this._breakpointTypeLabels[eventData.type]; var linkifiedNode = WebInspector.panels.elements.linkifyNodeById(eventData.nodeId); var substitutions = [typeLabel, linkifiedNode]; var targetNode = ""; if (targetNodeId) targetNode = WebInspector.panels.elements.linkifyNodeById(targetNodeId); if (eventData.type === this._breakpointTypes.SubtreeModified) { if (eventData.insertion) { if (targetNodeId !== eventData.nodeId) { message = "Paused on a \"%s\" breakpoint set on %s, because a new child was added to its descendant %s."; substitutions.push(targetNode); } else message = "Paused on a \"%s\" breakpoint set on %s, because a new child was added to that node."; } else { message = "Paused on a \"%s\" breakpoint set on %s, because its descendant %s was removed."; substitutions.push(targetNode); } } else message = "Paused on a \"%s\" breakpoint set on %s."; var element = document.createElement("span"); var formatters = { s: function(substitution) { return substitution; } }; function append(a, b) { if (typeof b === "string") b = document.createTextNode(b); element.appendChild(b); } WebInspector.formatLocalized(message, substitutions, formatters, "", append); callback(element); }, nodeRemoved: function(node) { this._removeBreakpointsForNode(node); if (!node.children) return; for (var i = 0; i < node.children.length; ++i) this._removeBreakpointsForNode(node.children[i]); this._saveBreakpoints(); }, _removeBreakpointsForNode: function(node) { for (var id in this._breakpointElements) { var element = this._breakpointElements[id]; if (element._node === node) this._removeBreakpoint(element._node, element._type); } }, _setBreakpoint: function(node, type, enabled) { var breakpointId = this._createBreakpointId(node.id, type); if (breakpointId in this._breakpointElements) return; var element = document.createElement("li"); element._node = node; element._type = type; element.addEventListener("contextmenu", this._contextMenu.bind(this, node, type), true); var checkboxElement = document.createElement("input"); checkboxElement.className = "checkbox-elem"; checkboxElement.type = "checkbox"; checkboxElement.checked = enabled; checkboxElement.addEventListener("click", this._checkboxClicked.bind(this, node, type), false); element._checkboxElement = checkboxElement; element.appendChild(checkboxElement); var labelElement = document.createElement("span"); element.appendChild(labelElement); var linkifiedNode = WebInspector.panels.elements.linkifyNodeById(node.id); linkifiedNode.addStyleClass("monospace"); labelElement.appendChild(linkifiedNode); var description = document.createElement("div"); description.className = "source-text"; description.textContent = this._breakpointTypeLabels[type]; labelElement.appendChild(description); var currentElement = this.listElement.firstChild; while (currentElement) { if (currentElement._type && currentElement._type < element._type) break; currentElement = currentElement.nextSibling; } this._addListElement(element, currentElement); this._breakpointElements[breakpointId] = element; if (enabled) BrowserDebuggerAgent.setDOMBreakpoint(node.id, type); }, _removeBreakpoint: function(node, type) { var breakpointId = this._createBreakpointId(node.id, type); var element = this._breakpointElements[breakpointId]; if (!element) return; this._removeListElement(element); delete this._breakpointElements[breakpointId]; if (element._checkboxElement.checked) BrowserDebuggerAgent.removeDOMBreakpoint(node.id, type); }, _contextMenu: function(node, type, event) { var contextMenu = new WebInspector.ContextMenu(); function removeBreakpoint() { this._removeBreakpoint(node, type); this._saveBreakpoints(); } contextMenu.appendItem(WebInspector.UIString("Remove Breakpoint"), removeBreakpoint.bind(this)); contextMenu.show(event); }, _checkboxClicked: function(node, type, event) { if (event.target.checked) BrowserDebuggerAgent.setDOMBreakpoint(node.id, type); else BrowserDebuggerAgent.removeDOMBreakpoint(node.id, type); this._saveBreakpoints(); }, highlightBreakpoint: function(eventData) { var breakpointId = this._createBreakpointId(eventData.nodeId, eventData.type); var element = this._breakpointElements[breakpointId]; if (!element) return; this.expanded = true; element.addStyleClass("breakpoint-hit"); this._highlightedElement = element; }, clearBreakpointHighlight: function() { if (this._highlightedElement) { this._highlightedElement.removeStyleClass("breakpoint-hit"); delete this._highlightedElement; } }, _createBreakpointId: function(nodeId, type) { return nodeId + ":" + type; }, _saveBreakpoints: function() { var breakpoints = []; var storedBreakpoints = WebInspector.settings.domBreakpoints; for (var i = 0; i < storedBreakpoints.length; ++i) { var breakpoint = storedBreakpoints[i]; if (breakpoint.url !== this._inspectedURL) breakpoints.push(breakpoint); } for (var id in this._breakpointElements) { var element = this._breakpointElements[id]; breakpoints.push({ url: this._inspectedURL, path: element._node.path(), type: element._type, enabled: element._checkboxElement.checked }); } WebInspector.settings.domBreakpoints = breakpoints; }, restoreBreakpoints: function() { var pathToBreakpoints = {}; function didPushNodeByPathToFrontend(path, nodeId) { var node = WebInspector.domAgent.nodeForId(nodeId); if (!node) return; var breakpoints = pathToBreakpoints[path]; for (var i = 0; i < breakpoints.length; ++i) this._setBreakpoint(node, breakpoints[i].type, breakpoints[i].enabled); } var breakpoints = WebInspector.settings.domBreakpoints; for (var i = 0; i < breakpoints.length; ++i) { var breakpoint = breakpoints[i]; if (breakpoint.url !== this._inspectedURL) continue; var path = breakpoint.path; if (!pathToBreakpoints[path]) { pathToBreakpoints[path] = []; WebInspector.domAgent.pushNodeByPathToFrontend(path, didPushNodeByPathToFrontend.bind(this, path)); } pathToBreakpoints[path].push(breakpoint); } } } WebInspector.DOMBreakpointsSidebarPane.prototype.__proto__ = WebInspector.NativeBreakpointsSidebarPane.prototype;