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