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