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