Web Inspector: Debugger: add a global breakpoint for pausing in the next microtask
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 20 Aug 2019 06:58:15 +0000 (06:58 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 20 Aug 2019 06:58:15 +0000 (06:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=200652

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

* inspector/protocol/Debugger.json:
Add `setPauseOnMicrotasks` command.

* inspector/agents/InspectorDebuggerAgent.h:
* inspector/agents/InspectorDebuggerAgent.cpp:
(Inspector::InspectorDebuggerAgent::disable):
(Inspector::InspectorDebuggerAgent::setPauseOnMicrotasks): Added.
(Inspector::InspectorDebuggerAgent::willRunMicrotask): Added.
(Inspector::InspectorDebuggerAgent::didRunMicrotask): Added.

* debugger/Debugger.h:
(JSC::Debugger::willRunMicrotask): Added.
(JSC::Debugger::didRunMicrotask): Added.
* inspector/ScriptDebugListener.h:
* inspector/ScriptDebugServer.h:
* inspector/ScriptDebugServer.cpp:
(Inspector::ScriptDebugServer::evaluateBreakpointAction):
(Inspector::ScriptDebugServer::sourceParsed):
(Inspector::ScriptDebugServer::willRunMicrotask): Added.
(Inspector::ScriptDebugServer::didRunMicrotask): Added.
(Inspector::ScriptDebugServer::canDispatchFunctionToListeners const): ADded.
(Inspector::ScriptDebugServer::dispatchFunctionToListeners): ADded.
(Inspector::ScriptDebugServer::handlePause):
(Inspector::ScriptDebugServer::dispatchDidPause): Deleted.
(Inspector::ScriptDebugServer::dispatchBreakpointActionLog): Deleted.
(Inspector::ScriptDebugServer::dispatchBreakpointActionSound): Deleted.
(Inspector::ScriptDebugServer::dispatchBreakpointActionProbe): Deleted.
(Inspector::ScriptDebugServer::dispatchDidContinue): Deleted.
(Inspector::ScriptDebugServer::dispatchDidParseSource): Deleted.
(Inspector::ScriptDebugServer::dispatchFailedToParseSource): Deleted.
Unify the various `dispatch*` functions to use lambdas so state management is centralized.

* runtime/JSMicrotask.cpp:
(JSC::JSMicrotask::run):

* inspector/agents/JSGlobalObjectDebuggerAgent.h:

Source/WebCore:

Test: inspector/debugger/setPauseOnMicrotasks.html

* inspector/agents/page/PageDebuggerAgent.h:
* inspector/agents/worker/WorkerDebuggerAgent.h:

* inspector/agents/InspectorTimelineAgent.h:
(WebCore::InspectorTimelineAgent::willRunMicrotask): Added.
(WebCore::InspectorTimelineAgent::didRunMicrotask): Added.

Source/WebInspectorUI:

* UserInterface/Controllers/DebuggerManager.js:
(WI.DebuggerManager):
(WI.DebuggerManager.prototype.initializeTarget):
(WI.DebuggerManager.prototype.get allMicrotasksBreakpoint): ADded.
(WI.DebuggerManager.prototype.isBreakpointSpecial):
(WI.DebuggerManager.prototype._pauseReasonFromPayload):
(WI.DebuggerManager.prototype._breakpointDisabledStateDidChange):

* UserInterface/Views/DebuggerSidebarPanel.js:
(WI.DebuggerSidebarPanel):
(WI.DebuggerSidebarPanel.prototype.saveStateToCookie):
(WI.DebuggerSidebarPanel.prototype.restoreStateFromCookie):
(WI.DebuggerSidebarPanel.prototype._addBreakpoint):
(WI.DebuggerSidebarPanel.prototype._addTreeElement):
(WI.DebuggerSidebarPanel.prototype._updatePauseReasonSection):
(WI.DebuggerSidebarPanel.prototype._handleBreakpointElementAddedOrRemoved):
(WI.DebuggerSidebarPanel.prototype._populateCreateBreakpointContextMenu):
* UserInterface/Views/SourcesNavigationSidebarPanel.js:
(WI.SourcesNavigationSidebarPanel):
(WI.SourcesNavigationSidebarPanel.prototype._insertDebuggerTreeElement):
(WI.SourcesNavigationSidebarPanel.prototype._addBreakpoint):
(WI.SourcesNavigationSidebarPanel.prototype._updatePauseReasonSection):
(WI.SourcesNavigationSidebarPanel.prototype._handleBreakpointElementAddedOrRemoved):
(WI.SourcesNavigationSidebarPanel.prototype._populateCreateBreakpointContextMenu):

* UserInterface/Views/BreakpointTreeElement.css:
(.breakpoint-microtask-icon .icon): Added.
* UserInterface/Images/Microtask.svg: Added.

* UserInterface/Base/Setting.js:
* Localizations/en.lproj/localizedStrings.js:

LayoutTests:

* inspector/debugger/setPauseOnMicrotasks.html: Added.
* inspector/debugger/setPauseOnMicrotasks-expected.txt: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@248894 268f45cc-cd09-0410-ab3c-d52691b4dbfc

25 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/debugger/setPauseOnMicrotasks-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/setPauseOnMicrotasks.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/debugger/Debugger.h
Source/JavaScriptCore/inspector/ScriptDebugListener.h
Source/JavaScriptCore/inspector/ScriptDebugServer.cpp
Source/JavaScriptCore/inspector/ScriptDebugServer.h
Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp
Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.h
Source/JavaScriptCore/inspector/agents/JSGlobalObjectDebuggerAgent.h
Source/JavaScriptCore/inspector/protocol/Debugger.json
Source/JavaScriptCore/runtime/JSMicrotask.cpp
Source/WebCore/ChangeLog
Source/WebCore/inspector/agents/InspectorTimelineAgent.h
Source/WebCore/inspector/agents/page/PageDebuggerAgent.h
Source/WebCore/inspector/agents/worker/WorkerDebuggerAgent.h
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Base/Setting.js
Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js
Source/WebInspectorUI/UserInterface/Images/Microtask.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/BreakpointTreeElement.css
Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.js

index f87a9e8..47db242 100644 (file)
@@ -1,5 +1,15 @@
 2019-08-19  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: Debugger: add a global breakpoint for pausing in the next microtask
+        https://bugs.webkit.org/show_bug.cgi?id=200652
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/debugger/setPauseOnMicrotasks.html: Added.
+        * inspector/debugger/setPauseOnMicrotasks-expected.txt: Added.
+
+2019-08-19  Devin Rousso  <drousso@apple.com>
+
         Web Inspector: have more aggressive checks for dataURLs provided to `console.screenshot`
         https://bugs.webkit.org/show_bug.cgi?id=200747
 
diff --git a/LayoutTests/inspector/debugger/setPauseOnMicrotasks-expected.txt b/LayoutTests/inspector/debugger/setPauseOnMicrotasks-expected.txt
new file mode 100644 (file)
index 0000000..1d90ced
--- /dev/null
@@ -0,0 +1,22 @@
+Tests for Debugger.setPauseOnMicrotasks command.
+
+
+== Running test suite: Debugger.setPauseOnMicrotasks
+-- Running test case: Debugger.setPauseOnMicrotasks.Disabled.queueMicrotask
+PASS: Should not have paused.
+
+-- Running test case: Debugger.setPauseOnMicrotasks.Enabled.queueMicrotask
+PASS: Should have paused.
+
+-- Running test case: Debugger.setPauseOnMicrotasks.Disabled.Promise.resolve
+PASS: Should not have paused.
+
+-- Running test case: Debugger.setPauseOnMicrotasks.Enabled.Promise.resolve
+PASS: Should have paused.
+
+-- Running test case: Debugger.setPauseOnMicrotasks.Disabled.Promise.reject
+PASS: Should not have paused.
+
+-- Running test case: Debugger.setPauseOnMicrotasks.Enabled.Promise.reject
+PASS: Should have paused.
+
diff --git a/LayoutTests/inspector/debugger/setPauseOnMicrotasks.html b/LayoutTests/inspector/debugger/setPauseOnMicrotasks.html
new file mode 100644 (file)
index 0000000..b5da184
--- /dev/null
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("Debugger.setPauseOnMicrotasks");
+
+    const tests = [
+        {
+            name: "queueMicrotask",
+            expression: `queueMicrotask(() => {})`,
+        },
+        {
+            name: "Promise.resolve",
+            expression: `Promise.resolve().then(() => {})`,
+        },
+        {
+            name: "Promise.reject",
+            expression: `Promise.reject().catch(() => {})`,
+        },
+    ];
+
+    for (let {name, expression} of tests) {
+        suite.addTestCase({
+            name: "Debugger.setPauseOnMicrotasks.Disabled." + name,
+            description: "Do not pause on microtasks when disabled.",
+            test(resolve, reject) {
+                WI.debuggerManager.allMicrotasksBreakpoint.disabled = true;
+
+                let didPause = false;
+                let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+                    WI.debuggerManager.resume();
+                    didPause = true;
+                });
+
+                InspectorTest.evaluateInPage(expression, () => {
+                    InspectorTest.expectThat(!didPause, "Should not have paused.");
+                    WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+                    resolve();
+                });
+            }
+        });
+
+        suite.addTestCase({
+            name: "Debugger.setPauseOnMicrotasks.Enabled." + name,
+            description: "Do not pause on microtasks when disabled.",
+            test(resolve, reject) {
+                WI.debuggerManager.allMicrotasksBreakpoint.disabled = false;
+
+                let didPause = false;
+                let listener = WI.debuggerManager.singleFireEventListener(WI.DebuggerManager.Event.Paused, (event) => {
+                    WI.debuggerManager.resume();
+                    didPause = true;
+                });
+
+                InspectorTest.evaluateInPage(expression, () => {
+                    InspectorTest.expectThat(didPause, "Should have paused.");
+                    WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, listener);
+                    resolve();
+                });
+            }
+        });
+    }
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Tests for Debugger.setPauseOnMicrotasks command.</p>
+</body>
+</html>
index a011c16..5ca5def 100644 (file)
@@ -1,5 +1,49 @@
 2019-08-19  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: Debugger: add a global breakpoint for pausing in the next microtask
