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 #include "InspectorDOMDebuggerAgent.h"
35 #if ENABLE(INSPECTOR) && ENABLE(JAVASCRIPT_DEBUGGER)
37 #include "HTMLElement.h"
38 #include "InspectorAgent.h"
39 #include "InspectorDOMAgent.h"
40 #include "InspectorDebuggerAgent.h"
41 #include "InspectorState.h"
42 #include "InspectorValues.h"
43 #include "InstrumentingAgents.h"
44 #include <wtf/text/WTFString.h>
48 enum DOMBreakpointType {
52 DOMBreakpointTypesCount
55 static const char* const domNativeBreakpointType = "DOM";
56 static const char* const eventListenerNativeBreakpointType = "EventListener";
57 static const char* const xhrNativeBreakpointType = "XHR";
59 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
60 const int domBreakpointDerivedTypeShift = 16;
66 namespace DOMDebuggerAgentState {
67 static const char eventListenerBreakpoints[] = "eventListenerBreakpoints";
68 static const char pauseOnAllXHRs[] = "pauseOnAllXHRs";
69 static const char xhrBreakpoints[] = "xhrBreakpoints";
72 PassOwnPtr<InspectorDOMDebuggerAgent> InspectorDOMDebuggerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorState* inspectorState, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent, InspectorAgent* inspectorAgent)
74 return adoptPtr(new InspectorDOMDebuggerAgent(instrumentingAgents, inspectorState, domAgent, debuggerAgent, inspectorAgent));
77 InspectorDOMDebuggerAgent::InspectorDOMDebuggerAgent(InstrumentingAgents* instrumentingAgents, InspectorState* inspectorState, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent, InspectorAgent* inspectorAgent)
78 : m_instrumentingAgents(instrumentingAgents)
79 , m_inspectorState(inspectorState)
80 , m_domAgent(domAgent)
81 , m_debuggerAgent(debuggerAgent)
82 , m_inspectorAgent(inspectorAgent)
84 m_debuggerAgent->setListener(this);
87 InspectorDOMDebuggerAgent::~InspectorDOMDebuggerAgent()
89 m_debuggerAgent->setListener(0);
90 ASSERT(!m_instrumentingAgents->inspectorDOMDebuggerAgent());
93 // Browser debugger agent enabled only when JS debugger is enabled.
94 void InspectorDOMDebuggerAgent::debuggerWasEnabled()
96 m_instrumentingAgents->setInspectorDOMDebuggerAgent(this);
99 void InspectorDOMDebuggerAgent::debuggerWasDisabled()
104 void InspectorDOMDebuggerAgent::disable()
106 m_instrumentingAgents->setInspectorDOMDebuggerAgent(0);
110 void InspectorDOMDebuggerAgent::clearFrontend()
115 void InspectorDOMDebuggerAgent::discardBindings()
117 m_domBreakpoints.clear();
120 void InspectorDOMDebuggerAgent::setEventListenerBreakpoint(ErrorString* error, const String& eventName)
122 if (eventName.isEmpty()) {
123 *error = "Event name is empty";
127 RefPtr<InspectorObject> eventListenerBreakpoints = m_inspectorState->getObject(DOMDebuggerAgentState::eventListenerBreakpoints);
128 eventListenerBreakpoints->setBoolean(eventName, true);
129 m_inspectorState->setObject(DOMDebuggerAgentState::eventListenerBreakpoints, eventListenerBreakpoints);
132 void InspectorDOMDebuggerAgent::removeEventListenerBreakpoint(ErrorString* error, const String& eventName)
134 if (eventName.isEmpty()) {
135 *error = "Event name is empty";
139 RefPtr<InspectorObject> eventListenerBreakpoints = m_inspectorState->getObject(DOMDebuggerAgentState::eventListenerBreakpoints);
140 eventListenerBreakpoints->remove(eventName);
141 m_inspectorState->setObject(DOMDebuggerAgentState::eventListenerBreakpoints, eventListenerBreakpoints);
144 void InspectorDOMDebuggerAgent::didInvalidateStyleAttr(Node* node)
146 if (hasBreakpoint(node, AttributeModified)) {
147 RefPtr<InspectorObject> eventData = InspectorObject::create();
148 descriptionForDOMEvent(node, AttributeModified, false, eventData.get());
149 eventData->setString("breakpointType", domNativeBreakpointType);
150 m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
154 void InspectorDOMDebuggerAgent::didInsertDOMNode(Node* node)
156 if (m_domBreakpoints.size()) {
157 uint32_t mask = m_domBreakpoints.get(InspectorDOMAgent::innerParentNode(node));
158 uint32_t inheritableTypesMask = (mask | (mask >> domBreakpointDerivedTypeShift)) & inheritableDOMBreakpointTypesMask;
159 if (inheritableTypesMask)
160 updateSubtreeBreakpoints(node, inheritableTypesMask, true);
164 void InspectorDOMDebuggerAgent::didRemoveDOMNode(Node* node)
166 if (m_domBreakpoints.size()) {
167 // Remove subtree breakpoints.
168 m_domBreakpoints.remove(node);
169 Vector<Node*> stack(1, InspectorDOMAgent::innerFirstChild(node));
171 Node* node = stack.last();
175 m_domBreakpoints.remove(node);
176 stack.append(InspectorDOMAgent::innerFirstChild(node));
177 stack.append(InspectorDOMAgent::innerNextSibling(node));
178 } while (!stack.isEmpty());
182 static int domTypeForName(const String& typeString)
184 if (typeString == "subtree-modified")
185 return SubtreeModified;
186 if (typeString == "attribute-modified")
187 return AttributeModified;
188 if (typeString == "node-removed")
190 return SubtreeModified;
193 static String domTypeName(int type)
196 case SubtreeModified: return "subtree-modified";
197 case AttributeModified: return "attribute-modified";
198 case NodeRemoved: return "node-removed";
204 void InspectorDOMDebuggerAgent::setDOMBreakpoint(ErrorString*, int nodeId, const String& typeString)
206 Node* node = m_domAgent->nodeForId(nodeId);
209 int type = domTypeForName(typeString);
211 uint32_t rootBit = 1 << type;
212 m_domBreakpoints.set(node, m_domBreakpoints.get(node) | rootBit);
213 if (rootBit & inheritableDOMBreakpointTypesMask) {
214 for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
215 updateSubtreeBreakpoints(child, rootBit, true);
219 void InspectorDOMDebuggerAgent::removeDOMBreakpoint(ErrorString*, int nodeId, const String& typeString)
221 Node* node = m_domAgent->nodeForId(nodeId);
224 int type = domTypeForName(typeString);
226 uint32_t rootBit = 1 << type;
227 uint32_t mask = m_domBreakpoints.get(node) & ~rootBit;
229 m_domBreakpoints.set(node, mask);
231 m_domBreakpoints.remove(node);
233 if ((rootBit & inheritableDOMBreakpointTypesMask) && !(mask & (rootBit << domBreakpointDerivedTypeShift))) {
234 for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
235 updateSubtreeBreakpoints(child, rootBit, false);
239 void InspectorDOMDebuggerAgent::willInsertDOMNode(Node*, Node* parent)
241 if (hasBreakpoint(parent, SubtreeModified)) {
242 RefPtr<InspectorObject> eventData = InspectorObject::create();
243 descriptionForDOMEvent(parent, SubtreeModified, true, eventData.get());
244 eventData->setString("breakpointType", domNativeBreakpointType);
245 m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
249 void InspectorDOMDebuggerAgent::willRemoveDOMNode(Node* node)
251 Node* parentNode = InspectorDOMAgent::innerParentNode(node);
252 if (hasBreakpoint(node, NodeRemoved)) {
253 RefPtr<InspectorObject> eventData = InspectorObject::create();
254 descriptionForDOMEvent(node, NodeRemoved, false, eventData.get());
255 eventData->setString("breakpointType", domNativeBreakpointType);
256 m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
257 } else if (parentNode && hasBreakpoint(parentNode, SubtreeModified)) {
258 RefPtr<InspectorObject> eventData = InspectorObject::create();
259 descriptionForDOMEvent(node, SubtreeModified, false, eventData.get());
260 eventData->setString("breakpointType", domNativeBreakpointType);
261 m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
265 void InspectorDOMDebuggerAgent::willModifyDOMAttr(Element* element)
267 if (hasBreakpoint(element, AttributeModified)) {
268 RefPtr<InspectorObject> eventData = InspectorObject::create();
269 descriptionForDOMEvent(element, AttributeModified, false, eventData.get());
270 eventData->setString("breakpointType", domNativeBreakpointType);
271 m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
275 void InspectorDOMDebuggerAgent::descriptionForDOMEvent(Node* target, int breakpointType, bool insertion, InspectorObject* description)
277 ASSERT(hasBreakpoint(target, breakpointType));
279 Node* breakpointOwner = target;
280 if ((1 << breakpointType) & inheritableDOMBreakpointTypesMask) {
281 // For inheritable breakpoint types, target node isn't always the same as the node that owns a breakpoint.
282 // Target node may be unknown to frontend, so we need to push it first.
283 RefPtr<InspectorObject> targetNodeObject = m_domAgent->resolveNode(target, InspectorDebuggerAgent::backtraceObjectGroup);
284 description->setObject("targetNode", targetNodeObject);
286 // Find breakpoint owner node.
288 breakpointOwner = InspectorDOMAgent::innerParentNode(target);
289 ASSERT(breakpointOwner);
290 while (!(m_domBreakpoints.get(breakpointOwner) & (1 << breakpointType))) {
291 breakpointOwner = InspectorDOMAgent::innerParentNode(breakpointOwner);
292 ASSERT(breakpointOwner);
295 if (breakpointType == SubtreeModified)
296 description->setBoolean("insertion", insertion);
299 int breakpointOwnerNodeId = m_domAgent->boundNodeId(breakpointOwner);
300 ASSERT(breakpointOwnerNodeId);
301 description->setNumber("nodeId", breakpointOwnerNodeId);
302 description->setString("type", domTypeName(breakpointType));
305 bool InspectorDOMDebuggerAgent::hasBreakpoint(Node* node, int type)
307 uint32_t rootBit = 1 << type;
308 uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
309 return m_domBreakpoints.get(node) & (rootBit | derivedBit);
312 void InspectorDOMDebuggerAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, bool set)
314 uint32_t oldMask = m_domBreakpoints.get(node);
315 uint32_t derivedMask = rootMask << domBreakpointDerivedTypeShift;
316 uint32_t newMask = set ? oldMask | derivedMask : oldMask & ~derivedMask;
318 m_domBreakpoints.set(node, newMask);
320 m_domBreakpoints.remove(node);
322 uint32_t newRootMask = rootMask & ~newMask;
326 for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
327 updateSubtreeBreakpoints(child, newRootMask, set);
330 void InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded(const String& categoryType, const String& eventName, bool synchronous)
332 String fullEventName = categoryType + ':' + eventName;
333 RefPtr<InspectorObject> eventListenerBreakpoints = m_inspectorState->getObject(DOMDebuggerAgentState::eventListenerBreakpoints);
334 if (eventListenerBreakpoints->find(fullEventName) == eventListenerBreakpoints->end())
337 RefPtr<InspectorObject> eventData = InspectorObject::create();
338 eventData->setString("breakpointType", eventListenerNativeBreakpointType);
339 eventData->setString("eventName", fullEventName);
341 m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
343 m_debuggerAgent->schedulePauseOnNextStatement(NativeBreakpointDebuggerEventType, eventData.release());
346 void InspectorDOMDebuggerAgent::setXHRBreakpoint(ErrorString*, const String& url)
349 m_inspectorState->setBoolean(DOMDebuggerAgentState::pauseOnAllXHRs, true);
353 RefPtr<InspectorObject> xhrBreakpoints = m_inspectorState->getObject(DOMDebuggerAgentState::xhrBreakpoints);
354 xhrBreakpoints->setBoolean(url, true);
355 m_inspectorState->setObject(DOMDebuggerAgentState::xhrBreakpoints, xhrBreakpoints);
358 void InspectorDOMDebuggerAgent::removeXHRBreakpoint(ErrorString*, const String& url)
361 m_inspectorState->setBoolean(DOMDebuggerAgentState::pauseOnAllXHRs, false);
365 RefPtr<InspectorObject> xhrBreakpoints = m_inspectorState->getObject(DOMDebuggerAgentState::xhrBreakpoints);
366 xhrBreakpoints->remove(url);
367 m_inspectorState->setObject(DOMDebuggerAgentState::xhrBreakpoints, xhrBreakpoints);
370 void InspectorDOMDebuggerAgent::willSendXMLHttpRequest(const String& url)
372 String breakpointURL;
373 if (m_inspectorState->getBoolean(DOMDebuggerAgentState::pauseOnAllXHRs))
376 RefPtr<InspectorObject> xhrBreakpoints = m_inspectorState->getObject(DOMDebuggerAgentState::xhrBreakpoints);
377 for (InspectorObject::iterator it = xhrBreakpoints->begin(); it != xhrBreakpoints->end(); ++it) {
378 if (url.contains(it->first)) {
379 breakpointURL = it->first;
385 if (breakpointURL.isNull())
388 RefPtr<InspectorObject> eventData = InspectorObject::create();
389 eventData->setString("breakpointType", xhrNativeBreakpointType);
390 eventData->setString("breakpointURL", breakpointURL);
391 eventData->setString("url", url);
392 m_debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
395 void InspectorDOMDebuggerAgent::clear()
397 m_domBreakpoints.clear();
400 } // namespace WebCore
402 #endif // ENABLE(INSPECTOR) && ENABLE(JAVASCRIPT_DEBUGGER)