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/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