Web Inspector: unable to evaluate in the isolated world of content scripts injected...
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Jan 2020 23:49:12 +0000 (23:49 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Jan 2020 23:49:12 +0000 (23:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=206110
<rdar://problem/16945643>

Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.

In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
to evaluate in non-normal isolated worlds.

Source/JavaScriptCore:

* inspector/protocol/Runtime.json:
Introduce an `ExecutionContextType` enum instead of `isPageContext` so the frontend can
decide whether/how to show a picker for that execution context.

Source/WebCore:

Test: inspector/runtime/executionContextCreated-isolated-world.html

* bindings/js/DOMWrapperWorld.h:
(WebCore::DOMWrapperWorld::create):
(WebCore::DOMWrapperWorld::type const): Added.
(WebCore::DOMWrapperWorld::isNormal const):
(WebCore::DOMWrapperWorld::name const): Added.
* bindings/js/DOMWrapperWorld.cpp:
(WebCore::DOMWrapperWorld::DOMWrapperWorld):
* bindings/js/ScriptController.h:
* bindings/js/ScriptController.cpp:
(WebCore::ScriptController::createWorld):
* bindings/js/WebCoreJSClientData.cpp:
(WebCore::JSVMClientData::initNormalWorld):
Require that a name is specified when creating an isolated world (except the normal world)
so that Web Inspector has something to show in the execution context picker.

* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::didClearWindowObjectInWorldImpl):
* inspector/agents/InspectorPageAgent.h:
* inspector/agents/InspectorPageAgent.cpp:
(WebCore::InspectorPageAgent::didClearWindowObjectInWorld):
* inspector/agents/page/PageDebuggerAgent.h:
* inspector/agents/page/PageDebuggerAgent.cpp:
(WebCore::PageDebuggerAgent::didClearWindowObjectInWorld):
* inspector/agents/page/PageRuntimeAgent.h:
* inspector/agents/page/PageRuntimeAgent.cpp:
(WebCore::PageRuntimeAgent::didClearWindowObjectInWorld):
(WebCore::PageRuntimeAgent::reportExecutionContextCreation):
(WebCore::toProtocol): Added.
(WebCore::PageRuntimeAgent::notifyContextCreated):
Allow this instrumentation call to pass through to the agents for non-`Normal` worlds.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::ensureIsolatedWorld):
* html/HTMLPlugInImageElement.cpp:
(WebCore::plugInImageElementIsolatedWorld):
* Modules/plugins/QuickTimePluginReplacement.mm:
(WebCore::QuickTimePluginReplacement::isolatedWorld):
Mark these worlds as `Internal`.

* testing/Internals.idl:
* testing/Internals.h:
* testing/Internals.cpp:
(WebCore::Internals::evaluateInWorldIgnoringException): Added.

Source/WebInspectorUI:

* UserInterface/Models/ExecutionContext.js:
(WI.ExecutionContext):
(WI.ExecutionContext.typeFromPayload): Added.
(WI.ExecutionContext.prototype.get type): Added.
(WI.ExecutionContext.prototype.get isPageContext): Deleted.

* UserInterface/Models/ExecutionContextList.js:
(WI.ExecutionContextList.prototype.add):
* UserInterface/Models/Frame.js:
(WI.Frame.prototype.addExecutionContext):
* UserInterface/Controllers/NetworkManager.js:
(WI.NetworkManager.prototype.executionContextCreated):
The `Normal` execution context (of which there should only be one) is considered the "page"
execution context.

* UserInterface/Protocol/DirectBackendTarget.js:
(WI.DirectBackendTarget):
* UserInterface/Protocol/PageTarget.js:
(WI.PageTarget):
* UserInterface/Protocol/WorkerTarget.js:
(WI.WorkerTarget):
Default to a `Normal` execution context.

* UserInterface/Views/QuickConsole.js:
(WI.QuickConsole):
(WI.QuickConsole.prototype._displayNameForExecutionContext): Added.
(WI.QuickConsole.prototype._resolveDesiredActiveExecutionContext): Added.
(WI.QuickConsole.prototype._setActiveExecutionContext): Added.
(WI.QuickConsole.prototype._updateActiveExecutionContextDisplay): Added.
(WI.QuickConsole.prototype._populateActiveExecutionContextNavigationItemContextMenu): Added.
(WI.QuickConsole.prototype._handleConsoleSavedResultAliasSettingChanged): Added.
(WI.QuickConsole.prototype._handleEngineeringShowInternalExecutionContextsSettingChanged): Added.
(WI.QuickConsole.prototype._handleFramePageExecutionContextChanged): Added.
(WI.QuickConsole.prototype._handleFrameExecutionContextsCleared): Added.
(WI.QuickConsole.prototype._handleDebuggerActiveCallFrameDidChange): Added.
(WI.QuickConsole.prototype._handleActiveExecutionContextChanged): Added.
(WI.QuickConsole.prototype._handleTransitionPageTarget): Added.
(WI.QuickConsole.prototype._handleTargetRemoved): Added.
(WI.QuickConsole.prototype._handleInspectedNodeChanged): Added.
(WI.QuickConsole.prototype._updateStyles):
(WI.QuickConsole.prototype.get navigationBar): Deleted.
(WI.QuickConsole.prototype._pageTargetTransitioned): Deleted.
(WI.QuickConsole.prototype._initializeMainExecutionContextPathComponent): Deleted.
(WI.QuickConsole.prototype.layout): Deleted.
(WI.QuickConsole.prototype._preferredNameForFrame): Deleted.
(WI.QuickConsole.prototype._selectExecutionContext): Deleted.
(WI.QuickConsole.prototype._updateAutomaticExecutionContextPathComponentTooltip): Deleted.
(WI.QuickConsole.prototype._executionContextPathComponentsToDisplay): Deleted.
(WI.QuickConsole.prototype._rebuildExecutionContextPathComponents): Deleted.
(WI.QuickConsole.prototype._framePageExecutionContextsChanged): Deleted.
(WI.QuickConsole.prototype._frameExecutionContextsCleared): Deleted.
(WI.QuickConsole.prototype._activeExecutionContextChanged): Deleted.
(WI.QuickConsole.prototype._createExecutionContextPathComponent): Deleted.
(WI.QuickConsole.prototype._compareExecutionContextPathComponents): Deleted.
(WI.QuickConsole.prototype._insertOtherExecutionContextPathComponent): Deleted.
(WI.QuickConsole.prototype._removeOtherExecutionContextPathComponent): Deleted.
(WI.QuickConsole.prototype._insertExecutionContextPathComponentForFrame): Deleted.
(WI.QuickConsole.prototype._removeExecutionContextPathComponentForFrame): Deleted.
(WI.QuickConsole.prototype._targetAdded): Deleted.
(WI.QuickConsole.prototype._targetRemoved): Deleted.
(WI.QuickConsole.prototype._pathComponentSelected): Deleted.
(WI.QuickConsole.prototype._pathComponentClicked): Deleted.
(WI.QuickConsole.prototype._debuggerActiveCallFrameDidChange): Deleted.
* UserInterface/Views/QuickConsole.css:
(.quick-console > .console-prompt):
(.quick-console > .navigation-bar):
(.quick-console > .navigation-bar .active-execution-context): Added.
(.quick-console > .navigation-bar .active-execution-context > .selector-arrows): Added.
(.quick-console > .navigation-bar .active-execution-context:not(.automatic)): Added.
(.quick-console > .navigation-bar .active-execution-context:not(.automatic) > .selector-arrows): Added.
(.quick-console .execution-context): Deleted.
(.quick-console > .navigation-bar > .hierarchical-path .execution-context): Deleted.
(.quick-console > .navigation-bar > .hierarchical-path .execution-context .separator): Deleted.
(.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context)): Deleted.
(.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context) .execution-context): Deleted.
(.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context) .execution-context .selector-arrows): Deleted.
Replace the `WI.HierarchicalPathNavigationItem` with a plain `WI.NavigationItem` that shows
a `WI.ContextMenu` with all valid execution contexts organized as follows:

    Auto - <display name for execution context of inspected DOM node>
    ----------
    Main Frame Normal Execution Context
        All User Exection Contexts for the Main Frame
        All Internal Exection Contexts for the Main Frame (with the engineering setting)
    Frames
        Frame Normal Execution Context
            All User Exection Contexts for the Frame
            All Internal Exection Contexts for the Frame (with the engineering setting)
        ...
    Workers
        Worker Execution Context
        ...

Everything is checkmark selectable other than the separator, "Frames", and "Workers".

* UserInterface/Controllers/RuntimeManager.js:
(WI.RuntimeManager):
(WI.RuntimeManager.prototype._frameExecutionContextsCleared): Deleted.
Let the UI (`WI.QuickConsole`) decide when to automatically update the active execution
context when a frame is removed that owned the active execution context.

* UserInterface/Controllers/TargetManager.js:
(WI.TargetManager.prototype.get workerTargets): Added.
(WI.TargetManager.prototype._terminatePageTarget):
Convenience function for getting the list of worker targets.

* UserInterface/Views/GroupNavigationItem.js:
(WI.GroupNavigationItem.prototype.update):
(WI.GroupNavigationItem.prototype.didAttach):
Update the items whenever the group updates.

* UserInterface/Main.html:
* UserInterface/Views/SizesToFitNavigationBar.js: Renamed from Source/WebInspectorUI/UserInterface/Views/QuickConsoleNavigationBar.js.
(WI.SizesToFitNavigationBar):
(WI.SizesToFitNavigationBar.prototype.get sizesToFit):
Rename to allow for other use cases.

* UserInterface/Views/ContextMenu.js:
(WI.ContextSubMenuItem.prototype.appendHeader): Added.
Convenience method for creating a disabled item.

* UserInterface/Base/Setting.js:
* UserInterface/Views/SettingsTabContentView.js:
(WI.SettingsTabContentView.prototype._createEngineeringSettingsView):
Create an engineering setting that controls whether `Internal` execution contexts are shown.

* UserInterface/Test/InspectorProtocol.js:
(InspectorProtocol.addEventListener):
(InspectorProtocol.removeEventListener): Added.

* Localizations/en.lproj/localizedStrings.js:

Source/WebKit:

* WebProcess/InjectedBundle/InjectedBundleScriptWorld.h:
* WebProcess/InjectedBundle/InjectedBundleScriptWorld.cpp:
(WebKit::InjectedBundleScriptWorld::create):

* WebProcess/UserContent/WebUserContentController.cpp:
(WebKit::WebUserContentController::addUserContentWorlds):
* WebProcess/InjectedBundle/API/glib/WebKitScriptWorld.cpp:
(webkit_script_world_new):
(webkit_script_world_new_with_name):
Treat isolated worlds created by API calls as `User` worlds.

Source/WebKitLegacy/mac:

* WebView/WebScriptWorld.mm:
(-[WebScriptWorld init]):
Treat isolated worlds created by API calls as `User` worlds.

Source/WebKitLegacy/win:

* WebScriptWorld.cpp:
(WebScriptWorld::createInstance):
Treat isolated worlds created by API calls as `User` worlds.

LayoutTests:

* inspector/runtime/executionContextCreated-isolated-world.html: Added.
* inspector/runtime/executionContextCreated-isolated-world-expected.txt: Added.

* inspector/runtime/change-execution-context-identifier.html:
* inspector/runtime/change-execution-context-identifier-expected.txt:
Don't expect the active execution context to change when the owner frame is removed, as that
is now handled by the UI (`WI.QuickConsole`) instead of the `WI.RuntimeManager`.

* inspector/runtime/executionContextCreated-onEnable.html:
Ignore internal worlds.

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

57 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/runtime/change-execution-context-identifier-expected.txt
LayoutTests/inspector/runtime/change-execution-context-identifier.html
LayoutTests/inspector/runtime/executionContextCreated-isolated-world-expected.txt [new file with mode: 0644]
LayoutTests/inspector/runtime/executionContextCreated-isolated-world.html [new file with mode: 0644]
LayoutTests/inspector/runtime/executionContextCreated-onEnable.html
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/Runtime.json
Source/WebCore/ChangeLog
Source/WebCore/Modules/plugins/QuickTimePluginReplacement.mm
Source/WebCore/bindings/js/DOMWrapperWorld.cpp
Source/WebCore/bindings/js/DOMWrapperWorld.h
Source/WebCore/bindings/js/ScriptController.cpp
Source/WebCore/bindings/js/ScriptController.h
Source/WebCore/bindings/js/WebCoreJSClientData.cpp
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLPlugInImageElement.cpp
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/agents/InspectorPageAgent.cpp
Source/WebCore/inspector/agents/InspectorPageAgent.h
Source/WebCore/inspector/agents/page/PageDebuggerAgent.cpp
Source/WebCore/inspector/agents/page/PageDebuggerAgent.h
Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp
Source/WebCore/inspector/agents/page/PageRuntimeAgent.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Base/Setting.js
Source/WebInspectorUI/UserInterface/Base/Utilities.js
Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js
Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js
Source/WebInspectorUI/UserInterface/Controllers/TargetManager.js
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/ExecutionContext.js
Source/WebInspectorUI/UserInterface/Models/ExecutionContextList.js
Source/WebInspectorUI/UserInterface/Models/Frame.js
Source/WebInspectorUI/UserInterface/Protocol/DirectBackendTarget.js
Source/WebInspectorUI/UserInterface/Protocol/PageTarget.js
Source/WebInspectorUI/UserInterface/Protocol/WorkerTarget.js
Source/WebInspectorUI/UserInterface/Test/InspectorProtocol.js
Source/WebInspectorUI/UserInterface/Views/ContextMenu.js
Source/WebInspectorUI/UserInterface/Views/GroupNavigationItem.js
Source/WebInspectorUI/UserInterface/Views/QuickConsole.css
Source/WebInspectorUI/UserInterface/Views/QuickConsole.js
Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js
Source/WebInspectorUI/UserInterface/Views/SizesToFitNavigationBar.js [moved from Source/WebInspectorUI/UserInterface/Views/QuickConsoleNavigationBar.js with 81% similarity]
Source/WebKit/ChangeLog
Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitScriptWorld.cpp
Source/WebKit/WebProcess/InjectedBundle/InjectedBundleScriptWorld.cpp
Source/WebKit/WebProcess/InjectedBundle/InjectedBundleScriptWorld.h
Source/WebKit/WebProcess/UserContent/WebUserContentController.cpp
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebView/WebScriptWorld.mm
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/WebScriptWorld.cpp

