Commit 304237d6 authored by Nate Chapin's avatar Nate Chapin Committed by Commit Bot

Implement nested dedicated workers

This change exposes the Worker() constructor to dedicated workers,
allowing them to create descendant workers.

Chrome feature entry:
https://www.chromestatus.com/feature/6080438103703552

Intent to Implement and Ship:
https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/KZx0i3e5nZM

Bug: 31666
Change-Id: I449978641f7ee4afba2a1087da91d84b78559abb
Reviewed-on: https://chromium-review.googlesource.com/953746Reviewed-by: default avatarOjan Vafai <ojan@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarHiroki Nakagawa <nhiroki@chromium.org>
Commit-Queue: Nate Chapin <japhet@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576225}
parent fac6d427
......@@ -28,10 +28,10 @@ namespace {
class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider {
public:
DedicatedWorkerHost(int process_id,
int parent_render_frame_id,
int ancestor_render_frame_id,
const url::Origin& origin)
: process_id_(process_id),
parent_render_frame_id_(parent_render_frame_id),
ancestor_render_frame_id_(ancestor_render_frame_id),
origin_(origin) {
RegisterMojoInterfaces();
}
......@@ -58,18 +58,21 @@ class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider {
&DedicatedWorkerHost::CreateWebSocket, base::Unretained(this)));
registry_.AddInterface(base::BindRepeating(
&DedicatedWorkerHost::CreateUsbDeviceManager, base::Unretained(this)));
registry_.AddInterface(base::BindRepeating(
&DedicatedWorkerHost::CreateDedicatedWorker, base::Unretained(this)));
}
void CreateUsbDeviceManager(device::mojom::UsbDeviceManagerRequest request) {
auto* host =
RenderFrameHostImpl::FromID(process_id_, parent_render_frame_id_);
RenderFrameHostImpl::FromID(process_id_, ancestor_render_frame_id_);
GetContentClient()->browser()->CreateUsbDeviceManager(host,
std::move(request));
}
void CreateWebSocket(network::mojom::WebSocketRequest request) {
network::mojom::AuthenticationHandlerPtr auth_handler;
auto* frame = RenderFrameHost::FromID(process_id_, parent_render_frame_id_);
auto* frame =
RenderFrameHost::FromID(process_id_, ancestor_render_frame_id_);
if (!frame) {
// In some cases |frame| can be null. In such cases the worker will
// soon be terminated too, so let's abort the connection.
......@@ -81,13 +84,22 @@ class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider {
GetContentClient()->browser()->WillCreateWebSocket(frame, &request,
&auth_handler);
WebSocketManager::CreateWebSocket(process_id_, parent_render_frame_id_,
WebSocketManager::CreateWebSocket(process_id_, ancestor_render_frame_id_,
origin_, std::move(auth_handler),
std::move(request));
}
void CreateDedicatedWorker(
blink::mojom::DedicatedWorkerFactoryRequest request) {
CreateDedicatedWorkerHostFactory(process_id_, ancestor_render_frame_id_,
origin_, std::move(request));
}
const int process_id_;
const int parent_render_frame_id_;
// ancestor_render_frame_id_ is the id of the frame that owns this worker,
// either directly, or (in the case of nested workers) indirectly via a tree
// of dedicated workers.
const int ancestor_render_frame_id_;
const url::Origin origin_;
service_manager::BinderRegistry registry_;
......@@ -100,10 +112,10 @@ class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider {
class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory {
public:
DedicatedWorkerFactoryImpl(int process_id,
int parent_render_frame_id,
int ancestor_render_frame_id,
const url::Origin& parent_context_origin)
: process_id_(process_id),
parent_render_frame_id_(parent_render_frame_id),
ancestor_render_frame_id_(ancestor_render_frame_id),
parent_context_origin_(parent_context_origin) {}
// blink::mojom::DedicatedWorkerFactory:
......@@ -114,7 +126,7 @@ class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory {
// with the request for |DedicatedWorkerFactory|, enforce that the worker's
// origin either matches the creating document's origin, or is unique.
mojo::MakeStrongBinding(std::make_unique<DedicatedWorkerHost>(
process_id_, parent_render_frame_id_, origin),
process_id_, ancestor_render_frame_id_, origin),
FilterRendererExposedInterfaces(
blink::mojom::kNavigation_DedicatedWorkerSpec,
process_id_, std::move(request)));
......@@ -122,7 +134,7 @@ class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory {
private:
const int process_id_;
const int parent_render_frame_id_;
const int ancestor_render_frame_id_;
const url::Origin parent_context_origin_;
DISALLOW_COPY_AND_ASSIGN(DedicatedWorkerFactoryImpl);
......@@ -132,11 +144,11 @@ class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory {
void CreateDedicatedWorkerHostFactory(
int process_id,
int parent_render_frame_id,
int ancestor_render_frame_id,
const url::Origin& origin,
blink::mojom::DedicatedWorkerFactoryRequest request) {
mojo::MakeStrongBinding(std::make_unique<DedicatedWorkerFactoryImpl>(
process_id, parent_render_frame_id, origin),
process_id, ancestor_render_frame_id, origin),
std::move(request));
}
......
......@@ -441,6 +441,9 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs(
WebRuntimeFeatures::EnableOffMainThreadWebSocket(
base::FeatureList::IsEnabled(features::kOffMainThreadWebSocket));
WebRuntimeFeatures::EnableNestedWorkers(
base::FeatureList::IsEnabled(blink::features::kNestedWorkers));
if (base::FeatureList::IsEnabled(
features::kExperimentalProductivityFeatures)) {
WebRuntimeFeatures::EnableExperimentalProductivityFeatures(true);
......
......@@ -222,6 +222,7 @@
"renderer": [
"blink.mojom.BudgetService",
"blink.mojom.CacheStorage",
"blink.mojom.DedicatedWorkerFactory",
"blink.mojom.LockManager",
"blink.mojom.NotificationService",
"blink.mojom.PermissionService",
......
......@@ -3520,6 +3520,10 @@ crbug.com/655458 external/wpt/workers/interfaces/WorkerGlobalScope/onerror/propa
crbug.com/655458 external/wpt/workers/interfaces/WorkerUtils/importScripts/006.html [ Failure Timeout ]
crbug.com/655458 external/wpt/workers/semantics/multiple-workers/007.html [ Timeout ]
# This fails because AllowedByNoSniff::MimeTypeAsScript() blocks the nested worker's
# worker script, because the script url has a .html file extension.
crbug.com/655458 external/wpt/workers/semantics/multiple-workers/003.html [ Timeout ]
crbug.com/490511 external/wpt/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-networkState.html [ Timeout ]
crbug.com/435547 http/tests/cachestorage/serviceworker/ignore-search-with-credentials.html [ Skip ]
......
......@@ -612,17 +612,17 @@
{
"prefix": "off-main-thread-websocket",
"base": "external/wpt/websockets",
"args": ["--disable-features=OffMainThreadWebSocket"]
"args": ["--disable-features=OffMainThreadWebSocket,NestedWorkers"]
},
{
"prefix": "off-main-thread-websocket",
"base": "http/tests/websocket",
"args": ["--disable-features=OffMainThreadWebSocket"]
"args": ["--disable-features=OffMainThreadWebSocket,NestedWorkers"]
},
{
"prefix": "off-main-thread-websocket",
"base": "http/tests/security/mixedContent/websocket",
"args": ["--disable-features=OffMainThreadWebSocket"]
"args": ["--disable-features=OffMainThreadWebSocket,NestedWorkers"]
},
{
"prefix": "webrtc-wpt-unified-plan",
......
This is a testharness.js-based test.
FAIL postMessaging to a dedicated sub-worker allows them to see each others' modifications Worker is not defined
FAIL postMessaging to a dedicated sub-worker allows them to see each others' modifications promise_test: Unhandled rejection with value: object "ReferenceError: SharedArrayBuffer is not defined"
Harness: the test ran to completion.
This is a testharness.js-based test.
Found 101 tests; 60 PASS, 41 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 101 tests; 61 PASS, 40 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS load nested browsing context <frame src>
PASS load nested browsing context <iframe src>
PASS load nested browsing context <object data>
......@@ -36,7 +36,7 @@ FAIL SVG <image> poll_for_stash is not defined
FAIL SVG <use> poll_for_stash is not defined
PASS XMLHttpRequest#open()
PASS importScripts() in a dedicated worker
FAIL Worker() in a dedicated worker assert_equals: expected "%C3%A5" but got "ReferenceError: Worker is not defined"
PASS Worker() in a dedicated worker
FAIL SharedWorker() in a dedicated worker assert_equals: expected "%C3%A5" but got "ReferenceError: SharedWorker is not defined"
FAIL importScripts() in a shared worker assert_equals: expected "%C3%A5" but got "importScripts failed to run"
FAIL Worker() in a shared worker assert_equals: expected "%C3%A5" but got "ReferenceError: Worker is not defined"
......
This is a testharness.js-based test.
Found 101 tests; 50 PASS, 51 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 101 tests; 51 PASS, 50 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS load nested browsing context <frame src>
PASS load nested browsing context <iframe src>
PASS load nested browsing context <object data>
......@@ -36,7 +36,7 @@ FAIL SVG <image> poll_for_stash is not defined
FAIL SVG <use> poll_for_stash is not defined
FAIL XMLHttpRequest#open() assert_equals: expected "%C3%A5" but got "%26%23229%3B"
PASS importScripts() in a dedicated worker
FAIL Worker() in a dedicated worker assert_equals: expected "%C3%A5" but got "ReferenceError: Worker is not defined"
PASS Worker() in a dedicated worker
FAIL SharedWorker() in a dedicated worker assert_equals: expected "%C3%A5" but got "ReferenceError: SharedWorker is not defined"
FAIL importScripts() in a shared worker assert_equals: expected "%C3%A5" but got "importScripts failed to run"
FAIL Worker() in a shared worker assert_equals: expected "%C3%A5" but got "ReferenceError: Worker is not defined"
......
This is a testharness.js-based test.
Found 101 tests; 53 PASS, 48 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 101 tests; 54 PASS, 47 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS load nested browsing context <frame src>
PASS load nested browsing context <iframe src>
PASS load nested browsing context <object data>
......@@ -36,7 +36,7 @@ FAIL SVG <image> poll_for_stash is not defined
FAIL SVG <use> poll_for_stash is not defined
FAIL XMLHttpRequest#open() assert_equals: expected "%C3%A5" but got "%E5"
PASS importScripts() in a dedicated worker
FAIL Worker() in a dedicated worker assert_equals: expected "%C3%A5" but got "ReferenceError: Worker is not defined"
PASS Worker() in a dedicated worker
FAIL SharedWorker() in a dedicated worker assert_equals: expected "%C3%A5" but got "ReferenceError: SharedWorker is not defined"
FAIL importScripts() in a shared worker assert_equals: expected "%C3%A5" but got "importScripts failed to run"
FAIL Worker() in a shared worker assert_equals: expected "%C3%A5" but got "ReferenceError: Worker is not defined"
......
This is a testharness.js-based test.
PASS HTTP worker
PASS HTTPS worker
FAIL HTTP nested worker assert_false: expected false got "Nested workers not supported."
PASS HTTP nested worker
PASS HTTPS nested worker
PASS HTTP worker from HTTPS subframe
FAIL HTTPS worker from HTTPS subframe assert_false: expected false got true
......
This is a testharness.js-based test.
PASS HTTP worker
PASS HTTPS worker
PASS HTTP nested worker
FAIL HTTPS nested worker assert_true: expected true got "Nested workers not supported."
PASS HTTP worker from HTTPS subframe
PASS HTTPS worker from HTTPS subframe
Harness: the test ran to completion.
This is a testharness.js-based test.
PASS fetch() in Worker should be intercepted after the client is claimed.
FAIL fetch() in nested Worker should be intercepted after the client is claimed. assert_equals: fetch() should not be intercepted. expected "a simple text file\n" but got "Fail: undefined"
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL WebSocket created after a worker self.close() Worker is not defined
FAIL WebSocket created after a worker self.close() assert_equals: Closed WebSocket created on worker shutdown is in closing state. expected 2 but got 3
Harness: the test ran to completion.
......@@ -3,7 +3,7 @@ PASS existence of XMLHttpRequest
PASS existence of WebSocket
PASS existence of EventSource
PASS existence of MessageChannel
FAIL existence of Worker assert_true: expected true got false
PASS existence of Worker
FAIL existence of SharedWorker assert_true: expected true got false
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Nested static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Static import and then dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Nested dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Dynamic import and then static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL eval(import()). promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Nested static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Static import and then dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Nested dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Dynamic import and then static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL eval(import()). promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Nested static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Static import and then dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Nested dynamic import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL Dynamic import and then static import. promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
FAIL eval(import()). promise_test: Unhandled rejection with value: object "ReferenceError: Worker is not defined"
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Nested worker Worker is not defined
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Test terminating a nested workers by calling terminate() from its parent worker assert_equals: expected "Pass" but got "Fail: ReferenceError: Worker is not defined"
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Nested worker that issues a sync XHR Worker is not defined
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Test terminating a chain of nested workers by calling terminate() from the owning document assert_equals: expected "Pass" but got "Fail: ReferenceError: Worker is not defined"
Harness: the test ran to completion.
This is a testharness.js-based test.
Found 61 tests; 57 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 61 tests; 58 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS The WorkerGlobalScope interface object should be exposed.
PASS The DedicatedWorkerGlobalScope interface object should be exposed.
FAIL The Worker interface object should be exposed. assert_own_property: expected property "Worker" missing
PASS The Worker interface object should be exposed.
FAIL The SharedWorker interface object should be exposed. assert_own_property: expected property "SharedWorker" missing
PASS The MessagePort interface object should be exposed.
PASS The MessageEvent interface object should be exposed.
......
Tests exception message from eval on nested worker context in console contains stack trace.
function foo()
{
throw {a:239};
}
function boo()
{
foo();
}
boo();
VM:3 Uncaught {a: 239}a: 239__proto__: Object
foo @ VM:3
boo @ VM:7
(anonymous) @ VM:9
(async function() {
TestRunner.addResult(`Tests exception message from eval on nested worker context in console contains stack trace.\n`);
await TestRunner.loadModule('console_test_runner');
await TestRunner.showPanel('console');
await TestRunner.evaluateInPagePromise(`
function startWorker()
{
var worker = new Worker("resources/nested-worker.js");
}
`);
TestRunner.addSniffer(SDK.RuntimeModel.prototype, '_executionContextCreated', contextCreated);
TestRunner.evaluateInPage('startWorker()');
var contexts_still_loading = 2;
function contextCreated() {
contexts_still_loading--;
if (contexts_still_loading > 0) {
TestRunner.addSniffer(SDK.RuntimeModel.prototype, '_executionContextCreated', contextCreated);
return;
}
ConsoleTestRunner.changeExecutionContext('\u2699 worker.js');
ConsoleTestRunner.evaluateInConsole('\
function foo()\n\
{\n\
throw {a:239};\n\
}\n\
function boo()\n\
{\n\
foo();\n\
}\n\
boo();', step2);
}
function step2() {
ConsoleTestRunner.expandConsoleMessages(step3);
}
function step3() {
ConsoleTestRunner.dumpConsoleMessages();
TestRunner.completeTest();
}
})();
This is a testharness.js-based test.
FAIL Nested worker that calls importScripts() Worker is not defined
FAIL WebSocket created after a worker self.close() Worker is not defined
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Nested work that closes itself Worker is not defined
PASS postMessaging to a dedicated sub-worker allows them to see each others' modifications
Harness: the test ran to completion.
......@@ -3120,6 +3120,15 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] setter onerror
[Worker] setter onmessage
[Worker] setter onopen
[Worker] interface Worker : EventTarget
[Worker] attribute @@toStringTag
[Worker] getter onerror
[Worker] getter onmessage
[Worker] method constructor
[Worker] method postMessage
[Worker] method terminate
[Worker] setter onerror
[Worker] setter onmessage
[Worker] interface WorkerGlobalScope : EventTarget
[Worker] attribute @@toStringTag
[Worker] getter caches
......
......@@ -3376,6 +3376,15 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] setter onerror
[Worker] setter onmessage
[Worker] setter onopen
[Worker] interface Worker : EventTarget
[Worker] attribute @@toStringTag
[Worker] getter onerror
[Worker] getter onmessage
[Worker] method constructor
[Worker] method postMessage
[Worker] method terminate
[Worker] setter onerror
[Worker] setter onmessage
[Worker] interface WorkerGlobalScope : EventTarget
[Worker] attribute @@toStringTag
[Worker] getter caches
......
......@@ -21,6 +21,10 @@ const base::Feature kLayoutNG{"LayoutNG", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kMojoBlobURLs{"MojoBlobURLs",
base::FEATURE_DISABLED_BY_DEFAULT};
// Nested workers. See https://crbug.com/31666
const base::Feature kNestedWorkers{"NestedWorkers",
base::FEATURE_ENABLED_BY_DEFAULT};
// Enable new service worker glue for NetworkService. Can be
// enabled independently of NetworkService.
const base::Feature kServiceWorkerServicification{
......
......@@ -16,6 +16,7 @@ BLINK_COMMON_EXPORT extern const base::Feature
BLINK_COMMON_EXPORT extern const base::Feature kLayoutNG;
BLINK_COMMON_EXPORT extern const base::Feature kMojoBlobURLs;
BLINK_COMMON_EXPORT extern const base::Feature kServiceWorkerServicification;
BLINK_COMMON_EXPORT extern const base::Feature kNestedWorkers;
} // namespace features
} // namespace blink
......
......@@ -104,6 +104,7 @@ class WebRuntimeFeatures {
BLINK_PLATFORM_EXPORT static void EnableModuleScriptsDynamicImport(bool);
BLINK_PLATFORM_EXPORT static void EnableModuleScriptsImportMetaUrl(bool);
BLINK_PLATFORM_EXPORT static void EnableNavigatorContentUtils(bool);
BLINK_PLATFORM_EXPORT static void EnableNestedWorkers(bool);
BLINK_PLATFORM_EXPORT static void EnableNetInfoDownlinkMax(bool);
BLINK_PLATFORM_EXPORT static void EnableNetworkService(bool);
BLINK_PLATFORM_EXPORT static void EnableNotificationConstructor(bool);
......
......@@ -31,7 +31,7 @@
ActiveScriptWrappable,
Constructor(DOMString scriptURL, optional WorkerOptions options),
ConstructorCallWith=ExecutionContext,
// TODO(foolip): Exposed=(Window,Worker),
Exposed(Window StableBlinkFeatures,DedicatedWorker NestedWorkers),
RaisesException=Constructor,
ImplementedAs=DedicatedWorker
] interface Worker : EventTarget {
......
......@@ -228,6 +228,10 @@ void WebRuntimeFeatures::EnableNavigatorContentUtils(bool enable) {
RuntimeEnabledFeatures::SetNavigatorContentUtilsEnabled(enable);
}
void WebRuntimeFeatures::EnableNestedWorkers(bool enable) {
RuntimeEnabledFeatures::SetNestedWorkersEnabled(enable);
}
void WebRuntimeFeatures::EnableNetInfoDownlinkMax(bool enable) {
RuntimeEnabledFeatures::SetNetInfoDownlinkMaxEnabled(enable);
}
......
......@@ -793,6 +793,10 @@
name: "NavigatorDeviceMemory",
status: "stable",
},
{
name: "NestedWorkers",
status: "stable",
},
{
name: "NetInfoDownlink",
status: "stable",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment