Web Inspector: Worker debugging should pause all targets and view call frames in...
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Nov 2016 04:02:59 +0000 (04:02 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Nov 2016 04:02:59 +0000 (04:02 +0000)
commit09a99f40ebcde1dd897613b9a3f3770b7c208e13
treeef732c54a75785ed68a59cb1b35d5927c68252e6
parent9dfc30bc516fd7d37e699a1dfd71c3b4a0fd0540
Web Inspector: Worker debugging should pause all targets and view call frames in all targets
https://bugs.webkit.org/show_bug.cgi?id=164305
<rdar://problem/29056192>

Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* inspector/InjectedScriptSource.js:
(InjectedScript.prototype._propertyDescriptors):
Accessing __proto__ does a ToThis(...) conversion on the receiver.
In the case of GlobalObjects (such as WorkerGlobalScope when paused)
this would return undefined and throw an exception. We can use
Object.getPrototypeOf to avoid that conversion and possible error.

* inspector/protocol/Debugger.json:
Provide a new way to effectively `resume` + `pause` immediately.
This must be implemented on the backend to correctly synchronize
the resuming and pausing.

* inspector/agents/InspectorDebuggerAgent.h:
* inspector/agents/InspectorDebuggerAgent.cpp:
(Inspector::InspectorDebuggerAgent::continueUntilNextRunLoop):
Treat this as `resume` and `pause`. Resume now, and trigger
a pause if the VM becomes idle and we didn't pause before then
(such as hitting a breakpoint after we resumed).

(Inspector::InspectorDebuggerAgent::pause):
(Inspector::InspectorDebuggerAgent::resume):
(Inspector::InspectorDebuggerAgent::schedulePauseOnNextStatement):
(Inspector::InspectorDebuggerAgent::cancelPauseOnNextStatement):
Clean up and correct pause on next statement logic.

(Inspector::InspectorDebuggerAgent::registerIdleHandler):
(Inspector::InspectorDebuggerAgent::willStepAndMayBecomeIdle):
(Inspector::InspectorDebuggerAgent::didBecomeIdle):
(Inspector::InspectorDebuggerAgent::didBecomeIdleAfterStepping): Deleted.
The idle handler may now also trigger a pause in the case
where continueUntilNextRunLoop resumed and wants to pause.

(Inspector::InspectorDebuggerAgent::didPause):
Eliminate the useless didPause. The DOMDebugger was keeping track
of its own state that was worse then the state in DebuggerAgent.

Source/WebCore:

Tests: inspector/debugger/continueUntilNextRunLoop
       inspector/worker/debugger-multiple-targets-pause

* workers/WorkerMessagingProxy.cpp:
(WebCore::WorkerMessagingProxy::postMessageToPageInspector):
Switch from postTask (callOnMainThread) to RunLoop::main().dispatch so
that a paused Worker can send Inspector protocol messages responses
back through the Main Page's InspectorWorkerAgent even if the Page
itself is paused and MainThread callbacks are paused.

* workers/WorkerRunLoop.h:
(WebCore::WorkerRunLoop::isNested):
* workers/WorkerRunLoop.cpp:
(WebCore::WorkerRunLoop::runInMode):
When running a nested WorkerRunLoop, running inspector debugger
commands, we should not fire timers on the Worker. Timers would
then be happening out of order and would not be debuggable.

* dom/EventTarget.cpp:
(WebCore::EventTarget::fireEventListeners):
* inspector/InspectorDOMDebuggerAgent.cpp:
(WebCore::InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded):
(WebCore::InspectorDOMDebuggerAgent::clear):
(WebCore::InspectorDOMDebuggerAgent::didPause): Deleted.
* inspector/InspectorDOMDebuggerAgent.h:
* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::willHandleEventImpl):
(WebCore::InspectorInstrumentation::didFireTimerImpl):
(WebCore::InspectorInstrumentation::didHandleEventImpl): Deleted.
(WebCore::InspectorInstrumentation::cancelPauseOnNativeEvent): Deleted.
* inspector/InspectorInstrumentation.h:
(WebCore::InspectorInstrumentation::willHandleEvent):
(WebCore::InspectorInstrumentation::didHandleEvent): Deleted.
Remove unnecessary code where WebCore is trying to keep track
of pause on next statement but that state is already more
accurately provided by InspectorDebuggerAgent.

Source/WebInspectorUI:

This implements a policy where, when one Target ("Thread") pauses
the frontend triggers a pause in all other Targets. The intended
user experience is "all threads pause" whenever the frontend shows
the debugger paused UI.

DebuggerManager has a few straight forward changes:

    - The paused state reflects if any target is paused.
    - The Paused Event is fired when going from !paused -> paused.
      This means when the first target pauses.
    - The Resumed Event is fired when going from paused -> !paused.
      This means only after all targets have resumed.
    - The CallFrameDidChange Event now includes the Target that updated.

