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 
     33 #include "InspectorBrowserDebuggerAgent.h"
     34 
     35 #if ENABLE(INSPECTOR) && ENABLE(JAVASCRIPT_DEBUGGER)
     36 
     37 #include "HTMLElement.h"
     38 #include "InspectorAgent.h"
     39 #include "InspectorDOMAgent.h"
     40 #include "InspectorDebuggerAgent.h"
     41 #include "InspectorState.h"
     42 #include "InspectorValues.h"
     43 #include "InstrumentingAgents.h"
     44 #include <wtf/text/StringConcatenate.h>
     45 
     46 namespace {
     47 
     48 enum DOMBreakpointType {
     49     SubtreeModified = 0,
     50     AttributeModified,
     51     NodeRemoved,
     52     DOMBreakpointTypesCount
     53 };
     54 
     55 static const char* const domNativeBreakpointType = "DOM";
     56 static const char* const eventListenerNativeBreakpointType = "EventListener";
     57 static const char* const xhrNativeBreakpointType = "XHR";
     58 
     59 const uint32_t inheritableDOMBreakpointTypesMask = (1 << SubtreeModified);
     60 const int domBreakpointDerivedTypeShift = 16;
     61 
     62 }
     63 
     64 namespace WebCore {
     65 
     66 namespace BrowserDebuggerAgentState {
     67 static const char eventListenerBreakpoints[] = "eventListenerBreakpoints";
     68 static const char pauseOnAllXHRs[] = "pauseOnAllXHRs";
     69 static const char xhrBreakpoints[] = "xhrBreakpoints";
     70 }
     71 
     72 PassOwnPtr<InspectorBrowserDebuggerAgent> InspectorBrowserDebuggerAgent::create(InstrumentingAgents* instrumentingAgents, InspectorState* inspectorState, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent, InspectorAgent* inspectorAgent)
     73 {
     74     return adoptPtr(new InspectorBrowserDebuggerAgent(instrumentingAgents, inspectorState, domAgent, debuggerAgent, inspectorAgent));
     75 }
     76 
     77 InspectorBrowserDebuggerAgent::InspectorBrowserDebuggerAgent(InstrumentingAgents* instrumentingAgents, InspectorState* inspectorState, InspectorDOMAgent* domAgent, InspectorDebuggerAgent* debuggerAgent, InspectorAgent* inspectorAgent)
     78     : m_instrumentingAgents(instrumentingAgents)
     79     , m_inspectorState(inspectorState)
     80     , m_domAgent(domAgent)
     81     , m_debuggerAgent(debuggerAgent)
     82     , m_inspectorAgent(inspectorAgent)
     83 {
     84     m_debuggerAgent->setListener(this);
     85 }
     86 
     87 InspectorBrowserDebuggerAgent::~InspectorBrowserDebuggerAgent()
     88 {
     89     m_debuggerAgent->setListener(0);
     90     ASSERT(!m_instrumentingAgents->inspectorBrowserDebuggerAgent());
     91 }
     92 
     93 // Browser debugger agent enabled only when JS debugger is enabled.
     94 void InspectorBrowserDebuggerAgent::debuggerWasEnabled()
     95 {
     96     m_instrumentingAgents->setInspectorBrowserDebuggerAgent(this);
     97 }
     98 
     99 void InspectorBrowserDebuggerAgent::debuggerWasDisabled()
    100 {
    101     disable();
    102 }
    103 
    104 void InspectorBrowserDebuggerAgent::disable()
    105 {
    106     m_instrumentingAgents->setInspectorBrowserDebuggerAgent(0);
    107     clear();
    108 }
    109 
    110 void InspectorBrowserDebuggerAgent::clearFrontend()
    111 {
    112     disable();
    113 }
    114 
    115 void InspectorBrowserDebuggerAgent::discardBindings()
    116 {
    117     m_domBreakpoints.clear();
    118 }
    119 
    120 void InspectorBrowserDebuggerAgent::setEventListenerBreakpoint(ErrorString* error, const String& eventName)
    121 {
    122     if (eventName.isEmpty()) {
    123         *error = "Event name is empty";
    124         return;
    125     }
    126 
    127     RefPtr<InspectorObject> eventListenerBreakpoints = m_inspectorState->getObject(BrowserDebuggerAgentState::eventListenerBreakpoints);
    128     eventListenerBreakpoints->setBoolean(eventName, true);
    129     m_inspectorState->setObject(BrowserDebuggerAgentState::eventListenerBreakpoints, eventListenerBreakpoints);
    130 }
    131 
    132 void InspectorBrowserDebuggerAgent::removeEventListenerBreakpoint(ErrorString* error, const String& eventName)
    133 {
    134     if (eventName.isEmpty()) {
    135         *error = "Event name is empty";
    136         return;
    137     }
    138 
    139     RefPtr<InspectorObject> eventListenerBreakpoints = m_inspectorState->getObject(BrowserDebuggerAgentState::eventListenerBreakpoints);
    140     eventListenerBreakpoints->remove(eventName);
    141     m_inspectorState->setObject(BrowserDebuggerAgentState::eventListenerBreakpoints, eventListenerBreakpoints);
    142 }
    143 
    144 void InspectorBrowserDebuggerAgent::didInsertDOMNode(Node* node)
    145 {
    146     if (m_domBreakpoints.size()) {
    147         uint32_t mask = m_domBreakpoints.get(InspectorDOMAgent::innerParentNode(node));
    148         uint32_t inheritableTypesMask = (mask | (mask >> domBreakpointDerivedTypeShift)) & inheritableDOMBreakpointTypesMask;
    149         if (inheritableTypesMask)
    150             updateSubtreeBreakpoints(node, inheritableTypesMask, true);
    151     }
    152 }
    153 
    154 void InspectorBrowserDebuggerAgent::didRemoveDOMNode(Node* node)
    155 {
    156     if (m_domBreakpoints.size()) {
    157         // Remove subtree breakpoints.
    158         m_domBreakpoints.remove(node);
    159         Vector<Node*> stack(1, InspectorDOMAgent::innerFirstChild(node));
    160         do {
    161             Node* node = stack.last();
    162             stack.removeLast();
    163             if (!node)
    164                 continue;
    165             m_domBreakpoints.remove(node);
    166             stack.append(InspectorDOMAgent::innerFirstChild(node));
    167             stack.append(InspectorDOMAgent::innerNextSibling(node));
    168         } while (!stack.isEmpty());
    169     }
    170 }
    171 
    172 void InspectorBrowserDebuggerAgent::setDOMBreakpoint(ErrorString*, int nodeId, int type)
    173 {
    174     Node* node = m_domAgent->nodeForId(nodeId);
    175     if (!node)
    176         return;
    177 
    178     uint32_t rootBit = 1 << type;
    179     m_domBreakpoints.set(node, m_domBreakpoints.get(node) | rootBit);
    180     if (rootBit & inheritableDOMBreakpointTypesMask) {
    181         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
    182             updateSubtreeBreakpoints(child, rootBit, true);
    183     }
    184 }
    185 
    186 void InspectorBrowserDebuggerAgent::removeDOMBreakpoint(ErrorString*, int nodeId, int type)
    187 {
    188     Node* node = m_domAgent->nodeForId(nodeId);
    189     if (!node)
    190         return;
    191 
    192     uint32_t rootBit = 1 << type;
    193     uint32_t mask = m_domBreakpoints.get(node) & ~rootBit;
    194     if (mask)
    195         m_domBreakpoints.set(node, mask);
    196     else
    197         m_domBreakpoints.remove(node);
    198 
    199     if ((rootBit & inheritableDOMBreakpointTypesMask) && !(mask & (rootBit << domBreakpointDerivedTypeShift))) {
    200         for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
    201             updateSubtreeBreakpoints(child, rootBit, false);
    202     }
    203 }
    204 
    205 void InspectorBrowserDebuggerAgent::willInsertDOMNode(Node*, Node* parent)
    206 {
    207     InspectorDebuggerAgent* debuggerAgent = m_debuggerAgent;
    208     if (!debuggerAgent)
    209         return;
    210 
    211     if (hasBreakpoint(parent, SubtreeModified)) {
    212         RefPtr<InspectorObject> eventData = InspectorObject::create();
    213         descriptionForDOMEvent(parent, SubtreeModified, true, eventData.get());
    214         eventData->setString("breakpointType", domNativeBreakpointType);
    215         debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
    216     }
    217 }
    218 
    219 void InspectorBrowserDebuggerAgent::willRemoveDOMNode(Node* node)
    220 {
    221     InspectorDebuggerAgent* debuggerAgent = m_debuggerAgent;
    222     if (!debuggerAgent)
    223         return;
    224 
    225     Node* parentNode = InspectorDOMAgent::innerParentNode(node);
    226     if (hasBreakpoint(node, NodeRemoved)) {
    227         RefPtr<InspectorObject> eventData = InspectorObject::create();
    228         descriptionForDOMEvent(node, NodeRemoved, false, eventData.get());
    229         eventData->setString("breakpointType", domNativeBreakpointType);
    230         debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
    231     } else if (parentNode && hasBreakpoint(parentNode, SubtreeModified)) {
    232         RefPtr<InspectorObject> eventData = InspectorObject::create();
    233         descriptionForDOMEvent(node, SubtreeModified, false, eventData.get());
    234         eventData->setString("breakpointType", domNativeBreakpointType);
    235         debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
    236     }
    237 }
    238 
    239 void InspectorBrowserDebuggerAgent::willModifyDOMAttr(Element* element)
    240 {
    241     InspectorDebuggerAgent* debuggerAgent = m_debuggerAgent;
    242     if (!debuggerAgent)
    243         return;
    244 
    245     if (hasBreakpoint(element, AttributeModified)) {
    246         RefPtr<InspectorObject> eventData = InspectorObject::create();
    247         descriptionForDOMEvent(element, AttributeModified, false, eventData.get());
    248         eventData->setString("breakpointType", domNativeBreakpointType);
    249         debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
    250     }
    251 }
    252 
    253 void InspectorBrowserDebuggerAgent::descriptionForDOMEvent(Node* target, int breakpointType, bool insertion, InspectorObject* description)
    254 {
    255     ASSERT(hasBreakpoint(target, breakpointType));
    256 
    257     Node* breakpointOwner = target;
    258     if ((1 << breakpointType) & inheritableDOMBreakpointTypesMask) {
    259         // For inheritable breakpoint types, target node isn't always the same as the node that owns a breakpoint.
    260         // Target node may be unknown to frontend, so we need to push it first.
    261         RefPtr<InspectorObject> targetNodeObject = m_domAgent->resolveNode(target);
    262         description->setObject("targetNode", targetNodeObject);
    263 
    264         // Find breakpoint owner node.
    265         if (!insertion)
    266             breakpointOwner = InspectorDOMAgent::innerParentNode(target);
    267         ASSERT(breakpointOwner);
    268         while (!(m_domBreakpoints.get(breakpointOwner) & (1 << breakpointType))) {
    269             breakpointOwner = InspectorDOMAgent::innerParentNode(breakpointOwner);
    270             ASSERT(breakpointOwner);
    271         }
    272 
    273         if (breakpointType == SubtreeModified)
    274             description->setBoolean("insertion", insertion);
    275     }
    276 
    277     int breakpointOwnerNodeId = m_domAgent->boundNodeId(breakpointOwner);
    278     ASSERT(breakpointOwnerNodeId);
    279     description->setNumber("nodeId", breakpointOwnerNodeId);
    280     description->setNumber("type", breakpointType);
    281 }
    282 
    283 bool InspectorBrowserDebuggerAgent::hasBreakpoint(Node* node, int type)
    284 {
    285     uint32_t rootBit = 1 << type;
    286     uint32_t derivedBit = rootBit << domBreakpointDerivedTypeShift;
    287     return m_domBreakpoints.get(node) & (rootBit | derivedBit);
    288 }
    289 
    290 void InspectorBrowserDebuggerAgent::updateSubtreeBreakpoints(Node* node, uint32_t rootMask, bool set)
    291 {
    292     uint32_t oldMask = m_domBreakpoints.get(node);
    293     uint32_t derivedMask = rootMask << domBreakpointDerivedTypeShift;
    294     uint32_t newMask = set ? oldMask | derivedMask : oldMask & ~derivedMask;
    295     if (newMask)
    296         m_domBreakpoints.set(node, newMask);
    297     else
    298         m_domBreakpoints.remove(node);
    299 
    300     uint32_t newRootMask = rootMask & ~newMask;
    301     if (!newRootMask)
    302         return;
    303 
    304     for (Node* child = InspectorDOMAgent::innerFirstChild(node); child; child = InspectorDOMAgent::innerNextSibling(child))
    305         updateSubtreeBreakpoints(child, newRootMask, set);
    306 }
    307 
    308 void InspectorBrowserDebuggerAgent::pauseOnNativeEventIfNeeded(const String& categoryType, const String& eventName, bool synchronous)
    309 {
    310     InspectorDebuggerAgent* debuggerAgent = m_debuggerAgent;
    311     if (!debuggerAgent)
    312         return;
    313 
    314     String fullEventName = makeString(categoryType, ":", eventName);
    315     RefPtr<InspectorObject> eventListenerBreakpoints = m_inspectorState->getObject(BrowserDebuggerAgentState::eventListenerBreakpoints);
    316     if (eventListenerBreakpoints->find(fullEventName) == eventListenerBreakpoints->end())
    317         return;
    318 
    319     RefPtr<InspectorObject> eventData = InspectorObject::create();
    320     eventData->setString("breakpointType", eventListenerNativeBreakpointType);
    321     eventData->setString("eventName", fullEventName);
    322     if (synchronous)
    323         debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
    324     else
    325         debuggerAgent->schedulePauseOnNextStatement(NativeBreakpointDebuggerEventType, eventData.release());
    326 }
    327 
    328 void InspectorBrowserDebuggerAgent::setXHRBreakpoint(ErrorString*, const String& url)
    329 {
    330     if (url.isEmpty()) {
    331         m_inspectorState->setBoolean(BrowserDebuggerAgentState::pauseOnAllXHRs, true);
    332         return;
    333     }
    334 
    335     RefPtr<InspectorObject> xhrBreakpoints = m_inspectorState->getObject(BrowserDebuggerAgentState::xhrBreakpoints);
    336     xhrBreakpoints->setBoolean(url, true);
    337     m_inspectorState->setObject(BrowserDebuggerAgentState::xhrBreakpoints, xhrBreakpoints);
    338 }
    339 
    340 void InspectorBrowserDebuggerAgent::removeXHRBreakpoint(ErrorString*, const String& url)
    341 {
    342     if (url.isEmpty()) {
    343         m_inspectorState->setBoolean(BrowserDebuggerAgentState::pauseOnAllXHRs, false);
    344         return;
    345     }
    346 
    347     RefPtr<InspectorObject> xhrBreakpoints = m_inspectorState->getObject(BrowserDebuggerAgentState::xhrBreakpoints);
    348     xhrBreakpoints->remove(url);
    349     m_inspectorState->setObject(BrowserDebuggerAgentState::xhrBreakpoints, xhrBreakpoints);
    350 }
    351 
    352 void InspectorBrowserDebuggerAgent::willSendXMLHttpRequest(const String& url)
    353 {
    354     InspectorDebuggerAgent* debuggerAgent = m_debuggerAgent;
    355     if (!debuggerAgent)
    356         return;
    357 
    358     String breakpointURL;
    359     if (m_inspectorState->getBoolean(BrowserDebuggerAgentState::pauseOnAllXHRs))
    360         breakpointURL = "";
    361     else {
    362         RefPtr<InspectorObject> xhrBreakpoints = m_inspectorState->getObject(BrowserDebuggerAgentState::xhrBreakpoints);
    363         for (InspectorObject::iterator it = xhrBreakpoints->begin(); it != xhrBreakpoints->end(); ++it) {
    364             if (url.contains(it->first)) {
    365                 breakpointURL = it->first;
    366                 break;
    367             }
    368         }
    369     }
    370 
    371     if (breakpointURL.isNull())
    372         return;
    373 
    374     RefPtr<InspectorObject> eventData = InspectorObject::create();
    375     eventData->setString("breakpointType", xhrNativeBreakpointType);
    376     eventData->setString("breakpointURL", breakpointURL);
    377     eventData->setString("url", url);
    378     debuggerAgent->breakProgram(NativeBreakpointDebuggerEventType, eventData.release());
    379 }
    380 
    381 void InspectorBrowserDebuggerAgent::clear()
    382 {
    383     m_domBreakpoints.clear();
    384 }
    385 
    386 } // namespace WebCore
    387 
    388 #endif // ENABLE(INSPECTOR) && ENABLE(JAVASCRIPT_DEBUGGER)
    389