Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime): Implement classic workers #11338

Merged
merged 28 commits into from
Aug 16, 2021
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a7a63ac
feat(runtime): Implement classic workers
Jul 8, 2021
314124e
Use `workerType` rather than `type`.
Jul 8, 2021
a53048a
lint
Jul 8, 2021
e7c2bea
Use primordials in importScripts
Jul 8, 2021
7ed8723
Add dedicated-worker-import-data-url.any.worker.html to the expectations
Jul 8, 2021
732b1b6
Revert "Add dedicated-worker-import-data-url.any.worker.html to the e…
Jul 9, 2021
002078c
Fix a bug with primordials
Jul 9, 2021
0c91ac4
Merge branch 'main' into classic-workers
Jul 10, 2021
0351973
Update WPT expectations.
Jul 10, 2021
8a2dde6
Split synchronous fetch into its own submodule
Jul 11, 2021
2822bb0
Allow importing data: and blob: scripts.
Jul 11, 2021
b578387
lint
Jul 11, 2021
c05fe53
Throw a NetworkError DOMException when the MIME type check fails.
Jul 11, 2021
a262e06
fix
Jul 12, 2021
01aa758
Merge branch 'main' into classic-workers
Jul 13, 2021
ae58a1b
Update expectations (see #11371).
Jul 13, 2021
5137ccc
rerun tests
Jul 13, 2021
050a76b
Merge remote-tracking branch 'origin/main' into classic-workers
lucacasonato Jul 23, 2021
c51426f
Gate classic workers behind --enable-testing-features-do-not-use.
Jul 23, 2021
b2c8c70
Merge branch 'main' into classic-workers
Jul 23, 2021
2cd414e
fix
Jul 23, 2021
c79c567
Make WorkerType a WebIDL enum
Jul 23, 2021
cc116ba
Merge branch 'main' into classic-workers
Jul 26, 2021
d6162e2
Update runtime/ops/mod.rs
Aug 14, 2021
2e87387
Merge branch 'main' into classic-workers
Aug 14, 2021
8c01a31
Use the WebSocketStream definitions for the NetworkError DomException.
Aug 14, 2021
3e04969
Update expectations.
Aug 14, 2021
5343200
Simplify how the `--enable-testing-features-do-not-use` flag status i…
Aug 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@ fn create_web_worker_callback(
.log_level
.map_or(false, |l| l == log::Level::Debug),
unstable: program_state.flags.unstable,
enable_testing_features: program_state.flags.enable_testing_features,
ca_data: program_state.ca_data.clone(),
user_agent: version::get_user_agent(),
seed: program_state.flags.seed,
module_loader,
create_web_worker_cb,
js_error_create_fn: Some(js_error_create_fn),
use_deno_namespace: args.use_deno_namespace,
worker_type: args.worker_type,
maybe_inspector_server,
runtime_version: version::deno(),
ts_version: version::TYPESCRIPT.to_string(),
Expand Down Expand Up @@ -188,6 +190,7 @@ pub fn create_main_worker(
.log_level
.map_or(false, |l| l == log::Level::Debug),
unstable: program_state.flags.unstable,
enable_testing_features: program_state.flags.enable_testing_features,
ca_data: program_state.ca_data.clone(),
user_agent: version::get_user_agent(),
seed: program_state.flags.seed,
Expand Down
1 change: 1 addition & 0 deletions cli/standalone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ pub async fn run(
debug_flag: metadata.log_level.map_or(false, |l| l == log::Level::Debug),
user_agent: version::get_user_agent(),
unstable: metadata.unstable,
enable_testing_features: false,
ca_data: metadata.ca_data,
seed: metadata.seed,
js_error_create_fn: None,
Expand Down
4 changes: 3 additions & 1 deletion extensions/fetch/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
use tokio_util::io::StreamReader;

pub use reqwest; // Re-export reqwest
// Re-export reqwest and data_url
pub use data_url;
pub use reqwest;

pub fn init<P: FetchPermissions + 'static>(
user_agent: String,
Expand Down
3 changes: 3 additions & 0 deletions runtime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
.or_else(|| deno_webgpu::error::get_error_class_name(e))
.or_else(|| deno_web::get_error_class_name(e))
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| {
crate::ops::web_worker::get_dom_exception_network_error_class_name(e)
})
.or_else(|| {
e.downcast_ref::<dlopen::Error>()
.map(get_dlopen_error_class)
Expand Down
1 change: 1 addition & 0 deletions runtime/examples/hello_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ async fn main() -> Result<(), AnyError> {
args: vec![],
debug_flag: false,
unstable: false,
enable_testing_features: false,
ca_data: None,
user_agent: "hello_runtime".to_string(),
seed: None,
Expand Down
31 changes: 20 additions & 11 deletions runtime/js/11_workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
ArrayIsArray,
ArrayPrototypeMap,
Error,
Uint8Array,
StringPrototypeStartsWith,
String,
SymbolIterator,
Expand All @@ -28,6 +27,7 @@
useDenoNamespace,
permissions,
name,
workerType,
) {
return core.opSync("op_create_worker", {
hasSourceCode,
Expand All @@ -36,6 +36,7 @@
sourceCode,
specifier,
useDenoNamespace,
workerType,
});
}

Expand Down Expand Up @@ -173,27 +174,29 @@
}
}

if (type !== "module") {
throw new Error(
'Not yet implemented: only "module" type workers are supported',
);
}

this.#name = name;
const hasSourceCode = false;
const sourceCode = core.decode(new Uint8Array());
const workerType = webidl.converters["WorkerType"](type);

if (
StringPrototypeStartsWith(specifier, "./") ||
StringPrototypeStartsWith(specifier, "../") ||
StringPrototypeStartsWith(specifier, "/") || type == "classic"
StringPrototypeStartsWith(specifier, "/") || workerType === "classic"
) {
const baseUrl = getLocationHref();
if (baseUrl != null) {
specifier = new URL(specifier, baseUrl).href;
}
}

this.#name = name;
let hasSourceCode, sourceCode;
if (workerType === "classic") {
hasSourceCode = true;
sourceCode = `importScripts("#");`;
} else {
hasSourceCode = false;
sourceCode = "";
}

const id = createWorker(
specifier,
hasSourceCode,
Expand All @@ -203,6 +206,7 @@
? null
: parsePermissions(workerDenoAttributes.permissions),
options?.name,
workerType,
);
this.#id = id;
this.#pollControl();
Expand Down Expand Up @@ -331,6 +335,11 @@
defineEventHandler(Worker.prototype, "message");
defineEventHandler(Worker.prototype, "messageerror");

webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
"classic",
"module",
]);

window.__bootstrap.worker = {
parsePermissions,
Worker,
Expand Down
52 changes: 52 additions & 0 deletions runtime/js/99_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ delete Object.prototype.__proto__;
((window) => {
const core = Deno.core;
const {
ArrayPrototypeMap,
Error,
FunctionPrototypeCall,
FunctionPrototypeBind,
Expand Down Expand Up @@ -161,6 +162,44 @@ delete Object.prototype.__proto__;
}
}

let loadedMainWorkerScript = false;

function importScripts(...urls) {
if (core.opSync("op_worker_get_type") === "module") {
throw new TypeError("Can't import scripts in a module worker.");
}

const baseUrl = location.getLocationHref();
const parsedUrls = ArrayPrototypeMap(urls, (scriptUrl) => {
try {
return new url.URL(scriptUrl, baseUrl ?? undefined).href;
} catch {
throw new domException.DOMException(
"Failed to parse URL.",
"SyntaxError",
);
}
});

// A classic worker's main script has looser MIME type checks than any
// imported scripts, so we use `loadedMainWorkerScript` to distinguish them.
// TODO(andreubotella) Refactor worker creation so the main script isn't
// loaded with `importScripts()`.
const scripts = core.opSync(
"op_worker_sync_fetch",
parsedUrls,
!loadedMainWorkerScript,
);
loadedMainWorkerScript = true;

for (const { url, script } of scripts) {
const err = core.evalContext(script, url)[1];
if (err !== null) {
throw err.thrown;
}
}
}

function opMainModule() {
return core.opSync("op_main_module");
}
Expand Down Expand Up @@ -233,6 +272,12 @@ delete Object.prototype.__proto__;
return new domException.DOMException(msg, "InvalidCharacterError");
},
);
core.registerErrorBuilder(
"DOMExceptionNetworkError",
function DOMExceptionNetworkError(msg) {
return new domException.DOMException(msg, "NetworkError");
},
);
}

