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