+        https://bugs.webkit.org/show_bug.cgi?id=200652
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/protocol/Debugger.json:
+        Add `setPauseOnMicrotasks` command.
+
+        * inspector/agents/InspectorDebuggerAgent.h:
+        * inspector/agents/InspectorDebuggerAgent.cpp:
+        (Inspector::InspectorDebuggerAgent::disable):
+        (Inspector::InspectorDebuggerAgent::setPauseOnMicrotasks): Added.
+        (Inspector::InspectorDebuggerAgent::willRunMicrotask): Added.
+        (Inspector::InspectorDebuggerAgent::didRunMicrotask): Added.
+
+        * debugger/Debugger.h:
+        (JSC::Debugger::willRunMicrotask): Added.
+        (JSC::Debugger::didRunMicrotask): Added.
+        * inspector/ScriptDebugListener.h:
+        * inspector/ScriptDebugServer.h:
+        * inspector/ScriptDebugServer.cpp:
+        (Inspector::ScriptDebugServer::evaluateBreakpointAction):
+        (Inspector::ScriptDebugServer::sourceParsed):
+        (Inspector::ScriptDebugServer::willRunMicrotask): Added.
+        (Inspector::ScriptDebugServer::didRunMicrotask): Added.
+        (Inspector::ScriptDebugServer::canDispatchFunctionToListeners const): ADded.
+        (Inspector::ScriptDebugServer::dispatchFunctionToListeners): ADded.
+        (Inspector::ScriptDebugServer::handlePause):
+        (Inspector::ScriptDebugServer::dispatchDidPause): Deleted.
+        (Inspector::ScriptDebugServer::dispatchBreakpointActionLog): Deleted.
+        (Inspector::ScriptDebugServer::dispatchBreakpointActionSound): Deleted.
+        (Inspector::ScriptDebugServer::dispatchBreakpointActionProbe): Deleted.
+        (Inspector::ScriptDebugServer::dispatchDidContinue): Deleted.
+        (Inspector::ScriptDebugServer::dispatchDidParseSource): Deleted.
+        (Inspector::ScriptDebugServer::dispatchFailedToParseSource): Deleted.
+        Unify the various `dispatch*` functions to use lambdas so state management is centralized.
+
+        * runtime/JSMicrotask.cpp:
+        (JSC::JSMicrotask::run):
+
+        * inspector/agents/JSGlobalObjectDebuggerAgent.h:
+
+2019-08-19  Devin Rousso  <drousso@apple.com>
+
         Web Inspector: Debugger: pause on assertion failures breakpoint doesn't work when inspecting a JSContext
         https://bugs.webkit.org/show_bug.cgi?id=200874
 
index ebabb59..ab8bea8 100644 (file)
@@ -122,6 +122,8 @@ public:
     void setSuppressAllPauses(bool suppress) { m_suppressAllPauses = suppress; }
 
     virtual void sourceParsed(ExecState*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0;
+    virtual void willRunMicrotask() { }
+    virtual void didRunMicrotask() { }
 
     void exception(CallFrame*, JSValue exceptionValue, bool hasCatchHandler);
     void atStatement(CallFrame*);
index 7702a89..56b86cb 100644 (file)
@@ -56,6 +56,10 @@ public:
 
     virtual void didParseSource(JSC::SourceID, const Script&) = 0;
     virtual void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) = 0;
+
+    virtual void willRunMicrotask() = 0;
+    virtual void didRunMicrotask() = 0;
+
     virtual void didPause(JSC::ExecState&, JSC::JSValue callFrames, JSC::JSValue exception) = 0;
     virtual void didContinue() = 0;
 
index e0a065c..a54f77d 100644 (file)
@@ -91,10 +91,12 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
     DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame();
 
     switch (breakpointAction.type) {
-    case ScriptBreakpointActionTypeLog: {
-        dispatchBreakpointActionLog(debuggerCallFrame.globalExec(), breakpointAction.data);
+    case ScriptBreakpointActionTypeLog:
+        dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+            listener.breakpointActionLog(*debuggerCallFrame.globalExec(), breakpointAction.data);
+        });
         break;
-    }
+
     case ScriptBreakpointActionTypeEvaluate: {
         NakedPtr<Exception> exception;
         JSObject* scopeExtensionObject = nullptr;
@@ -103,9 +105,13 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
             reportException(debuggerCallFrame.globalExec(), exception);
         break;
     }
+
     case ScriptBreakpointActionTypeSound:
-        dispatchBreakpointActionSound(debuggerCallFrame.globalExec(), breakpointAction.identifier);
+        dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+            listener.breakpointActionSound(breakpointAction.identifier);
+        });
         break;
+
     case ScriptBreakpointActionTypeProbe: {
         NakedPtr<Exception> exception;
         JSObject* scopeExtensionObject = nullptr;
@@ -114,9 +120,12 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
         if (exception)
             reportException(exec, exception);
 
-        dispatchBreakpointActionProbe(exec, breakpointAction, exception ? exception->value() : result);
+        dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+            listener.breakpointActionProbe(*exec, breakpointAction, m_currentProbeBatchId, m_nextProbeSampleId++, exception ? exception->value() : result);
+        });
         break;
     }
+
     default:
         ASSERT_NOT_REACHED();
     }
@@ -124,67 +133,22 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
     return true;
 }
 
