Skip to content

Commit

Permalink
feat(runtime): support classic workers for internal testing (#11338)
Browse files Browse the repository at this point in the history
This commit implements classic workers, but only when the `--enable-testing-features-do-not-use` flag is provided. This change is not user facing. Classic workers are used extensively in WPT tests. The classic workers do not support loading from disk, and do not support TypeScript.

Co-authored-by: Luca Casonato <hello@lcas.dev>
  • Loading branch information
Andreu Botella and lucacasonato authored Aug 16, 2021
1 parent d1d2388 commit ddbb7b8
Show file tree
Hide file tree
Showing 15 changed files with 485 additions and 15 deletions.
3 changes: 3 additions & 0 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ 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,
unsafely_ignore_certificate_errors: program_state
.flags
.unsafely_ignore_certificate_errors
Expand All @@ -120,6 +121,7 @@ fn create_web_worker_callback(
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 @@ -193,6 +195,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,
unsafely_ignore_certificate_errors: program_state
.flags
.unsafely_ignore_certificate_errors
Expand Down
1 change: 1 addition & 0 deletions cli/standalone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,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,
unsafely_ignore_certificate_errors: metadata
.unsafely_ignore_certificate_errors,
root_cert_store: Some(root_cert_store),
Expand Down
4 changes: 3 additions & 1 deletion ext/fetch/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,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
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,
unsafely_ignore_certificate_errors: None,
root_cert_store: None,
user_agent: "hello_runtime".to_string(),
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 @@ -183,27 +184,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 @@ -213,6 +216,7 @@
? null
: parsePermissions(workerDenoAttributes.permissions),
options?.name,
workerType,
);
this.#id = id;
this.#pollControl();
Expand Down Expand Up @@ -344,6 +348,11 @@
defineEventHandler(Worker.prototype, "message");
defineEventHandler(Worker.prototype, "messageerror");

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

window.__bootstrap.worker = {
parsePermissions,
Worker,
Expand Down
46 changes: 46 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 @@ -164,6 +165,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 @@ -597,6 +636,13 @@ delete Object.prototype.__proto__;
}
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
2 changes: 2 additions & 0 deletions runtime/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,5 @@ pub fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) {
let state = state.borrow();
state.borrow::<UnstableChecker>().check_unstable(api_name)
}

pub struct TestingFeaturesEnabled(pub bool);
16 changes: 16 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,8 @@ use deno_web::JsMessageData;
use std::cell::RefCell;
use std::rc::Rc;

use self::sync_fetch::op_worker_sync_fetch;

pub fn init() -> Extension {
Extension::builder()
.ops(vec![
Expand All @@ -25,6 +30,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 +86,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

0 comments on commit ddbb7b8

Please sign in to comment.