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