Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / inspector / InspectorDOMDebuggerAgent.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2015 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "InspectorDOMDebuggerAgent.h"
34
35 #include "HTMLElement.h"
36 #include "InspectorDOMAgent.h"
37 #include "InstrumentingAgents.h"
38 #include <inspector/InspectorFrontendDispatchers.h>
39 #include <inspector/InspectorValues.h>
40
41 namespace {
42
43 enum DOMBreakpointType {
44     SubtreeModified = 0,
45     AttributeModified,
46     NodeRemoved,
47     DOMBreakpointTypesCount
48 };
49
50 static const char* const listenerEventCategoryType = "listener:";
51 static const char* const instrumentationEventCategoryType = "instrumentation:";
52
53 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
54 const int domBreakpointDerivedTypeShift = 16;
55
56 }
57
58 using namespace Inspector;
59
60 namespace WebCore {
61
62 InspectorDOMDebuggerAgent::InspectorDOMDebuggerAgent(WebAgentContext& context, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent)
63     : InspectorAgentBase(ASCIILiteral("DOMDebugger"), context)
64     , m_backendDispatcher(Inspector::DOMDebuggerBackendDispatcher::create(context.backendDispatcher, this))
65     , m_domAgent(domAgent)
66     , m_debuggerAgent(debuggerAgent)
67 {
68     m_debuggerAgent->setListener(this);
69 }
70
71 InspectorDOMDebuggerAgent::~InspectorDOMDebuggerAgent()
72 {
73     ASSERT(!m_debuggerAgent);
74     ASSERT(!m_instrumentingAgents.inspectorDOMDebuggerAgent());
75 }
76
77 // Browser debugger agent enabled only when JS debugger is enabled.
78 void InspectorDOMDebuggerAgent::debuggerWasEnabled()
79 {
80     m_instrumentingAgents.setInspectorDOMDebuggerAgent(this);
81 }
82
83 void InspectorDOMDebuggerAgent::debuggerWasDisabled()
84 {
85     disable();
86 }
87
88 void InspectorDOMDebuggerAgent::stepInto()
89 {
90     m_pauseInNextEventListener = true;
91 }
92
93 void InspectorDOMDebuggerAgent::didPause()
94 {
95     m_pauseInNextEventListener = false;
96 }
97
98 void InspectorDOMDebuggerAgent::disable()
99 {
100     m_instrumentingAgents.setInspectorDOMDebuggerAgent(nullptr);
101     clear();
102 }
103
104 void InspectorDOMDebuggerAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
105 {
106 }
107
108 void InspectorDOMDebuggerAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
109 {
110     disable();
111 }
112
113 void InspectorDOMDebuggerAgent::discardAgent()
114 {
115     m_debuggerAgent->setListener(nullptr);
116     m_debuggerAgent = nullptr;
117 }
118
119 void InspectorDOMDebuggerAgent::discardBindings()
120 {
121     m_domBreakpoints.clear();
122 }
123
124 void InspectorDOMDebuggerAgent::setEventListenerBreakpoint(ErrorString& error, const String& eventName)
125 {
126     setBreakpoint(error, String(listenerEventCategoryType) + eventName);
127 }
128
129 void InspectorDOMDebuggerAgent::setInstrumentationBreakpoint(ErrorString& error, const String& eventName)
130 {
131     setBreakpoint(error, String(instrumentationEventCategoryType) + eventName);
132 }
133
134 void InspectorDOMDebuggerAgent::setBreakpoint(ErrorString& error, const String& eventName)
135 {
136     if (eventName.isEmpty()) {
137         error = ASCIILiteral("Event name is empty");
138         return;
139     }
140
141     m_eventListenerBreakpoints.add(eventName);
142 }
143
144 void InspectorDOMDebuggerAgent::removeEventListenerBreakpoint(ErrorString& error, const String& eventName)
145 {
146     removeBreakpoint(error, String(listenerEventCategoryType) + eventName);
147 }
148
149 void InspectorDOMDebuggerAgent::removeInstrumentationBreakpoint(ErrorString& error, const String& eventName)
150 {
151     removeBreakpoint(error, String(instrumentationEventCategoryType) + eventName);
152 }
153
154 void InspectorDOMDebuggerAgent::removeBreakpoint(ErrorString& error, const String& eventName)
155 {
156     if (eventName.isEmpty()) {
157         error = ASCIILiteral("Event name is empty");
158         return;
159     }
160
161     m_eventListenerBreakpoints.remove(eventName);
162 }
163
164 void InspectorDOMDebuggerAgent::didInvalidateStyleAttr(Node& node)
165 {
166     if (hasBreakpoint(&node, AttributeModified)) {
167         Ref<InspectorObject> eventData = InspectorObject::create();
168         descriptionForDOMEvent(node, AttributeModified, false, eventData.get());
169         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
170     }
171 }
172
173 void InspectorDOMDebuggerAgent::didInsertDOMNode(Node& node)
174 {
175     if (m_domBreakpoints.size()) {
176         uint32_t mask = m_domBreakpoints.get(InspectorDOMAgent::innerParentNode(&node));
177         uint32_t inheritableTypesMask = (mask | (mask >> domBreakpointDerivedTypeShift)) & inheritableDOMBreakpointTypesMask;
178         if (inheritableTypesMask)
179             updateSubtreeBreakpoints(&node, inheritableTypesMask, true);
180     }
181 }
182
183 void InspectorDOMDebuggerAgent::didRemoveDOMNode(Node& node)
184 {
185     if (m_domBreakpoints.size()) {
186         // Remove subtree breakpoints.
187         m_domBreakpoints.remove(&node);
188         Vector<Node*> stack(1, InspectorDOMAgent::innerFirstChild(&node));
189         do {
190             Node* node = stack.last();
191             stack.removeLast();
192             if (!node)
193                 continue;
194             m_domBreakpoints.remove(node);
195             stack.append(InspectorDOMAgent::innerFirstChild(node));
196             stack.append(InspectorDOMAgent::innerNextSibling(node));
197         } while (!stack.isEmpty());
198     }
199 }
200
201 static int domTypeForName(ErrorString& errorString, const String& typeString)
202 {
203     if (typeString == "subtree-modified")
204         return SubtreeModified;
205     if (typeString == "attribute-modified")
206         return AttributeModified;
207     if (typeString == "node-removed")
208         return NodeRemoved;
209     errorString = makeString("Unknown DOM breakpoint type: ", typeString);
210     return -1;
211 }
212
213 static String domTypeName(int type)
214 {
215     switch (type) {
216     case SubtreeModified: return "subtree-modified";
217     case AttributeModified: return "attribute-modified";
218     case NodeRemoved: return "node-removed";
219     default: break;
220     }
221     return "";
222 }
223
224 void InspectorDOMDebuggerAgent::setDOMBreakpoint(ErrorString& errorString, int nodeId, const String& typeString)
225 {
226     Node* node = m_domAgent->assertNode(errorString, nodeId);
227     if (!node)
228         return;
229
230     int type = domTypeForName(errorString, typeString);
231     if (type == -1)
232         return;
233
234     uint32_t rootBit = 1 << type;
235     m_domBreakpoints.set(node, m_domBreakpoints.get(node) | rootBit);
236     if (rootBit & inheritableDOMBreakpointTypesMask) {
237         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
238             updateSubtreeBreakpoints(child, rootBit, true);
239     }
240 }
241
242 void InspectorDOMDebuggerAgent::removeDOMBreakpoint(ErrorString& errorString, int nodeId, const String& typeString)
243 {
244     Node* node = m_domAgent->assertNode(errorString, nodeId);
245     if (!node)
246         return;
247     int type = domTypeForName(errorString, typeString);
248     if (type == -1)
249         return;
250
251     uint32_t rootBit = 1 << type;
252     uint32_t mask = m_domBreakpoints.get(node) & ~rootBit;
253     if (mask)
254         m_domBreakpoints.set(node, mask);
255     else
256         m_domBreakpoints.remove(node);
257
258     if ((rootBit & inheritableDOMBreakpointTypesMask) && !(mask & (rootBit << domBreakpointDerivedTypeShift))) {
259         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
260             updateSubtreeBreakpoints(child, rootBit, false);
261     }
262 }
263
264 void InspectorDOMDebuggerAgent::willInsertDOMNode(Node& parent)
265 {
266     if (hasBreakpoint(&parent, SubtreeModified)) {
267         Ref<InspectorObject> eventData = InspectorObject::create();
268         descriptionForDOMEvent(parent, SubtreeModified, true, eventData.get());
269         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
270     }
271 }
272
273 void InspectorDOMDebuggerAgent::willRemoveDOMNode(Node& node)
274 {
275     Node* parentNode = InspectorDOMAgent::innerParentNode(&node);
276     if (hasBreakpoint(&node, NodeRemoved)) {
277         Ref<InspectorObject> eventData = InspectorObject::create();
278         descriptionForDOMEvent(node, NodeRemoved, false, eventData.get());
279         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
280     } else if (parentNode && hasBreakpoint(parentNode, SubtreeModified)) {
281         Ref<InspectorObject> eventData = InspectorObject::create();
282         descriptionForDOMEvent(node, SubtreeModified, false, eventData.get());
283         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
284     }
285 }
286
287 void InspectorDOMDebuggerAgent::willModifyDOMAttr(Element& element)
288 {
289     if (hasBreakpoint(&element, AttributeModified)) {
290         Ref<InspectorObject> eventData = InspectorObject::create();
291         descriptionForDOMEvent(element, AttributeModified, false, eventData.get());
292         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::DOM, WTFMove(eventData));
293     }
294 }
295
296 void InspectorDOMDebuggerAgent::descriptionForDOMEvent(Node& target, int breakpointType, bool insertion, InspectorObject& description)
297 {
298     ASSERT(hasBreakpoint(&target, breakpointType));
299
300     Node* breakpointOwner = &target;
301     if ((1 << breakpointType) & inheritableDOMBreakpointTypesMask) {
302         // For inheritable breakpoint types, target node isn't always the same as the node that owns a breakpoint.
303         // Target node may be unknown to frontend, so we need to push it first.
304         RefPtr<Inspector::Protocol::Runtime::RemoteObject> targetNodeObject = m_domAgent->resolveNode(&target, InspectorDebuggerAgent::backtraceObjectGroup);
305         description.setValue("targetNode", targetNodeObject);
306
307         // Find breakpoint owner node.
308         if (!insertion)
309             breakpointOwner = InspectorDOMAgent::innerParentNode(&target);
310         ASSERT(breakpointOwner);
311         while (!(m_domBreakpoints.get(breakpointOwner) & (1 << breakpointType))) {
312             Node* parentNode = InspectorDOMAgent::innerParentNode(breakpointOwner);
313             if (!parentNode)
314                 break;
315             breakpointOwner = parentNode;
316         }
317
318         if (breakpointType == SubtreeModified)
319             description.setBoolean("insertion", insertion);
320     }
321
322     int breakpointOwnerNodeId = m_domAgent->boundNodeId(breakpointOwner);
323     ASSERT(breakpointOwnerNodeId);
324     description.setInteger("nodeId", breakpointOwnerNodeId);
325     description.setString("type", domTypeName(breakpointType));
326 }
327
328 bool InspectorDOMDebuggerAgent::hasBreakpoint(Node* node, int type)
329 {
330     uint32_t rootBit = 1 << type;
331     uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
332     return m_domBreakpoints.get(node) & (rootBit | derivedBit);
333 }
334
335 void InspectorDOMDebuggerAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, bool set)
336 {
337     uint32_t oldMask = m_domBreakpoints.get(node);
338     uint32_t derivedMask = rootMask << domBreakpointDerivedTypeShift;
339     uint32_t newMask = set ? oldMask | derivedMask : oldMask & ~derivedMask;
340     if (newMask)
341         m_domBreakpoints.set(node, newMask);
342     else
343         m_domBreakpoints.remove(node);
344
345     uint32_t newRootMask = rootMask & ~newMask;
346     if (!newRootMask)
347         return;
348
349     for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
350         updateSubtreeBreakpoints(child, newRootMask, set);
351 }
352
353 void InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded(bool isDOMEvent, const String& eventName, bool synchronous)
354 {
355     String fullEventName = (isDOMEvent ? listenerEventCategoryType : instrumentationEventCategoryType) + eventName;
356     if (m_pauseInNextEventListener)
357         m_pauseInNextEventListener = false;
358     else {
359         if (!m_eventListenerBreakpoints.contains(fullEventName))
360             return;
361     }
362
363     Ref<InspectorObject> eventData = InspectorObject::create();
364     eventData->setString("eventName", fullEventName);
365     if (synchronous)
366         m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::EventListener, WTFMove(eventData));
367     else
368         m_debuggerAgent->schedulePauseOnNextStatement(Inspector::DebuggerFrontendDispatcher::Reason::EventListener, WTFMove(eventData));
369 }
370
371 void InspectorDOMDebuggerAgent::setXHRBreakpoint(ErrorString&, const String& url)
372 {
373     if (url.isEmpty()) {
374         m_pauseOnAllXHRsEnabled = true;
375         return;
376     }
377
378     m_xhrBreakpoints.add(url);
379 }
380
381 void InspectorDOMDebuggerAgent::removeXHRBreakpoint(ErrorString&, const String& url)
382 {
383     if (url.isEmpty()) {
384         m_pauseOnAllXHRsEnabled = false;
385         return;
386     }
387
388     m_xhrBreakpoints.remove(url);
389 }
390
391 void InspectorDOMDebuggerAgent::willSendXMLHttpRequest(const String& url)
392 {
393     String breakpointURL;
394     if (m_pauseOnAllXHRsEnabled)
395         breakpointURL = emptyString();
396     else {
397         for (auto& breakpoint : m_xhrBreakpoints) {
398             if (url.contains(breakpoint)) {
399                 breakpointURL = breakpoint;
400                 break;
401             }
402         }
403     }
404
405     if (breakpointURL.isNull())
406         return;
407
408     Ref<InspectorObject> eventData = InspectorObject::create();
409     eventData->setString("breakpointURL", breakpointURL);
410     eventData->setString("url", url);
411     m_debuggerAgent->breakProgram(Inspector::DebuggerFrontendDispatcher::Reason::XHR, WTFMove(eventData));
412 }
413
414 void InspectorDOMDebuggerAgent::clear()
415 {
416     m_domBreakpoints.clear();
417     m_pauseInNextEventListener = false;
418 }
419
420 } // namespace WebCore