class Navigator {
Expand Down Expand Up @@ -552,6 +597,13 @@ delete Object.prototype.__proto__;
ObjectDefineProperties(globalThis, windowOrWorkerGlobalScope);
ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties);
ObjectDefineProperties(globalThis, { name: util.readOnly(name) });
if (runtimeOptions.enableTestingFeaturesFlag) {
ObjectDefineProperty(
globalThis,
"importScripts",
util.writable(importScripts),
);
}
ObjectSetPrototypeOf(globalThis, DedicatedWorkerGlobalScope.prototype);

const consoleFromDeno = globalThis.console;
Expand Down
32 changes: 32 additions & 0 deletions runtime/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,35 @@ pub fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) {
let state = state.borrow();
state.borrow::<UnstableChecker>().check_unstable(api_name)
}

pub struct TestingFeatureChecker {
pub enable_testing_features: bool,
}

impl TestingFeatureChecker {
pub fn check_testing_features(&self, feature_name: &str) {
if !self.enable_testing_features {
eprintln!(
"Testing feature '{}'. The --enable-testing-features-do-not-use must be provided.",
andreubotella marked this conversation as resolved.
Show resolved Hide resolved
feature_name
);
std::process::exit(70);
}
}
}

pub fn check_testing_features(state: &OpState, feature_name: &str) {
state
.borrow::<TestingFeatureChecker>()
.check_testing_features(feature_name)
}

