Home | History | Annotate | Download | only in inspector
      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