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