-void ScriptDebugServer::dispatchDidPause(ScriptDebugListener* listener)
-{
-    ASSERT(isPaused());
-    DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame();
-    JSGlobalObject* globalObject = debuggerCallFrame.scope()->globalObject();
-    JSC::ExecState& state = *globalObject->globalExec();
-    JSValue jsCallFrame = toJS(&state, globalObject, JavaScriptCallFrame::create(debuggerCallFrame).ptr());
-    listener->didPause(state, jsCallFrame, exceptionOrCaughtValue(&state));
-}
-
-void ScriptDebugServer::dispatchBreakpointActionLog(ExecState* exec, const String& message)
-{
-    if (m_callingListeners)
-        return;
-
-    if (m_listeners.isEmpty())
-        return;
-
-    SetForScope<bool> change(m_callingListeners, true);
-
-    for (auto* listener : copyToVector(m_listeners))
-        listener->breakpointActionLog(*exec, message);
-}
-
-void ScriptDebugServer::dispatchBreakpointActionSound(ExecState*, int breakpointActionIdentifier)
-{
-    if (m_callingListeners)
-        return;
-
-    if (m_listeners.isEmpty())
-        return;
-
-    SetForScope<bool> change(m_callingListeners, true);
-
-    for (auto* listener : copyToVector(m_listeners))
-        listener->breakpointActionSound(breakpointActionIdentifier);
-}
-
-void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState* exec, const ScriptBreakpointAction& action, JSC::JSValue sampleValue)
+void ScriptDebugServer::sourceParsed(ExecState* exec, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
 {
-    if (m_callingListeners)
+    // Preemptively check whether we can dispatch so that we don't do any unnecessary allocations.
+    if (!canDispatchFunctionToListeners())
         return;
 
-    if (m_listeners.isEmpty())
+    if (errorLine != -1) {
+        auto url = sourceProvider->url();
+        auto data = sourceProvider->source().toString();
+        auto firstLine = sourceProvider->startPosition().m_line.oneBasedInt();
+        dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+            listener.failedToParseSource(url, data, firstLine, errorLine, errorMessage);
+        });
         return;
+    }
 
-    SetForScope<bool> change(m_callingListeners, true);
-
-    unsigned sampleId = m_nextProbeSampleId++;
-
-    for (auto* listener : copyToVector(m_listeners))
-        listener->breakpointActionProbe(*exec, action, m_currentProbeBatchId, sampleId, sampleValue);
-}
-
-void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener* listener)
-{
-    listener->didContinue();
-}
-
-void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, SourceProvider* sourceProvider, bool isContentScript)
-{
     JSC::SourceID sourceID = sourceProvider->asID();
 
     // FIXME: <https://webkit.org/b/162773> Web Inspector: Simplify ScriptDebugListener::Script to use SourceProvider
@@ -194,7 +158,7 @@ void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, Sou
     script.source = sourceProvider->source().toString();
     script.startLine = sourceProvider->startPosition().m_line.zeroBasedInt();
     script.startColumn = sourceProvider->startPosition().m_column.zeroBasedInt();
-    script.isContentScript = isContentScript;
+    script.isContentScript = isContentScript(exec);
     script.sourceURL = sourceProvider->sourceURLDirective();
     script.sourceMappingURL = sourceProvider->sourceMappingURLDirective();
 
@@ -214,54 +178,43 @@ void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, Sou
     else
         script.endColumn = sourceLength - lastLineStart;
 
-    for (auto* listener : copyToVector(listeners))
-        listener->didParseSource(sourceID, script);
+    dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+        listener.didParseSource(sourceID, script);
+    });
 }
 
-void ScriptDebugServer::dispatchFailedToParseSource(const ListenerSet& listeners, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
+void ScriptDebugServer::willRunMicrotask()
 {
-    String url = sourceProvider->url();
-    String data = sourceProvider->source().toString();
-    int firstLine = sourceProvider->startPosition().m_line.oneBasedInt();
+    dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+        listener.willRunMicrotask();
+    });
+}
 
-    for (auto* listener : copyToVector(listeners))
-        listener->failedToParseSource(url, data, firstLine, errorLine, errorMessage);
+void ScriptDebugServer::didRunMicrotask()
+{
+    dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+        listener.didRunMicrotask();
+    });
 }
 
-void ScriptDebugServer::sourceParsed(ExecState* exec, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
+bool ScriptDebugServer::canDispatchFunctionToListeners() const
 {
     if (m_callingListeners)
-        return;
-
+        return false;
     if (m_listeners.isEmpty())
-        return;
-
-    SetForScope<bool> change(m_callingListeners, true);
-
-    bool isError = errorLine != -1;
-    if (isError)
-        dispatchFailedToParseSource(m_listeners, sourceProvider, errorLine, errorMessage);
-    else
-        dispatchDidParseSource(m_listeners, sourceProvider, isContentScript(exec));
+        return false;
+    return true;
 }
 
-void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback)
+void ScriptDebugServer::dispatchFunctionToListeners(Function<void(ScriptDebugListener&)> callback)
 {
-    if (m_callingListeners)
-        return;
-
-    if (m_listeners.isEmpty())
+    if (!canDispatchFunctionToListeners())
         return;
 
     SetForScope<bool> change(m_callingListeners, true);
 
-    dispatchFunctionToListeners(m_listeners, callback);
-}
-
-void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptExecutionCallback callback)
-{
-    for (auto* listener : copyToVector(listeners))
-        (this->*callback)(listener);
+    for (auto* listener : copyToVector(m_listeners))
+        callback(*listener);
 }
 
 void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
@@ -294,14 +247,25 @@ void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState* exe
 
 void ScriptDebugServer::handlePause(JSGlobalObject* vmEntryGlobalObject, Debugger::ReasonForPause)
 {
-    dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause);
+    dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+        ASSERT(isPaused());
+        auto& debuggerCallFrame = currentDebuggerCallFrame();
+        auto* globalObject = debuggerCallFrame.scope()->globalObject();
+        auto& state = *globalObject->globalExec();
+        auto jsCallFrame = toJS(&state, globalObject, JavaScriptCallFrame::create(debuggerCallFrame).ptr());
+        listener.didPause(state, jsCallFrame, exceptionOrCaughtValue(&state));
+    });
+
     didPause(vmEntryGlobalObject);
 
     m_doneProcessingDebuggerEvents = false;
     runEventLoopWhilePaused();
 
     didContinue(vmEntryGlobalObject);
-    dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue);
+
+    dispatchFunctionToListeners([&] (ScriptDebugListener& listener) {
+        listener.didContinue();
+    });
 }
 
 void ScriptDebugServer::addListener(ScriptDebugListener* listener)
index 6484bd6..1eeb4f8 100644 (file)
@@ -32,6 +32,7 @@
 #include "Debugger.h"
 #include "ScriptBreakpoint.h"
 #include "ScriptDebugListener.h"
+#include <wtf/Function.h>
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/text/WTFString.h>
@@ -60,9 +61,6 @@ public:
     void removeListener(ScriptDebugListener*, bool isBeingDestroyed);
 
 protected:
-    typedef HashSet<ScriptDebugListener*> ListenerSet;
-    typedef void (ScriptDebugServer::*JavaScriptExecutionCallback)(ScriptDebugListener*);
-
     ScriptDebugServer(JSC::VM&);
     ~ScriptDebugServer();
 
@@ -77,15 +75,8 @@ protected:
 
     bool evaluateBreakpointAction(const ScriptBreakpointAction&);
 
