Diff
Modified: trunk/LayoutTests/ChangeLog (220035 => 220036)
--- trunk/LayoutTests/ChangeLog 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/LayoutTests/ChangeLog 2017-07-29 00:33:12 UTC (rev 220036)
@@ -1,3 +1,16 @@
+2017-07-28 Matt Baker <[email protected]>
+
+ Web Inspector: capture an async stack trace when web content calls addEventListener
+ https://bugs.webkit.org/show_bug.cgi?id=174739
+ <rdar://problem/33468197>
+
+ Reviewed by Brian Burg.
+
+ Add test case checking that async stack traces are recorded for addEventListener.
+
+ * inspector/debugger/async-stack-trace-expected.txt:
+ * inspector/debugger/async-stack-trace.html:
+
2017-07-28 Matt Rajca <[email protected]>
Don't add autoplay restrictions to media elements created in response to user gestures.
Modified: trunk/LayoutTests/inspector/debugger/async-stack-trace-expected.txt (220035 => 220036)
--- trunk/LayoutTests/inspector/debugger/async-stack-trace-expected.txt 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/LayoutTests/inspector/debugger/async-stack-trace-expected.txt 2017-07-29 00:33:12 UTC (rev 220036)
@@ -64,6 +64,18 @@
3: --- setInterval ---
4: [F] testReferenceCounting
5: [P] Global Code
+
+-- Running test case: CheckAsyncStackTrace.AddEventListener
+PAUSE #1
+CALL STACK:
+0: [F] pauseThenFinishTest
+1: [F] clickFired
+2: [F] testAddEventListener
+3: [P] Global Code
+ASYNC CALL STACK:
+4: --- addEventListener ---
+5: [F] testAddEventListener
+6: [P] Global Code
-- Running test setup.
Save DebuggerManager.asyncStackTraceDepth
Modified: trunk/LayoutTests/inspector/debugger/async-stack-trace.html (220035 => 220036)
--- trunk/LayoutTests/inspector/debugger/async-stack-trace.html 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/LayoutTests/inspector/debugger/async-stack-trace.html 2017-07-29 00:33:12 UTC (rev 220036)
@@ -41,6 +41,15 @@
}, timerDelay);
}
+function testAddEventListener() {
+ document.body.addEventListener("click", function clickFired() {
+ document.body.removeEventListener("click", clickFired);
+ pauseThenFinishTest();
+ });
+
+ document.body.click();
+}
+
function recursiveCallThenTest(testFunction, depth) {
if (depth) {
recursiveCallThenTest(testFunction, depth - 1);
@@ -93,6 +102,7 @@
addSimpleTestCase("SetInterval", "testSetInterval(3)");
addSimpleTestCase("ChainedRequestAnimationFrame", "testChainedRequestAnimationFrame()");
addSimpleTestCase("ReferenceCounting", "testReferenceCounting()");
+ addSimpleTestCase("AddEventListener", "testAddEventListener()");
function setup(resolve) {
InspectorTest.log("Save DebuggerManager.asyncStackTraceDepth");
Modified: trunk/Source/_javascript_Core/ChangeLog (220035 => 220036)
--- trunk/Source/_javascript_Core/ChangeLog 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-07-29 00:33:12 UTC (rev 220036)
@@ -1,3 +1,19 @@
+2017-07-28 Matt Baker <[email protected]>
+
+ Web Inspector: capture an async stack trace when web content calls addEventListener
+ https://bugs.webkit.org/show_bug.cgi?id=174739
+ <rdar://problem/33468197>
+
+ Reviewed by Brian Burg.
+
+ Allow debugger agents to perform custom logic when asynchronous stack
+ trace data is cleared. For example, the PageDebuggerAgent would clear
+ its list of registered listeners for which call stacks have been recorded.
+
+ * inspector/agents/InspectorDebuggerAgent.cpp:
+ (Inspector::InspectorDebuggerAgent::clearAsyncStackTraceData):
+ * inspector/agents/InspectorDebuggerAgent.h:
+
2017-07-28 Mark Lam <[email protected]>
ObjectToStringAdaptiveStructureWatchpoint should not fire if it's dying imminently.
Modified: trunk/Source/_javascript_Core/inspector/agents/InspectorDebuggerAgent.cpp (220035 => 220036)
--- trunk/Source/_javascript_Core/inspector/agents/InspectorDebuggerAgent.cpp 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/_javascript_Core/inspector/agents/InspectorDebuggerAgent.cpp 2017-07-29 00:33:12 UTC (rev 220036)
@@ -1145,6 +1145,8 @@
{
m_pendingAsyncCalls.clear();
m_currentAsyncCallIdentifier = std::nullopt;
+
+ didClearAsyncStackTraceData();
}
} // namespace Inspector
Modified: trunk/Source/_javascript_Core/inspector/agents/InspectorDebuggerAgent.h (220035 => 220036)
--- trunk/Source/_javascript_Core/inspector/agents/InspectorDebuggerAgent.h 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/_javascript_Core/inspector/agents/InspectorDebuggerAgent.h 2017-07-29 00:33:12 UTC (rev 220036)
@@ -93,6 +93,7 @@
enum class AsyncCallType {
DOMTimer,
+ EventListener,
RequestAnimationFrame,
};
@@ -135,6 +136,7 @@
virtual String sourceMapURLForScript(const Script&);
void didClearGlobalObject();
+ virtual void didClearAsyncStackTraceData() { }
private:
Ref<Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>> currentCallFrames(const InjectedScript&);
Modified: trunk/Source/WebCore/ChangeLog (220035 => 220036)
--- trunk/Source/WebCore/ChangeLog 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/WebCore/ChangeLog 2017-07-29 00:33:12 UTC (rev 220036)
@@ -1,3 +1,37 @@
+2017-07-28 Matt Baker <[email protected]>
+
+ Web Inspector: capture an async stack trace when web content calls addEventListener
+ https://bugs.webkit.org/show_bug.cgi?id=174739
+ <rdar://problem/33468197>
+
+ Reviewed by Brian Burg.
+
+ Test: inspector/debugger/async-stack-trace.html
+
+ Add instrumentation to EventTarget to support showing asynchronous
+ stack traces when the debugger breaks in a script event listener.
+
+ * dom/EventTarget.cpp:
+ (WebCore::EventTarget::addEventListener):
+ (WebCore::EventTarget::removeEventListener):
+ (WebCore::EventTarget::fireEventListeners):
+ * inspector/InspectorInstrumentation.cpp:
+ (WebCore::InspectorInstrumentation::didAddEventListenerImpl):
+ (WebCore::InspectorInstrumentation::willRemoveEventListenerImpl):
+ (WebCore::InspectorInstrumentation::willHandleEventImpl):
+ (WebCore::InspectorInstrumentation::didHandleEventImpl):
+ * inspector/InspectorInstrumentation.h:
+ (WebCore::InspectorInstrumentation::didAddEventListener):
+ (WebCore::InspectorInstrumentation::willRemoveEventListener):
+ (WebCore::InspectorInstrumentation::willHandleEvent):
+ (WebCore::InspectorInstrumentation::didHandleEvent):
+ * inspector/PageDebuggerAgent.cpp:
+ (WebCore::PageDebuggerAgent::didClearAsyncStackTraceData):
+ (WebCore::PageDebuggerAgent::didAddEventListener):
+ (WebCore::PageDebuggerAgent::willRemoveEventListener):
+ (WebCore::PageDebuggerAgent::willHandleEvent):
+ * inspector/PageDebuggerAgent.h:
+
2017-07-28 Matt Rajca <[email protected]>
Don't add autoplay restrictions to media elements created in response to user gestures.
Modified: trunk/Source/WebCore/dom/EventTarget.cpp (220035 => 220036)
--- trunk/Source/WebCore/dom/EventTarget.cpp 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/WebCore/dom/EventTarget.cpp 2017-07-29 00:33:12 UTC (rev 220036)
@@ -68,7 +68,15 @@
bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
- return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once });
+ bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();
+
+ if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once }))
+ return false;
+
+ if (listenerCreatedFromScript)
+ InspectorInstrumentation::didAddEventListener(*this, eventType);
+
+ return true;
}
void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, AddEventListenerOptionsOrBoolean&& variant)
@@ -102,7 +110,12 @@
bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
{
auto* data = ""
- return data && data->eventListenerMap.remove(eventType, listener, options.capture);
+ if (!data)
+ return false;
+
+ InspectorInstrumentation::willRemoveEventListener(*this, eventType, listener, options.capture);
+
+ return data->eventListenerMap.remove(eventType, listener, options.capture);
}
bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
@@ -260,8 +273,9 @@
if (registeredListener->isPassive())
event.setInPassiveListener(true);
- InspectorInstrumentation::willHandleEvent(&context, event);
+ InspectorInstrumentation::willHandleEvent(context, event, *registeredListener);
registeredListener->callback().handleEvent(context, event);
+ InspectorInstrumentation::didHandleEvent(context);
if (registeredListener->isPassive())
event.setInPassiveListener(false);
Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp (220035 => 220036)
--- trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.cpp 2017-07-29 00:33:12 UTC (rev 220036)
@@ -61,6 +61,7 @@
#include "RenderObject.h"
#include "RenderView.h"
#include "ScriptController.h"
+#include "ScriptExecutionContext.h"
#include "WebConsoleAgent.h"
#include "WebGLRenderingContextBase.h"
#include "WebSocketFrame.h"
@@ -350,6 +351,18 @@
timelineAgent->didRemoveTimer(timerId, frameForScriptExecutionContext(context));
}
+void InspectorInstrumentation::didAddEventListenerImpl(InstrumentingAgents& instrumentingAgents, EventTarget& target, const AtomicString& eventType)
+{
+ if (PageDebuggerAgent* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent())
+ pageDebuggerAgent->didAddEventListener(target, eventType);
+}
+
+void InspectorInstrumentation::willRemoveEventListenerImpl(InstrumentingAgents& instrumentingAgents, EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
+{
+ if (PageDebuggerAgent* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent())
+ pageDebuggerAgent->willRemoveEventListener(target, eventType, listener, capture);
+}
+
InspectorInstrumentationCookie InspectorInstrumentation::willCallFunctionImpl(InstrumentingAgents& instrumentingAgents, const String& scriptName, int scriptLine, ScriptExecutionContext* context)
{
int timelineAgentId = 0;
@@ -378,11 +391,20 @@
return InspectorInstrumentationCookie(instrumentingAgents, timelineAgentId);
}
-void InspectorInstrumentation::willHandleEventImpl(InstrumentingAgents& instrumentingAgents, const Event& event)
+void InspectorInstrumentation::willHandleEventImpl(InstrumentingAgents& instrumentingAgents, const Event& event, const RegisteredEventListener& listener)
{
+ if (PageDebuggerAgent* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent())
+ pageDebuggerAgent->willHandleEvent(listener);
+
pauseOnNativeEventIfNeeded(instrumentingAgents, true, event.type(), false);
}
+void InspectorInstrumentation::didHandleEventImpl(InstrumentingAgents& instrumentingAgents)
+{
+ if (InspectorDebuggerAgent* debuggerAgent = instrumentingAgents.inspectorDebuggerAgent())
+ debuggerAgent->didDispatchAsyncCall();
+}
+
void InspectorInstrumentation::didDispatchEventImpl(const InspectorInstrumentationCookie& cookie)
{
if (InspectorTimelineAgent* timelineAgent = retrieveTimelineAgent(cookie))
Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.h (220035 => 220036)
--- trunk/Source/WebCore/inspector/InspectorInstrumentation.h 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.h 2017-07-29 00:33:12 UTC (rev 220036)
@@ -36,6 +36,7 @@
#include "CanvasRenderingContext.h"
#include "DocumentThreadableLoader.h"
#include "Element.h"
+#include "EventTarget.h"
#include "FormData.h"
#include "Frame.h"
#include "HTMLCanvasElement.h"
@@ -43,7 +44,6 @@
#include "InspectorController.h"
#include "InspectorInstrumentationCookie.h"
#include "Page.h"
-#include "ScriptExecutionContext.h"
#include "StorageArea.h"
#include "WorkerGlobalScope.h"
#include "WorkerInspectorController.h"
@@ -65,6 +65,7 @@
class Database;
class Document;
class DocumentLoader;
+class EventListener;
class HTTPHeaderMap;
class InspectorTimelineAgent;
class InstrumentingAgents;
@@ -71,11 +72,13 @@
class NetworkLoadMetrics;
class Node;
class PseudoElement;
+class RegisteredEventListener;
class RenderLayer;
class RenderObject;
class ResourceLoader;
class ResourceRequest;
class ResourceResponse;
+class ScriptExecutionContext;
class SecurityOrigin;
class ShadowRoot;
class URL;
@@ -129,9 +132,12 @@
static InspectorInstrumentationCookie willCallFunction(ScriptExecutionContext*, const String& scriptName, int scriptLine);
static void didCallFunction(const InspectorInstrumentationCookie&, ScriptExecutionContext*);
+ static void didAddEventListener(EventTarget&, const AtomicString& eventType);
+ static void willRemoveEventListener(EventTarget&, const AtomicString& eventType, EventListener&, bool capture);
static InspectorInstrumentationCookie willDispatchEvent(Document&, const Event&, bool hasEventListeners);
static void didDispatchEvent(const InspectorInstrumentationCookie&);
- static void willHandleEvent(ScriptExecutionContext*, const Event&);
+ static void willHandleEvent(ScriptExecutionContext&, const Event&, const RegisteredEventListener&);
+ static void didHandleEvent(ScriptExecutionContext&);
static InspectorInstrumentationCookie willDispatchEventOnWindow(Frame*, const Event&, DOMWindow&);
static void didDispatchEventOnWindow(const InspectorInstrumentationCookie&);
static InspectorInstrumentationCookie willEvaluateScript(Frame&, const String& url, int lineNumber);
@@ -289,8 +295,11 @@
static InspectorInstrumentationCookie willCallFunctionImpl(InstrumentingAgents&, const String& scriptName, int scriptLine, ScriptExecutionContext*);
static void didCallFunctionImpl(const InspectorInstrumentationCookie&, ScriptExecutionContext*);
+ static void didAddEventListenerImpl(InstrumentingAgents&, EventTarget&, const AtomicString& eventType);
+ static void willRemoveEventListenerImpl(InstrumentingAgents&, EventTarget&, const AtomicString& eventType, EventListener&, bool capture);
static InspectorInstrumentationCookie willDispatchEventImpl(InstrumentingAgents&, Document&, const Event&, bool hasEventListeners);
- static void willHandleEventImpl(InstrumentingAgents&, const Event&);
+ static void willHandleEventImpl(InstrumentingAgents&, const Event&, const RegisteredEventListener&);
+ static void didHandleEventImpl(InstrumentingAgents&);
static void didDispatchEventImpl(const InspectorInstrumentationCookie&);
static InspectorInstrumentationCookie willDispatchEventOnWindowImpl(InstrumentingAgents&, const Event&, DOMWindow&);
static void didDispatchEventOnWindowImpl(const InspectorInstrumentationCookie&);
@@ -635,6 +644,20 @@
didRemoveTimerImpl(*instrumentingAgents, timerId, context);
}
+inline void InspectorInstrumentation::didAddEventListener(EventTarget& target, const AtomicString& eventType)
+{
+ FAST_RETURN_IF_NO_FRONTENDS(void());
+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForContext(target.scriptExecutionContext()))
+ didAddEventListenerImpl(*instrumentingAgents, target, eventType);
+}
+
+inline void InspectorInstrumentation::willRemoveEventListener(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
+{
+ FAST_RETURN_IF_NO_FRONTENDS(void());
+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForContext(target.scriptExecutionContext()))
+ willRemoveEventListenerImpl(*instrumentingAgents, target, eventType, listener, capture);
+}
+
inline InspectorInstrumentationCookie InspectorInstrumentation::willCallFunction(ScriptExecutionContext* context, const String& scriptName, int scriptLine)
{
FAST_RETURN_IF_NO_FRONTENDS(InspectorInstrumentationCookie());
@@ -665,13 +688,20 @@
didDispatchEventImpl(cookie);
}
-inline void InspectorInstrumentation::willHandleEvent(ScriptExecutionContext* context, const Event& event)
+inline void InspectorInstrumentation::willHandleEvent(ScriptExecutionContext& context, const Event& event, const RegisteredEventListener& listener)
{
FAST_RETURN_IF_NO_FRONTENDS(void());
if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForContext(context))
- return willHandleEventImpl(*instrumentingAgents, event);
+ return willHandleEventImpl(*instrumentingAgents, event, listener);
}
+inline void InspectorInstrumentation::didHandleEvent(ScriptExecutionContext& context)
+{
+ FAST_RETURN_IF_NO_FRONTENDS(void());
+ if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForContext(context))
+ return didHandleEventImpl(*instrumentingAgents);
+}
+
inline InspectorInstrumentationCookie InspectorInstrumentation::willDispatchEventOnWindow(Frame* frame, const Event& event, DOMWindow& window)
{
FAST_RETURN_IF_NO_FRONTENDS(InspectorInstrumentationCookie());
Modified: trunk/Source/WebCore/inspector/PageDebuggerAgent.cpp (220035 => 220036)
--- trunk/Source/WebCore/inspector/PageDebuggerAgent.cpp 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/WebCore/inspector/PageDebuggerAgent.cpp 2017-07-29 00:33:12 UTC (rev 220036)
@@ -34,6 +34,8 @@
#include "CachedResource.h"
#include "Document.h"
+#include "EventListener.h"
+#include "EventTarget.h"
#include "InspectorOverlay.h"
#include "InspectorPageAgent.h"
#include "InstrumentingAgents.h"
@@ -41,6 +43,7 @@
#include "Page.h"
#include "PageConsoleClient.h"
#include "PageScriptDebugServer.h"
+#include "ScriptExecutionContext.h"
#include "ScriptState.h"
#include <inspector/InjectedScript.h>
#include <inspector/InjectedScriptManager.h>
@@ -93,6 +96,12 @@
return InspectorDebuggerAgent::sourceMapURLForScript(script);
}
+void PageDebuggerAgent::didClearAsyncStackTraceData()
+{
+ m_registeredEventListeners.clear();
+ m_nextEventListenerIdentifier = 1;
+}
+
void PageDebuggerAgent::muteConsole()
{
PageConsoleClient::mute();
@@ -151,6 +160,51 @@
setSuppressAllPauses(false);
}
+void PageDebuggerAgent::didAddEventListener(EventTarget& target, const AtomicString& eventType)
+{
+ if (!breakpointsActive())
+ return;
+
+ auto& eventListeners = target.eventListeners(eventType);
+ const RefPtr<RegisteredEventListener>& listener = eventListeners.last();
+ if (m_registeredEventListeners.contains(listener.get())) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ JSC::ExecState* scriptState = target.scriptExecutionContext()->execState();
+ if (!scriptState)
+ return;
+
+ int identifier = m_nextEventListenerIdentifier++;
+ m_registeredEventListeners.set(listener.get(), identifier);
+
+ didScheduleAsyncCall(scriptState, InspectorDebuggerAgent::AsyncCallType::EventListener, identifier, listener->isOnce());
+}
+
+void PageDebuggerAgent::willRemoveEventListener(EventTarget& target, const AtomicString& eventType, EventListener& listener, bool capture)
+{
+ auto& eventListeners = target.eventListeners(eventType);
+ size_t listenerIndex = eventListeners.findMatching([&](auto& registeredListener) {
+ return ®isteredListener->callback() == &listener && registeredListener->useCapture() == capture;
+ });
+
+ if (listenerIndex == notFound)
+ return;
+
+ int identifier = m_registeredEventListeners.take(eventListeners[listenerIndex].get());
+ didCancelAsyncCall(InspectorDebuggerAgent::AsyncCallType::EventListener, identifier);
+}
+
+void PageDebuggerAgent::willHandleEvent(const RegisteredEventListener& listener)
+{
+ auto it = m_registeredEventListeners.find(&listener);
+ if (it == m_registeredEventListeners.end())
+ return;
+
+ willDispatchAsyncCall(InspectorDebuggerAgent::AsyncCallType::EventListener, it->value);
+}
+
void PageDebuggerAgent::didRequestAnimationFrame(int callbackId, Document& document)
{
if (!breakpointsActive())
Modified: trunk/Source/WebCore/inspector/PageDebuggerAgent.h (220035 => 220036)
--- trunk/Source/WebCore/inspector/PageDebuggerAgent.h 2017-07-29 00:25:49 UTC (rev 220035)
+++ trunk/Source/WebCore/inspector/PageDebuggerAgent.h 2017-07-29 00:33:12 UTC (rev 220036)
@@ -36,9 +36,12 @@
namespace WebCore {
class Document;
+class EventListener;
+class EventTarget;
class InspectorOverlay;
class InspectorPageAgent;
class Page;
+class RegisteredEventListener;
class PageDebuggerAgent final : public WebDebuggerAgent {
WTF_MAKE_NONCOPYABLE(PageDebuggerAgent);
@@ -57,6 +60,10 @@
void willFireAnimationFrame(int callbackId);
void didCancelAnimationFrame(int callbackId);
+ void didAddEventListener(EventTarget&, const AtomicString& eventType);
+ void willRemoveEventListener(EventTarget&, const AtomicString& eventType, EventListener&, bool capture);
+ void willHandleEvent(const RegisteredEventListener&);
+
protected:
void enable() override;
void disable(bool isBeingDestroyed) override;
@@ -63,6 +70,8 @@
String sourceMapURLForScript(const Script&) override;
+ void didClearAsyncStackTraceData() override;
+
private:
void muteConsole() override;
void unmuteConsole() override;
@@ -76,6 +85,9 @@
InspectorPageAgent* m_pageAgent;
InspectorOverlay* m_overlay { nullptr };
+
+ HashMap<const RegisteredEventListener*, int> m_registeredEventListeners;
+ int m_nextEventListenerIdentifier { 1 };
};
} // namespace WebCore