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