pub fn check_testing_features2(
state: &Rc<RefCell<OpState>>,
feature_name: &str,
) {
let state = state.borrow();
state
.borrow::<TestingFeatureChecker>()
.check_testing_features(feature_name)
}
18 changes: 18 additions & 0 deletions runtime/ops/web_worker.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

mod sync_fetch;

use crate::web_worker::WebWorkerInternalHandle;
use crate::web_worker::WebWorkerType;
use crate::web_worker::WorkerControlEvent;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
Expand All @@ -13,6 +16,10 @@ use deno_web::JsMessageData;
use std::cell::RefCell;
use std::rc::Rc;

use self::sync_fetch::op_worker_sync_fetch;

pub use sync_fetch::get_dom_exception_network_error_class_name;

pub fn init() -> Extension {
Extension::builder()
.ops(vec![
Expand All @@ -25,6 +32,8 @@ pub fn init() -> Extension {
"op_worker_unhandled_error",
op_sync(op_worker_unhandled_error),
),
("op_worker_get_type", op_sync(op_worker_get_type)),
("op_worker_sync_fetch", op_sync(op_worker_sync_fetch)),
])
.build()
}
Expand Down Expand Up @@ -79,3 +88,12 @@ fn op_worker_unhandled_error(
.expect("Failed to propagate error event to parent worker");
Ok(())
}

fn op_worker_get_type(
state: &mut OpState,
_: (),
_: (),
) -> Result<WebWorkerType, AnyError> {
let handle = state.borrow::<WebWorkerInternalHandle>().clone();
Ok(handle.worker_type)
}
Loading