-    void dispatchFunctionToListeners(JavaScriptExecutionCallback);
-    void dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptExecutionCallback);
-    void dispatchDidPause(ScriptDebugListener*);
-    void dispatchDidContinue(ScriptDebugListener*);
-    void dispatchDidParseSource(const ListenerSet& listeners, JSC::SourceProvider*, bool isContentScript);
-    void dispatchFailedToParseSource(const ListenerSet& listeners, JSC::SourceProvider*, int errorLine, const String& errorMessage);
-    void dispatchBreakpointActionLog(JSC::ExecState*, const String&);
-    void dispatchBreakpointActionSound(JSC::ExecState*, int breakpointActionIdentifier);
-    void dispatchBreakpointActionProbe(JSC::ExecState*, const ScriptBreakpointAction&, JSC::JSValue sample);
+    bool canDispatchFunctionToListeners() const;
+    void dispatchFunctionToListeners(Function<void(ScriptDebugListener&)> callback);
 
     bool m_doneProcessingDebuggerEvents { true };
 
@@ -93,6 +84,8 @@ private:
     typedef HashMap<JSC::BreakpointID, BreakpointActions> BreakpointIDToActionsMap;
 
     void sourceParsed(JSC::ExecState*, JSC::SourceProvider*, int errorLine, const String& errorMsg) final;
+    void willRunMicrotask() final;
+    void didRunMicrotask() final;
     void handleBreakpointHit(JSC::JSGlobalObject*, const JSC::Breakpoint&) final;
     void handleExceptionInBreakpointCondition(JSC::ExecState*, JSC::Exception*) const final;
     void handlePause(JSC::JSGlobalObject*, JSC::Debugger::ReasonForPause) final;
@@ -101,7 +94,7 @@ private:
     JSC::JSValue exceptionOrCaughtValue(JSC::ExecState*);
 
     BreakpointIDToActionsMap m_breakpointIDToActions;
-    ListenerSet m_listeners;
+    HashSet<ScriptDebugListener*> m_listeners;
     bool m_callingListeners { false };
     unsigned m_nextProbeSampleId { 1 };
     unsigned m_currentProbeBatchId { 0 };
index 25f1d3e..58913c6 100644 (file)
@@ -118,6 +118,7 @@ void InspectorDebuggerAgent::disable(bool isBeingDestroyed)
     clearAsyncStackTraceData();
 
     m_pauseOnAssertionFailures = false;
+    m_pauseOnMicrotasks = false;
 
     m_enabled = false;
 }
@@ -826,6 +827,11 @@ void InspectorDebuggerAgent::setPauseOnAssertions(ErrorString&, bool enabled)
     m_pauseOnAssertionFailures = enabled;
 }
 
+void InspectorDebuggerAgent::setPauseOnMicrotasks(ErrorString&, bool enabled)
+{
+    m_pauseOnMicrotasks = enabled;
+}
+
 void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString& errorString, const String& callFrameId, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, const bool* /* emulateUserGesture */, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex)
 {
     if (!m_currentCallStack) {
@@ -962,6 +968,24 @@ void InspectorDebuggerAgent::failedToParseSource(const String& url, const String
     m_frontendDispatcher->scriptFailedToParse(url, data, firstLine, errorLine, errorMessage);
 }
 
+void InspectorDebuggerAgent::willRunMicrotask()
+{
+    if (!m_scriptDebugServer.breakpointsActive())
+        return;
+
+    if (m_pauseOnMicrotasks)
+        schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason::Microtask, nullptr);
+}
+
+void InspectorDebuggerAgent::didRunMicrotask()
+{
+    if (!m_scriptDebugServer.breakpointsActive())
+        return;
+
+    if (m_pauseOnMicrotasks)
+        cancelPauseOnNextStatement();
+}
+
 void InspectorDebuggerAgent::didPause(JSC::ExecState& scriptState, JSC::JSValue callFrames, JSC::JSValue exceptionOrCaughtValue)
 {
     ASSERT(!m_pausedScriptState);
index 042168b..23a8da0 100644 (file)
@@ -63,7 +63,6 @@ public:
     // DebuggerBackendDispatcherHandler
     void enable(ErrorString&) final;
     void disable(ErrorString&) final;
-    void setPauseForInternalScripts(ErrorString&, bool shouldPause) final;
     void setAsyncStackTraceDepth(ErrorString&, int depth) final;
     void setBreakpointsActive(ErrorString&, bool active) final;
     void setBreakpointByUrl(ErrorString&, int lineNumber, const String* optionalURL, const String* optionalURLRegex, const int* optionalColumnNumber, const JSON::Object* options, Protocol::Debugger::BreakpointId*, RefPtr<JSON::ArrayOf<Protocol::Debugger::Location>>& locations) final;
@@ -71,16 +70,18 @@ public:
     void removeBreakpoint(ErrorString&, const String& breakpointIdentifier) final;
     void continueUntilNextRunLoop(ErrorString&) final;
     void continueToLocation(ErrorString&, const JSON::Object& location) final;
-    void searchInContent(ErrorString&, const String& scriptID, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<JSON::ArrayOf<Protocol::GenericTypes::SearchMatch>>&) final;
-    void getScriptSource(ErrorString&, const String& scriptID, String* scriptSource) final;
-    void getFunctionDetails(ErrorString&, const String& functionId, RefPtr<Protocol::Debugger::FunctionDetails>&) final;
-    void pause(ErrorString&) final;
-    void resume(ErrorString&) final;
     void stepOver(ErrorString&) final;
     void stepInto(ErrorString&) final;
     void stepOut(ErrorString&) final;
+    void pause(ErrorString&) final;
+    void resume(ErrorString&) final;
+    void searchInContent(ErrorString&, const String& scriptID, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<JSON::ArrayOf<Protocol::GenericTypes::SearchMatch>>&) final;
+    void getScriptSource(ErrorString&, const String& scriptID, String* scriptSource) final;
+    void getFunctionDetails(ErrorString&, const String& functionId, RefPtr<Protocol::Debugger::FunctionDetails>&) final;
     void setPauseOnExceptions(ErrorString&, const String& pauseState) final;
     void setPauseOnAssertions(ErrorString&, bool enabled) final;
+    void setPauseOnMicrotasks(ErrorString&, bool enabled) final;
+    void setPauseForInternalScripts(ErrorString&, bool shouldPause) final;
     void evaluateOnCallFrame(ErrorString&, const String& callFrameId, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, const bool* emulateUserGesture, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex) override;
 
     bool isPaused() const;
@@ -131,8 +132,6 @@ protected:
 
     virtual void enable();
     virtual void disable(bool skipRecompile);
-    void didPause(JSC::ExecState&, JSC::JSValue callFrames, JSC::JSValue exceptionOrCaughtValue) final;
-    void didContinue() final;
 
     virtual String sourceMapURLForScript(const Script&);
 
@@ -142,9 +141,13 @@ protected:
 private:
     Ref<JSON::ArrayOf<Protocol::Debugger::CallFrame>> currentCallFrames(const InjectedScript&);
 
+    // JSC::ScriptDebugListener
     void didParseSource(JSC::SourceID, const Script&) final;
     void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) final;
-
+    void willRunMicrotask() final;
+    void didRunMicrotask() final;
+    void didPause(JSC::ExecState&, JSC::JSValue callFrames, JSC::JSValue exceptionOrCaughtValue) final;
+    void didContinue() final;
     void breakpointActionSound(int breakpointActionIdentifier) final;
     void breakpointActionProbe(JSC::ExecState&, const ScriptBreakpointAction&, unsigned batchId, unsigned sampleId, JSC::JSValue sample) final;
 
@@ -199,6 +202,7 @@ private:
     bool m_enabled { false };
     bool m_enablePauseWhenIdle { false };
     bool m_pauseOnAssertionFailures { false };
+    bool m_pauseOnMicrotasks { false };
     bool m_pauseForInternalScripts { false };
     bool m_javaScriptPauseScheduled { false };
     bool m_didPauseStopwatch { false };
index 03dbc5a..e2f39db 100644 (file)
@@ -38,8 +38,10 @@ public:
     JSGlobalObjectDebuggerAgent(JSAgentContext&, InspectorConsoleAgent*);
     virtual ~JSGlobalObjectDebuggerAgent() { }
 
+private:
     InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) override;
 
