InspectorDOMDebuggerAgent.cpp 17 KB

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