index ac33f00..1f826bd 100644 (file)
@@ -1,3 +1,25 @@
+2020-01-27  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: unable to evaluate in the isolated world of content scripts injected by safari app extensions
+        https://bugs.webkit.org/show_bug.cgi?id=206110
+        <rdar://problem/16945643>
+
+        Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.
+
+        In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
+        to evaluate in non-normal isolated worlds.
+
+        * inspector/runtime/executionContextCreated-isolated-world.html: Added.
+        * inspector/runtime/executionContextCreated-isolated-world-expected.txt: Added.
+
+        * inspector/runtime/change-execution-context-identifier.html:
+        * inspector/runtime/change-execution-context-identifier-expected.txt:
+        Don't expect the active execution context to change when the owner frame is removed, as that
+        is now handled by the UI (`WI.QuickConsole`) instead of the `WI.RuntimeManager`.
+
+        * inspector/runtime/executionContextCreated-onEnable.html:
+        Ignore internal worlds.
+
 2020-01-27  Jacob Uphoff  <jacob_uphoff@apple.com>
 
         [macOS iOS ] animations/animation-direction-normal.html is flaky failing
index 19ecf37..77b5954 100644 (file)
@@ -25,5 +25,5 @@ PASS: The passphrase should match the phrase defined in the main frame.
 -- Running test case: ScriptExecutionContextRemoveSubframe
 PASS: The test page should only have one sub-frame.
 PASS: The test page should now have no sub-frames.
-PASS: When a selected non-top-level execution context is removed, the active execution context should revert to the main frame context.
+PASS: When a selected non-top-level execution context is removed, the active execution context should not change, as that is handled by the UI.
 
index e6a2e47..851dac5 100644 (file)
@@ -85,6 +85,7 @@ function test()
 
             // Set the execution context to the subframe so we can switch away from it when the frame is detached.
             WI.runtimeManager.activeExecutionContext = subframes[0].pageExecutionContext;
+            let activeExecutionContextId = WI.runtimeManager.activeExecutionContext.id;
 
             // Force-override the contextId, otherwise we won't be able to access the iframe's DOM element when evaluating in the iframe execution context.
             let expression = `document.getElementById("subframe").remove();`;
@@ -92,7 +93,7 @@ function test()
             let contextId = WI.RuntimeManager.TopLevelExecutionContextIdentifier;
             RuntimeAgent.evaluate.invoke({expression, objectGroup, contextId}, () => {
                 InspectorTest.expectEqual(WI.networkManager.frames.length, 1, "The test page should now have no sub-frames.");
-                InspectorTest.expectEqual(WI.runtimeManager.activeExecutionContext.id, WI.RuntimeManager.TopLevelExecutionContextIdentifier, "When a selected non-top-level execution context is removed, the active execution context should revert to the main frame context.");
+                InspectorTest.expectEqual(WI.runtimeManager.activeExecutionContext.id, activeExecutionContextId, "When a selected non-top-level execution context is removed, the active execution context should not change, as that is handled by the UI.");
                 resolve();
             });
         }