When a Target first pauses the frontend then immediately pauses all
other Targets. This puts them into a "pausing" state (we display as
Idle) and they will pause as soon as they start executing JavaScript.

When a Target steps the "paused" state isn't changing. So this is
just a CallFramesDidChange update.

When clicking Resume we resume all targets. This is will be the normal,
expected way users resume execution. Note that one of the threads may
then hit a breakpoint and re-pause all threads.

Sometimes when multiple threads are paused you may want to run an
individual thread to completion but keep other threads paused. There
is a context menu on the ThreadTreeElement to resume just that
single thread. It will continue and pause for its next run loop.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Images/Thread.svg: Added.
* UserInterface/Images/gtk/Thread.svg: Added.
* UserInterface/Main.html:
New strings and files.

* UserInterface/Base/Main.js:
(WebInspector.loaded):
* UserInterface/Test/Test.js:
(WebInspector.loaded):
Place the TargetManager first since other managers may want to listen
for TargetAdded / TargetRemoved events.

* UserInterface/Controllers/DebuggerManager.js:
(WebInspector.DebuggerManager.prototype.get paused):
This is now a computed state.

(WebInspector.DebuggerManager.prototype.pause):
(WebInspector.DebuggerManager.prototype.resume):
Affect all targets.

(WebInspector.DebuggerManager.prototype.stepOver):
(WebInspector.DebuggerManager.prototype.stepInto):
(WebInspector.DebuggerManager.prototype.stepOut):
(WebInspector.DebuggerManager.prototype.reset):
Update to use the paused computed property.

(WebInspector.DebuggerManager.prototype.continueUntilNextRunLoop):
Issue the new Debugger.continueUntilNextRunLoop command
on a given target.

(WebInspector.DebuggerManager.prototype.initializeTarget):
When a new Target is created and we were already paused,
then start that Worker in a paused state.

(WebInspector.DebuggerManager.prototype.debuggerDidPause):
Recover from bad cases where the backend informs the frontend about
internal JavaScript that it shouldn't know about. Legacy backend do
this but also there are corner cases we need to handle.
Dispatch events appropriately now that multiple targets may be paused.

(WebInspector.DebuggerManager.prototype._didResumeInternal):
Dispatch events appropriately now that multiple targets may be paused.

(WebInspector.DebuggerManager.prototype._targetRemoved):
Remove debugger data for targets that go away to avoid leaks.

* UserInterface/Models/DebuggerData.js:
(WebInspector.DebuggerData):
(WebInspector.DebuggerData.prototype.get paused):
(WebInspector.DebuggerData.prototype.get pausing):
Move some more per-Target state into DebuggerData.

(WebInspector.DebuggerData.prototype.pauseIfNeeded):
(WebInspector.DebuggerData.prototype.resumeIfNeeded):
(WebInspector.DebuggerData.prototype.continueUntilNextRunLoop):
These should only be called by DebuggerManager. They correctly
update the state of the DebuggerData for this Target, and also
issue the underlying command to the target.

(WebInspector.DebuggerData.prototype.updateForPause):
(WebInspector.DebuggerData.prototype.updateForResume):
Handle a special case where continueUntilNextRunLoop triggers
an invisible "pause" on the backend that we should mirror.

* UserInterface/Protocol/Target.js:
(WebInspector.MainTarget):
(WebInspector.MainTarget.prototype.get displayName):
(WebInspector.MainTarget.prototype.initialize):
Better display names.

* UserInterface/Views/DebuggerSidebarPanel.js:
(WebInspector.DebuggerSidebarPanel):
(WebInspector.DebuggerSidebarPanel.prototype._debuggerDidPause):
(WebInspector.DebuggerSidebarPanel.prototype._debuggerDidResume):
(WebInspector.DebuggerSidebarPanel.prototype._updateSingleThreadCallStacks):
(WebInspector.DebuggerSidebarPanel.prototype._selectActiveCallFrameTreeElement):
(WebInspector.DebuggerSidebarPanel.prototype._showSingleThreadCallStacks):
(WebInspector.DebuggerSidebarPanel.prototype._showMultipleThreadCallStacks):
(WebInspector.DebuggerSidebarPanel.prototype._findThreadTreeElementForTarget):
(WebInspector.DebuggerSidebarPanel.prototype._targetAdded):
(WebInspector.DebuggerSidebarPanel.prototype._targetRemoved):
(WebInspector.DebuggerSidebarPanel.prototype._debuggerCallFramesDidChange):
(WebInspector.DebuggerSidebarPanel.prototype._debuggerActiveCallFrameDidChange):
The DebuggerSidebar still has a single "Call Stacks" section, but maintains
two TreeOutlines and only shows one at a time. The Single Thread view shows
a flat list of the call frames for the Main Target when it is the only target.
The Multiple Threads view shows a list of Threads and their call frames.
We always keep both up to date, because we may need to swap between them
purely as Targets are added / removed. There is a bit of extra logic to
ensure we select elements properly based only on the visible tree outline.

