script: Move TaskManager to GlobalScope (#34827)

This is a simplification of the internal `TaskQueue` API that moves the
`TaskManager` to the `GlobalScope` itself. In addition, the handling of
cancellers is moved to the `TaskManager` as well. This means that no
arguments other than the `task` are necessary for queueing tasks, which
makes the API a lot easier to use and cleaner.

`TaskSource` now also keeps a copy of the canceller with it, so that
they always know the proper way to cancel any tasks queued on them.

There is one complication here. The event loop `sender` for dedicated
workers is constantly changing as it is set to `None` when not handling
messages. This is because this sender keeps a handle to the main
thread's `Worker` object, preventing garbage collection while any
messages are still in flight or being handled. This change allows
setting the `sender` on the `TaskManager` to `None` to allow proper
garbabge collection.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-01-04 09:41:50 +01:00 committed by GitHub
parent 75a22cfe2e
commit b2eda71952
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 1060 additions and 1516 deletions

View file

@ -40,7 +40,6 @@ use crate::dom::bindings::codegen::UnionTypes::{
};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::import::module::SafeJSContext;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
@ -49,12 +48,8 @@ use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::cryptokey::{CryptoKey, Handle};
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::window::Window;
use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext};
use crate::task::TaskCanceller;
use crate::task_source::TaskSource;
// String constants for algorithms/curves
const ALG_AES_CBC: &str = "AES-CBC";
@ -140,20 +135,6 @@ impl SubtleCrypto {
CanGc::note(),
)
}
fn task_source_with_canceller(&self) -> (TaskSource, TaskCanceller) {
if let Some(window) = self.global().downcast::<Window>() {
window
.task_manager()
.dom_manipulation_task_source_with_canceller()
} else if let Some(worker_global) = self.global().downcast::<WorkerGlobalScope>() {
let task_source = worker_global.task_manager().dom_manipulation_task_source();
let canceller = worker_global.task_canceller();
(task_source, canceller)
} else {
unreachable!("Couldn't downcast to Window or WorkerGlobalScope!");
}
}
}
impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
@ -181,13 +162,13 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
let key_alg = key.algorithm();
let valid_usage = key.usages().contains(&KeyUsage::Encrypt);
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(encrypt: move || {
let subtle = this.root();
let promise = trusted_promise.root();
@ -206,8 +187,7 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
return;
}
promise.resolve_native(&*array_buffer_ptr.handle());
}),
&canceller,
})
);
promise
@ -237,13 +217,13 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
let key_alg = key.algorithm();
let valid_usage = key.usages().contains(&KeyUsage::Decrypt);
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(decrypt: move || {
let subtle = this.root();
let promise = trusted_promise.root();
@ -262,8 +242,7 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
}
promise.resolve_native(&*array_buffer_ptr.handle());
}),
&canceller,
})
);
promise
@ -304,51 +283,48 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// NOTE: We did that in preparation of Step 4.
// Step 6. Return promise and perform the remaining steps in parallel.
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
let _ = task_source.queue_with_canceller(
task!(sign: move || {
// Step 7. If the following steps or referenced procedures say to throw an error, reject promise
// with the returned error and then terminate the algorithm.
let promise = trusted_promise.root();
let key = trusted_key.root();
let _ = task_source.queue(task!(sign: move || {
// Step 7. If the following steps or referenced procedures say to throw an error, reject promise
// with the returned error and then terminate the algorithm.
let promise = trusted_promise.root();
let key = trusted_key.root();
// Step 8. If the name member of normalizedAlgorithm is not equal to the name attribute of the
// [[algorithm]] internal slot of key then throw an InvalidAccessError.
if normalized_algorithm.name() != key.algorithm() {
promise.reject_error(Error::InvalidAccess);
// Step 8. If the name member of normalizedAlgorithm is not equal to the name attribute of the
// [[algorithm]] internal slot of key then throw an InvalidAccessError.
if normalized_algorithm.name() != key.algorithm() {
promise.reject_error(Error::InvalidAccess);
return;
}
// Step 9. If the [[usages]] internal slot of key does not contain an entry that is "sign",
// then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Sign) {
promise.reject_error(Error::InvalidAccess);
return;
}
// Step 10. Let result be the result of performing the sign operation specified by normalizedAlgorithm
// using key and algorithm and with data as message.
let cx = GlobalScope::get_cx();
let result = match normalized_algorithm.sign(cx, &key, &data) {
Ok(signature) => signature,
Err(e) => {
promise.reject_error(e);
return;
}
};
// Step 9. If the [[usages]] internal slot of key does not contain an entry that is "sign",
// then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Sign) {
promise.reject_error(Error::InvalidAccess);
return;
}
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<ArrayBufferU8>(cx, &result, array_buffer_ptr.handle_mut())
.expect("failed to create buffer source for exported key.");
// Step 10. Let result be the result of performing the sign operation specified by normalizedAlgorithm
// using key and algorithm and with data as message.
let cx = GlobalScope::get_cx();
let result = match normalized_algorithm.sign(cx, &key, &data) {
Ok(signature) => signature,
Err(e) => {
promise.reject_error(e);
return;
}
};
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<ArrayBufferU8>(cx, &result, array_buffer_ptr.handle_mut())
.expect("failed to create buffer source for exported key.");
// Step 9. Resolve promise with result.
promise.resolve_native(&*array_buffer_ptr);
}),
&canceller,
);
// Step 9. Resolve promise with result.
promise.resolve_native(&*array_buffer_ptr);
}));
promise
}
@ -397,47 +373,44 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// NOTE: We did that in preparation of Step 6.
// Step 7. Return promise and perform the remaining steps in parallel.
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
let _ = task_source.queue_with_canceller(
task!(sign: move || {
// Step 8. If the following steps or referenced procedures say to throw an error, reject promise
// with the returned error and then terminate the algorithm.
let promise = trusted_promise.root();
let key = trusted_key.root();
let _ = task_source.queue(task!(sign: move || {
// Step 8. If the following steps or referenced procedures say to throw an error, reject promise
// with the returned error and then terminate the algorithm.
let promise = trusted_promise.root();
let key = trusted_key.root();
// Step 9. If the name member of normalizedAlgorithm is not equal to the name attribute of the
// [[algorithm]] internal slot of key then throw an InvalidAccessError.
if normalized_algorithm.name() != key.algorithm() {
promise.reject_error(Error::InvalidAccess);
// Step 9. If the name member of normalizedAlgorithm is not equal to the name attribute of the
// [[algorithm]] internal slot of key then throw an InvalidAccessError.
if normalized_algorithm.name() != key.algorithm() {
promise.reject_error(Error::InvalidAccess);
return;
}
// Step 10. If the [[usages]] internal slot of key does not contain an entry that is "verify",
// then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Verify) {
promise.reject_error(Error::InvalidAccess);
return;
}
// Step 1. Let result be the result of performing the verify operation specified by normalizedAlgorithm
// using key, algorithm and signature and with data as message.
let cx = GlobalScope::get_cx();
let result = match normalized_algorithm.verify(cx, &key, &data, &signature) {
Ok(result) => result,
Err(e) => {
promise.reject_error(e);
return;
}
};
// Step 10. If the [[usages]] internal slot of key does not contain an entry that is "verify",
// then throw an InvalidAccessError.
if !key.usages().contains(&KeyUsage::Verify) {
promise.reject_error(Error::InvalidAccess);
return;
}
// Step 1. Let result be the result of performing the verify operation specified by normalizedAlgorithm
// using key, algorithm and signature and with data as message.
let cx = GlobalScope::get_cx();
let result = match normalized_algorithm.verify(cx, &key, &data, &signature) {
Ok(result) => result,
Err(e) => {
promise.reject_error(e);
return;
}
};
// Step 9. Resolve promise with result.
promise.resolve_native(&result);
}),
&canceller,
);
// Step 9. Resolve promise with result.
promise.resolve_native(&result);
}));
promise
}
@ -476,10 +449,10 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// NOTE: We did that in preparation of Step 4.
// Step 6. Return promise and perform the remaining steps in parallel.
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let trusted_promise = TrustedPromise::new(promise.clone());
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(generate_key: move || {
// Step 7. If the following steps or referenced procedures say to throw an error, reject promise
// with the returned error and then terminate the algorithm.
@ -503,8 +476,7 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// Step 9. Resolve promise with result.
promise.resolve_native(&*array_buffer_ptr);
}),
&canceller,
})
);
promise
@ -529,22 +501,19 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
},
};
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let _ = task_source.queue_with_canceller(
task!(generate_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = normalized_algorithm.generate_key(&subtle, key_usages, extractable);
let _ = task_source.queue(task!(generate_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = normalized_algorithm.generate_key(&subtle, key_usages, extractable);
match key {
Ok(key) => promise.resolve_native(&key),
Err(e) => promise.reject_error(e),
}
}),
&canceller,
);
match key {
Ok(key) => promise.resolve_native(&key),
Err(e) => promise.reject_error(e),
}
}));
promise
}
@ -604,11 +573,11 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// NOTE: We created the promise earlier, after Step 1.
// Step 9. Return promise and perform the remaining steps in parallel.
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_base_key = Trusted::new(base_key);
let this = Trusted::new(self);
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(derive_key: move || {
// Step 10. If the following steps or referenced procedures say to throw an error, reject promise
// with the returned error and then terminate the algorithm.
@ -674,7 +643,6 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// Step 17. Resolve promise with result.
promise.resolve_native(&*result);
}),
&canceller,
);
promise
@ -709,47 +677,44 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
// NOTE: We did that in preparation of Step 3.
// Step 5. Return promise and perform the remaining steps in parallel.
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_base_key = Trusted::new(base_key);
let _ = task_source.queue_with_canceller(
task!(import_key: move || {
// Step 6. If the following steps or referenced procedures say to throw an error,
// reject promise with the returned error and then terminate the algorithm.
let _ = task_source.queue(task!(import_key: move || {
// Step 6. If the following steps or referenced procedures say to throw an error,
// reject promise with the returned error and then terminate the algorithm.
// TODO Step 7. If the name member of normalizedAlgorithm is not equal to the name attribute
// of the [[algorithm]] internal slot of baseKey then throw an InvalidAccessError.
let promise = trusted_promise.root();
let base_key = trusted_base_key.root();
// TODO Step 7. If the name member of normalizedAlgorithm is not equal to the name attribute
// of the [[algorithm]] internal slot of baseKey then throw an InvalidAccessError.
let promise = trusted_promise.root();
let base_key = trusted_base_key.root();
// Step 8. If the [[usages]] internal slot of baseKey does not contain an entry that
// is "deriveBits", then throw an InvalidAccessError.
if !base_key.usages().contains(&KeyUsage::DeriveBits) {
promise.reject_error(Error::InvalidAccess);
// Step 8. If the [[usages]] internal slot of baseKey does not contain an entry that
// is "deriveBits", then throw an InvalidAccessError.
if !base_key.usages().contains(&KeyUsage::DeriveBits) {
promise.reject_error(Error::InvalidAccess);
return;
}
// Step 9. Let result be the result of creating an ArrayBuffer containing the result of performing the
// derive bits operation specified by normalizedAlgorithm using baseKey, algorithm and length.
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
let result = match normalized_algorithm.derive_bits(&base_key, length) {
Ok(derived_bits) => derived_bits,
Err(e) => {
promise.reject_error(e);
return;
}
};
// Step 9. Let result be the result of creating an ArrayBuffer containing the result of performing the
// derive bits operation specified by normalizedAlgorithm using baseKey, algorithm and length.
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
let result = match normalized_algorithm.derive_bits(&base_key, length) {
Ok(derived_bits) => derived_bits,
Err(e) => {
promise.reject_error(e);
return;
}
};
create_buffer_source::<ArrayBufferU8>(cx, &result, array_buffer_ptr.handle_mut())
.expect("failed to create buffer source for derived bits.");
create_buffer_source::<ArrayBufferU8>(cx, &result, array_buffer_ptr.handle_mut())
.expect("failed to create buffer source for derived bits.");
// Step 10. Resolve promise with result.
promise.resolve_native(&*array_buffer_ptr);
}),
&canceller,
);
// Step 10. Resolve promise with result.
promise.resolve_native(&*array_buffer_ptr);
}));
promise
}
@ -801,10 +766,10 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
},
};
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(import_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
@ -814,7 +779,6 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
Err(e) => promise.reject_error(e),
};
}),
&canceller,
);
promise
@ -830,11 +794,11 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
) -> Rc<Promise> {
let promise = Promise::new_in_current_realm(comp, can_gc);
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let this = Trusted::new(self);
let trusted_key = Trusted::new(key);
let trusted_promise = TrustedPromise::new(promise.clone());
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(export_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
@ -872,7 +836,6 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
Err(e) => promise.reject_error(e),
}
}),
&canceller,
);
promise
@ -898,12 +861,12 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
},
};
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let this = Trusted::new(self);
let trusted_key = Trusted::new(key);
let trusted_wrapping_key = Trusted::new(wrapping_key);
let trusted_promise = TrustedPromise::new(promise.clone());
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(wrap_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
@ -996,7 +959,6 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
Err(e) => promise.reject_error(e),
}
}),
&canceller
);
promise
@ -1037,11 +999,11 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
},
};
let (task_source, canceller) = self.task_source_with_canceller();
let task_source = self.global().task_manager().dom_manipulation_task_source();
let this = Trusted::new(self);
let trusted_key = Trusted::new(unwrapping_key);
let trusted_promise = TrustedPromise::new(promise.clone());
let _ = task_source.queue_with_canceller(
let _ = task_source.queue(
task!(unwrap_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
@ -1103,7 +1065,6 @@ impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
Err(e) => promise.reject_error(e),
}
}),
&canceller
);
promise