• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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