+    // JSC::ScriptDebugListener
     void breakpointActionLog(JSC::ExecState&, const String&) final;
 
     // NOTE: JavaScript inspector does not yet need to mute a console because no messages
@@ -47,7 +49,6 @@ public:
     void muteConsole() final { }
     void unmuteConsole() final { }
 
-private:
     InspectorConsoleAgent* m_consoleAgent { nullptr };
 };
 
index 0f0bb9a..637076a 100644 (file)
             ]
         },
         {
+            "name": "setPauseOnMicrotasks",
+            "description": "Pause when running the next JavaScript microtask.",
+            "parameters": [
+                { "name": "enabled", "type": "boolean" }
+            ]
+        },
+        {
             "name": "setPauseForInternalScripts",
             "description": "Change whether to pause in the debugger for internal scripts. The default value is false.",
             "parameters": [
             "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria.",
             "parameters": [
                 { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." },
-                { "name": "reason", "type": "string", "enum": ["XHR", "Fetch", "DOM", "AnimationFrame", "Interval", "Listener", "Timeout", "exception", "assert", "CSPViolation", "DebuggerStatement", "Breakpoint", "PauseOnNextStatement", "other"], "description": "Pause reason." },
+                { "name": "reason", "type": "string", "enum": ["XHR", "Fetch", "DOM", "AnimationFrame", "Interval", "Listener", "Timeout", "exception", "assert", "CSPViolation", "DebuggerStatement", "Breakpoint", "PauseOnNextStatement", "Microtask", "other"], "description": "Pause reason." },
                 { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." },
                 { "name": "asyncStackTrace", "$ref": "Console.StackTrace", "optional": true, "description": "Linked list of asynchronous StackTraces." }
             ]
index b08ed30..a4a2e1c 100644 (file)
@@ -27,6 +27,7 @@
 #include "JSMicrotask.h"
 
 #include "CatchScope.h"
+#include "Debugger.h"
 #include "Error.h"
 #include "Exception.h"
 #include "JSCInlines.h"
@@ -86,6 +87,10 @@ void JSMicrotask::run(ExecState* exec)
         if (UNLIKELY(handlerArguments.hasOverflowed()))
             return;
     }
+
+    if (UNLIKELY(exec->lexicalGlobalObject()->hasDebugger()))
+        exec->lexicalGlobalObject()->debugger()->willRunMicrotask();
+
     profiledCall(exec, ProfilingReason::Microtask, m_job.get(), handlerCallType, handlerCallData, jsUndefined(), handlerArguments);
     scope.clearException();
 }
index 35a11b9..ae63860 100644 (file)
@@ -1,3 +1,19 @@
+2019-08-19  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Debugger: add a global breakpoint for pausing in the next microtask
+        https://bugs.webkit.org/show_bug.cgi?id=200652
+
+        Reviewed by Joseph Pecoraro.
+
+        Test: inspector/debugger/setPauseOnMicrotasks.html
+
+        * inspector/agents/page/PageDebuggerAgent.h:
+        * inspector/agents/worker/WorkerDebuggerAgent.h:
+
+        * inspector/agents/InspectorTimelineAgent.h:
+        (WebCore::InspectorTimelineAgent::willRunMicrotask): Added.
+        (WebCore::InspectorTimelineAgent::didRunMicrotask): Added.
+
 2019-08-19  Sam Weinig  <weinig@apple.com>
 
         [WHLSL] Make generated Metal code should be indented properly to ease reading while debugging
index f79be8b..9fdba4f 100644 (file)
@@ -144,12 +144,13 @@ public:
     void mainFrameNavigated();
 
 private:
-    // ScriptDebugListener
+    // JSC::ScriptDebugListener
     void didParseSource(JSC::SourceID, const Script&) final { }
     void failedToParseSource(const String&, const String&, int, int, const String&) final { }
+    void willRunMicrotask() final { }
+    void didRunMicrotask() final { }
     void didPause(JSC::ExecState&, JSC::JSValue, JSC::JSValue) final { }
     void didContinue() final { }
-
     void breakpointActionLog(JSC::ExecState&, const String&) final { }
     void breakpointActionSound(int) final { }
     void breakpointActionProbe(JSC::ExecState&, const Inspector::ScriptBreakpointAction&, unsigned batchId, unsigned sampleId, JSC::JSValue result) final;
index db5d4cd..25750fc 100644 (file)
@@ -80,6 +80,7 @@ private:
     void muteConsole() override;
     void unmuteConsole() override;
 
+    // JSC::ScriptDebugListener
     void breakpointActionLog(JSC::ExecState&, const String&) override;
 
     Inspector::InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) override;
index 5094e6c..e913147 100644 (file)
@@ -43,7 +43,9 @@ private:
     void muteConsole() override { }
     void unmuteConsole() override { }
 
+    // JSC::ScriptDebugListener
     void breakpointActionLog(JSC::ExecState&, const String&) override;
+
     Inspector::InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) override;
 
     WorkerGlobalScope& m_workerGlobalScope;
index b33e448..4948698 100644 (file)
@@ -1,5 +1,44 @@
 2019-08-19  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: Debugger: add a global breakpoint for pausing in the next microtask
+        https://bugs.webkit.org/show_bug.cgi?id=200652
+
+        Reviewed by Joseph Pecoraro.
+
+        * UserInterface/Controllers/DebuggerManager.js:
+        (WI.DebuggerManager):
+        (WI.DebuggerManager.prototype.initializeTarget):
+        (WI.DebuggerManager.prototype.get allMicrotasksBreakpoint): ADded.
+        (WI.DebuggerManager.prototype.isBreakpointSpecial):
+        (WI.DebuggerManager.prototype._pauseReasonFromPayload):
+        (WI.DebuggerManager.prototype._breakpointDisabledStateDidChange):
+
+        * UserInterface/Views/DebuggerSidebarPanel.js:
+        (WI.DebuggerSidebarPanel):
+        (WI.DebuggerSidebarPanel.prototype.saveStateToCookie):
+        (WI.DebuggerSidebarPanel.prototype.restoreStateFromCookie):
+        (WI.DebuggerSidebarPanel.prototype._addBreakpoint):
+        (WI.DebuggerSidebarPanel.prototype._addTreeElement):
+        (WI.DebuggerSidebarPanel.prototype._updatePauseReasonSection):
+        (WI.DebuggerSidebarPanel.prototype._handleBreakpointElementAddedOrRemoved):
+        (WI.DebuggerSidebarPanel.prototype._populateCreateBreakpointContextMenu):
+        * UserInterface/Views/SourcesNavigationSidebarPanel.js:
+        (WI.SourcesNavigationSidebarPanel):
+        (WI.SourcesNavigationSidebarPanel.prototype._insertDebuggerTreeElement):
+        (WI.SourcesNavigationSidebarPanel.prototype._addBreakpoint):
+        (WI.SourcesNavigationSidebarPanel.prototype._updatePauseReasonSection):
+        (WI.SourcesNavigationSidebarPanel.prototype._handleBreakpointElementAddedOrRemoved):
+        (WI.SourcesNavigationSidebarPanel.prototype._populateCreateBreakpointContextMenu):
+
+        * UserInterface/Views/BreakpointTreeElement.css:
+        (.breakpoint-microtask-icon .icon): Added.
+        * UserInterface/Images/Microtask.svg: Added.
+
+        * UserInterface/Base/Setting.js:
+        * Localizations/en.lproj/localizedStrings.js:
+
+2019-08-19  Devin Rousso  <drousso@apple.com>
+
         Web Inspector: REGRESSION(r248682): Elements: Computed: go-to arrows in the Variables section are misaligned
         https://bugs.webkit.org/show_bug.cgi?id=200841
 