* UserInterface/Views/LogContentView.js:
(WebInspector.LogContentView.prototype.didAppendConsoleMessageView):
When evaluating in a particular target, "runAfterPendingDispatches"
must wait for all other commands in that particular target to have
completed. So use the target specific version.

* UserInterface/Views/NavigationSidebarPanel.js:
(WebInspector.NavigationSidebarPanel.prototype._isTreeElementWithoutRepresentedObject):
Gracefully handle a few more TreeElements without a represented object.

* UserInterface/Views/IdleTreeElement.css: Added.
(.details-section.call-stack .idle .icon):
* UserInterface/Views/IdleTreeElement.js: Added.
(WebInspector.IdleTreeElement):
Very basic tree element to encapsulate an Idle call frame with an
empty represented object.

* UserInterface/Views/ThreadTreeElement.css: Added.
(.details-section.call-stack .thread .icon):
* UserInterface/Views/ThreadTreeElement.js: Added.
(WebInspector.ThreadTreeElement):
(WebInspector.ThreadTreeElement.prototype.get target):
(WebInspector.ThreadTreeElement.prototype.refresh):
(WebInspector.ThreadTreeElement.prototype.onattach):
(WebInspector.ThreadTreeElement.prototype.oncontextmenu):
ThreadTreeElement has no represented object, but makes it easy
to refresh a list of CallFrameTreeElements for a given target.

LayoutTests:

* inspector/debugger/continueUntilNextRunLoop-expected.txt: Added.
* inspector/debugger/continueUntilNextRunLoop.html: Added.
New test for new Debugger.continueUntilNextRunLoop protocol method.

* inspector/worker/debugger-multiple-targets-pause-expected.txt: Added.
* inspector/worker/debugger-multiple-targets-pause.html: Added.
* inspector/worker/resources/worker-debugger-thread-1.js: Added.
* inspector/worker/resources/worker-debugger-thread-2.js: Added.
This tests uses a 250ms timeout because we have to have the worker thread
evaluate some work and trigger a pause on other threads before their work
starts. On debug builds, shorter times, like 100ms, would not be enough.

* inspector/worker/debugger-pause-expected.txt:
* inspector/worker/debugger-pause.html:
Now that all threads pause, the first InspectorTest.log evaluates JavaScript on
the page and causes a pause. So make the first log empty to keep the test unchanged.

* inspector/worker/runtime-basic-expected.txt:
* inspector/unit-tests/target-manager-expected.txt:
Updated display name of the mainTarget.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@208725 268f45cc-cd09-0410-ab3c-d52691b4dbfc
43 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/debugger/continueUntilNextRunLoop-expected.txt [new file with mode: 0644]
LayoutTests/inspector/debugger/continueUntilNextRunLoop.html [new file with mode: 0644]
LayoutTests/inspector/unit-tests/target-manager-expected.txt
LayoutTests/inspector/worker/debugger-multiple-targets-pause-expected.txt [new file with mode: 0644]
LayoutTests/inspector/worker/debugger-multiple-targets-pause.html [new file with mode: 0644]
LayoutTests/inspector/worker/debugger-pause-expected.txt
LayoutTests/inspector/worker/debugger-pause.html
LayoutTests/inspector/worker/resources-in-worker.html
LayoutTests/inspector/worker/resources/worker-debugger-thread-1.js [new file with mode: 0644]
LayoutTests/inspector/worker/resources/worker-debugger-thread-2.js [new file with mode: 0644]
LayoutTests/inspector/worker/runtime-basic-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/InjectedScriptSource.js
Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp
Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.h
Source/JavaScriptCore/inspector/protocol/Debugger.json
Source/WebCore/ChangeLog
Source/WebCore/dom/EventTarget.cpp
Source/WebCore/inspector/InspectorDOMDebuggerAgent.cpp
Source/WebCore/inspector/InspectorDOMDebuggerAgent.h
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/InspectorInstrumentation.h
Source/WebCore/workers/WorkerMessagingProxy.cpp
Source/WebCore/workers/WorkerRunLoop.cpp
Source/WebCore/workers/WorkerRunLoop.h
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Base/Main.js
Source/WebInspectorUI/UserInterface/Controllers/DebuggerManager.js
Source/WebInspectorUI/UserInterface/Images/Thread.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Images/gtk/Thread.svg [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/DebuggerData.js
Source/WebInspectorUI/UserInterface/Protocol/Target.js
Source/WebInspectorUI/UserInterface/Test/Test.js
Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/IdleTreeElement.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/IdleTreeElement.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/LogContentView.js
Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/ThreadTreeElement.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/ThreadTreeElement.js [new file with mode: 0644]