Remove ENABLE(JAVASCRIPT_DEBUGGER) guards
[WebKit-https.git] / Source / WebCore / inspector / InspectorDOMDebuggerAgent.cpp
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
31 #include "config.h"
32 #include "InspectorDOMDebuggerAgent.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "HTMLElement.h"
37 #include "InspectorDOMAgent.h"
38 #include "InspectorInstrumentation.h"
39 #include "InspectorWebFrontendDispatchers.h"
40 #include "InstrumentingAgents.h"
41 #include <inspector/InspectorValues.h>
42 #include <wtf/text/WTFString.h>
43
44 namespace {
45
46 enum DOMBreakpointType {
47     SubtreeModified = 0,
48     AttributeModified,
49     NodeRemoved,
50     DOMBreakpointTypesCount
51 };
52
53 static const char* const listenerEventCategoryType = "listener:";
54 static const char* const instrumentationEventCategoryType = "instrumentation:";
55
56 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
57 const int domBreakpointDerivedTypeShift = 16;
58
59 }
60
61 using namespace Inspector;
62
63 namespace WebCore {
64
65 InspectorDOMDebuggerAgent::InspectorDOMDebuggerAgent(InstrumentingAgents* instrumentingAgents, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent)
66     : InspectorAgentBase(ASCIILiteral("DOMDebugger"), instrumentingAgents)
67     , m_domAgent(domAgent)
68     , m_debuggerAgent(debuggerAgent)
69     , m_pauseInNextEventListener(false)
70     , m_pauseOnAllXHRsEnabled(false)
71 {
72     m_debuggerAgent->setListener(this);
73 }
74
75 InspectorDOMDebuggerAgent::~InspectorDOMDebuggerAgent()
76 {
77     ASSERT(!m_debuggerAgent);
78     ASSERT(!m_instrumentingAgents->inspectorDOMDebuggerAgent());
79 }
80
81 // Browser debugger agent enabled only when JS debugger is enabled.
82 void InspectorDOMDebuggerAgent::debuggerWasEnabled()
83 {
84     m_instrumentingAgents->setInspectorDOMDebuggerAgent(this);
85 }
86
87 void InspectorDOMDebuggerAgent::debuggerWasDisabled()
88 {
89     disable();
90 }
91
92 void InspectorDOMDebuggerAgent::stepInto()
93 {
94     m_pauseInNextEventListener = true;
95 }
96
97 void InspectorDOMDebuggerAgent::didPause()
98 {
99     m_pauseInNextEventListener = false;
100 }
101
102 void InspectorDOMDebuggerAgent::disable()
103 {
104     m_instrumentingAgents->setInspectorDOMDebuggerAgent(nullptr);
105     clear();
106 }
107
108 void InspectorDOMDebuggerAgent::didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel*, InspectorBackendDispatcher* backendDispatcher)
109 {
110     m_backendDispatcher = InspectorDOMDebuggerBackendDispatcher::create(backendDispatcher, this);
111 }
112
113 void InspectorDOMDebuggerAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason)
114 {
115     m_backendDispatcher.clear();
116
117     disable();
118 }
119
120 void InspectorDOMDebuggerAgent::discardAgent()
121 {
122     m_debuggerAgent->setListener(nullptr);
123     m_debuggerAgent = nullptr;
124 }
125
126 void InspectorDOMDebuggerAgent::discardBindings()
127 {
128     m_domBreakpoints.clear();
129 }
130
131 void InspectorDOMDebuggerAgent::setEventListenerBreakpoint(ErrorString* error, const String& eventName)
132 {
133     setBreakpoint(error, String(listenerEventCategoryType) + eventName);
134 }
135
136 void InspectorDOMDebuggerAgent::setInstrumentationBreakpoint(ErrorString* error, const String& eventName)
137 {
138     setBreakpoint(error, String(instrumentationEventCategoryType) + eventName);
139 }
140
141 void InspectorDOMDebuggerAgent::setBreakpoint(ErrorString* error, const String& eventName)
142 {
143     if (eventName.isEmpty()) {
144         *error = "Event name is empty";
145         return;
146     }
147
148     m_eventListenerBreakpoints.add(eventName);
149 }
150
151 void InspectorDOMDebuggerAgent::removeEventListenerBreakpoint(ErrorString* error, const String& eventName)
152 {
153     removeBreakpoint(error, String(listenerEventCategoryType) + eventName);
154 }
155
156 void InspectorDOMDebuggerAgent::removeInstrumentationBreakpoint(ErrorString* error, const String& eventName)
157 {
158     removeBreakpoint(error, String(instrumentationEventCategoryType) + eventName);
159 }
160
161 void InspectorDOMDebuggerAgent::removeBreakpoint(ErrorString* error, const String& eventName)
162 {
163     if (eventName.isEmpty()) {
164         *error = "Event name is empty";
165         return;
166     }
167
168     m_eventListenerBreakpoints.remove(eventName);
169 }
170
171 void InspectorDOMDebuggerAgent::didInvalidateStyleAttr(Node* node)
172 {
173     if (hasBreakpoint(node, AttributeModified)) {
174         RefPtr<InspectorObject> eventData = InspectorObject::create();
175         descriptionForDOMEvent(node, AttributeModified, false, eventData.get());
176         m_debuggerAgent->breakProgram(InspectorDebuggerFrontendDispatcher::Reason::DOM, eventData.release());
177     }
178 }
179
180 void InspectorDOMDebuggerAgent::didInsertDOMNode(Node* node)
181 {
182     if (m_domBreakpoints.size()) {
183         uint32_t mask = m_domBreakpoints.get(InspectorDOMAgent::innerParentNode(node));
184         uint32_t inheritableTypesMask = (mask | (mask >> domBreakpointDerivedTypeShift)) & inheritableDOMBreakpointTypesMask;
185         if (inheritableTypesMask)
186             updateSubtreeBreakpoints(node, inheritableTypesMask, true);
187     }
188 }
189
190 void InspectorDOMDebuggerAgent::didRemoveDOMNode(Node* node)
191 {
192     if (m_domBreakpoints.size()) {
193         // Remove subtree breakpoints.
194         m_domBreakpoints.remove(node);
195         Vector<Node*> stack(1, InspectorDOMAgent::innerFirstChild(node));
196         do {
197             Node* node = stack.last();
198             stack.removeLast();
199             if (!node)
200                 continue;
201             m_domBreakpoints.remove(node);
202             stack.append(InspectorDOMAgent::innerFirstChild(node));
203             stack.append(InspectorDOMAgent::innerNextSibling(node));
204         } while (!stack.isEmpty());
205     }
206 }
207
208 static int domTypeForName(ErrorString* errorString, const String& typeString)
209 {
210     if (typeString == "subtree-modified")
211         return SubtreeModified;
212     if (typeString == "attribute-modified")
213         return AttributeModified;
214     if (typeString == "node-removed")
215         return NodeRemoved;
216     *errorString = makeString("Unknown DOM breakpoint type: ", typeString);
217     return -1;
218 }
219
220 static String domTypeName(int type)
221 {
222     switch (type) {
223     case SubtreeModified: return "subtree-modified";
224     case AttributeModified: return "attribute-modified";
225     case NodeRemoved: return "node-removed";
226     default: break;
227     }
228     return "";
229 }
230
231 void InspectorDOMDebuggerAgent::setDOMBreakpoint(ErrorString* errorString, int nodeId, const String& typeString)
232 {
233     Node* node = m_domAgent->assertNode(errorString, nodeId);
234     if (!node)
235         return;
236
237     int type = domTypeForName(errorString, typeString);
238     if (type == -1)
239         return;
240
241     uint32_t rootBit = 1 << type;
242     m_domBreakpoints.set(node, m_domBreakpoints.get(node) | rootBit);
243     if (rootBit & inheritableDOMBreakpointTypesMask) {
244         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
245             updateSubtreeBreakpoints(child, rootBit, true);
246     }
247 }
248
249 void InspectorDOMDebuggerAgent::removeDOMBreakpoint(ErrorString* errorString, int nodeId, const String& typeString)
250 {
251     Node* node = m_domAgent->assertNode(errorString, nodeId);
252     if (!node)
253         return;
254     int type = domTypeForName(errorString, typeString);
255     if (type == -1)
256         return;
257
258     uint32_t rootBit = 1 << type;
259     uint32_t mask = m_domBreakpoints.get(node) & ~rootBit;
260     if (mask)
261         m_domBreakpoints.set(node, mask);
262     else
263         m_domBreakpoints.remove(node);
264
265     if ((rootBit & inheritableDOMBreakpointTypesMask) && !(mask & (rootBit << domBreakpointDerivedTypeShift))) {
266         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
267             updateSubtreeBreakpoints(child, rootBit, false);
268     }
269 }
270
271 void InspectorDOMDebuggerAgent::willInsertDOMNode(Node* parent)
272 {
273     if (hasBreakpoint(parent, SubtreeModified)) {
274         RefPtr<InspectorObject> eventData = InspectorObject::create();
275         descriptionForDOMEvent(parent, SubtreeModified, true, eventData.get());
276         m_debuggerAgent->breakProgram(InspectorDebuggerFrontendDispatcher::Reason::DOM, eventData.release());
277     }
278 }
279
280 void InspectorDOMDebuggerAgent::willRemoveDOMNode(Node* node)
281 {
282     Node* parentNode = InspectorDOMAgent::innerParentNode(node);
283     if (hasBreakpoint(node, NodeRemoved)) {
284         RefPtr<InspectorObject> eventData = InspectorObject::create();
285         descriptionForDOMEvent(node, NodeRemoved, false, eventData.get());
286         m_debuggerAgent->breakProgram(InspectorDebuggerFrontendDispatcher::Reason::DOM, eventData.release());
287     } else if (parentNode && hasBreakpoint(parentNode, SubtreeModified)) {
288         RefPtr<InspectorObject> eventData = InspectorObject::create();
289         descriptionForDOMEvent(node, SubtreeModified, false, eventData.get());
290         m_debuggerAgent->breakProgram(InspectorDebuggerFrontendDispatcher::Reason::DOM, eventData.release());
291     }
292 }
293
294 void InspectorDOMDebuggerAgent::willModifyDOMAttr(Element* element)
295 {
296     if (hasBreakpoint(element, AttributeModified)) {
297         RefPtr<InspectorObject> eventData = InspectorObject::create();
298         descriptionForDOMEvent(element, AttributeModified, false, eventData.get());
299         m_debuggerAgent->breakProgram(InspectorDebuggerFrontendDispatcher::Reason::DOM, eventData.release());
300     }
301 }
302
303 void InspectorDOMDebuggerAgent::descriptionForDOMEvent(Node* target, int breakpointType, bool insertion, InspectorObject* description)
304 {
305     ASSERT(hasBreakpoint(target, breakpointType));
306
307     Node* breakpointOwner = target;
308     if ((1 << breakpointType) & inheritableDOMBreakpointTypesMask) {
309         // For inheritable breakpoint types, target node isn't always the same as the node that owns a breakpoint.
310         // Target node may be unknown to frontend, so we need to push it first.
311         RefPtr<Inspector::TypeBuilder::Runtime::RemoteObject> targetNodeObject = m_domAgent->resolveNode(target, InspectorDebuggerAgent::backtraceObjectGroup);
312         description->setValue("targetNode", targetNodeObject);
313
314         // Find breakpoint owner node.
315         if (!insertion)
316             breakpointOwner = InspectorDOMAgent::innerParentNode(target);
317         ASSERT(breakpointOwner);
318         while (!(m_domBreakpoints.get(breakpointOwner) & (1 << breakpointType))) {
319             Node* parentNode = InspectorDOMAgent::innerParentNode(breakpointOwner);
320             if (!parentNode)
321                 break;
322             breakpointOwner = parentNode;
323         }
324
325         if (breakpointType == SubtreeModified)
326             description->setBoolean("insertion", insertion);
327     }
328
329     int breakpointOwnerNodeId = m_domAgent->boundNodeId(breakpointOwner);
330     ASSERT(breakpointOwnerNodeId);
331     description->setNumber("nodeId", breakpointOwnerNodeId);
332     description->setString("type", domTypeName(breakpointType));
333 }
334
335 bool InspectorDOMDebuggerAgent::hasBreakpoint(Node* node, int type)
336 {
337     uint32_t rootBit = 1 << type;
338     uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
339     return m_domBreakpoints.get(node) & (rootBit | derivedBit);
340 }
341
342 void InspectorDOMDebuggerAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, bool set)
343 {
344     uint32_t oldMask = m_domBreakpoints.get(node);
345     uint32_t derivedMask = rootMask << domBreakpointDerivedTypeShift;
346     uint32_t newMask = set ? oldMask | derivedMask : oldMask & ~derivedMask;
347     if (newMask)
348         m_domBreakpoints.set(node, newMask);
349     else
350         m_domBreakpoints.remove(node);
351
352     uint32_t newRootMask = rootMask & ~newMask;
353     if (!newRootMask)
354         return;
355
356     for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
357         updateSubtreeBreakpoints(child, newRootMask, set);
358 }
359
360 void InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded(bool isDOMEvent, const String& eventName, bool synchronous)
361 {
362     String fullEventName = (isDOMEvent ? listenerEventCategoryType : instrumentationEventCategoryType) + eventName;
363     if (m_pauseInNextEventListener)
364         m_pauseInNextEventListener = false;
365     else {
366         if (!m_eventListenerBreakpoints.contains(fullEventName))
367             return;
368     }
369
370     RefPtr<InspectorObject> eventData = InspectorObject::create();
371     eventData->setString("eventName", fullEventName);
372     if (synchronous)
373         m_debuggerAgent->breakProgram(InspectorDebuggerFrontendDispatcher::Reason::EventListener, eventData.release());
374     else
375         m_debuggerAgent->schedulePauseOnNextStatement(InspectorDebuggerFrontendDispatcher::Reason::EventListener, eventData.release());
376 }
377
378 void InspectorDOMDebuggerAgent::setXHRBreakpoint(ErrorString*, const String& url)
379 {
380     if (url.isEmpty()) {
381         m_pauseOnAllXHRsEnabled = true;
382         return;
383     }
384
385     m_xhrBreakpoints.add(url);
386 }
387
388 void InspectorDOMDebuggerAgent::removeXHRBreakpoint(ErrorString*, const String& url)
389 {
390     if (url.isEmpty()) {
391         m_pauseOnAllXHRsEnabled = false;
392         return;
393     }
394
395     m_xhrBreakpoints.remove(url);
396 }
397
398 void InspectorDOMDebuggerAgent::willSendXMLHttpRequest(const String& url)
399 {
400     String breakpointURL;
401     if (m_pauseOnAllXHRsEnabled)
402         breakpointURL = "";
403     else {
404         for (auto it = m_xhrBreakpoints.begin(), end = m_xhrBreakpoints.end(); it != end; ++it) {
405             if (url.contains(*it)) {
406                 breakpointURL = *it;
407                 break;
408             }
409         }
410     }
411
412     if (breakpointURL.isNull())
413         return;
414
415     RefPtr<InspectorObject> eventData = InspectorObject::create();
416     eventData->setString("breakpointURL", breakpointURL);
417     eventData->setString("url", url);
418     m_debuggerAgent->breakProgram(InspectorDebuggerFrontendDispatcher::Reason::XHR, eventData.release());
419 }
420
421 void InspectorDOMDebuggerAgent::clear()
422 {
423     m_domBreakpoints.clear();
424     m_pauseInNextEventListener = false;
425 }
426
427 } // namespace WebCore
428
429 #endif // ENABLE(INSPECTOR)