index 3c2b681..fb8a81b 100644 (file)
@@ -105,6 +105,7 @@ localizedStrings["All Events"] = "All Events";
 localizedStrings["All Exceptions"] = "All Exceptions";
 localizedStrings["All Intervals"] = "All Intervals";
 localizedStrings["All Layers"] = "All Layers";
+localizedStrings["All Microtasks"] = "All Microtasks";
 /* A submenu item of 'Break on' that breaks (pauses) before all network requests */
 localizedStrings["All Requests"] = "All Requests";
 localizedStrings["All Resources"] = "All Resources";
@@ -691,6 +692,7 @@ localizedStrings["Memory: %s"] = "Memory: %s";
 localizedStrings["Message"] = "Message";
 localizedStrings["Method"] = "Method";
 localizedStrings["Microtask Dispatched"] = "Microtask Dispatched";
+localizedStrings["Microtask Fired"] = "Microtask Fired";
 localizedStrings["Missing result level"] = "Missing result level";
 localizedStrings["Mixed"] = "Mixed";
 localizedStrings["Module Code"] = "Module Code";
index b86a5a6..dc25a5a 100644 (file)
@@ -162,6 +162,7 @@ WI.settings = {
     showAllAnimationFramesBreakpoint: new WI.Setting("show-all-animation-frames-breakpoint", false),
     showAllIntervalsBreakpoint: new WI.Setting("show-all-inteverals-breakpoint", false),
     showAllListenersBreakpoint: new WI.Setting("show-all-listeners-breakpoint", false),
+    showAllMicrotasksBreakpoint: new WI.Setting("show-all-microtasks-breakpoint", false),
     showAllRequestsBreakpoint: new WI.Setting("show-all-requests-breakpoint", false),
     showAllTimeoutsBreakpoint: new WI.Setting("show-all-timeouts-breakpoint", false),
     showAssertionFailuresBreakpoint: new WI.Setting("show-assertion-failures-breakpoint", true),
index d93a8ee..81127c7 100644 (file)
@@ -51,28 +51,34 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
         WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
 
         this._breakpointsEnabledSetting = new WI.Setting("breakpoints-enabled", true);
-        this._allExceptionsBreakpointEnabledSetting = new WI.Setting("break-on-all-exceptions", false);
-        this._uncaughtExceptionsBreakpointEnabledSetting = new WI.Setting("break-on-uncaught-exceptions", false);
-        this._assertionFailuresBreakpointEnabledSetting = new WI.Setting("break-on-assertion-failures", false);
         this._asyncStackTraceDepthSetting = new WI.Setting("async-stack-trace-depth", 200);
 
-        let specialBreakpointLocation = new WI.SourceCodeLocation(null, Infinity, Infinity);
+        const specialBreakpointLocation = new WI.SourceCodeLocation(null, Infinity, Infinity);
 
+        this._allExceptionsBreakpointEnabledSetting = new WI.Setting("break-on-all-exceptions", false);
         this._allExceptionsBreakpoint = new WI.Breakpoint(specialBreakpointLocation, {
             disabled: !this._allExceptionsBreakpointEnabledSetting.value,
         });
         this._allExceptionsBreakpoint.resolved = true;
 
+        this._uncaughtExceptionsBreakpointEnabledSetting = new WI.Setting("break-on-uncaught-exceptions", false);
         this._uncaughtExceptionsBreakpoint = new WI.Breakpoint(specialBreakpointLocation, {
             disabled: !this._uncaughtExceptionsBreakpointEnabledSetting.value,
         });
         this._uncaughtExceptionsBreakpoint.resolved = true;
 
+        this._assertionFailuresBreakpointEnabledSetting = new WI.Setting("break-on-assertion-failures", false);
         this._assertionFailuresBreakpoint = new WI.Breakpoint(specialBreakpointLocation, {
             disabled: !this._assertionFailuresBreakpointEnabledSetting.value,
         });
         this._assertionFailuresBreakpoint.resolved = true;
 
+        this._allMicrotasksBreakpointEnabledSetting = new WI.Setting("break-on-all-microtasks", false);
+        this._allMicrotasksBreakpoint = new WI.Breakpoint(specialBreakpointLocation, {
+            disabled: !this._allMicrotasksBreakpointEnabledSetting.value,
+        });
+        this._allMicrotasksBreakpoint.resolved = true;
+
         this._breakpoints = [];
         this._breakpointContentIdentifierMap = new Multimap;
         this._breakpointScriptIdentifierMap = new Multimap;
@@ -141,7 +147,11 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
 
         // COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
         if (target.DebuggerAgent.setPauseOnAssertions)
-            target.DebuggerAgent.setPauseOnAssertions(this._assertionFailuresBreakpointEnabledSetting.value);
+            target.DebuggerAgent.setPauseOnAssertions(!this._assertionFailuresBreakpoint.disabled);
+
+        // COMPATIBILITY (iOS 13): DebuggerAgent.setPauseOnMicrotasks did not exist yet.
+        if (target.DebuggerAgent.setPauseOnMicrotasks)
+            target.DebuggerAgent.setPauseOnMicrotasks(!this._allMicrotasksBreakpoint.disabled);
 
         // COMPATIBILITY (iOS 10): Debugger.setAsyncStackTraceDepth did not exist yet.
         if (target.DebuggerAgent.setAsyncStackTraceDepth)
@@ -209,6 +219,7 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
     get allExceptionsBreakpoint() { return this._allExceptionsBreakpoint; }
     get uncaughtExceptionsBreakpoint() { return this._uncaughtExceptionsBreakpoint; }
     get assertionFailuresBreakpoint() { return this._assertionFailuresBreakpoint; }
+    get allMicrotasksBreakpoint() { return this._allMicrotasksBreakpoint; }
     get breakpoints() { return this._breakpoints; }
 
     breakpointForIdentifier(id)
@@ -261,7 +272,8 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
     isBreakpointSpecial(breakpoint)
     {
         return !this.isBreakpointRemovable(breakpoint)
-            || breakpoint === this._assertionFailuresBreakpoint;
+            || breakpoint === this._assertionFailuresBreakpoint
+            || breakpoint === this._allMicrotasksBreakpoint;
     }
 
     isBreakpointEditable(breakpoint)
@@ -857,6 +869,8 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
             return WI.DebuggerManager.PauseReason.Interval;
         case DebuggerAgent.PausedReason.Listener:
             return WI.DebuggerManager.PauseReason.Listener;
+        case DebuggerAgent.PausedReason.Microtask:
+            return WI.DebuggerManager.PauseReason.Microtask;
         case DebuggerAgent.PausedReason.PauseOnNextStatement:
             return WI.DebuggerManager.PauseReason.PauseOnNextStatement;
         case DebuggerAgent.PausedReason.Timeout:
@@ -1043,8 +1057,8 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
     _breakpointDisabledStateDidChange(event)
     {
         let breakpoint = event.target;
-
-        if (breakpoint === this._allExceptionsBreakpoint) {
+        switch (breakpoint) {
+        case this._allExceptionsBreakpoint:
             if (!breakpoint.disabled && !this.breakpointsDisabledTemporarily)
                 this.breakpointsEnabled = true;
             this._allExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
@@ -1052,9 +1066,8 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
             for (let target of WI.targets)
                 target.DebuggerAgent.setPauseOnExceptions(this._breakOnExceptionsState);
             return;
-        }
 
-        if (breakpoint === this._uncaughtExceptionsBreakpoint) {
+        case this._uncaughtExceptionsBreakpoint:
             if (!breakpoint.disabled && !this.breakpointsDisabledTemporarily)
                 this.breakpointsEnabled = true;
             this._uncaughtExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
@@ -1062,14 +1075,21 @@ WI.DebuggerManager = class DebuggerManager extends WI.Object
             for (let target of WI.targets)
                 target.DebuggerAgent.setPauseOnExceptions(this._breakOnExceptionsState);
             return;
-        }
 
-        if (breakpoint === this._assertionFailuresBreakpoint) {
+        case this._assertionFailuresBreakpoint:
             if (!breakpoint.disabled && !this.breakpointsDisabledTemporarily)
                 this.breakpointsEnabled = true;
             this._assertionFailuresBreakpointEnabledSetting.value = !breakpoint.disabled;
             for (let target of WI.targets)
-                target.DebuggerAgent.setPauseOnAssertions(this._assertionFailuresBreakpointEnabledSetting.value);
+                target.DebuggerAgent.setPauseOnAssertions(!breakpoint.disabled);
+            return;
+
+        case this._allMicrotasksBreakpoint:
+            if (!breakpoint.disabled && !this.breakpointsDisabledTemporarily)
+                this.breakpointsEnabled = true;
+            this._allMicrotasksBreakpointEnabledSetting.value = !breakpoint.disabled;
+            for (let target of WI.targets)
+                target.DebuggerAgent.setPauseOnMicrotasks(!breakpoint.disabled);
             return;
         }
 
@@ -1383,6 +1403,7 @@ WI.DebuggerManager.PauseReason = {
     Fetch: "fetch",
     Interval: "interval",
     Listener: "listener",
+    Microtask: "microtask",
     PauseOnNextStatement: "pause-on-next-statement",
     Timeout: "timeout",
     XHR: "xhr",
diff --git a/Source/WebInspectorUI/UserInterface/Images/Microtask.svg b/Source/WebInspectorUI/UserInterface/Images/Microtask.svg
new file mode 100644 (file)
index 0000000..e5ba227
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright © 2019 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <path fill="rgb(148, 183, 219)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 Z"/>
+    <path fill="rgb(106, 136, 170)" d="M 13 1 L 3 1 C 1.898438 1 1 1.898438 1 3 L 1 13 C 1 14.101562 1.898438 15 3 15 L 13 15 C 14.101562 15 15 14.101562 15 13 L 15 3 C 15 1.898438 14.101562 1 13 1 M 13 2 C 13.550781 2 14 2.449219 14 3 L 14 13 C 14 13.550781 13.550781 14 13 14 L 3 14 C 2.449219 14 2 13.550781 2 13 L 2 3 C 2 2.449219 2.449219 2 3 2 L 13 2"/>
+    <path fill="rgb(113  146  184)" d="M 5.5 3 C 6 3 6.2 3.33 6.5 4 L 8 7 L 9.5 4 C 9.8 3.33 10.2 3 10.5 3 L 10.5 3 L 12 3 C 12.66 3 13 3.33 13 4 L 13 12 C 13 12.66 12.66 13 12 13 L 10.5 13 C 9.8 13 9.5 12.66 9.5 12 L 9.5 11 L 9 12 C 8.5 13 8.5 13 8 13 L 8 13 C 7.5 13 7.5 13 7 12 L 7 12 L 6.5 11 L 6.5 12 C 6.5 12.5 6 13 5.5 13 L 5.5 13 L 4 13 C 3.5 13 3 12.75 3 12 L 3 12 L 3 4 C 3 3.5 3.5 3 4 3 L 4 3 L 5.5 3 Z"/>
+    <path fill="white" d="M 4 12 V 4 H 5.5 L 8 9 L 10.5 4 H 12 V 12 H 10.5 V 7 L 8 12 L 5.5 7 V 12 H 4 Z"/>
+</svg>
index f4a4978..f2b2e95 100644 (file)
     content: url(../Images/Assertion.svg);
 }
 
+.breakpoint-microtask-icon .icon {
+    content: url(../Images/Microtask.svg);
+}
+
 .breakpoint-paused-icon .icon {
     content: url(../Images/PausedBreakpoint.svg);
 }
index 3a04138..8154685 100644 (file)
@@ -216,6 +216,10 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
         if (InspectorBackend.domains.Debugger.setPauseOnAssertions && WI.settings.showAssertionFailuresBreakpoint.value)
             WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
 
+        // COMPATIBILITY (iOS 13): DebuggerAgent.setPauseOnMicrotasks did not exist yet.
+        if (InspectorBackend.domains.Debugger.setPauseOnMicrotasks && WI.settings.showAllMicrotasksBreakpoint.value)
+            WI.debuggerManager.addBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
+
         for (let target of WI.targets)
             this._addTarget(target);
         this._updateCallStackTreeOutline();
@@ -389,6 +393,10 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
             cookie[DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey] = true;
             return;
 
+        case WI.debuggerManager.allMicrotasksBreakpoint:
+            cookie[DebuggerSidebarPanel.SelectedAllMicrotasksCookieKey] = true;
+            return;
+
         case WI.domDebuggerManager.allAnimationFramesBreakpoint:
             cookie[DebuggerSidebarPanel.SelectedAllAnimationFramesBreakpoint] = true;
             return;
@@ -432,6 +440,8 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
             revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.uncaughtExceptionsBreakpoint);
         else if (cookie[DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey])
             revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.assertionFailuresBreakpoint);
+        else if (cookie[DebuggerSidebarPanel.SelectedAllMicrotasksCookieKey])
+            revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.allMicrotasksBreakpoint);
         else if (cookie[DebuggerSidebarPanel.SelectedAllAnimationFramesBreakpoint])
             revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allAnimationFramesBreakpoint);
         else if (cookie[DebuggerSidebarPanel.SelectedAllIntervalsBreakpoint])
