• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/inspector/InspectorDOMDebuggerAgent.h"
33 
34 #include "InspectorFrontend.h"
35 #include "core/events/Event.h"
36 #include "core/inspector/InspectorDOMAgent.h"
37 #include "core/inspector/InspectorState.h"
38 #include "core/inspector/InstrumentingAgents.h"
39 #include "platform/JSONValues.h"
40 
41 namespace {
42 
43 enum DOMBreakpointType {
44     SubtreeModified = 0,
45     AttributeModified,
46     NodeRemoved,
47     DOMBreakpointTypesCount
48 };
49 
50 static const char listenerEventCategoryType[] = "listener:";
51 static const char instrumentationEventCategoryType[] = "instrumentation:";
52 
53 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
54 const int domBreakpointDerivedTypeShift = 16;
55 
56 }
57 
58 namespace WebCore {
59 
60 static const char requestAnimationFrameEventName[] = "requestAnimationFrame";
61 static const char cancelAnimationFrameEventName[] = "cancelAnimationFrame";
62 static const char animationFrameFiredEventName[] = "animationFrameFired";
63 static const char setTimerEventName[] = "setTimer";
64 static const char clearTimerEventName[] = "clearTimer";
65 static const char timerFiredEventName[] = "timerFired";
66 static const char webglErrorFiredEventName[] = "webglErrorFired";
67 static const char webglWarningFiredEventName[] = "webglWarningFired";
68 static const char webglErrorNameProperty[] = "webglErrorName";
69 
70 namespace DOMDebuggerAgentState {
71 static const char eventListenerBreakpoints[] = "eventListenerBreakpoints";
72 static const char pauseOnAllXHRs[] = "pauseOnAllXHRs";
73 static const char xhrBreakpoints[] = "xhrBreakpoints";
74 }
75 
create(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * inspectorState,InspectorDOMAgent * domAgent,InspectorDebuggerAgent * debuggerAgent)76 PassOwnPtr<InspectorDOMDebuggerAgent> InspectorDOMDebuggerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent)
77 {
78     return adoptPtr(new InspectorDOMDebuggerAgent(instrumentingAgents, inspectorState, domAgent, debuggerAgent));
79 }
80 
InspectorDOMDebuggerAgent(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * inspectorState,InspectorDOMAgent * domAgent,InspectorDebuggerAgent * debuggerAgent)81 InspectorDOMDebuggerAgent::InspectorDOMDebuggerAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent)
82     : InspectorBaseAgent<InspectorDOMDebuggerAgent>("DOMDebugger", instrumentingAgents, inspectorState)
83     , m_domAgent(domAgent)
84     , m_debuggerAgent(debuggerAgent)
85     , m_pauseInNextEventListener(false)
86 {
87     m_debuggerAgent->setListener(this);
88 }
89 
~InspectorDOMDebuggerAgent()90 InspectorDOMDebuggerAgent::~InspectorDOMDebuggerAgent()
91 {
92     ASSERT(!m_debuggerAgent);
93     ASSERT(!m_instrumentingAgents->inspectorDOMDebuggerAgent());
94 }
95 
96 // Browser debugger agent enabled only when JS debugger is enabled.
debuggerWasEnabled()97 void InspectorDOMDebuggerAgent::debuggerWasEnabled()
98 {
99     m_instrumentingAgents->setInspectorDOMDebuggerAgent(this);
100 }
101 
debuggerWasDisabled()102 void InspectorDOMDebuggerAgent::debuggerWasDisabled()
103 {
104     disable();
105 }
106 
stepInto()107 void InspectorDOMDebuggerAgent::stepInto()
108 {
109     m_pauseInNextEventListener = true;
110 }
111 
didPause()112 void InspectorDOMDebuggerAgent::didPause()
113 {
114     m_pauseInNextEventListener = false;
115 }
116 
didProcessTask()117 void InspectorDOMDebuggerAgent::didProcessTask()
118 {
119     if (!m_pauseInNextEventListener)
120         return;
121     if (m_debuggerAgent && m_debuggerAgent->runningNestedMessageLoop())
122         return;
123     m_pauseInNextEventListener = false;
124 }
125 
disable()126 void InspectorDOMDebuggerAgent::disable()
127 {
128     m_instrumentingAgents->setInspectorDOMDebuggerAgent(0);
129     clear();
130 }
131 
clearFrontend()132 void InspectorDOMDebuggerAgent::clearFrontend()
133 {
134     disable();
135 }
136 
discardAgent()137 void InspectorDOMDebuggerAgent::discardAgent()
138 {
139     m_debuggerAgent->setListener(0);
140     m_debuggerAgent = 0;
141 }
142 
discardBindings()143 void InspectorDOMDebuggerAgent::discardBindings()
144 {
145     m_domBreakpoints.clear();
146 }
147 
setEventListenerBreakpoint(ErrorString * error,const String & eventName)148 void InspectorDOMDebuggerAgent::setEventListenerBreakpoint(ErrorString* error, const String& eventName)
149 {
150     setBreakpoint(error, String(listenerEventCategoryType) + eventName);
151 }
152 
setInstrumentationBreakpoint(ErrorString * error,const String & eventName)153 void InspectorDOMDebuggerAgent::setInstrumentationBreakpoint(ErrorString* error, const String& eventName)
154 {
155     setBreakpoint(error, String(instrumentationEventCategoryType) + eventName);
156 }
157 
setBreakpoint(ErrorString * error,const String & eventName)158 void InspectorDOMDebuggerAgent::setBreakpoint(ErrorString* error, const String& eventName)
159 {
160     if (eventName.isEmpty()) {
161         *error = "Event name is empty";
162         return;
163     }
164 
165     RefPtr<JSONObject> eventListenerBreakpoints = m_state->getObject(DOMDebuggerAgentState::eventListenerBreakpoints);
166     eventListenerBreakpoints->setBoolean(eventName, true);
167     m_state->setObject(DOMDebuggerAgentState::eventListenerBreakpoints, eventListenerBreakpoints);
168 }
169 
removeEventListenerBreakpoint(ErrorString * error,const String & eventName)170 void InspectorDOMDebuggerAgent::removeEventListenerBreakpoint(ErrorString* error, const String& eventName)
171 {
172     removeBreakpoint(error, String(listenerEventCategoryType) + eventName);
173 }
174 
removeInstrumentationBreakpoint(ErrorString * error,const String & eventName)175 void InspectorDOMDebuggerAgent::removeInstrumentationBreakpoint(ErrorString* error, const String& eventName)
176 {
177     removeBreakpoint(error, String(instrumentationEventCategoryType) + eventName);
178 }
179 
removeBreakpoint(ErrorString * error,const String & eventName)180 void InspectorDOMDebuggerAgent::removeBreakpoint(ErrorString* error, const String& eventName)
181 {
182     if (eventName.isEmpty()) {
183         *error = "Event name is empty";
184         return;
185     }
186 
187     RefPtr<JSONObject> eventListenerBreakpoints = m_state->getObject(DOMDebuggerAgentState::eventListenerBreakpoints);
188     eventListenerBreakpoints->remove(eventName);
189     m_state->setObject(DOMDebuggerAgentState::eventListenerBreakpoints, eventListenerBreakpoints);
190 }
191 
didInvalidateStyleAttr(Node * node)192 void InspectorDOMDebuggerAgent::didInvalidateStyleAttr(Node* node)
193 {
194     if (hasBreakpoint(node, AttributeModified)) {
195         RefPtr<JSONObject> eventData = JSONObject::create();
196         descriptionForDOMEvent(node, AttributeModified, false, eventData.get());
197         m_debuggerAgent->breakProgram(InspectorFrontend::Debugger::Reason::DOM, eventData.release());
198     }
199 }
200 
didInsertDOMNode(Node * node)201 void InspectorDOMDebuggerAgent::didInsertDOMNode(Node* node)
202 {
203     if (m_domBreakpoints.size()) {
204         uint32_t mask = m_domBreakpoints.get(InspectorDOMAgent::innerParentNode(node));
205         uint32_t inheritableTypesMask = (mask | (mask >> domBreakpointDerivedTypeShift)) & inheritableDOMBreakpointTypesMask;
206         if (inheritableTypesMask)
207             updateSubtreeBreakpoints(node, inheritableTypesMask, true);
208     }
209 }
210 
didRemoveDOMNode(Node * node)211 void InspectorDOMDebuggerAgent::didRemoveDOMNode(Node* node)
212 {
213     if (m_domBreakpoints.size()) {
214         // Remove subtree breakpoints.
215         m_domBreakpoints.remove(node);
216         Vector<Node*> stack(1, InspectorDOMAgent::innerFirstChild(node));
217         do {
218             Node* node = stack.last();
219             stack.removeLast();
220             if (!node)
221                 continue;
222             m_domBreakpoints.remove(node);
223             stack.append(InspectorDOMAgent::innerFirstChild(node));
224             stack.append(InspectorDOMAgent::innerNextSibling(node));
225         } while (!stack.isEmpty());
226     }
227 }
228 
domTypeForName(ErrorString * errorString,const String & typeString)229 static int domTypeForName(ErrorString* errorString, const String& typeString)
230 {
231     if (typeString == "subtree-modified")
232         return SubtreeModified;
233     if (typeString == "attribute-modified")
234         return AttributeModified;
235     if (typeString == "node-removed")
236         return NodeRemoved;
237     *errorString = "Unknown DOM breakpoint type: " + typeString;
238     return -1;
239 }
240 
domTypeName(int type)241 static String domTypeName(int type)
242 {
243     switch (type) {
244     case SubtreeModified: return "subtree-modified";
245     case AttributeModified: return "attribute-modified";
246     case NodeRemoved: return "node-removed";
247     default: break;
248     }
249     return "";
250 }
251 
setDOMBreakpoint(ErrorString * errorString,int nodeId,const String & typeString)252 void InspectorDOMDebuggerAgent::setDOMBreakpoint(ErrorString* errorString, int nodeId, const String& typeString)
253 {
254     Node* node = m_domAgent->assertNode(errorString, nodeId);
255     if (!node)
256         return;
257 
258     int type = domTypeForName(errorString, typeString);
259     if (type == -1)
260         return;
261 
262     uint32_t rootBit = 1 << type;
263     m_domBreakpoints.set(node, m_domBreakpoints.get(node) | rootBit);
264     if (rootBit & inheritableDOMBreakpointTypesMask) {
265         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
266             updateSubtreeBreakpoints(child, rootBit, true);
267     }
268 }
269 
removeDOMBreakpoint(ErrorString * errorString,int nodeId,const String & typeString)270 void InspectorDOMDebuggerAgent::removeDOMBreakpoint(ErrorString* errorString, int nodeId, const String& typeString)
271 {
272     Node* node = m_domAgent->assertNode(errorString, nodeId);
273     if (!node)
274         return;
275     int type = domTypeForName(errorString, typeString);
276     if (type == -1)
277         return;
278 
279     uint32_t rootBit = 1 << type;
280     uint32_t mask = m_domBreakpoints.get(node) & ~rootBit;
281     if (mask)
282         m_domBreakpoints.set(node, mask);
283     else
284         m_domBreakpoints.remove(node);
285 
286     if ((rootBit & inheritableDOMBreakpointTypesMask) && !(mask & (rootBit << domBreakpointDerivedTypeShift))) {
287         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
288             updateSubtreeBreakpoints(child, rootBit, false);
289     }
290 }
291 
willInsertDOMNode(Node * parent)292 void InspectorDOMDebuggerAgent::willInsertDOMNode(Node* parent)
293 {
294     if (hasBreakpoint(parent, SubtreeModified)) {
295         RefPtr<JSONObject> eventData = JSONObject::create();
296         descriptionForDOMEvent(parent, SubtreeModified, true, eventData.get());
297         m_debuggerAgent->breakProgram(InspectorFrontend::Debugger::Reason::DOM, eventData.release());
298     }
299 }
300 
willRemoveDOMNode(Node * node)301 void InspectorDOMDebuggerAgent::willRemoveDOMNode(Node* node)
302 {
303     Node* parentNode = InspectorDOMAgent::innerParentNode(node);
304     if (hasBreakpoint(node, NodeRemoved)) {
305         RefPtr<JSONObject> eventData = JSONObject::create();
306         descriptionForDOMEvent(node, NodeRemoved, false, eventData.get());
307         m_debuggerAgent->breakProgram(InspectorFrontend::Debugger::Reason::DOM, eventData.release());
308     } else if (parentNode && hasBreakpoint(parentNode, SubtreeModified)) {
309         RefPtr<JSONObject> eventData = JSONObject::create();
310         descriptionForDOMEvent(node, SubtreeModified, false, eventData.get());
311         m_debuggerAgent->breakProgram(InspectorFrontend::Debugger::Reason::DOM, eventData.release());
312     }
313     didRemoveDOMNode(node);
314 }
315 
willModifyDOMAttr(Element * element,const AtomicString &,const AtomicString &)316 void InspectorDOMDebuggerAgent::willModifyDOMAttr(Element* element, const AtomicString&, const AtomicString&)
317 {
318     if (hasBreakpoint(element, AttributeModified)) {
319         RefPtr<JSONObject> eventData = JSONObject::create();
320         descriptionForDOMEvent(element, AttributeModified, false, eventData.get());
321         m_debuggerAgent->breakProgram(InspectorFrontend::Debugger::Reason::DOM, eventData.release());
322     }
323 }
324 
descriptionForDOMEvent(Node * target,int breakpointType,bool insertion,JSONObject * description)325 void InspectorDOMDebuggerAgent::descriptionForDOMEvent(Node* target, int breakpointType, bool insertion, JSONObject* description)
326 {
327     ASSERT(hasBreakpoint(target, breakpointType));
328 
329     Node* breakpointOwner = target;
330     if ((1 << breakpointType) & inheritableDOMBreakpointTypesMask) {
331         // For inheritable breakpoint types, target node isn't always the same as the node that owns a breakpoint.
332         // Target node may be unknown to frontend, so we need to push it first.
333         RefPtr<TypeBuilder::Runtime::RemoteObject> targetNodeObject = m_domAgent->resolveNode(target, InspectorDebuggerAgent::backtraceObjectGroup);
334         description->setValue("targetNode", targetNodeObject);
335 
336         // Find breakpoint owner node.
337         if (!insertion)
338             breakpointOwner = InspectorDOMAgent::innerParentNode(target);
339         ASSERT(breakpointOwner);
340         while (!(m_domBreakpoints.get(breakpointOwner) & (1 << breakpointType))) {
341             Node* parentNode = InspectorDOMAgent::innerParentNode(breakpointOwner);
342             if (!parentNode)
343                 break;
344             breakpointOwner = parentNode;
345         }
346 
347         if (breakpointType == SubtreeModified)
348             description->setBoolean("insertion", insertion);
349     }
350 
351     int breakpointOwnerNodeId = m_domAgent->boundNodeId(breakpointOwner);
352     ASSERT(breakpointOwnerNodeId);
353     description->setNumber("nodeId", breakpointOwnerNodeId);
354     description->setString("type", domTypeName(breakpointType));
355 }
356 
hasBreakpoint(Node * node,int type)357 bool InspectorDOMDebuggerAgent::hasBreakpoint(Node* node, int type)
358 {
359     uint32_t rootBit = 1 << type;
360     uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
361     return m_domBreakpoints.get(node) & (rootBit | derivedBit);
362 }
363 
updateSubtreeBreakpoints(Node * node,uint32_t rootMask,bool set)364 void InspectorDOMDebuggerAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, bool set)
365 {
366     uint32_t oldMask = m_domBreakpoints.get(node);
367     uint32_t derivedMask = rootMask << domBreakpointDerivedTypeShift;
368     uint32_t newMask = set ? oldMask | derivedMask : oldMask & ~derivedMask;
369     if (newMask)
370         m_domBreakpoints.set(node, newMask);
371     else
372         m_domBreakpoints.remove(node);
373 
374     uint32_t newRootMask = rootMask & ~newMask;
375     if (!newRootMask)
376         return;
377 
378     for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
379         updateSubtreeBreakpoints(child, newRootMask, set);
380 }
381 
pauseOnNativeEventIfNeeded(PassRefPtr<JSONObject> eventData,bool synchronous)382 void InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded(PassRefPtr<JSONObject> eventData, bool synchronous)
383 {
384     if (!eventData)
385         return;
386     if (synchronous)
387         m_debuggerAgent->breakProgram(InspectorFrontend::Debugger::Reason::EventListener, eventData);
388     else
389         m_debuggerAgent->schedulePauseOnNextStatement(InspectorFrontend::Debugger::Reason::EventListener, eventData);
390 }
391 
preparePauseOnNativeEventData(bool isDOMEvent,const String & eventName)392 PassRefPtr<JSONObject> InspectorDOMDebuggerAgent::preparePauseOnNativeEventData(bool isDOMEvent, const String& eventName)
393 {
394     String fullEventName = (isDOMEvent ? listenerEventCategoryType : instrumentationEventCategoryType) + eventName;
395     if (m_pauseInNextEventListener)
396         m_pauseInNextEventListener = false;
397     else {
398         RefPtr<JSONObject> eventListenerBreakpoints = m_state->getObject(DOMDebuggerAgentState::eventListenerBreakpoints);
399         if (eventListenerBreakpoints->find(fullEventName) == eventListenerBreakpoints->end())
400             return 0;
401     }
402 
403     RefPtr<JSONObject> eventData = JSONObject::create();
404     eventData->setString("eventName", fullEventName);
405     return eventData.release();
406 }
407 
didInstallTimer(ExecutionContext * context,int timerId,int timeout,bool singleShot)408 void InspectorDOMDebuggerAgent::didInstallTimer(ExecutionContext* context, int timerId, int timeout, bool singleShot)
409 {
410     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(false, setTimerEventName), true);
411 }
412 
didRemoveTimer(ExecutionContext * context,int timerId)413 void InspectorDOMDebuggerAgent::didRemoveTimer(ExecutionContext* context, int timerId)
414 {
415     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(false, clearTimerEventName), true);
416 }
417 
willFireTimer(ExecutionContext * context,int timerId)418 void InspectorDOMDebuggerAgent::willFireTimer(ExecutionContext* context, int timerId)
419 {
420     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(false, timerFiredEventName), false);
421 }
422 
didRequestAnimationFrame(Document * document,int callbackId)423 void InspectorDOMDebuggerAgent::didRequestAnimationFrame(Document* document, int callbackId)
424 {
425     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(false, requestAnimationFrameEventName), true);
426 }
427 
didCancelAnimationFrame(Document * document,int callbackId)428 void InspectorDOMDebuggerAgent::didCancelAnimationFrame(Document* document, int callbackId)
429 {
430     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(false, cancelAnimationFrameEventName), true);
431 }
432 
willFireAnimationFrame(Document * document,int callbackId)433 void InspectorDOMDebuggerAgent::willFireAnimationFrame(Document* document, int callbackId)
434 {
435     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(false, animationFrameFiredEventName), false);
436 }
437 
willHandleEvent(Event * event)438 void InspectorDOMDebuggerAgent::willHandleEvent(Event* event)
439 {
440     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(true, event->type()), false);
441 }
442 
didFireWebGLError(const String & errorName)443 void InspectorDOMDebuggerAgent::didFireWebGLError(const String& errorName)
444 {
445     RefPtr<JSONObject> eventData = preparePauseOnNativeEventData(false, webglErrorFiredEventName);
446     if (!eventData)
447         return;
448     if (!errorName.isEmpty())
449         eventData->setString(webglErrorNameProperty, errorName);
450     pauseOnNativeEventIfNeeded(eventData.release(), m_debuggerAgent->canBreakProgram());
451 }
452 
didFireWebGLWarning()453 void InspectorDOMDebuggerAgent::didFireWebGLWarning()
454 {
455     pauseOnNativeEventIfNeeded(preparePauseOnNativeEventData(false, webglWarningFiredEventName), m_debuggerAgent->canBreakProgram());
456 }
457 
didFireWebGLErrorOrWarning(const String & message)458 void InspectorDOMDebuggerAgent::didFireWebGLErrorOrWarning(const String& message)
459 {
460     if (message.findIgnoringCase("error") != WTF::kNotFound)
461         didFireWebGLError(String());
462     else
463         didFireWebGLWarning();
464 }
465 
setXHRBreakpoint(ErrorString *,const String & url)466 void InspectorDOMDebuggerAgent::setXHRBreakpoint(ErrorString*, const String& url)
467 {
468     if (url.isEmpty()) {
469         m_state->setBoolean(DOMDebuggerAgentState::pauseOnAllXHRs, true);
470         return;
471     }
472 
473     RefPtr<JSONObject> xhrBreakpoints = m_state->getObject(DOMDebuggerAgentState::xhrBreakpoints);
474     xhrBreakpoints->setBoolean(url, true);
475     m_state->setObject(DOMDebuggerAgentState::xhrBreakpoints, xhrBreakpoints);
476 }
477 
removeXHRBreakpoint(ErrorString *,const String & url)478 void InspectorDOMDebuggerAgent::removeXHRBreakpoint(ErrorString*, const String& url)
479 {
480     if (url.isEmpty()) {
481         m_state->setBoolean(DOMDebuggerAgentState::pauseOnAllXHRs, false);
482         return;
483     }
484 
485     RefPtr<JSONObject> xhrBreakpoints = m_state->getObject(DOMDebuggerAgentState::xhrBreakpoints);
486     xhrBreakpoints->remove(url);
487     m_state->setObject(DOMDebuggerAgentState::xhrBreakpoints, xhrBreakpoints);
488 }
489 
willSendXMLHttpRequest(const String & url)490 void InspectorDOMDebuggerAgent::willSendXMLHttpRequest(const String& url)
491 {
492     String breakpointURL;
493     if (m_state->getBoolean(DOMDebuggerAgentState::pauseOnAllXHRs))
494         breakpointURL = "";
495     else {
496         RefPtr<JSONObject> xhrBreakpoints = m_state->getObject(DOMDebuggerAgentState::xhrBreakpoints);
497         for (JSONObject::iterator it = xhrBreakpoints->begin(); it != xhrBreakpoints->end(); ++it) {
498             if (url.contains(it->key)) {
499                 breakpointURL = it->key;
500                 break;
501             }
502         }
503     }
504 
505     if (breakpointURL.isNull())
506         return;
507 
508     RefPtr<JSONObject> eventData = JSONObject::create();
509     eventData->setString("breakpointURL", breakpointURL);
510     eventData->setString("url", url);
511     m_debuggerAgent->breakProgram(InspectorFrontend::Debugger::Reason::XHR, eventData.release());
512 }
513 
clear()514 void InspectorDOMDebuggerAgent::clear()
515 {
516     m_domBreakpoints.clear();
517     m_pauseInNextEventListener = false;
518 }
519 
520 } // namespace WebCore
521 
522