diff --git a/LayoutTests/inspector/runtime/executionContextCreated-isolated-world-expected.txt b/LayoutTests/inspector/runtime/executionContextCreated-isolated-world-expected.txt
new file mode 100644 (file)
index 0000000..2df4e85
--- /dev/null
@@ -0,0 +1,9 @@
+
+Test that exactly one Runtime.executionContextCreated event is fired for each existing context when Runtime.enable is called.
+
+
+== Running test suite: Runtime.executionContextCreated.IsolatedWorld
+-- Running test case: Runtime.executionContextCreated.IsolatedWorld.Internal
+PASS: Received event for internal isolated world.
+PASS: Execution context should be internal.
+
diff --git a/LayoutTests/inspector/runtime/executionContextCreated-isolated-world.html b/LayoutTests/inspector/runtime/executionContextCreated-isolated-world.html
new file mode 100644 (file)
index 0000000..819aba8
--- /dev/null
@@ -0,0 +1,63 @@
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/protocol-test.js"></script>
+<script src="../../http/tests/inspector/resources/stable-id-map.js"></script>
+<script>
+function createIsolatedWorld() {
+    const name = "InspectorTestInternalIsolatedWorld";
+    function source() { return 42; }
+
+    if (window.internals)
+        window.internals.evaluateInWorldIgnoringException(name, source.toString());
+}
+
+function test()
+{
+    ProtocolTest.debug();
+
+    let suite = ProtocolTest.createAsyncSuite("Runtime.executionContextCreated.IsolatedWorld");
+
+    suite.addTestCase({
+        name: "Runtime.executionContextCreated.IsolatedWorld.Internal",
+        description: "Test that Runtime.executionContextCreated events are dispatched for internal isolated worlds.",
+        async test() {
+            let completion = new WI.WrappedPromise;
+
+            let listener = InspectorProtocol.addEventListener("Runtime.executionContextCreated", (messageObject) => {
+                let {id, name, type} = messageObject.params.context;
+                if (name !== "InspectorTestInternalIsolatedWorld")
+                    return;
+
+                InspectorProtocol.removeEventListener("Runtime.executionContextCreated", listener);
+
+                ProtocolTest.pass("Received event for internal isolated world.");
+                ProtocolTest.expectEqual(type, "internal", "Execution context should be internal.");
+
+                completion.resolve();
+            });
+
+            await Promise.all([
+                InspectorProtocol.awaitCommand({method: "Page.enable"}),
+                InspectorProtocol.awaitCommand({method: "Runtime.enable"}),
+            ]);
+
+            await InspectorProtocol.awaitCommand({
+                method: "Runtime.evaluate",
+                params: {
+                    expression: `createIsolatedWorld()`,
+                },
+            });
+
+            await completion.promise;
+        },
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body>
+<iframe id="subframe" src="resources/change-execution-context-identifier-subframe.html" onload="runTest()"></iframe>
+<p>Test that exactly one Runtime.executionContextCreated event is fired for each existing context when Runtime.enable is called.</p>
+</body>
+</html>
index ba5c93d..274c172 100644 (file)
@@ -16,8 +16,11 @@ function test()
             let contextCount = 0;
 
             InspectorProtocol.addEventListener("Runtime.executionContextCreated", (messageObject) => {
-                let {id, isPageContext, frameId} = messageObject.params.context;
-                ProtocolTest.log(`Execution context created: id=${contextIdMap.get(id)} frameId=${frameIdMap.get(frameId)} isPageContext=${isPageContext}`)
+                let {id, type, frameId} = messageObject.params.context;
+                if (type === "internal")
+                    return;
+
+                ProtocolTest.log(`Execution context created: id=${contextIdMap.get(id)} frameId=${frameIdMap.get(frameId)} isPageContext=${type === "normal"}`)
                 ++contextCount;
             });
 
index 2aba7bf..603c6f1 100644 (file)
@@ -1,3 +1,18 @@
+2020-01-27  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: unable to evaluate in the isolated world of content scripts injected by safari app extensions
+        https://bugs.webkit.org/show_bug.cgi?id=206110
+        <rdar://problem/16945643>
+
+        Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.
+
+        In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
+        to evaluate in non-normal isolated worlds.
+
+        * inspector/protocol/Runtime.json:
+        Introduce an `ExecutionContextType` enum instead of `isPageContext` so the frontend can
+        decide whether/how to show a picker for that execution context.
+
 2020-01-27  Stephan Szabo  <stephan.szabo@sony.com>
 
         Python 3: generate-js-builtins hits SyntaxWarning for "is 0"
index c17c15f..6eebc4a 100644 (file)
             "description": "Id of an execution context."
         },
         {
+            "id": "ExecutionContextType",
+            "type": "string",
+            "enum": ["normal", "user", "internal"],
+            "description": "Type of the execution context."
+        },
+        {
             "id": "ExecutionContextDescription",
             "type": "object",
             "description": "Description of an isolated world.",
             "properties": [
                 { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." },
-                { "name": "isPageContext", "type": "boolean", "description": "True if this is a context where inpspected web page scripts run. False if it is a content script isolated context." },
+                { "name": "type", "$ref": "ExecutionContextType" },
                 { "name": "name", "type": "string", "description": "Human readable name describing given context."},
                 { "name": "frameId", "$ref": "Network.FrameId", "description": "Id of the owning frame." }
             ]
index e25c25a..4c6caa2 100644 (file)
@@ -1,3 +1,60 @@
+2020-01-27  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: unable to evaluate in the isolated world of content scripts injected by safari app extensions
+        https://bugs.webkit.org/show_bug.cgi?id=206110
+        <rdar://problem/16945643>
+
+        Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.
+
+        In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
+        to evaluate in non-normal isolated worlds.
+
+        Test: inspector/runtime/executionContextCreated-isolated-world.html
+
+        * bindings/js/DOMWrapperWorld.h:
+        (WebCore::DOMWrapperWorld::create):
+        (WebCore::DOMWrapperWorld::type const): Added.
+        (WebCore::DOMWrapperWorld::isNormal const):
+        (WebCore::DOMWrapperWorld::name const): Added.
+        * bindings/js/DOMWrapperWorld.cpp:
+        (WebCore::DOMWrapperWorld::DOMWrapperWorld):
+        * bindings/js/ScriptController.h:
+        * bindings/js/ScriptController.cpp:
+        (WebCore::ScriptController::createWorld):
+        * bindings/js/WebCoreJSClientData.cpp:
+        (WebCore::JSVMClientData::initNormalWorld):
+        Require that a name is specified when creating an isolated world (except the normal world)
+        so that Web Inspector has something to show in the execution context picker.
+
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore::InspectorInstrumentation::didClearWindowObjectInWorldImpl):
+        * inspector/agents/InspectorPageAgent.h:
+        * inspector/agents/InspectorPageAgent.cpp:
+        (WebCore::InspectorPageAgent::didClearWindowObjectInWorld):
+        * inspector/agents/page/PageDebuggerAgent.h:
+        * inspector/agents/page/PageDebuggerAgent.cpp:
+        (WebCore::PageDebuggerAgent::didClearWindowObjectInWorld):
+        * inspector/agents/page/PageRuntimeAgent.h:
+        * inspector/agents/page/PageRuntimeAgent.cpp:
+        (WebCore::PageRuntimeAgent::didClearWindowObjectInWorld):
+        (WebCore::PageRuntimeAgent::reportExecutionContextCreation):
+        (WebCore::toProtocol): Added.
+        (WebCore::PageRuntimeAgent::notifyContextCreated):
+        Allow this instrumentation call to pass through to the agents for non-`Normal` worlds.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::ensureIsolatedWorld):
+        * html/HTMLPlugInImageElement.cpp:
+        (WebCore::plugInImageElementIsolatedWorld):
+        * Modules/plugins/QuickTimePluginReplacement.mm:
+        (WebCore::QuickTimePluginReplacement::isolatedWorld):
+        Mark these worlds as `Internal`.
+
+        * testing/Internals.idl:
+        * testing/Internals.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::evaluateInWorldIgnoringException): Added.
+
 2020-01-27  Peng Liu  <peng.liu6@apple.com>
 
         Crash in WebCore::HTMLMediaElement::detachMediaSource()
index 03d21de..5cea5e1 100644 (file)
@@ -137,7 +137,7 @@ RenderPtr<RenderElement> QuickTimePluginReplacement::createElementRenderer(HTMLP
 
 DOMWrapperWorld& QuickTimePluginReplacement::isolatedWorld()
 {
-    static DOMWrapperWorld& isolatedWorld = DOMWrapperWorld::create(commonVM()).leakRef();
+    static DOMWrapperWorld& isolatedWorld = DOMWrapperWorld::create(commonVM(), DOMWrapperWorld::Type::Internal, "QuickTimePluginReplacement"_s).leakRef();
     return isolatedWorld;
 }
 
index abb5191..6b99081 100644 (file)
 namespace WebCore {
 using namespace JSC;
 
-DOMWrapperWorld::DOMWrapperWorld(JSC::VM& vm, bool isNormal)
+DOMWrapperWorld::DOMWrapperWorld(JSC::VM& vm, Type type, const String& name)
     : m_vm(vm)
-    , m_isNormal(isNormal)
+    , m_name(name)
+    , m_type(type)
 {
+    ASSERT(!name.isEmpty() || m_type == Type::Normal);
+
     VM::ClientData* clientData = m_vm.clientData;
     ASSERT(clientData);
     static_cast<JSVMClientData*>(clientData)->rememberWorld(*this);
index 105ef20..ec3d950 100644 (file)
@@ -32,9 +32,15 @@ typedef HashMap<void*, JSC::Weak<JSC::JSObject>> DOMObjectWrapperMap;
 
 class DOMWrapperWorld : public RefCounted<DOMWrapperWorld> {
 public:
-    static Ref<DOMWrapperWorld> create(JSC::VM& vm, bool isNormal = false)
+    enum class Type {
+        Normal,   // Main (e.g. Page)
+        User,     // User Scripts (e.g. Extensions)
+        Internal, // WebKit Internal (e.g. Media Controls)
+    };
+
+    static Ref<DOMWrapperWorld> create(JSC::VM& vm, Type type = Type::Internal, const String& name = { })
     {
-        return adoptRef(*new DOMWrapperWorld(vm, isNormal));
+        return adoptRef(*new DOMWrapperWorld(vm, type, name));
     }
     WEBCORE_EXPORT ~DOMWrapperWorld();
 
@@ -52,19 +58,24 @@ public:
 
     DOMObjectWrapperMap& wrappers() { return m_wrappers; }
 
-    bool isNormal() const { return m_isNormal; }
+    Type type() const { return m_type; }
+    bool isNormal() const { return m_type == Type::Normal; }
+
+    const String& name() const { return m_name; }
 
     JSC::VM& vm() const { return m_vm; }
 
 protected:
-    DOMWrapperWorld(JSC::VM&, bool isNormal);
+    DOMWrapperWorld(JSC::VM&, Type, const String& name);
 
 private:
     JSC::VM& m_vm;
     HashSet<WindowProxy*> m_jsWindowProxies;
     DOMObjectWrapperMap m_wrappers;
 
-    bool m_isNormal;
+    String m_name;
+    Type m_type { Type::Internal };
+
     bool m_shadowRootIsAlwaysOpen { false };
     bool m_shouldDisableOverrideBuiltinsBehavior { false };
 };
index a873080..a2385cd 100644 (file)
@@ -26,6 +26,7 @@
 #include "CommonVM.h"
 #include "ContentSecurityPolicy.h"
 #include "CustomHeaderFields.h"
+#include "DOMWrapperWorld.h"
 #include "DocumentLoader.h"
 #include "Event.h"
 #include "Frame.h"
@@ -247,9 +248,9 @@ JSC::JSValue ScriptController::evaluateModule(const URL& sourceURL, JSModuleReco
     return evaluateModule(sourceURL, moduleRecord, mainThreadNormalWorld());
 }
 
-Ref<DOMWrapperWorld> ScriptController::createWorld()
+Ref<DOMWrapperWorld> ScriptController::createWorld(const String& name, WorldType type)
 {
-    return DOMWrapperWorld::create(commonVM());
+    return DOMWrapperWorld::create(commonVM(), type == WorldType::User ? DOMWrapperWorld::Type::User : DOMWrapperWorld::Type::Internal, name);
 }
 
 void ScriptController::getAllWorlds(Vector<Ref<DOMWrapperWorld>>& worlds)
index 7a2b4f2..cb7e1bb 100644 (file)
@@ -87,7 +87,8 @@ public:
     explicit ScriptController(Frame&);
     ~ScriptController();
 
-    WEBCORE_EXPORT static Ref<DOMWrapperWorld> createWorld();
+    enum class WorldType { User, Internal };
+    WEBCORE_EXPORT static Ref<DOMWrapperWorld> createWorld(const String& name, WorldType = WorldType::Internal);
 
     JSDOMWindow* globalObject(DOMWrapperWorld& world)
     {
@@ -104,7 +105,7 @@ public:
     WEBCORE_EXPORT ValueOrException executeUserAgentScriptInWorld(DOMWrapperWorld&, const String& script, bool forceUserGesture);
     WEBCORE_EXPORT void executeAsynchronousUserAgentScriptInWorld(DOMWrapperWorld&, RunJavaScriptParameters&&, ResolveFunction&&);
     JSC::JSValue evaluateIgnoringException(const ScriptSourceCode&);
-    JSC::JSValue evaluateInWorldIgnoringException(const ScriptSourceCode&, DOMWrapperWorld&);
+    WEBCORE_EXPORT JSC::JSValue evaluateInWorldIgnoringException(const ScriptSourceCode&, DOMWrapperWorld&);
 
     Expected<void, ExceptionDetails> shouldAllowUserAgentScripts(Document&) const;
 
index 8de78a9..efe8ba1 100644 (file)
@@ -114,10 +114,10 @@ void JSVMClientData::initNormalWorld(VM* vm)
 {
     JSVMClientData* clientData = new JSVMClientData(*vm);
     vm->clientData = clientData; // ~VM deletes this pointer.
-    
+
     vm->heap.addMarkingConstraint(makeUnique<DOMGCOutputConstraint>(*vm, *clientData));
-        
-    clientData->m_normalWorld = DOMWrapperWorld::create(*vm, true);
+
+    clientData->m_normalWorld = DOMWrapperWorld::create(*vm, DOMWrapperWorld::Type::Normal);
     vm->m_typedArrayController = adoptRef(new WebCoreTypedArrayController());
 }
 
index 190089e..c5f49da 100644 (file)
@@ -7223,7 +7223,7 @@ RefPtr<VideoPlaybackQuality> HTMLMediaElement::getVideoPlaybackQuality()
 DOMWrapperWorld& HTMLMediaElement::ensureIsolatedWorld()
 {
     if (!m_isolatedWorld)
-        m_isolatedWorld = DOMWrapperWorld::create(commonVM());
+        m_isolatedWorld = DOMWrapperWorld::create(commonVM(), DOMWrapperWorld::Type::Internal, makeString("Media Controls (", localName(), ')'));
     return *m_isolatedWorld;
 }
 
index 1ed3716..56231a5 100644 (file)
@@ -363,7 +363,7 @@ void HTMLPlugInImageElement::updateSnapshot(Image* image)
 
 static DOMWrapperWorld& plugInImageElementIsolatedWorld()
 {
-    static auto& isolatedWorld = DOMWrapperWorld::create(commonVM()).leakRef();
+    static auto& isolatedWorld = DOMWrapperWorld::create(commonVM(), DOMWrapperWorld::Type::Internal, "Plugin"_s).leakRef();
     return isolatedWorld;
 }
 
index 4f18b58..d0c430e 100644 (file)
@@ -121,17 +121,14 @@ static Frame* frameForScriptExecutionContext(ScriptExecutionContext& context)
 
 void InspectorInstrumentation::didClearWindowObjectInWorldImpl(InstrumentingAgents& instrumentingAgents, Frame& frame, DOMWrapperWorld& world)
 {
-    if (&world != &mainThreadNormalWorld())
-        return;
-
     if (auto* pageDebuggerAgent = instrumentingAgents.pageDebuggerAgent())
-        pageDebuggerAgent->didClearWindowObjectInWorld(frame);
+        pageDebuggerAgent->didClearWindowObjectInWorld(frame, world);
 
     if (auto* pageRuntimeAgent = instrumentingAgents.pageRuntimeAgent())
-        pageRuntimeAgent->didClearWindowObjectInWorld(frame);
+        pageRuntimeAgent->didClearWindowObjectInWorld(frame, world);
 
     if (auto* pageAgent = instrumentingAgents.inspectorPageAgent())
-        pageAgent->didClearWindowObjectInWorld(frame);
+        pageAgent->didClearWindowObjectInWorld(frame, world);
 }
 
 bool InspectorInstrumentation::isDebuggerPausedImpl(InstrumentingAgents& instrumentingAgents)
index d4e6b5d..d678cdd 100644 (file)
@@ -37,6 +37,7 @@
 #include "Cookie.h"
 #include "CookieJar.h"
 #include "CustomHeaderFields.h"
+#include "DOMWrapperWorld.h"
 #include "Document.h"
 #include "DocumentLoader.h"
 #include "Frame.h"
@@ -770,8 +771,11 @@ void InspectorPageAgent::defaultAppearanceDidChange(bool useDarkAppearance)
     m_frontendDispatcher->defaultAppearanceDidChange(useDarkAppearance ? Inspector::Protocol::Page::Appearance::Dark : Inspector::Protocol::Page::Appearance::Light);
 }
 
-void InspectorPageAgent::didClearWindowObjectInWorld(Frame& frame)
+void InspectorPageAgent::didClearWindowObjectInWorld(Frame& frame, DOMWrapperWorld& world)
 {
+    if (&world != &mainThreadNormalWorld())
+        return;
+
     if (m_bootstrapScript.isEmpty())
         return;
 
index 3f3bbb8..110173d 100644 (file)
@@ -42,6 +42,7 @@
 
 namespace WebCore {
 
+class DOMWrapperWorld;
 class DocumentLoader;
 class Frame;
 class InspectorClient;
@@ -127,7 +128,7 @@ public:
     void defaultAppearanceDidChange(bool useDarkAppearance);
     void applyUserAgentOverride(String&);
     void applyEmulatedMedia(String&);
-    void didClearWindowObjectInWorld(Frame&);
+    void didClearWindowObjectInWorld(Frame&, DOMWrapperWorld&);
     void didPaint(RenderObject&, const LayoutRect&);
     void didLayout();
     void didScroll();
index a0d8f1f..cc0b052 100644 (file)
@@ -33,6 +33,7 @@
 #include "PageDebuggerAgent.h"
 
 #include "CachedResource.h"
+#include "DOMWrapperWorld.h"
 #include "Document.h"
 #include "Frame.h"
 #include "InspectorPageAgent.h"
@@ -139,9 +140,9 @@ InjectedScript PageDebuggerAgent::injectedScriptForEval(ErrorString& errorString
     return injectedScript;
 }
 
-void PageDebuggerAgent::didClearWindowObjectInWorld(Frame& frame)
+void PageDebuggerAgent::didClearWindowObjectInWorld(Frame& frame, DOMWrapperWorld& world)
 {
-    if (!frame.isMainFrame())
+    if (!frame.isMainFrame() || &world != &mainThreadNormalWorld())
         return;
 
     didClearGlobalObject();
index 0a782ea..d139108 100644 (file)
@@ -35,6 +35,7 @@
 
 namespace WebCore {
 
+class DOMWrapperWorld;
 class Document;
 class Frame;
 class Page;
@@ -54,7 +55,7 @@ public:
     void breakpointActionLog(JSC::JSGlobalObject*, const String&) override;
 
     // InspectorInstrumentation
-    void didClearWindowObjectInWorld(Frame&);
+    void didClearWindowObjectInWorld(Frame&, DOMWrapperWorld&);
     void mainFrameStartedLoading();
     void mainFrameStoppedLoading();
     void mainFrameNavigated();
index 864ce3c..252a1ec 100644 (file)
@@ -32,6 +32,7 @@
 #include "config.h"
 #include "PageRuntimeAgent.h"
 
+#include "DOMWrapperWorld.h"
 #include "Document.h"
 #include "Frame.h"
 #include "InspectorPageAgent.h"
@@ -46,8 +47,6 @@
 #include <JavaScriptCore/InjectedScript.h>
 #include <JavaScriptCore/InjectedScriptManager.h>
 
-using Inspector::Protocol::Runtime::ExecutionContextDescription;
-
 namespace WebCore {
 
 using namespace Inspector;
@@ -97,15 +96,13 @@ void PageRuntimeAgent::frameNavigated(Frame& frame)
     mainWorldExecState(&frame);
 }
 
-void PageRuntimeAgent::didClearWindowObjectInWorld(Frame& frame)
+void PageRuntimeAgent::didClearWindowObjectInWorld(Frame& frame, DOMWrapperWorld& world)
 {
     auto* pageAgent = m_instrumentingAgents.inspectorPageAgent();
     if (!pageAgent)
         return;
 
-    auto frameId = pageAgent->frameId(&frame);
-    auto* scriptState = mainWorldExecState(&frame);
-    notifyContextCreated(frameId, scriptState, nullptr, true);
+    notifyContextCreated(pageAgent->frameId(&frame), frame.script().globalObject(world), world);
 }
 
 InjectedScript PageRuntimeAgent::injectedScriptForEval(ErrorString& errorString, const int* executionContextId)
@@ -140,39 +137,55 @@ void PageRuntimeAgent::reportExecutionContextCreation()
     if (!pageAgent)
         return;
 
-    Vector<std::pair<JSC::JSGlobalObject*, SecurityOrigin*>> isolatedContexts;
-    for (Frame* frame = &m_inspectedPage.mainFrame(); frame; frame = frame->tree().traverseNext()) {
+    for (auto* frame = &m_inspectedPage.mainFrame(); frame; frame = frame->tree().traverseNext()) {
         if (!frame->script().canExecuteScripts(NotAboutToExecuteScript))
             continue;
 
-        String frameId = pageAgent->frameId(frame);
+        auto frameId = pageAgent->frameId(frame);
 
-        JSC::JSGlobalObject* scriptState = mainWorldExecState(frame);
-        notifyContextCreated(frameId, scriptState, nullptr, true);
-        frame->script().collectIsolatedContexts(isolatedContexts);
-        if (isolatedContexts.isEmpty())
-            continue;
-        for (auto& [globalObject, securityOrigin] : isolatedContexts) {
-            if (globalObject != scriptState)
-                notifyContextCreated(frameId, globalObject, securityOrigin, false);
+        // Always send the main world first.
+        auto* mainGlobalObject = mainWorldExecState(frame);
+        notifyContextCreated(frameId, mainGlobalObject, mainThreadNormalWorld());
+
+        for (auto& jsWindowProxy : frame->windowProxy().jsWindowProxiesAsVector()) {
+            auto* globalObject = jsWindowProxy->window();
+            if (globalObject == mainGlobalObject)
+                continue;
+
+            auto& securityOrigin = downcast<DOMWindow>(jsWindowProxy->wrapped()).document()->securityOrigin();
+            notifyContextCreated(frameId, globalObject, jsWindowProxy->world(), &securityOrigin);
         }
-        isolatedContexts.clear();
     }
 }
 
-void PageRuntimeAgent::notifyContextCreated(const String& frameId, JSC::JSGlobalObject* scriptState, SecurityOrigin* securityOrigin, bool isPageContext)
+static Inspector::Protocol::Runtime::ExecutionContextType toProtocol(DOMWrapperWorld::Type type)
 {
-    ASSERT(securityOrigin || isPageContext);
+    switch (type) {
+    case DOMWrapperWorld::Type::Normal:
+        return Inspector::Protocol::Runtime::ExecutionContextType::Normal;
+    case DOMWrapperWorld::Type::User:
+        return Inspector::Protocol::Runtime::ExecutionContextType::User;
+    case DOMWrapperWorld::Type::Internal:
+        return Inspector::Protocol::Runtime::ExecutionContextType::Internal;
+    }
 
-    InjectedScript result = injectedScriptManager().injectedScriptFor(scriptState);
-    if (result.hasNoValue())
+    ASSERT_NOT_REACHED();
+    return Inspector::Protocol::Runtime::ExecutionContextType::Internal;
+}
+
+void PageRuntimeAgent::notifyContextCreated(const String& frameId, JSC::JSGlobalObject* globalObject, const DOMWrapperWorld& world, SecurityOrigin* securityOrigin)
+{
+    auto injectedScript = injectedScriptManager().injectedScriptFor(globalObject);
+    if (injectedScript.hasNoValue())
         return;
 
-    int executionContextId = injectedScriptManager().injectedScriptIdFor(scriptState);
-    String name = securityOrigin ? securityOrigin->toRawString() : String();
-    m_frontendDispatcher->executionContextCreated(ExecutionContextDescription::create()
-        .setId(executionContextId)
-        .setIsPageContext(isPageContext)
+    auto name = world.name();
+    if (name.isEmpty() && securityOrigin)
+        name = securityOrigin->toRawString();
+
+    m_frontendDispatcher->executionContextCreated(Inspector::Protocol::Runtime::ExecutionContextDescription::create()
+        .setId(injectedScriptManager().injectedScriptIdFor(globalObject))
+        .setType(toProtocol(world.type()))
         .setName(name)
         .setFrameId(frameId)
         .release());
index 54a00e0..2af3739 100644 (file)
@@ -41,6 +41,7 @@ class CallFrame;
 
 namespace WebCore {
 
+class DOMWrapperWorld;
 class Frame;
 class Page;
 class SecurityOrigin;
@@ -61,14 +62,14 @@ public:
 
     // InspectorInstrumentation
     void frameNavigated(Frame&);
-    void didClearWindowObjectInWorld(Frame&);
+    void didClearWindowObjectInWorld(Frame&, DOMWrapperWorld&);
 
 private:
     Inspector::InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) override;
     void muteConsole() override;
     void unmuteConsole() override;
     void reportExecutionContextCreation();
-    void notifyContextCreated(const String& frameId, JSC::JSGlobalObject*, SecurityOrigin*, bool isPageContext);
+    void notifyContextCreated(const String& frameId, JSC::JSGlobalObject*, const DOMWrapperWorld&, SecurityOrigin* = nullptr);
 
     std::unique_ptr<Inspector::RuntimeFrontendDispatcher> m_frontendDispatcher;
     RefPtr<Inspector::RuntimeBackendDispatcher> m_backendDispatcher;
index f481d81..ca909ba 100644 (file)
 #include "SVGPathStringBuilder.h"
 #include "SVGSVGElement.h"
 #include "SWClientConnection.h"
+#include "ScriptController.h"
 #include "ScriptedAnimationController.h"
 #include "ScrollingCoordinator.h"
 #include "ScrollingMomentumCalculator.h"
@@ -3487,6 +3488,14 @@ bool Internals::isFromCurrentWorld(JSC::JSValue value) const
     return isWorldCompatible(*vm.topCallFrame->lexicalGlobalObject(vm), value);
 }
 
+JSC::JSValue Internals::evaluateInWorldIgnoringException(const String& name, const String& source)
+{
+    auto* document = contextDocument();
+    auto& scriptController = document->frame()->script();
+    auto world = ScriptController::createWorld(name);
+    return scriptController.evaluateInWorldIgnoringException(ScriptSourceCode(source), world);
+}
+
 void Internals::setUsesOverlayScrollbars(bool enabled)
 {
     WebCore::DeprecatedGlobalSettings::setUsesOverlayScrollbars(enabled);
index 4edba26..2deb483 100644 (file)
@@ -516,6 +516,7 @@ public:
     Ref<SerializedScriptValue> deserializeBuffer(ArrayBuffer&) const;
 
     bool isFromCurrentWorld(JSC::JSValue) const;
+    JSC::JSValue evaluateInWorldIgnoringException(const String& name, const String& source);
 
     void setUsesOverlayScrollbars(bool);
     void setUsesMockScrollAnimator(bool);
index b52c880..57ea10e 100644 (file)
@@ -548,6 +548,7 @@ enum CompositingPolicy {
     ArrayBuffer serializeObject(SerializedScriptValue object);
 
     boolean isFromCurrentWorld(any obj);
+    any evaluateInWorldIgnoringException(DOMString name, DOMString source);
 
     void setUsesOverlayScrollbars(boolean enabled);
     void setUsesMockScrollAnimator(boolean enabled);
index 04b165c..039597b 100644 (file)
@@ -1,3 +1,146 @@
+2020-01-27  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: unable to evaluate in the isolated world of content scripts injected by safari app extensions
+        https://bugs.webkit.org/show_bug.cgi?id=206110
+        <rdar://problem/16945643>
+
+        Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.
+
+        In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
+        to evaluate in non-normal isolated worlds.
+
+        * UserInterface/Models/ExecutionContext.js:
+        (WI.ExecutionContext):
+        (WI.ExecutionContext.typeFromPayload): Added.
+        (WI.ExecutionContext.prototype.get type): Added.
+        (WI.ExecutionContext.prototype.get isPageContext): Deleted.
+
+        * UserInterface/Models/ExecutionContextList.js:
+        (WI.ExecutionContextList.prototype.add):
+        * UserInterface/Models/Frame.js:
+        (WI.Frame.prototype.addExecutionContext):
+        * UserInterface/Controllers/NetworkManager.js:
+        (WI.NetworkManager.prototype.executionContextCreated):
+        The `Normal` execution context (of which there should only be one) is considered the "page"
+        execution context.
+
+        * UserInterface/Protocol/DirectBackendTarget.js:
+        (WI.DirectBackendTarget):
+        * UserInterface/Protocol/PageTarget.js:
+        (WI.PageTarget):
+        * UserInterface/Protocol/WorkerTarget.js:
+        (WI.WorkerTarget):
+        Default to a `Normal` execution context.
+
+        * UserInterface/Views/QuickConsole.js:
+        (WI.QuickConsole):
+        (WI.QuickConsole.prototype._displayNameForExecutionContext): Added.
+        (WI.QuickConsole.prototype._resolveDesiredActiveExecutionContext): Added.
+        (WI.QuickConsole.prototype._setActiveExecutionContext): Added.
+        (WI.QuickConsole.prototype._updateActiveExecutionContextDisplay): Added.
+        (WI.QuickConsole.prototype._populateActiveExecutionContextNavigationItemContextMenu): Added.
+        (WI.QuickConsole.prototype._handleConsoleSavedResultAliasSettingChanged): Added.
+        (WI.QuickConsole.prototype._handleEngineeringShowInternalExecutionContextsSettingChanged): Added.
+        (WI.QuickConsole.prototype._handleFramePageExecutionContextChanged): Added.
+        (WI.QuickConsole.prototype._handleFrameExecutionContextsCleared): Added.
+        (WI.QuickConsole.prototype._handleDebuggerActiveCallFrameDidChange): Added.
+        (WI.QuickConsole.prototype._handleActiveExecutionContextChanged): Added.
+        (WI.QuickConsole.prototype._handleTransitionPageTarget): Added.
+        (WI.QuickConsole.prototype._handleTargetRemoved): Added.
+        (WI.QuickConsole.prototype._handleInspectedNodeChanged): Added.
+        (WI.QuickConsole.prototype._updateStyles):
+        (WI.QuickConsole.prototype.get navigationBar): Deleted.
+        (WI.QuickConsole.prototype._pageTargetTransitioned): Deleted.
+        (WI.QuickConsole.prototype._initializeMainExecutionContextPathComponent): Deleted.
+        (WI.QuickConsole.prototype.layout): Deleted.
+        (WI.QuickConsole.prototype._preferredNameForFrame): Deleted.
+        (WI.QuickConsole.prototype._selectExecutionContext): Deleted.
+        (WI.QuickConsole.prototype._updateAutomaticExecutionContextPathComponentTooltip): Deleted.
+        (WI.QuickConsole.prototype._executionContextPathComponentsToDisplay): Deleted.
+        (WI.QuickConsole.prototype._rebuildExecutionContextPathComponents): Deleted.
+        (WI.QuickConsole.prototype._framePageExecutionContextsChanged): Deleted.
+        (WI.QuickConsole.prototype._frameExecutionContextsCleared): Deleted.
+        (WI.QuickConsole.prototype._activeExecutionContextChanged): Deleted.
+        (WI.QuickConsole.prototype._createExecutionContextPathComponent): Deleted.
+        (WI.QuickConsole.prototype._compareExecutionContextPathComponents): Deleted.
+        (WI.QuickConsole.prototype._insertOtherExecutionContextPathComponent): Deleted.
+        (WI.QuickConsole.prototype._removeOtherExecutionContextPathComponent): Deleted.
+        (WI.QuickConsole.prototype._insertExecutionContextPathComponentForFrame): Deleted.
+        (WI.QuickConsole.prototype._removeExecutionContextPathComponentForFrame): Deleted.
+        (WI.QuickConsole.prototype._targetAdded): Deleted.
+        (WI.QuickConsole.prototype._targetRemoved): Deleted.
+        (WI.QuickConsole.prototype._pathComponentSelected): Deleted.
+        (WI.QuickConsole.prototype._pathComponentClicked): Deleted.
+        (WI.QuickConsole.prototype._debuggerActiveCallFrameDidChange): Deleted.
+        * UserInterface/Views/QuickConsole.css:
+        (.quick-console > .console-prompt):
+        (.quick-console > .navigation-bar):
+        (.quick-console > .navigation-bar .active-execution-context): Added.
+        (.quick-console > .navigation-bar .active-execution-context > .selector-arrows): Added.
+        (.quick-console > .navigation-bar .active-execution-context:not(.automatic)): Added.
+        (.quick-console > .navigation-bar .active-execution-context:not(.automatic) > .selector-arrows): Added.
+        (.quick-console .execution-context): Deleted.
+        (.quick-console > .navigation-bar > .hierarchical-path .execution-context): Deleted.
+        (.quick-console > .navigation-bar > .hierarchical-path .execution-context .separator): Deleted.
+        (.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context)): Deleted.
+        (.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context) .execution-context): Deleted.
+        (.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context) .execution-context .selector-arrows): Deleted.
+        Replace the `WI.HierarchicalPathNavigationItem` with a plain `WI.NavigationItem` that shows
+        a `WI.ContextMenu` with all valid execution contexts organized as follows:
+
+            Auto - <display name for execution context of inspected DOM node>
+            ----------
+            Main Frame Normal Execution Context
+                All User Exection Contexts for the Main Frame
+                All Internal Exection Contexts for the Main Frame (with the engineering setting)
+            Frames
+                Frame Normal Execution Context
+                    All User Exection Contexts for the Frame
+                    All Internal Exection Contexts for the Frame (with the engineering setting)
+                ...
+            Workers
+                Worker Execution Context
+                ...
+
+        Everything is checkmark selectable other than the separator, "Frames", and "Workers".
+
+        * UserInterface/Controllers/RuntimeManager.js:
+        (WI.RuntimeManager):
+        (WI.RuntimeManager.prototype._frameExecutionContextsCleared): Deleted.
+        Let the UI (`WI.QuickConsole`) decide when to automatically update the active execution
+        context when a frame is removed that owned the active execution context.
+
+        * UserInterface/Controllers/TargetManager.js:
+        (WI.TargetManager.prototype.get workerTargets): Added.
+        (WI.TargetManager.prototype._terminatePageTarget):
+        Convenience function for getting the list of worker targets.
+
+        * UserInterface/Views/GroupNavigationItem.js:
+        (WI.GroupNavigationItem.prototype.update):
+        (WI.GroupNavigationItem.prototype.didAttach):
+        Update the items whenever the group updates.
+
+        * UserInterface/Main.html:
+        * UserInterface/Views/SizesToFitNavigationBar.js: Renamed from Source/WebInspectorUI/UserInterface/Views/QuickConsoleNavigationBar.js.
+        (WI.SizesToFitNavigationBar):
+        (WI.SizesToFitNavigationBar.prototype.get sizesToFit):
+        Rename to allow for other use cases.
+
+        * UserInterface/Views/ContextMenu.js:
+        (WI.ContextSubMenuItem.prototype.appendHeader): Added.
+        Convenience method for creating a disabled item.
+
+        * UserInterface/Base/Setting.js:
+        * UserInterface/Views/SettingsTabContentView.js:
+        (WI.SettingsTabContentView.prototype._createEngineeringSettingsView):
+        Create an engineering setting that controls whether `Internal` execution contexts are shown.
+
+        * UserInterface/Test/InspectorProtocol.js:
+        (InspectorProtocol.addEventListener):
+        (InspectorProtocol.removeEventListener): Added.
+
+        * Localizations/en.lproj/localizedStrings.js:
+
 2020-01-21  Keith Rollin  <krollin@apple.com>
 
         Fix tvOS values in SUPPORTED_PLATFORMS
index 038e065..59f3fa3 100644 (file)
@@ -158,9 +158,8 @@ localizedStrings["Audit Warning: %s"] = "Audit Warning: %s";
 localizedStrings["Audit version: %s"] = "Audit version: %s";
 localizedStrings["Audits"] = "Audits";
 localizedStrings["Author Style Sheet"] = "Author Style Sheet";
-localizedStrings["Auto"] = "Auto";
-localizedStrings["Auto - %s"] = "Auto - %s";
 localizedStrings["Auto Increment"] = "Auto Increment";
+localizedStrings["Auto \u2014 %s"] = "Auto \u2014 %s";
 localizedStrings["Auto-expand"] = "Auto-expand";
 localizedStrings["Automatically continue after evaluating"] = "Automatically continue after evaluating";
 localizedStrings["Available Style Sheets"] = "Available Style Sheets";
@@ -1312,8 +1311,8 @@ localizedStrings["With Object Properties"] = "With Object Properties";
 localizedStrings["Worker"] = "Worker";
 localizedStrings["Worker Thread"] = "Worker Thread";
 localizedStrings["Worker Threads"] = "Worker Threads";
-localizedStrings["Worker \u2014 %s"] = "Worker \u2014 %s";
 localizedStrings["Worker: %s"] = "Worker: %s";
+localizedStrings["Workers"] = "Workers";
 localizedStrings["Wrap lines to editor width"] = "Wrap lines to editor width";
 localizedStrings["XBM"] = "XBM";
 localizedStrings["XHR"] = "XHR";
index 1a53cb9..d4c154d 100644 (file)
@@ -227,6 +227,7 @@ WI.settings = {
     protocolFilterMultiplexingBackendMessages: new WI.Setting("protocol-filter-multiplexing-backend-messages", true),
 
     // Engineering
+    engineeringShowInternalExecutionContexts: new WI.EngineeringSetting("engineering-show-internal-execution-contexts", false),
     engineeringShowInternalScripts: new WI.EngineeringSetting("engineering-show-internal-scripts", false),
     engineeringPauseForInternalScripts: new WI.EngineeringSetting("engineering-pause-for-internal-scripts", false),
     engineeringShowInternalObjectsInHeapSnapshot: new WI.EngineeringSetting("engineering-show-internal-objects-in-heap-snapshot", false),
index 19be99d..b774231 100644 (file)
@@ -1646,7 +1646,7 @@ function isWebKitInternalScript(url)
 
 function isWebKitExtensionScheme(scheme)
 {
-    return scheme && scheme.endsWith("extension");
+    return scheme && scheme.endsWith("-extension");
 }
 
 function isFunctionStringNativeCode(str)
index fbf1d87..877fe05 100644 (file)
@@ -976,16 +976,16 @@ WI.NetworkManager = class NetworkManager extends WI.Object
 
     // RuntimeObserver
 
-    executionContextCreated(contextPayload)
+    executionContextCreated(payload)
     {
-        let frame = this.frameForIdentifier(contextPayload.frameId);
+        let frame = this.frameForIdentifier(payload.frameId);
         console.assert(frame);
         if (!frame)
             return;
 
-        let displayName = contextPayload.name || frame.mainResource.displayName;
+        let type = WI.ExecutionContext.typeFromPayload(payload);
         let target = frame.mainResource.target;
-        let executionContext = new WI.ExecutionContext(target, contextPayload.id, displayName, contextPayload.isPageContext, frame);
+        let executionContext = new WI.ExecutionContext(target, payload.id, type, payload.name, frame);
         frame.addExecutionContext(executionContext);
     }
 
index 80cc1fc..1495d89 100644 (file)
@@ -38,8 +38,6 @@ WI.RuntimeManager = class RuntimeManager extends WI.Object
                     target.RuntimeAgent.setSavedResultAlias(WI.settings.consoleSavedResultAlias.value);
             }
         });
-
-        WI.Frame.addEventListener(WI.Frame.Event.ExecutionContextsCleared, this._frameExecutionContextsCleared, this);
     }
 
     // Static
@@ -189,15 +187,6 @@ WI.RuntimeManager = class RuntimeManager extends WI.Object
 
     // Private
 
-    _frameExecutionContextsCleared(event)
-    {
-        let contexts = event.data.contexts || [];
-
-        let currentContextWasDestroyed = contexts.some((context) => context.id === this._activeExecutionContext.id);
-        if (currentContextWasDestroyed)
-            this.activeExecutionContext = WI.mainTarget.executionContext;
-    }
-
     _tryApplyAwaitConvenience(originalExpression)
     {
         let esprimaSyntaxTree;
index 6c766b3..84fa026 100644 (file)
@@ -44,6 +44,11 @@ WI.TargetManager = class TargetManager extends WI.Object
         return this._cachedTargetsList;
     }
 
+    get workerTargets()
+    {
+        return this.targets.filter((target) => target.type === WI.TargetType.Worker);
+    }
+
     get allTargets()
     {
         return Array.from(this._targets.values());
@@ -275,8 +280,7 @@ WI.TargetManager = class TargetManager extends WI.Object
         console.assert(WI.sharedApp.debuggableType === WI.DebuggableType.WebPage);
 
         // Remove any Worker targets associated with this page.
-        let workerTargets = WI.targets.filter((x) => x.type === WI.TargetType.Worker);
-        for (let workerTarget of workerTargets)
+        for (let workerTarget of this.workerTargets)
             WI.workerManager.workerTerminated(workerTarget.identifier);
 
         WI.pageTarget = null;
index e33d122..dd1d4c6 100644 (file)
     <script src="Views/ProfileView.js"></script>
     <script src="Views/ProgressView.js"></script>
     <script src="Views/QuickConsole.js"></script>
-    <script src="Views/QuickConsoleNavigationBar.js"></script>
     <script src="Views/RadioButtonNavigationItem.js"></script>
     <script src="Views/RangeChart.js"></script>
     <script src="Views/RecordingActionTreeElement.js"></script>
     <script src="Views/ShaderProgramContentView.js"></script>
     <script src="Views/ShaderProgramTreeElement.js"></script>
     <script src="Views/Sidebar.js"></script>
+    <script src="Views/SizesToFitNavigationBar.js"></script>
     <script src="Views/Slider.js"></script>
     <script src="Views/SoftContextMenu.js"></script>
     <script src="Views/SourceCodeTextEditor.js"></script>
index 3bbb2c5..e40fbb1 100644 (file)
 
 WI.ExecutionContext = class ExecutionContext
 {
-    constructor(target, id, name, isPageContext, frame)
+    constructor(target, id, type, name, frame)
     {
         console.assert(target instanceof WI.Target);
         console.assert(typeof id === "number" || id === WI.RuntimeManager.TopLevelExecutionContextIdentifier);
-        console.assert(typeof name === "string");
+        console.assert(Object.values(WI.ExecutionContext.Type).includes(type));
+        console.assert(!name || typeof name === "string");
+        console.assert(frame instanceof WI.Frame || id === WI.RuntimeManager.TopLevelExecutionContextIdentifier);
 
         this._target = target;
         this._id = id;
-        this._name = name;
-        this._isPageContext = isPageContext || false;
+        this._type = type || WI.ExecutionContext.Type.Internal;
+        this._name = name || "";
         this._frame = frame || null;
     }
 
+    // Static
+
+    static typeFromPayload(payload)
+    {
+        // COMPATIBILITY (iOS 13.1): `Runtime.ExecutionContextType` did not exist yet.
+        if (!("type" in payload))
+            return payload.isPageContext ? WI.ExecutionContext.Type.Normal : WI.ExecutionContext.Type.Internal;
+
+        switch (payload.type) {
+        case InspectorBackend.Enum.Runtime.ExecutionContextType.Normal:
+            return WI.ExecutionContext.Type.Normal;
+        case InspectorBackend.Enum.Runtime.ExecutionContextType.User:
+            return WI.ExecutionContext.Type.User;
+        case InspectorBackend.Enum.Runtime.ExecutionContextType.Internal:
+            return WI.ExecutionContext.Type.Internal;
+        }
+
+        console.assert(false, "Unknown Runtime.ExecutionContextType", payload.type);
+        return WI.ExecutionContext.Type.Internal;
+    }
+
     // Public
 
     get target() { return this._target; }
     get id() { return this._id; }
+    get type() { return this._type; }
     get name() { return this._name; }
-    get isPageContext() { return this._isPageContext; }
     get frame() { return this._frame; }
 };
+
+WI.ExecutionContext.Type = {
+    Normal: "normal",
+    User: "user",
+    Internal: "internal",
+};
index 63d9573..b28a32e 100644 (file)
@@ -47,20 +47,17 @@ WI.ExecutionContextList = class ExecutionContextList
     {
         // COMPATIBILITY (iOS 13.0): Older iOS releases will send duplicates.
         // Newer releases will not and this check should be removed eventually.
-        if (context.isPageContext && this._pageExecutionContext) {
+        if (context.type === WI.ExecutionContext.Type.Normal && this._pageExecutionContext) {
             console.assert(context.id === this._pageExecutionContext.id);
-            return false;
+            return;
         }
 
         this._contexts.push(context);
 
-        if (context.isPageContext) {
+        if (context.type === WI.ExecutionContext.Type.Normal && context.target.type === WI.TargetType.Page) {
             console.assert(!this._pageExecutionContext);
             this._pageExecutionContext = context;
-            return true;
         }
-
-        return false;
     }
 
     clear()
index 3587863..db1d723 100644 (file)
@@ -223,9 +223,11 @@ WI.Frame = class Frame extends WI.Object
 
     addExecutionContext(context)
     {
-        var changedPageContext = this._executionContextList.add(context);
+        this._executionContextList.add(context);
 
-        if (changedPageContext)
+        this.dispatchEventToListeners(WI.Frame.Event.ExecutionContextAdded, {context});
+
+        if (this._executionContextList.pageExecutionContext === context)
             this.dispatchEventToListeners(WI.Frame.Event.PageExecutionContextChanged);
     }
 
@@ -509,6 +511,7 @@ WI.Frame.Event = {
     ChildFrameWasRemoved: "frame-child-frame-was-removed",
     AllChildFramesRemoved: "frame-all-child-frames-removed",
     PageExecutionContextChanged: "frame-page-execution-context-changed",
+    ExecutionContextAdded: "frame-execution-context-added",
     ExecutionContextsCleared: "frame-execution-contexts-cleared"
 };
 
index 819a34c..bcfe22a 100644 (file)
@@ -35,7 +35,7 @@ WI.DirectBackendTarget = class DirectBackendTarget extends WI.Target
         let {type, displayName} = DirectBackendTarget.connectionInfoForDebuggable();
         super(parentTarget, targetId, displayName, type, InspectorBackend.backendConnection);
 
-        this._executionContext = new WI.ExecutionContext(this, WI.RuntimeManager.TopLevelContextExecutionIdentifier, displayName, true, null);
+        this._executionContext = new WI.ExecutionContext(this, WI.RuntimeManager.TopLevelContextExecutionIdentifier, WI.ExecutionContext.Type.Normal, displayName);
         this._mainResource = null;
     }
 
index d387661..b37fa3b 100644 (file)
@@ -29,7 +29,6 @@ WI.PageTarget = class PageTarget extends WI.Target
     {
         super(parentTarget, targetId, name, WI.TargetType.Page, connection, options);
 
-        const isPageContext = true;
-        this._executionContext = new WI.ExecutionContext(this, WI.RuntimeManager.TopLevelContextExecutionIdentifier, this.displayName, isPageContext, null);
+        this._executionContext = new WI.ExecutionContext(this, WI.RuntimeManager.TopLevelContextExecutionIdentifier, WI.ExecutionContext.Type.Normal, this.displayName);
     }
 };
index 2c5a012..d5af371 100644 (file)
@@ -29,8 +29,7 @@ WI.WorkerTarget = class WorkerTarget extends WI.Target
     {
         super(parentTarget, workerId, name, WI.TargetType.Worker, connection, options);
 
-        const isPageContext = false;
-        this._executionContext = new WI.ExecutionContext(this, WI.RuntimeManager.TopLevelContextExecutionIdentifier, this.displayName, isPageContext, null);
+        this._executionContext = new WI.ExecutionContext(this, WI.RuntimeManager.TopLevelContextExecutionIdentifier, WI.ExecutionContext.Type.Normal, this.displayName);
     }
 
     // Protected (Target)
index 60ec624..ff57881 100644 (file)
@@ -120,6 +120,27 @@ InspectorProtocol.addEventListener = function(eventTypeOrObject, listener)
         throw new Error("Cannot register the same listener more than once.");
 
     listeners.push(listener);
+    return listener;
+};
+
+InspectorProtocol.removeEventListener = function(eventTypeOrObject, listener)
+{
+    let event = eventTypeOrObject;
+    if (typeof eventTypeOrObject === "object")
+        ({event, listener} = eventTypeOrObject);
+
+    if (typeof event !== "string")
+        throw new Error("Event name must be a string.");
+
+    if (typeof listener !== "function")
+        throw new Error("Event listener must be callable.");
+
+    // Convert to an array of listeners.
+    let listeners = InspectorProtocol.eventHandler[event];
+    if (!listeners)
+        return;
+
+    listeners.removeAll(listener);
 };
 
 InspectorProtocol.checkForError = function(responseObject)
index 3a4c810..a16465d 100644 (file)
@@ -116,6 +116,13 @@ WI.ContextSubMenuItem = class ContextSubMenuItem extends WI.ContextMenuItem
         return item;
     }
 
+    appendHeader(label)
+    {
+        return this.appendItem(label, () => {
+            console.assert(false, "not reached");
+        }, true);
+    }
+
     appendSeparator()
     {
         if (this._items.length)
index ff4bdca..ebb88d8 100644 (file)
@@ -71,6 +71,8 @@ WI.GroupNavigationItem = class GroupNavigationItem extends WI.NavigationItem
     {
         super.update(options);
 
+        this._updateItems();
+
         for (let item of this._navigationItems)
             item.update(options);
     }
@@ -79,6 +81,8 @@ WI.GroupNavigationItem = class GroupNavigationItem extends WI.NavigationItem
     {
         super.didAttach(navigationBar);
 
+        this._updateItems();
+
         for (let item of this._navigationItems)
             item.didAttach(navigationBar);
     }
index f4bf940..27d2d67 100644 (file)
@@ -51,7 +51,7 @@
 
     align-items: flex-start;
 
-    max-height: 150px;
+    max-height: 33vh;
 
     overflow-y: auto;
     overflow-x: hidden;
 }
 
 .quick-console > .navigation-bar {
-    background: transparent;
-    border: none;
-
     height: 21px;
+    border-bottom: none;
 }
 
-.quick-console .execution-context {
-    margin-top: -1px;
-}
-
-.quick-console > .navigation-bar > .hierarchical-path .execution-context {
+.quick-console > .navigation-bar .active-execution-context {
     -webkit-margin-end: 7px;
+    -webkit-padding-start: 5px;
+    -webkit-padding-end: 1px;
+    font-family: -webkit-system-font, sans-serif;
+    font-size: 11px;
 }
 
-.quick-console > .navigation-bar > .hierarchical-path .execution-context .separator {
-    display: none;
-}
-
-.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context) {
-    -webkit-padding-start: 5px;
+.quick-console > .navigation-bar .active-execution-context > .selector-arrows {
+    width: 5px;
+    height: 16px;
+    margin-top: 2px;
+    margin-bottom: 2px;
+    -webkit-margin-start: 4px;
+    -webkit-margin-end: 3px;
+    opacity: 0.6;
 }
 
-.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context) .execution-context {
-    background: var(--selected-background-color);
-    border-radius: 3px;
+.quick-console > .navigation-bar .active-execution-context:not(.automatic) {
+    margin-top: -1px;
+    height: 20px;
+    -webkit-margin-start: 5px;
+    line-height: 20px;
     color: var(--selected-foreground-color);
+    background-color: var(--selected-background-color);
+    border-radius: 3px;
 }
 
-.quick-console > .navigation-bar > .hierarchical-path:not(.automatic-execution-context) .execution-context .selector-arrows {
+.quick-console > .navigation-bar .active-execution-context:not(.automatic) > .selector-arrows {
     color: var(--selected-foreground-color);
     opacity: 1;
 }
index fc9a88b..9d234b2 100644 (file)
@@ -33,17 +33,8 @@ WI.QuickConsole = class QuickConsole extends WI.View
         this._toggleOrFocusKeyboardShortcut.implicitlyPreventsDefault = false;
         this._keyboardShortcutDisabled = false;
 
-        this._automaticExecutionContextPathComponent = this._createExecutionContextPathComponent(null, WI.UIString("Auto"));
-        this._updateAutomaticExecutionContextPathComponentTooltip();
-
-        this._mainExecutionContextPathComponent = null;
-        this._otherExecutionContextPathComponents = [];
-
-        this._frameToPathComponent = new Map;
-        this._targetToPathComponent = new Map;
-
-        this._shouldAutomaticallySelectExecutionContext = true;
-        this._restoreSelectedExecutionContextForFrame = false;
+        this._useExecutionContextOfInspectedNode = InspectorBackend.hasDomain("DOM");
+        this._restoreSelectedExecutionContextForFrame = null;
 
         this.element.classList.add("quick-console");
         this.element.addEventListener("mousedown", this._handleMouseDown.bind(this));
@@ -60,48 +51,45 @@ WI.QuickConsole = class QuickConsole extends WI.View
         // would be for CodeMirror's event handler to pass if it doesn't do anything.
         this.prompt.escapeKeyHandlerWhenEmpty = function() { WI.toggleSplitConsole(); };
 
-        this._navigationBar = new WI.QuickConsoleNavigationBar;
+        this._navigationBar = new WI.SizesToFitNavigationBar;
         this.addSubview(this._navigationBar);
 
-        this._executionContextSelectorItem = new WI.HierarchicalPathNavigationItem;
-        this._executionContextSelectorItem.showSelectorArrows = true;
-        this._navigationBar.addNavigationItem(this._executionContextSelectorItem);
+        this._activeExecutionContextNavigationItemDivider = new WI.DividerNavigationItem;
+        this._navigationBar.addNavigationItem(this._activeExecutionContextNavigationItemDivider);
 
-        this._executionContextSelectorDivider = new WI.DividerNavigationItem;
-        this._navigationBar.addNavigationItem(this._executionContextSelectorDivider);
+        this._activeExecutionContextNavigationItem = new WI.NavigationItem("active-execution-context");
+        WI.addMouseDownContextMenuHandlers(this._activeExecutionContextNavigationItem.element, this._populateActiveExecutionContextNavigationItemContextMenu.bind(this));
+        this._navigationBar.addNavigationItem(this._activeExecutionContextNavigationItem);
 
-        WI.settings.consoleSavedResultAlias.addEventListener(WI.Setting.Event.Changed, this._updateAutomaticExecutionContextPathComponentTooltip, this);
+        this._updateActiveExecutionContextDisplay();
 
-        WI.consoleDrawer.toggleButtonShortcutTooltip(this._toggleOrFocusKeyboardShortcut);
-        WI.consoleDrawer.addEventListener(WI.ConsoleDrawer.Event.CollapsedStateChanged, this._updateStyles, this);
+        WI.settings.consoleSavedResultAlias.addEventListener(WI.Setting.Event.Changed, this._handleConsoleSavedResultAliasSettingChanged, this);
+        WI.settings.engineeringShowInternalExecutionContexts.addEventListener(WI.Setting.Event.Changed, this._handleEngineeringShowInternalExecutionContextsSettingChanged, this);
+
+        WI.Frame.addEventListener(WI.Frame.Event.PageExecutionContextChanged, this._handleFramePageExecutionContextChanged, this);
+        WI.Frame.addEventListener(WI.Frame.Event.ExecutionContextsCleared, this._handleFrameExecutionContextsCleared, this);
 
-        WI.Frame.addEventListener(WI.Frame.Event.PageExecutionContextChanged, this._framePageExecutionContextsChanged, this);
-        WI.Frame.addEventListener(WI.Frame.Event.ExecutionContextsCleared, this._frameExecutionContextsCleared, this);
+        WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._handleDebuggerActiveCallFrameDidChange, this);
 
-        WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this);
+        WI.runtimeManager.addEventListener(WI.RuntimeManager.Event.ActiveExecutionContextChanged, this._handleActiveExecutionContextChanged, this);
 
-        WI.runtimeManager.addEventListener(WI.RuntimeManager.Event.ActiveExecutionContextChanged, this._activeExecutionContextChanged, this);
-        WI.notifications.addEventListener(WI.Notification.TransitionPageTarget, this._pageTargetTransitioned, this);
+        WI.notifications.addEventListener(WI.Notification.TransitionPageTarget, this._handleTransitionPageTarget, this);
 
-        WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._targetAdded, this);
-        WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
+        WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._handleTargetRemoved, this);
 
         WI.domManager.addEventListener(WI.DOMManager.Event.InspectedNodeChanged, this._handleInspectedNodeChanged, this);
 
+        WI.consoleDrawer.toggleButtonShortcutTooltip(this._toggleOrFocusKeyboardShortcut);
+        WI.consoleDrawer.addEventListener(WI.ConsoleDrawer.Event.CollapsedStateChanged, this._updateStyles, this);
         WI.TabBrowser.addEventListener(WI.TabBrowser.Event.SelectedTabContentViewDidChange, this._updateStyles, this);
 
         WI.whenTargetsAvailable().then(() => {
-            this._initializeMainExecutionContextPathComponent();
+            this._updateActiveExecutionContextDisplay();
         });
     }
 
     // Public
 
-    get navigationBar()
-    {
-        return this._navigationBar;
-    }
-
     set keyboardShortcutDisabled(disabled)
     {
         this._keyboardShortcutDisabled = disabled;
@@ -120,75 +108,182 @@ WI.QuickConsole = class QuickConsole extends WI.View
         super.closed();
     }
 
-    _pageTargetTransitioned()
+    // Private
+
+    _displayNameForExecutionContext(context, maxLength = Infinity)
     {
-        this._initializeMainExecutionContextPathComponent();
+        function truncate(string, length) {
+            if (!Number.isFinite(maxLength))
+                return string;
+            return string.trim().truncateMiddle(length);
+        }
+
+        if (context.type === WI.ExecutionContext.Type.Internal)
+            return WI.unlocalizedString("[Internal] ") + context.name;
+
+        let target = context.target;
+        if (target.type === WI.TargetType.Worker)
+            return truncate(target.displayName, maxLength);
+
+        let frame = context.frame;
+        if (frame) {
+            if (context === frame.executionContextList.pageExecutionContext) {
+                let resourceName = frame.mainResource.displayName;
+                let frameName = frame.name;
+                if (frameName) {
+                    // Attempt to show all of the frame name, but ensure that at least 20 characters
+                    // of the resource name are shown as well.
+                    let frameNameMaxLength = Math.max(maxLength - resourceName.length, 20);
+                    return WI.UIString("%s (%s)").format(truncate(frameName, frameNameMaxLength), truncate(resourceName, maxLength - frameNameMaxLength));
+                }
+                return truncate(resourceName, maxLength);
+            }
+        }
+
+        return truncate(context.name, maxLength);
     }
 
-    _initializeMainExecutionContextPathComponent()
+    _resolveDesiredActiveExecutionContext(forceInspectedNode)
     {
-        if (!WI.mainTarget || WI.mainTarget instanceof WI.MultiplexingBackendTarget)
-            return;
-
-        let nextSibling = this._mainExecutionContextPathComponent ? this._mainExecutionContextPathComponent.nextSibling : null;
+        let executionContext = null;
 
-        this._mainExecutionContextPathComponent = this._createExecutionContextPathComponent(WI.mainTarget.executionContext);
-        this._mainExecutionContextPathComponent.previousSibling = this._automaticExecutionContextPathComponent;
-        this._mainExecutionContextPathComponent.nextSibling = nextSibling;
+        if (this._useExecutionContextOfInspectedNode || forceInspectedNode) {
+            let inspectedNode = WI.domManager.inspectedNode;
+            if (inspectedNode) {
+                let frame = inspectedNode.frame;
+                if (frame) {
+                    let pageExecutionContext = frame.pageExecutionContext;
+                    if (pageExecutionContext)
+                        executionContext = pageExecutionContext;
+                }
+            }
+        }
 
-        this._automaticExecutionContextPathComponent.nextSibling = this._mainExecutionContextPathComponent;
+        if (!executionContext && WI.networkManager.mainFrame)
+            executionContext = WI.networkManager.mainFrame.pageExecutionContext;
 
-        this._shouldAutomaticallySelectExecutionContext = true;
-        this._selectExecutionContext(WI.mainTarget.executionContext);
-        this._rebuildExecutionContextPathComponents();
+        return executionContext || WI.mainTarget.executionContext;
     }
 
-    // Protected
-
-    layout()
+    _setActiveExecutionContext(context)
     {
-        // A hard maximum size of 33% of the window.
-        let maximumAllowedHeight = Math.round(window.innerHeight * 0.33);
-        this.prompt.element.style.maxHeight = maximumAllowedHeight + "px";
-    }
+        let wasActive = WI.runtimeManager.activeExecutionContext === context;
 
-    // Private
+        WI.runtimeManager.activeExecutionContext = context;
 
-    _preferredNameForFrame(frame)
-    {
-        if (frame.name)
-            return WI.UIString("%s (%s)").format(frame.name, frame.mainResource.displayName);
-        return frame.mainResource.displayName;
+        if (wasActive)
+            this._updateActiveExecutionContextDisplay();
     }
 
-    _selectExecutionContext(executionContext)
+    _updateActiveExecutionContextDisplay()
     {
-        let preferredName = null;
-
-        let inspectedNode = WI.domManager.inspectedNode;
-        if (inspectedNode) {
-            let frame = inspectedNode.frame;
-            if (frame) {
-                if (this._shouldAutomaticallySelectExecutionContext)
-                    executionContext = frame.pageExecutionContext;
-                preferredName = this._preferredNameForFrame(frame);
-            }
+        let toggleHidden = (hidden) => {
+            this._activeExecutionContextNavigationItemDivider.hidden = hidden;
+            this._activeExecutionContextNavigationItem.hidden = hidden;
+        };
+
+        if (WI.debuggerManager.activeCallFrame) {
+            toggleHidden(true);
+            return;
+        }
+
+        if (!WI.runtimeManager.activeExecutionContext || !WI.networkManager.mainFrame) {
+            toggleHidden(true);
+            return;
+        }
+
+        if (WI.networkManager.frames.length === 1 && WI.networkManager.mainFrame.executionContextList.contexts.length === 1 && !WI.targetManager.workerTargets.length) {
+            toggleHidden(true);
+            return;
         }
 
-        if (!executionContext)
-            executionContext = WI.mainTarget.executionContext;
+        const maxLength = 40;
+
+        if (this._useExecutionContextOfInspectedNode) {
+            this._activeExecutionContextNavigationItem.element.classList.add("automatic");
+            this._activeExecutionContextNavigationItem.element.textContent = WI.UIString("Auto \u2014 %s").format(this._displayNameForExecutionContext(WI.runtimeManager.activeExecutionContext, maxLength));
+            this._activeExecutionContextNavigationItem.tooltip = WI.UIString("Execution context for %s").format(WI.RuntimeManager.preferredSavedResultPrefix() + "0");
+        } else {
+            this._activeExecutionContextNavigationItem.element.classList.remove("automatic");
+            this._activeExecutionContextNavigationItem.element.textContent = this._displayNameForExecutionContext(WI.runtimeManager.activeExecutionContext, maxLength);
+            this._activeExecutionContextNavigationItem.tooltip = this._displayNameForExecutionContext(WI.runtimeManager.activeExecutionContext);
+        }
 
-        this._automaticExecutionContextPathComponent.displayName = WI.UIString("Auto - %s").format(preferredName || executionContext.name);
+        this._activeExecutionContextNavigationItem.element.appendChild(WI.ImageUtilities.useSVGSymbol("Images/UpDownArrows.svg", "selector-arrows"));
 
-        let changed = WI.runtimeManager.activeExecutionContext !== executionContext;
-        if (changed)
-            WI.runtimeManager.activeExecutionContext = executionContext;
-        return changed;
+        toggleHidden(false);
     }
 
-    _updateAutomaticExecutionContextPathComponentTooltip()
+    _populateActiveExecutionContextNavigationItemContextMenu(contextMenu)
     {
-        this._automaticExecutionContextPathComponent.tooltip = WI.UIString("Execution context for %s").format(WI.RuntimeManager.preferredSavedResultPrefix() + "0");
+        const maxLength = 120;
+
+        let activeExecutionContext = WI.runtimeManager.activeExecutionContext;
+
+        if (InspectorBackend.hasDomain("DOM")) {
+            let executionContextForInspectedNode = this._resolveDesiredActiveExecutionContext(true);
+            contextMenu.appendCheckboxItem(WI.UIString("Auto \u2014 %s").format(this._displayNameForExecutionContext(executionContextForInspectedNode, maxLength)), () => {
+                this._useExecutionContextOfInspectedNode = true;
+                this._setActiveExecutionContext(executionContextForInspectedNode);
+            }, this._useExecutionContextOfInspectedNode);
+
+            contextMenu.appendSeparator();
+        }
+
+        let indent = 0;
+        let addExecutionContext = (context) => {
+            if (context.type === WI.ExecutionContext.Type.Internal && !WI.settings.engineeringShowInternalExecutionContexts.value)
+                return;
+
+            let additionalIndent = (context.frame && context !== context.frame.executionContextList.pageExecutionContext) || context.type !== WI.ExecutionContext.Type.Normal;
+
+            // Mimic macOS `-[NSMenuItem setIndentationLevel]`.
+            contextMenu.appendCheckboxItem("   ".repeat(indent + additionalIndent) + this._displayNameForExecutionContext(context, maxLength), () => {
+                this._useExecutionContextOfInspectedNode = false;
+                this._setActiveExecutionContext(context);
+            }, activeExecutionContext === context);
+        };
+
+        let addExecutionContextsForFrame = (frame) => {
+            let pageExecutionContext = frame.executionContextList.pageExecutionContext;
+
+            let contexts = frame.executionContextList.contexts.sort((a, b) => {
+                if (a === pageExecutionContext)
+                    return -1;
+                if (b === pageExecutionContext)
+                    return 1;
+
+                const executionContextTypeRanking = [
+                    WI.ExecutionContext.Type.Normal,
+                    WI.ExecutionContext.Type.User,
+                    WI.ExecutionContext.Type.Internal,
+                ];
+                return executionContextTypeRanking.indexOf(a.type) - executionContextTypeRanking.indexOf(b.type);
+            });
+            for (let context of contexts)
+                addExecutionContext(context);
+        };
+
+        let mainFrame = WI.networkManager.mainFrame;
+        addExecutionContextsForFrame(mainFrame);
+
+        indent = 1;
+
+        let otherFrames = WI.networkManager.frames.filter((frame) => frame !== mainFrame && frame.executionContextList.pageExecutionContext);
+        if (otherFrames.length) {
+            contextMenu.appendHeader(WI.UIString("Frames"));
+
+            for (let frame of otherFrames)
+                addExecutionContextsForFrame(frame);
+        }
+
+        let workerTargets = WI.targetManager.workerTargets;
+        if (workerTargets.length) {
+            contextMenu.appendHeader(WI.UIString("Workers"));
+
+            for (let target of workerTargets)
+                addExecutionContext(target.executionContext);
+        }
     }
 
     _handleMouseDown(event)
@@ -226,228 +321,87 @@ WI.QuickConsole = class QuickConsole extends WI.View
         }
     }
 
-    _executionContextPathComponentsToDisplay()
-    {
-        // If we are in the debugger the console will use the active call frame, don't show the selector.
-        if (WI.debuggerManager.activeCallFrame)
-            return [];
-
-        // If there is only the Main ExecutionContext, don't show the selector.
-        if (!this._otherExecutionContextPathComponents.length)
-            return [];
-
-        if (this._shouldAutomaticallySelectExecutionContext)
-            return [this._automaticExecutionContextPathComponent];
-
-        if (WI.runtimeManager.activeExecutionContext === WI.mainTarget.executionContext)
-            return [this._mainExecutionContextPathComponent];
-
-        return this._otherExecutionContextPathComponents.filter((component) => component.representedObject === WI.runtimeManager.activeExecutionContext);
-    }
-
-    _rebuildExecutionContextPathComponents()
+    _handleConsoleSavedResultAliasSettingChanged()
     {
-        let components = this._executionContextPathComponentsToDisplay();
-        let isEmpty = !components.length;
-
-        this._executionContextSelectorItem.element.classList.toggle("automatic-execution-context", this._shouldAutomaticallySelectExecutionContext);
-        this._executionContextSelectorItem.components = components;
-
-        this._executionContextSelectorItem.hidden = isEmpty;
-        this._executionContextSelectorDivider.hidden = isEmpty;
-
+        this._updateActiveExecutionContextDisplay();
     }
 
-    _framePageExecutionContextsChanged(event)
+    _handleEngineeringShowInternalExecutionContextsSettingChanged(event)
     {
-        let frame = event.target;
-
-        let newExecutionContextPathComponent = this._insertExecutionContextPathComponentForFrame(frame);
-
-        if (this._restoreSelectedExecutionContextForFrame === frame) {
-            this._restoreSelectedExecutionContextForFrame = null;
+        if (WI.runtimeManager.activeExecutionContext.type !== WI.ExecutionContext.Type.Internal)
+            return;
 
-            this._selectExecutionContext(newExecutionContextPathComponent.representedObject);
-        }
+        this._useExecutionContextOfInspectedNode = InspectorBackend.hasDomain("DOM");
+        this._setActiveExecutionContext(this._resolveDesiredActiveExecutionContext());
     }
 
-    _frameExecutionContextsCleared(event)
+    _handleFramePageExecutionContextChanged(event)
     {
-        let frame = event.target;
+        if (this._restoreSelectedExecutionContextForFrame !== event.target)
+            return;
 
-        // If this frame is navigating and it is selected in the UI we want to reselect its new item after navigation.
-        if (event.data.committingProvisionalLoad && !this._restoreSelectedExecutionContextForFrame) {
-            let executionContextPathComponent = this._frameToPathComponent.get(frame);
-            if (executionContextPathComponent && executionContextPathComponent.representedObject === WI.runtimeManager.activeExecutionContext) {
-                this._restoreSelectedExecutionContextForFrame = frame;
-                // As a fail safe, if the frame never gets an execution context, clear the restore value.
-                setTimeout(() => {
-                    this._restoreSelectedExecutionContextForFrame = false;
-                }, 10);
-            }
-        }
+        this._restoreSelectedExecutionContextForFrame = null;
 
-        this._removeExecutionContextPathComponentForFrame(frame);
-    }
+        let {context} = event.data;
 
-    _activeExecutionContextChanged(event)
-    {
-        this._rebuildExecutionContextPathComponents();
+        this._useExecutionContextOfInspectedNode = false;
+        this._setActiveExecutionContext(context);
     }
 
-    _createExecutionContextPathComponent(executionContext, preferredName)
+    _handleFrameExecutionContextsCleared(event)
     {
-        console.assert(!executionContext || executionContext instanceof WI.ExecutionContext);
+        let {committingProvisionalLoad, contexts} = event.data;
 
-        let pathComponent = new WI.HierarchicalPathComponent(preferredName || executionContext.name, "execution-context", executionContext, true, true);
-        pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
-        pathComponent.addEventListener(WI.HierarchicalPathComponent.Event.Clicked, this._pathComponentClicked, this);
-        pathComponent.truncatedDisplayNameLength = 50;
-        return pathComponent;
-    }
-
-    _compareExecutionContextPathComponents(a, b)
-    {
-        let aExecutionContext = a.representedObject;
-        let bExecutionContext = b.representedObject;
-
-        // "Targets" (workers) at the top.
-        let aNonMainTarget = aExecutionContext.target !== WI.mainTarget;
-        let bNonMainTarget = bExecutionContext.target !== WI.mainTarget;
-        if (aNonMainTarget && !bNonMainTarget)
-            return -1;
-        if (bNonMainTarget && !aNonMainTarget)
-            return 1;
-        if (aNonMainTarget && bNonMainTarget)
-            return a.displayName.extendedLocaleCompare(b.displayName);
-
-        // "Main Frame" follows.
-        if (aExecutionContext === WI.mainTarget.executionContext)
-            return -1;
-        if (bExecutionContext === WI.mainTarget.executionContext)
-            return 1;
-
-        // Only Frame contexts remain.
-        console.assert(aExecutionContext.frame);
-        console.assert(bExecutionContext.frame);
-
-        // Frames with a name above frames without a name.
-        if (aExecutionContext.frame.name && !bExecutionContext.frame.name)
-            return -1;
-        if (!aExecutionContext.frame.name && bExecutionContext.frame.name)
-            return 1;
-
-        return a.displayName.extendedLocaleCompare(b.displayName);
-    }
+        let hasActiveExecutionContext = contexts.some((context) => context === WI.runtimeManager.activeExecutionContext);
+        if (!hasActiveExecutionContext)
+            return;
 
-    _insertOtherExecutionContextPathComponent(executionContextPathComponent)
-    {
-        let index = insertionIndexForObjectInListSortedByFunction(executionContextPathComponent, this._otherExecutionContextPathComponents, this._compareExecutionContextPathComponents);
+        // If this frame is navigating and it is selected in the UI we want to reselect its new item after navigation.
+        if (committingProvisionalLoad && !this._restoreSelectedExecutionContextForFrame) {
+            this._restoreSelectedExecutionContextForFrame = event.target;
 
-        let prev = index > 0 ? this._otherExecutionContextPathComponents[index - 1] : this._mainExecutionContextPathComponent;
-        let next = this._otherExecutionContextPathComponents[index] || null;
-        if (prev) {
-            prev.nextSibling = executionContextPathComponent;
-            executionContextPathComponent.previousSibling = prev;
-        }
-        if (next) {
-            next.previousSibling = executionContextPathComponent;
-            executionContextPathComponent.nextSibling = next;
+            // As a fail safe, if the frame never gets an execution context, clear the restore value.
+            setTimeout(() => {
+                this._restoreSelectedExecutionContextForFrame = null;
+            }, 10);
+            return;
         }
 
-        this._otherExecutionContextPathComponents.splice(index, 0, executionContextPathComponent);
-
-        this._rebuildExecutionContextPathComponents();
+        this._useExecutionContextOfInspectedNode = InspectorBackend.hasDomain("DOM");
+        this._setActiveExecutionContext(this._resolveDesiredActiveExecutionContext());
     }
 
-    _removeOtherExecutionContextPathComponent(executionContextPathComponent)
+    _handleDebuggerActiveCallFrameDidChange(event)
     {
-        executionContextPathComponent.removeEventListener(WI.HierarchicalPathComponent.Event.SiblingWasSelected, this._pathComponentSelected, this);
-        executionContextPathComponent.removeEventListener(WI.HierarchicalPathComponent.Event.Clicked, this._pathComponentClicked, this);
-
-        let prev = executionContextPathComponent.previousSibling;
-        let next = executionContextPathComponent.nextSibling;
-        if (prev)
-            prev.nextSibling = next;
-        if (next)
-            next.previousSibling = prev;
-
-        this._otherExecutionContextPathComponents.remove(executionContextPathComponent, true);
-
-        this._rebuildExecutionContextPathComponents();
+        this._updateActiveExecutionContextDisplay();
     }
 
-    _insertExecutionContextPathComponentForFrame(frame)
+    _handleActiveExecutionContextChanged(event)
     {
-        if (frame.isMainFrame())
-            return this._mainExecutionContextPathComponent;
-
-        let executionContextPathComponent = this._createExecutionContextPathComponent(frame.pageExecutionContext, this._preferredNameForFrame(frame));
-        this._insertOtherExecutionContextPathComponent(executionContextPathComponent);
-        this._frameToPathComponent.set(frame, executionContextPathComponent);
-        return executionContextPathComponent;
+        this._updateActiveExecutionContextDisplay();
     }
 
-    _removeExecutionContextPathComponentForFrame(frame)
+    _handleTransitionPageTarget()
     {
-        if (frame.isMainFrame()) {
-            this._shouldAutomaticallySelectExecutionContext = true;
-            this._initializeMainExecutionContextPathComponent();
-            return;
-        }
-
-        let executionContextPathComponent = this._frameToPathComponent.take(frame);
-        this._removeOtherExecutionContextPathComponent(executionContextPathComponent);
+        this._updateActiveExecutionContextDisplay();
     }
 
-    _targetAdded(event)
+    _handleTargetRemoved(event)
     {
-        let target = event.data.target;
-        if (target.type !== WI.TargetType.Worker)
+        let {target} = event.data;
+        if (target !== WI.runtimeManager.activeExecutionContext)
             return;
 
-        console.assert(target.type === WI.TargetType.Worker);
-        let preferredName = WI.UIString("Worker \u2014 %s").format(target.displayName);
-        let executionContextPathComponent = this._createExecutionContextPathComponent(target.executionContext, preferredName);
-
-        this._targetToPathComponent.set(target, executionContextPathComponent);
-        this._insertOtherExecutionContextPathComponent(executionContextPathComponent);
+        this._useExecutionContextOfInspectedNode = InspectorBackend.hasDomain("DOM");
+        this._setActiveExecutionContext(this._resolveDesiredActiveExecutionContext());
     }
 
-    _targetRemoved(event)
+    _handleInspectedNodeChanged(event)
     {
-        let target = event.data.target;
-        if (target.type !== WI.TargetType.Worker)
+        if (!this._useExecutionContextOfInspectedNode)
             return;
 
-        let executionContextPathComponent = this._targetToPathComponent.take(target);
-
-        if (WI.runtimeManager.activeExecutionContext === executionContextPathComponent.representedObject) {
-            this._shouldAutomaticallySelectExecutionContext = true;
-            this._selectExecutionContext();
-        }
-
-        this._removeOtherExecutionContextPathComponent(executionContextPathComponent);
-    }
-
-    _pathComponentSelected(event)
-    {
-        this._shouldAutomaticallySelectExecutionContext = event.data.pathComponent === this._automaticExecutionContextPathComponent;
-
-        // Only manually rebuild the execution context path components if the newly selected
-        // execution context matches the previously selected one.
-        if (!this._selectExecutionContext(event.data.pathComponent.representedObject))
-            this._rebuildExecutionContextPathComponents();
-    }
-
-    _pathComponentClicked(event)
-    {
-        this.prompt.focus();
-    }
-
-    _debuggerActiveCallFrameDidChange(event)
-    {
-        this._rebuildExecutionContextPathComponents();
+        this._setActiveExecutionContext(this._resolveDesiredActiveExecutionContext());
     }
 
     _toggleOrFocus(event)
@@ -468,9 +422,4 @@ WI.QuickConsole = class QuickConsole extends WI.View
     {
         this.element.classList.toggle("showing-log", WI.isShowingConsoleTab() || WI.isShowingSplitConsole());
     }
-
-    _handleInspectedNodeChanged(event)
-    {
-        this._selectExecutionContext(WI.runtimeManager.activeExecutionContext);
-    }
 };
index ccd9024..a7d3940 100644 (file)
@@ -405,7 +405,10 @@ WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
         let elementsGroup = engineeringSettingsView.addGroup(WI.unlocalizedString("Elements:"));
         elementsGroup.addSetting(WI.settings.engineeringAllowEditingUserAgentShadowTrees, WI.unlocalizedString("Allow editing UserAgent shadow trees"));
 
+        engineeringSettingsView.addSeparator();
+
         let debuggingGroup = engineeringSettingsView.addGroup(WI.unlocalizedString("Debugging:"));
+        debuggingGroup.addSetting(WI.settings.engineeringShowInternalExecutionContexts, WI.unlocalizedString("Show WebKit-internal execution contexts"));
         debuggingGroup.addSetting(WI.settings.engineeringShowInternalScripts, WI.unlocalizedString("Show WebKit-internal scripts"));
         debuggingGroup.addSetting(WI.settings.engineeringPauseForInternalScripts, WI.unlocalizedString("Pause in WebKit-internal scripts"));
 
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WI.QuickConsoleNavigationBar = class QuickConsoleNavigationBar extends WI.NavigationBar
+WI.SizesToFitNavigationBar = class SizesToFitNavigationBar extends WI.NavigationBar
 {
     get sizesToFit()
     {
         return true;
     }
-
-    addNavigationItem(navigationItem)
-    {
-        // Add new navigation items to the left.
-        return this.insertNavigationItem(navigationItem, 0);
-    }
 };
index 77637b7..ea2dc32 100644 (file)
@@ -1,3 +1,25 @@
+2020-01-27  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: unable to evaluate in the isolated world of content scripts injected by safari app extensions
+        https://bugs.webkit.org/show_bug.cgi?id=206110
+        <rdar://problem/16945643>
+
+        Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.
+
+        In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
+        to evaluate in non-normal isolated worlds.
+
+        * WebProcess/InjectedBundle/InjectedBundleScriptWorld.h:
+        * WebProcess/InjectedBundle/InjectedBundleScriptWorld.cpp:
+        (WebKit::InjectedBundleScriptWorld::create):
+
+        * WebProcess/UserContent/WebUserContentController.cpp:
+        (WebKit::WebUserContentController::addUserContentWorlds):
+        * WebProcess/InjectedBundle/API/glib/WebKitScriptWorld.cpp:
+        (webkit_script_world_new):
+        (webkit_script_world_new_with_name):
+        Treat isolated worlds created by API calls as `User` worlds.
+
 2020-01-27  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed, revert r253984 as it appears to be causing assertion leaks.
index 85f3b2f..ff276ee 100644 (file)
@@ -152,7 +152,7 @@ WebKitScriptWorld* webkit_script_world_get_default(void)
  */
 WebKitScriptWorld* webkit_script_world_new(void)
 {
-    return webkitScriptWorldCreate(InjectedBundleScriptWorld::create());
+    return webkitScriptWorldCreate(InjectedBundleScriptWorld::create(InjectedBundleScriptWorld::Type::User));
 }
 
 /**
@@ -173,7 +173,7 @@ WebKitScriptWorld* webkit_script_world_new_with_name(const char* name)
 {
     g_return_val_if_fail(name, nullptr);
 
-    return webkitScriptWorldCreate(InjectedBundleScriptWorld::create(String::fromUTF8(name)));
+    return webkitScriptWorldCreate(InjectedBundleScriptWorld::create(String::fromUTF8(name), InjectedBundleScriptWorld::Type::User));
 }
 
 /**
index 9cd3a4a..dbf35b4 100644 (file)
@@ -50,14 +50,14 @@ static String uniqueWorldName()
     return makeString("UniqueWorld_", uniqueWorldNameNumber++);
 }
 
-Ref<InjectedBundleScriptWorld> InjectedBundleScriptWorld::create()
+Ref<InjectedBundleScriptWorld> InjectedBundleScriptWorld::create(Type type)
 {
-    return adoptRef(*new InjectedBundleScriptWorld(ScriptController::createWorld(), uniqueWorldName()));
+    return InjectedBundleScriptWorld::create(uniqueWorldName(), type);
 }
 
-Ref<InjectedBundleScriptWorld> InjectedBundleScriptWorld::create(const String& name)
+Ref<InjectedBundleScriptWorld> InjectedBundleScriptWorld::create(const String& name, Type type)
 {
-    return adoptRef(*new InjectedBundleScriptWorld(ScriptController::createWorld(), name));
+    return adoptRef(*new InjectedBundleScriptWorld(ScriptController::createWorld(name, type == Type::User ? ScriptController::WorldType::User : ScriptController::WorldType::Internal), name));
 }
 
 Ref<InjectedBundleScriptWorld> InjectedBundleScriptWorld::getOrCreate(DOMWrapperWorld& world)
index c8b6656..243b7cd 100644 (file)
@@ -39,8 +39,9 @@ namespace WebKit {
 
 class InjectedBundleScriptWorld : public API::ObjectImpl<API::Object::Type::BundleScriptWorld> {
 public:
-    static Ref<InjectedBundleScriptWorld> create();
-    static Ref<InjectedBundleScriptWorld> create(const String&);
+    enum class Type { User, Internal };
+    static Ref<InjectedBundleScriptWorld> create(Type = Type::Internal);
+    static Ref<InjectedBundleScriptWorld> create(const String& name, Type = Type::Internal);
     static Ref<InjectedBundleScriptWorld> getOrCreate(WebCore::DOMWrapperWorld&);
     static InjectedBundleScriptWorld* find(const String&);
     static InjectedBundleScriptWorld& normalWorld();
index 69294ad..9549730 100644 (file)
@@ -115,7 +115,7 @@ void WebUserContentController::addUserContentWorld(const std::pair<ContentWorldI
         if (auto* existingWorld = InjectedBundleScriptWorld::find(world.second))
             return std::make_pair(Ref<InjectedBundleScriptWorld>(*existingWorld), 1);
 #endif
-        return std::make_pair(InjectedBundleScriptWorld::create(world.second), 1);
+        return std::make_pair(InjectedBundleScriptWorld::create(world.second, InjectedBundleScriptWorld::Type::User), 1);
     });
 }
 
index ca07122..c5a28c5 100644 (file)
@@ -1,3 +1,18 @@
+2020-01-27  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: unable to evaluate in the isolated world of content scripts injected by safari app extensions
+        https://bugs.webkit.org/show_bug.cgi?id=206110
+        <rdar://problem/16945643>
+
+        Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.
+
+        In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
+        to evaluate in non-normal isolated worlds.
+
+        * WebView/WebScriptWorld.mm:
+        (-[WebScriptWorld init]):
+        Treat isolated worlds created by API calls as `User` worlds.
+
 2020-01-27  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
index 24d6a7e..1180802 100644 (file)
@@ -66,7 +66,7 @@ static WorldMap& allWorlds()
 
 - (id)init
 {
-    return [self initWithWorld:WebCore::ScriptController::createWorld()];
+    return [self initWithWorld:WebCore::ScriptController::createWorld("WebScriptWorld"_s, WebCore::ScriptController::WorldType::User)];
 }
 
 - (void)unregisterWorld
index 00e0da2..dcbdbef 100644 (file)
@@ -1,3 +1,18 @@
+2020-01-27  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: unable to evaluate in the isolated world of content scripts injected by safari app extensions
+        https://bugs.webkit.org/show_bug.cgi?id=206110
+        <rdar://problem/16945643>
+
+        Reviewed by Timothy Hatcher, Joseph Pecoraro, and Brian Burg.
+
+        In addition to evaluating in subframe execution contexts, add the ability for Web Inspector
+        to evaluate in non-normal isolated worlds.
+
+        * WebScriptWorld.cpp:
+        (WebScriptWorld::createInstance):
+        Treat isolated worlds created by API calls as `User` worlds.
+
 2020-01-27  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
index 1a398ad..268101a 100644 (file)
@@ -67,7 +67,7 @@ WebScriptWorld* WebScriptWorld::standardWorld()
 
 COMPtr<WebScriptWorld> WebScriptWorld::createInstance()
 {
-    return createInstance(ScriptController::createWorld());
+    return createInstance(ScriptController::createWorld("WebScriptWorld"_s, WebCore::ScriptController::WorldType::User));
 }
 
 COMPtr<WebScriptWorld> WebScriptWorld::createInstance(RefPtr<DOMWrapperWorld>&& world)