@@ -533,6 +543,9 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
         } else if (breakpoint === WI.debuggerManager.assertionFailuresBreakpoint) {
             options.className = WI.DebuggerSidebarPanel.AssertionIconStyleClassName;
             options.title = WI.repeatedUIString.assertionFailures();
+        } else if (breakpoint === WI.debuggerManager.allMicrotasksBreakpoint) {
+            options.className = "breakpoint-microtask-icon";
+            options.title = WI.UIString("All Microtasks");
         } else if (breakpoint instanceof WI.DOMBreakpoint) {
             if (!breakpoint.domNodeIdentifier)
                 return null;
@@ -1118,6 +1131,7 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
                 (treeElement) => treeElement.representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.debuggerManager.assertionFailuresBreakpoint,
                 (treeElement) => treeElement instanceof WI.BreakpointTreeElement || treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement,
+                (treeElement) => treeElement.representedObject === WI.debuggerManager.allMicrotasksBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allAnimationFramesBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allTimeoutsBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allIntervalsBreakpoint,
@@ -1373,6 +1387,11 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
             this._pauseReasonGroup.rows = [eventBreakpointRow];
             return true;
 
+        case WI.DebuggerManager.PauseReason.Microtask:
+            this._pauseReasonTextRow.text = WI.UIString("Microtask Fired");
+            this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
+            return true;
+
         case WI.DebuggerManager.PauseReason.PauseOnNextStatement:
             this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested");
             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
@@ -1550,6 +1569,10 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
             setting = WI.settings.showAssertionFailuresBreakpoint;
             break;
 
+        case WI.debuggerManager.allMicrotasksBreakpoint:
+            setting = WI.settings.showAllMicrotasksBreakpoint;
+            break;
+
         case WI.domDebuggerManager.allAnimationFramesBreakpoint:
             setting = WI.settings.showAllAnimationFramesBreakpoint;
             break;
@@ -1590,6 +1613,22 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
             }, assertionFailuresBreakpointShown);
         }
 
+        contextMenu.appendSeparator();
+
+        // COMPATIBILITY (iOS 13): DebuggerAgent.setPauseOnMicrotasks did not exist yet.
+        if (InspectorBackend.domains.Debugger.setPauseOnMicrotasks) {
+            let allMicrotasksBreakpointShown = WI.settings.showAllMicrotasksBreakpoint.value;
+
+            contextMenu.appendCheckboxItem(WI.UIString("All Microtasks"), () => {
+                if (allMicrotasksBreakpointShown)
+                    WI.debuggerManager.removeBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
+                else {
+                    WI.debuggerManager.allMicrotasksBreakpoint.disabled = false;
+                    WI.debuggerManager.addBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
+                }
+            }, allMicrotasksBreakpointShown);
+        }
+
         if (WI.DOMDebuggerManager.supportsEventBreakpoints() || WI.DOMDebuggerManager.supportsEventListenerBreakpoints()) {
             function addToggleForSpecialEventBreakpoint(breakpoint, label, checked) {
                 contextMenu.appendCheckboxItem(label, () => {
@@ -1602,8 +1641,6 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
                 }, checked);
             }
 
-            contextMenu.appendSeparator();
-
             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allAnimationFramesBreakpoint, WI.UIString("All Animation Frames"), WI.settings.showAllAnimationFramesBreakpoint.value);
             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allTimeoutsBreakpoint, WI.UIString("All Timeouts"), WI.settings.showAllTimeoutsBreakpoint.value);
             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allIntervalsBreakpoint, WI.UIString("All Intervals"), WI.settings.showAllIntervalsBreakpoint.value);
@@ -1617,11 +1654,11 @@ WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSideba
                 let popover = new WI.EventBreakpointPopover(this);
                 popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
             });
-
-            contextMenu.appendSeparator();
         }
 
         if (WI.DOMDebuggerManager.supportsURLBreakpoints() || WI.DOMDebuggerManager.supportsXHRBreakpoints()) {
+            contextMenu.appendSeparator();
+
             let allRequestsBreakpointShown = WI.settings.showAllRequestsBreakpoint.value;
             contextMenu.appendCheckboxItem(WI.repeatedUIString.allRequests(), () => {
                 if (allRequestsBreakpointShown)
@@ -1648,6 +1685,7 @@ WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-
 WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
 WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey = "debugger-sidebar-panel-uncaught-exceptions-breakpoint";
 WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey = "debugger-sidebar-panel-assertion-failures-breakpoint";
+WI.DebuggerSidebarPanel.SelectedAllMicrotasksCookieKey = "debugger-sidebar-panel-all-microtasks-breakpoint";
 WI.DebuggerSidebarPanel.SelectedAllAnimationFramesBreakpoint = "debugger-sidebar-panel-all-animation-frames-breakpoint";
 WI.DebuggerSidebarPanel.SelectedAllIntervalsBreakpoint = "debugger-sidebar-panel-all-intervals-breakpoint";
 WI.DebuggerSidebarPanel.SelectedAllListenersBreakpoint = "debugger-sidebar-panel-all-listeners-breakpoint";
index 0ad26a4..9e9d048 100644 (file)
@@ -328,6 +328,10 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
         if (InspectorBackend.domains.Debugger.setPauseOnAssertions && WI.settings.showAssertionFailuresBreakpoint.value)
             WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
 
+        // COMPATIBILITY (iOS 13): DebuggerAgent.setPauseOnMicrotasks did not exist yet.
+        if (InspectorBackend.domains.Debugger.setPauseOnMicrotasks && WI.settings.showAllMicrotasksBreakpoint.value)
+            WI.debuggerManager.addBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
+
         for (let target of WI.targets)
             this._addTarget(target);
 
@@ -914,6 +918,7 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
                 (treeElement) => treeElement.representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.debuggerManager.assertionFailuresBreakpoint,
                 (treeElement) => treeElement instanceof WI.BreakpointTreeElement || treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement,
+                (treeElement) => treeElement.representedObject === WI.debuggerManager.allMicrotasksBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allAnimationFramesBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allTimeoutsBreakpoint,
                 (treeElement) => treeElement.representedObject === WI.domDebuggerManager.allIntervalsBreakpoint,
@@ -988,6 +993,9 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
         } else if (breakpoint === WI.debuggerManager.assertionFailuresBreakpoint) {
             options.className = "breakpoint-assertion-icon";
             options.title = WI.repeatedUIString.assertionFailures();
+        } else if (breakpoint === WI.debuggerManager.allMicrotasksBreakpoint) {
+            options.className = "breakpoint-microtask-icon";
+            options.title = WI.UIString("All Microtasks");
         } else if (breakpoint instanceof WI.DOMBreakpoint) {
             if (!breakpoint.domNodeIdentifier)
                 return null;
@@ -1405,6 +1413,11 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
             return true;
         }
 
+        case WI.DebuggerManager.PauseReason.Microtask:
+            this._pauseReasonTextRow.text = WI.UIString("Microtask Fired");
+            this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
+            return true;
+
         case WI.DebuggerManager.PauseReason.PauseOnNextStatement:
             this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested");
             this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
@@ -1590,6 +1603,10 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
             setting = WI.settings.showAssertionFailuresBreakpoint;
             break;
 
+        case WI.debuggerManager.allMicrotasksBreakpoint:
+            setting = WI.settings.showAllMicrotasksBreakpoint;
+            break;
+
         case WI.domDebuggerManager.allAnimationFramesBreakpoint:
             setting = WI.settings.showAllAnimationFramesBreakpoint;
             break;
@@ -1631,6 +1648,22 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
             }, assertionFailuresBreakpointShown);
         }
 
+        contextMenu.appendSeparator();
+
+        // COMPATIBILITY (iOS 13): DebuggerAgent.setPauseOnMicrotasks did not exist yet.
+        if (InspectorBackend.domains.Debugger.setPauseOnMicrotasks) {
+            let allMicrotasksBreakpointShown = WI.settings.showAllMicrotasksBreakpoint.value;
+
+            contextMenu.appendCheckboxItem(WI.UIString("All Microtasks"), () => {
+                if (allMicrotasksBreakpointShown)
+                    WI.debuggerManager.removeBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
+                else {
+                    WI.debuggerManager.allMicrotasksBreakpoint.disabled = false;
+                    WI.debuggerManager.addBreakpoint(WI.debuggerManager.allMicrotasksBreakpoint);
+                }
+            }, allMicrotasksBreakpointShown);
+        }
+
         if (WI.DOMDebuggerManager.supportsEventBreakpoints() || WI.DOMDebuggerManager.supportsEventListenerBreakpoints()) {
             function addToggleForSpecialEventBreakpoint(breakpoint, label, checked) {
                 contextMenu.appendCheckboxItem(label, () => {
@@ -1643,8 +1676,6 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
                 }, checked);
             }
 
-            contextMenu.appendSeparator();
-
             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allAnimationFramesBreakpoint, WI.UIString("All Animation Frames"), WI.settings.showAllAnimationFramesBreakpoint.value);
             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allTimeoutsBreakpoint, WI.UIString("All Timeouts"), WI.settings.showAllTimeoutsBreakpoint.value);
             addToggleForSpecialEventBreakpoint(WI.domDebuggerManager.allIntervalsBreakpoint, WI.UIString("All Intervals"), WI.settings.showAllIntervalsBreakpoint.value);
@@ -1658,11 +1689,11 @@ WI.SourcesNavigationSidebarPanel = class SourcesNavigationSidebarPanel extends W
                 let popover = new WI.EventBreakpointPopover(this);
                 popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
             });
-
-            contextMenu.appendSeparator();
         }
 
         if (WI.DOMDebuggerManager.supportsURLBreakpoints() || WI.DOMDebuggerManager.supportsXHRBreakpoints()) {
+            contextMenu.appendSeparator();
+
             let allRequestsBreakpointShown = WI.settings.showAllRequestsBreakpoint.value;
             contextMenu.appendCheckboxItem(WI.repeatedUIString.allRequests(), () => {
                 if (allRequestsBreakpointShown)