mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
Debug mozjs builds don't like when we try to trace members of one global object (realm) that originate from another global object (realm). This triggers when we use `Dom<DebuggerGlobalScope>` in our global objects, since all `Dom<T>` pointers are expected to in the same realm as the containing object. To address this, we store a JS value of the debugger global's reflector wrapped in the current global's relam, and use value to reconstitute a `DebuggerGlobalScope` pointer as needed. Testing: We don't run debug-mozjs in CI, but this change makes a bunch of tests stop asserting. Fixes: #44385 --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
1140 lines
43 KiB
Rust
1140 lines
43 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||
|
||
use std::cell::{RefCell, RefMut};
|
||
use std::default::Default;
|
||
use std::rc::Rc;
|
||
use std::sync::Arc;
|
||
use std::sync::atomic::{AtomicBool, Ordering};
|
||
use std::time::Duration;
|
||
|
||
use content_security_policy::CspList;
|
||
use devtools_traits::{DevtoolScriptControlMsg, WorkerId};
|
||
use dom_struct::dom_struct;
|
||
use encoding_rs::UTF_8;
|
||
use fonts::FontContext;
|
||
use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
|
||
use js::jsapi::{Heap, JSContext as RawJSContext, Value};
|
||
use js::realm::CurrentRealm;
|
||
use js::rust::{HandleValue, MutableHandleValue, ParentRuntime};
|
||
use mime::Mime;
|
||
use net_traits::blob_url_store::UrlWithBlobClaim;
|
||
use net_traits::policy_container::PolicyContainer;
|
||
use net_traits::request::{
|
||
CredentialsMode, Destination, InsecureRequestsPolicy, ParserMetadata, RequestBuilder, RequestId,
|
||
};
|
||
use net_traits::{FetchMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming};
|
||
use profile_traits::mem::{ProcessReports, perform_memory_report};
|
||
use script_bindings::conversions::{SafeToJSValConvertible, root_from_handlevalue};
|
||
use script_bindings::reflector::DomObject;
|
||
use script_bindings::root::rooted_heap_handle;
|
||
use servo_base::cross_process_instant::CrossProcessInstant;
|
||
use servo_base::generic_channel::{GenericSend, GenericSender, RoutedReceiver};
|
||
use servo_base::id::{PipelineId, PipelineNamespace};
|
||
use servo_canvas_traits::webgl::WebGLChan;
|
||
use servo_constellation_traits::WorkerGlobalScopeInit;
|
||
use servo_url::{MutableOrigin, ServoUrl};
|
||
use timers::TimerScheduler;
|
||
use uuid::Uuid;
|
||
|
||
use crate::dom::bindings::cell::{DomRefCell, Ref};
|
||
use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
|
||
ImageBitmapOptions, ImageBitmapSource,
|
||
};
|
||
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
|
||
use crate::dom::bindings::codegen::Bindings::ReportingObserverBinding::Report;
|
||
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||
use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
||
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
|
||
use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
|
||
use crate::dom::bindings::codegen::UnionTypes::{
|
||
RequestOrUSVString, TrustedScriptOrString, TrustedScriptOrStringOrFunction,
|
||
TrustedScriptURLOrUSVString,
|
||
};
|
||
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
|
||
use crate::dom::bindings::inheritance::Castable;
|
||
use crate::dom::bindings::refcounted::Trusted;
|
||
use crate::dom::bindings::reflector::DomGlobal;
|
||
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
||
use crate::dom::bindings::str::{DOMString, USVString};
|
||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||
use crate::dom::bindings::utils::define_all_exposed_interfaces;
|
||
use crate::dom::crypto::Crypto;
|
||
use crate::dom::csp::{GlobalCspReporting, Violation, parse_csp_list_from_metadata};
|
||
use crate::dom::debugger::debuggerglobalscope::DebuggerGlobalScope;
|
||
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
|
||
use crate::dom::global_scope_script_execution::{ErrorReporting, RethrowErrors};
|
||
use crate::dom::globalscope::GlobalScope;
|
||
use crate::dom::htmlscriptelement::{SCRIPT_JS_MIMES, Script};
|
||
use crate::dom::idbfactory::IDBFactory;
|
||
use crate::dom::performance::performance::Performance;
|
||
use crate::dom::performance::performanceresourcetiming::InitiatorType;
|
||
use crate::dom::promise::Promise;
|
||
use crate::dom::reporting::reportingendpoint::{ReportingEndpoint, SendReportsToEndpoints};
|
||
use crate::dom::reporting::reportingobserver::ReportingObserver;
|
||
use crate::dom::sharedworkerglobalscope::SharedWorkerGlobalScope;
|
||
use crate::dom::trustedtypes::trustedscripturl::TrustedScriptURL;
|
||
use crate::dom::trustedtypes::trustedtypepolicyfactory::TrustedTypePolicyFactory;
|
||
use crate::dom::types::ImageBitmap;
|
||
#[cfg(feature = "webgpu")]
|
||
use crate::dom::webgpu::identityhub::IdentityHub;
|
||
use crate::dom::window::{base64_atob, base64_btoa};
|
||
use crate::dom::workerlocation::WorkerLocation;
|
||
use crate::dom::workernavigator::WorkerNavigator;
|
||
use crate::fetch::{CspViolationsProcessor, Fetch, RequestWithGlobalScope, load_whole_resource};
|
||
use crate::messaging::{CommonScriptMsg, ScriptEventLoopReceiver, ScriptEventLoopSender};
|
||
use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask};
|
||
use crate::network_listener::{FetchResponseListener, ResourceTimingListener, submit_timing};
|
||
use crate::realms::{AlreadyInRealm, InRealm, enter_auto_realm};
|
||
use crate::script_module::ScriptFetchOptions;
|
||
use crate::script_runtime::{CanGc, IntroductionType, JSContext, JSContextHelper, Runtime};
|
||
use crate::task::TaskCanceller;
|
||
use crate::timers::{IsInterval, TimerCallback};
|
||
|
||
pub(crate) fn prepare_workerscope_init(
|
||
global: &GlobalScope,
|
||
devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
|
||
worker_id: Option<WorkerId>,
|
||
webgl_chan: Option<WebGLChan>,
|
||
) -> WorkerGlobalScopeInit {
|
||
WorkerGlobalScopeInit {
|
||
resource_threads: global.resource_threads().clone(),
|
||
storage_threads: global.storage_threads().clone(),
|
||
mem_profiler_chan: global.mem_profiler_chan().clone(),
|
||
to_devtools_sender: global.devtools_chan().cloned(),
|
||
time_profiler_chan: global.time_profiler_chan().clone(),
|
||
from_devtools_sender: devtools_sender,
|
||
script_to_constellation_chan: global.script_to_constellation_chan().clone(),
|
||
script_to_embedder_chan: global.script_to_embedder_chan().clone(),
|
||
worker_id: worker_id.unwrap_or_else(|| WorkerId(Uuid::new_v4())),
|
||
pipeline_id: global.pipeline_id(),
|
||
origin: global.origin().immutable().clone(),
|
||
inherited_secure_context: Some(global.is_secure_context()),
|
||
unminify_js: global.unminify_js(),
|
||
webgl_chan,
|
||
}
|
||
}
|
||
|
||
pub(crate) struct ScriptFetchContext {
|
||
scope: Trusted<WorkerGlobalScope>,
|
||
response: Option<Metadata>,
|
||
body_bytes: Vec<u8>,
|
||
url: ServoUrl,
|
||
policy_container: PolicyContainer,
|
||
}
|
||
|
||
impl ScriptFetchContext {
|
||
pub(crate) fn new(
|
||
scope: Trusted<WorkerGlobalScope>,
|
||
url: ServoUrl,
|
||
policy_container: PolicyContainer,
|
||
) -> ScriptFetchContext {
|
||
ScriptFetchContext {
|
||
scope,
|
||
response: None,
|
||
body_bytes: Vec::new(),
|
||
url,
|
||
policy_container,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl FetchResponseListener for ScriptFetchContext {
|
||
fn process_request_body(&mut self, _request_id: RequestId) {}
|
||
|
||
fn process_response(
|
||
&mut self,
|
||
_: &mut js::context::JSContext,
|
||
_request_id: RequestId,
|
||
metadata: Result<FetchMetadata, NetworkError>,
|
||
) {
|
||
self.response = metadata.ok().map(|m| match m {
|
||
FetchMetadata::Unfiltered(m) => m,
|
||
FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
|
||
});
|
||
}
|
||
|
||
fn process_response_chunk(
|
||
&mut self,
|
||
_: &mut js::context::JSContext,
|
||
_: RequestId,
|
||
mut chunk: Vec<u8>,
|
||
) {
|
||
self.body_bytes.append(&mut chunk);
|
||
}
|
||
|
||
fn process_response_eof(
|
||
mut self,
|
||
cx: &mut js::context::JSContext,
|
||
_request_id: RequestId,
|
||
response: Result<(), NetworkError>,
|
||
timing: ResourceFetchTiming,
|
||
) {
|
||
let scope = self.scope.root();
|
||
|
||
if response
|
||
.as_ref()
|
||
.inspect_err(|e| error!("error loading script {} ({:?})", self.url, e))
|
||
.is_err() ||
|
||
self.response.is_none()
|
||
{
|
||
scope.on_complete(cx, None);
|
||
return;
|
||
}
|
||
let metadata = self.response.take().unwrap();
|
||
|
||
// The processResponseConsumeBody steps defined inside
|
||
// [run a worker](https://html.spec.whatwg.org/multipage/#run-a-worker)
|
||
scope.process_response_for_workerscope(&metadata, &self.policy_container);
|
||
|
||
// The processResponseConsumeBody steps defined inside
|
||
// [fetch a classic worker script](https://html.spec.whatwg.org/multipage/#fetch-a-classic-worker-script)
|
||
|
||
// Step 1 Set response to response's unsafe response. Done in process_response
|
||
|
||
// Step 2 If any of the following are true: bodyBytes is null or failure; or response's status is not an ok status,
|
||
if !metadata.status.is_success() {
|
||
// then run onComplete given null, and abort these steps.
|
||
scope.on_complete(cx, None);
|
||
return;
|
||
}
|
||
|
||
// Step 3 If all of the following are true:
|
||
// response's URL's scheme is an HTTP(S) scheme;
|
||
let is_http_scheme = matches!(metadata.final_url.scheme(), "http" | "https");
|
||
// and the result of extracting a MIME type from response's header list is not a JavaScript MIME type,
|
||
let not_a_javascript_mime_type = !metadata.content_type.is_some_and(|ct| {
|
||
let mime: Mime = ct.into_inner().into();
|
||
SCRIPT_JS_MIMES.contains(&mime.essence_str())
|
||
});
|
||
|
||
if is_http_scheme && not_a_javascript_mime_type {
|
||
// then run onComplete given null, and abort these steps.
|
||
scope.on_complete(cx, None);
|
||
return;
|
||
}
|
||
|
||
// Step 4 Let sourceText be the result of UTF-8 decoding bodyBytes.
|
||
let (source, _) = UTF_8.decode_with_bom_removal(&self.body_bytes);
|
||
|
||
let global_scope = scope.upcast::<GlobalScope>();
|
||
|
||
// Step 5 Let script be the result of creating a classic script using
|
||
// sourceText, settingsObject, response's URL, and the default script fetch options.
|
||
let script = global_scope.create_a_classic_script(
|
||
cx,
|
||
source,
|
||
scope.worker_url.borrow().clone(),
|
||
ScriptFetchOptions::default_classic_script(),
|
||
ErrorReporting::Unmuted,
|
||
Some(IntroductionType::WORKER),
|
||
1,
|
||
true,
|
||
);
|
||
|
||
// Step 6 Run onComplete given script.
|
||
scope.on_complete(cx, Some(Script::Classic(script)));
|
||
|
||
submit_timing(cx, &self, &response, &timing);
|
||
}
|
||
|
||
fn process_csp_violations(
|
||
&mut self,
|
||
_request_id: RequestId,
|
||
violations: Vec<content_security_policy::Violation>,
|
||
) {
|
||
let scope = self.scope.root();
|
||
|
||
if let Some(worker_scope) = scope.downcast::<DedicatedWorkerGlobalScope>() {
|
||
worker_scope.report_csp_violations(violations);
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ResourceTimingListener for ScriptFetchContext {
|
||
fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
|
||
(InitiatorType::Other, self.url.clone())
|
||
}
|
||
|
||
fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
|
||
self.scope.root().global()
|
||
}
|
||
}
|
||
|
||
// https://html.spec.whatwg.org/multipage/#the-workerglobalscope-common-interface
|
||
#[dom_struct]
|
||
pub(crate) struct WorkerGlobalScope {
|
||
globalscope: GlobalScope,
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#microtask-queue>
|
||
#[conditional_malloc_size_of]
|
||
microtask_queue: Rc<MicrotaskQueue>,
|
||
|
||
worker_name: DOMString,
|
||
worker_type: WorkerType,
|
||
|
||
#[no_trace]
|
||
worker_id: WorkerId,
|
||
#[no_trace]
|
||
worker_url: DomRefCell<ServoUrl>,
|
||
#[conditional_malloc_size_of]
|
||
closing: Arc<AtomicBool>,
|
||
execution_ready: AtomicBool,
|
||
#[ignore_malloc_size_of = "Defined in js"]
|
||
runtime: DomRefCell<Option<Runtime>>,
|
||
location: MutNullableDom<WorkerLocation>,
|
||
navigator: MutNullableDom<WorkerNavigator>,
|
||
crypto: MutNullableDom<Crypto>,
|
||
#[no_trace]
|
||
/// <https://html.spec.whatwg.org/multipage/#the-workerglobalscope-common-interface:policy-container>
|
||
policy_container: DomRefCell<PolicyContainer>,
|
||
|
||
#[ignore_malloc_size_of = "Defined in base"]
|
||
#[no_trace]
|
||
/// A `Sender` for sending messages to devtools. This is unused but is stored here to
|
||
/// keep the channel alive.
|
||
_devtools_sender: Option<GenericSender<DevtoolScriptControlMsg>>,
|
||
|
||
#[ignore_malloc_size_of = "Defined in base"]
|
||
#[no_trace]
|
||
/// A `Receiver` for receiving messages from devtools.
|
||
devtools_receiver: Option<RoutedReceiver<DevtoolScriptControlMsg>>,
|
||
|
||
#[no_trace]
|
||
navigation_start: CrossProcessInstant,
|
||
performance: MutNullableDom<Performance>,
|
||
trusted_types: MutNullableDom<TrustedTypePolicyFactory>,
|
||
|
||
/// A [`TimerScheduler`] used to schedule timers for this [`WorkerGlobalScope`].
|
||
/// Timers are handled in the service worker event loop.
|
||
#[no_trace]
|
||
timer_scheduler: RefCell<TimerScheduler>,
|
||
|
||
#[no_trace]
|
||
insecure_requests_policy: InsecureRequestsPolicy,
|
||
|
||
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-registered-reporting-observer-list>
|
||
reporting_observer_list: DomRefCell<Vec<DomRoot<ReportingObserver>>>,
|
||
|
||
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-reports>
|
||
report_list: DomRefCell<Vec<Report>>,
|
||
|
||
/// <https://w3c.github.io/reporting/#windoworworkerglobalscope-endpoints>
|
||
#[no_trace]
|
||
endpoints_list: DomRefCell<Vec<ReportingEndpoint>>,
|
||
|
||
/// The debugger global object associated with this worker global.
|
||
/// All traced members of DOM objects must be same-compartment with the
|
||
/// realm being traced, so this is the debugger global object wrapped into
|
||
/// this global's compartment.
|
||
#[ignore_malloc_size_of = "Measured by the JS engine"]
|
||
debugger_global: Heap<Value>,
|
||
}
|
||
|
||
impl WorkerGlobalScope {
|
||
#[allow(clippy::too_many_arguments)]
|
||
pub(crate) fn new_inherited(
|
||
init: WorkerGlobalScopeInit,
|
||
worker_name: DOMString,
|
||
worker_type: WorkerType,
|
||
worker_url: ServoUrl,
|
||
runtime: Runtime,
|
||
devtools_receiver: RoutedReceiver<DevtoolScriptControlMsg>,
|
||
closing: Arc<AtomicBool>,
|
||
#[cfg(feature = "webgpu")] gpu_id_hub: Arc<IdentityHub>,
|
||
insecure_requests_policy: InsecureRequestsPolicy,
|
||
font_context: Option<Arc<FontContext>>,
|
||
) -> Self {
|
||
// Install a pipeline-namespace in the current thread.
|
||
PipelineNamespace::auto_install();
|
||
|
||
let devtools_receiver = match init.from_devtools_sender {
|
||
Some(..) => Some(devtools_receiver),
|
||
None => None,
|
||
};
|
||
|
||
Self {
|
||
globalscope: GlobalScope::new_inherited(
|
||
init.pipeline_id,
|
||
init.to_devtools_sender,
|
||
init.mem_profiler_chan,
|
||
init.time_profiler_chan,
|
||
init.script_to_constellation_chan,
|
||
init.script_to_embedder_chan,
|
||
init.resource_threads,
|
||
init.storage_threads,
|
||
MutableOrigin::new(init.origin),
|
||
worker_url.clone(),
|
||
None,
|
||
#[cfg(feature = "webgpu")]
|
||
gpu_id_hub,
|
||
init.inherited_secure_context,
|
||
init.unminify_js,
|
||
font_context,
|
||
),
|
||
microtask_queue: runtime.microtask_queue.clone(),
|
||
worker_id: init.worker_id,
|
||
worker_name,
|
||
worker_type,
|
||
worker_url: DomRefCell::new(worker_url),
|
||
closing,
|
||
execution_ready: AtomicBool::new(false),
|
||
runtime: DomRefCell::new(Some(runtime)),
|
||
location: Default::default(),
|
||
navigator: Default::default(),
|
||
crypto: Default::default(),
|
||
policy_container: Default::default(),
|
||
devtools_receiver,
|
||
_devtools_sender: init.from_devtools_sender,
|
||
navigation_start: CrossProcessInstant::now(),
|
||
performance: Default::default(),
|
||
timer_scheduler: RefCell::default(),
|
||
insecure_requests_policy,
|
||
trusted_types: Default::default(),
|
||
reporting_observer_list: Default::default(),
|
||
report_list: Default::default(),
|
||
endpoints_list: Default::default(),
|
||
debugger_global: Default::default(),
|
||
}
|
||
}
|
||
|
||
pub(crate) fn enqueue_microtask(&self, job: Microtask) {
|
||
self.microtask_queue.enqueue(job, GlobalScope::get_cx());
|
||
}
|
||
|
||
/// Perform a microtask checkpoint.
|
||
pub(crate) fn perform_a_microtask_checkpoint(&self, cx: &mut js::context::JSContext) {
|
||
// Only perform the checkpoint if we're not shutting down.
|
||
if !self.is_closing() {
|
||
self.microtask_queue.checkpoint(
|
||
cx,
|
||
|_| Some(DomRoot::from_ref(&self.globalscope)),
|
||
vec![DomRoot::from_ref(&self.globalscope)],
|
||
);
|
||
}
|
||
}
|
||
|
||
/// Returns a policy value that should be used by fetches initiated by this worker.
|
||
pub(crate) fn insecure_requests_policy(&self) -> InsecureRequestsPolicy {
|
||
self.insecure_requests_policy
|
||
}
|
||
|
||
/// Clear various items when the worker event-loop shuts-down.
|
||
pub(crate) fn clear_js_runtime(&self) {
|
||
self.upcast::<GlobalScope>()
|
||
.remove_web_messaging_and_dedicated_workers_infra();
|
||
|
||
// Drop the runtime.
|
||
let runtime = self.runtime.borrow_mut().take();
|
||
drop(runtime);
|
||
}
|
||
|
||
pub(crate) fn runtime_handle(&self) -> ParentRuntime {
|
||
self.runtime
|
||
.borrow()
|
||
.as_ref()
|
||
.unwrap()
|
||
.prepare_for_new_child()
|
||
}
|
||
|
||
pub(crate) fn devtools_receiver(&self) -> Option<&RoutedReceiver<DevtoolScriptControlMsg>> {
|
||
self.devtools_receiver.as_ref()
|
||
}
|
||
|
||
#[expect(unsafe_code)]
|
||
pub(crate) fn get_cx(&self) -> JSContext {
|
||
unsafe { JSContext::from_ptr(js::rust::Runtime::get().unwrap().as_ptr()) }
|
||
}
|
||
|
||
pub(crate) fn is_closing(&self) -> bool {
|
||
self.closing.load(Ordering::SeqCst)
|
||
}
|
||
|
||
pub(crate) fn is_execution_ready(&self) -> bool {
|
||
self.execution_ready.load(Ordering::Relaxed)
|
||
}
|
||
|
||
pub(crate) fn get_url(&self) -> Ref<'_, ServoUrl> {
|
||
self.worker_url.borrow()
|
||
}
|
||
|
||
pub(crate) fn set_url(&self, url: ServoUrl) {
|
||
*self.worker_url.borrow_mut() = url;
|
||
}
|
||
|
||
pub(crate) fn worker_name(&self) -> DOMString {
|
||
self.worker_name.clone()
|
||
}
|
||
|
||
pub(crate) fn worker_id(&self) -> WorkerId {
|
||
self.worker_id
|
||
}
|
||
|
||
pub(crate) fn pipeline_id(&self) -> PipelineId {
|
||
self.globalscope.pipeline_id()
|
||
}
|
||
|
||
pub(crate) fn policy_container(&self) -> Ref<'_, PolicyContainer> {
|
||
self.policy_container.borrow()
|
||
}
|
||
|
||
pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) {
|
||
self.policy_container.borrow_mut().set_csp_list(csp_list);
|
||
}
|
||
|
||
pub(crate) fn set_referrer_policy(&self, referrer_policy: ReferrerPolicy) {
|
||
self.policy_container
|
||
.borrow_mut()
|
||
.set_referrer_policy(referrer_policy);
|
||
}
|
||
|
||
pub(crate) fn append_reporting_observer(&self, reporting_observer: DomRoot<ReportingObserver>) {
|
||
self.reporting_observer_list
|
||
.borrow_mut()
|
||
.push(reporting_observer);
|
||
}
|
||
|
||
pub(crate) fn remove_reporting_observer(&self, reporting_observer: &ReportingObserver) {
|
||
if let Some(index) = self
|
||
.reporting_observer_list
|
||
.borrow()
|
||
.iter()
|
||
.position(|observer| &**observer == reporting_observer)
|
||
{
|
||
self.reporting_observer_list.borrow_mut().remove(index);
|
||
}
|
||
}
|
||
|
||
pub(crate) fn registered_reporting_observers(&self) -> Vec<DomRoot<ReportingObserver>> {
|
||
self.reporting_observer_list.borrow().clone()
|
||
}
|
||
|
||
pub(crate) fn append_report(&self, report: Report) {
|
||
self.report_list.borrow_mut().push(report);
|
||
let trusted_worker = Trusted::new(self);
|
||
self.upcast::<GlobalScope>()
|
||
.task_manager()
|
||
.dom_manipulation_task_source()
|
||
.queue(task!(send_to_reporting_endpoints: move || {
|
||
let worker = trusted_worker.root();
|
||
let reports = std::mem::take(&mut *worker.report_list.borrow_mut());
|
||
worker.upcast::<GlobalScope>().send_reports_to_endpoints(
|
||
reports,
|
||
worker.endpoints_list.borrow().clone(),
|
||
);
|
||
}));
|
||
}
|
||
|
||
pub(crate) fn buffered_reports(&self) -> Vec<Report> {
|
||
self.report_list.borrow().clone()
|
||
}
|
||
|
||
pub(crate) fn set_endpoints_list(&self, endpoints: Option<Vec<ReportingEndpoint>>) {
|
||
if let Some(endpoints) = endpoints {
|
||
*self.endpoints_list.borrow_mut() = endpoints;
|
||
}
|
||
}
|
||
|
||
/// Get a mutable reference to the [`TimerScheduler`] for this [`ServiceWorkerGlobalScope`].
|
||
pub(crate) fn timer_scheduler(&self) -> RefMut<'_, TimerScheduler> {
|
||
self.timer_scheduler.borrow_mut()
|
||
}
|
||
|
||
/// Return a copy to the shared task canceller that is used to cancel all tasks
|
||
/// when this worker is closing.
|
||
pub(crate) fn shared_task_canceller(&self) -> TaskCanceller {
|
||
TaskCanceller {
|
||
cancelled: self.closing.clone(),
|
||
}
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#initialize-worker-policy-container> and
|
||
/// <https://html.spec.whatwg.org/multipage/#creating-a-policy-container-from-a-fetch-response>
|
||
fn initialize_policy_container_for_worker_global_scope(
|
||
&self,
|
||
metadata: &Metadata,
|
||
parent_policy_container: &PolicyContainer,
|
||
) {
|
||
// Step 1. If workerGlobalScope's url is local but its scheme is not "blob":
|
||
//
|
||
// Note that we also allow for blob here, as the parent_policy_container is in both cases
|
||
// the container that we need to clone.
|
||
if metadata.final_url.is_local_scheme() {
|
||
// Step 1.2. Set workerGlobalScope's policy container to a clone of workerGlobalScope's
|
||
// owner set[0]'s relevant settings object's policy container.
|
||
//
|
||
// Step 1. If response's URL's scheme is "blob", then return a clone of response's URL's
|
||
// blob URL entry's environment's policy container.
|
||
self.set_csp_list(parent_policy_container.csp_list.clone());
|
||
self.set_referrer_policy(parent_policy_container.get_referrer_policy());
|
||
return;
|
||
}
|
||
// Step 3. Set result's CSP list to the result of parsing a response's Content Security Policies given response.
|
||
self.set_csp_list(parse_csp_list_from_metadata(&metadata.headers));
|
||
// Step 5. Set result's referrer policy to the result of parsing the `Referrer-Policy`
|
||
// header given response. [REFERRERPOLICY]
|
||
let referrer_policy = metadata
|
||
.headers
|
||
.as_ref()
|
||
.and_then(|headers| headers.typed_get::<ReferrerPolicyHeader>())
|
||
.into();
|
||
self.set_referrer_policy(referrer_policy);
|
||
}
|
||
|
||
/// onComplete algorithm defined inside <https://html.spec.whatwg.org/multipage/#run-a-worker>
|
||
#[expect(unsafe_code)]
|
||
pub(crate) fn on_complete(&self, cx: &mut js::context::JSContext, script: Option<Script>) {
|
||
// Step 1. If script is null or if script's error to rethrow is non-null, then:
|
||
let script = match script {
|
||
Some(Script::Classic(script)) if script.record.is_ok() => Script::Classic(script),
|
||
Some(Script::Module(module_tree))
|
||
if module_tree.get_rethrow_error().borrow().is_none() =>
|
||
{
|
||
Script::Module(module_tree)
|
||
},
|
||
_ => {
|
||
// Step 1.1 Queue a global task on the DOM manipulation task source given
|
||
// worker's relevant global object to fire an event named error at worker.
|
||
if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
|
||
dedicated.forward_simple_error_at_worker();
|
||
}
|
||
|
||
// TODO Step 1.2. Run the environment discarding steps for inside settings.
|
||
// Step 1.3 Abort these steps.
|
||
return;
|
||
},
|
||
};
|
||
|
||
unsafe {
|
||
// Handle interrupt requests
|
||
js::rust::wrappers2::JS_AddInterruptCallback(cx, Some(interrupt_callback));
|
||
}
|
||
|
||
if self.is_closing() {
|
||
return;
|
||
}
|
||
|
||
{
|
||
let mut realm = enter_auto_realm(cx, self);
|
||
let cx = &mut realm.current_realm();
|
||
define_all_exposed_interfaces(cx, self.upcast());
|
||
self.execution_ready.store(true, Ordering::Relaxed);
|
||
match script {
|
||
Script::Classic(script) => {
|
||
_ = self
|
||
.globalscope
|
||
.run_a_classic_script(cx, script, RethrowErrors::No);
|
||
},
|
||
Script::Module(module_tree) => {
|
||
self.globalscope.run_a_module_script(cx, module_tree, false);
|
||
},
|
||
_ => unreachable!(),
|
||
}
|
||
if let Some(dedicated) = self.downcast::<DedicatedWorkerGlobalScope>() {
|
||
dedicated.fire_queued_messages(CanGc::from_cx(cx));
|
||
}
|
||
}
|
||
}
|
||
|
||
// The processResponseConsumeBody steps defined inside
|
||
// [run a worker](https://html.spec.whatwg.org/multipage/#run-a-worker)
|
||
pub(crate) fn process_response_for_workerscope(
|
||
&self,
|
||
metadata: &Metadata,
|
||
policy_container: &PolicyContainer,
|
||
) {
|
||
// Step 1. Set worker global scope's url to response's url.
|
||
self.set_url(metadata.final_url.clone());
|
||
|
||
// Step 2. Set inside settings's creation URL to response's url.
|
||
self.globalscope
|
||
.set_creation_url(metadata.final_url.clone());
|
||
|
||
// Step 3. Initialize worker global scope's policy container given worker global scope, response, and inside settings.
|
||
self.initialize_policy_container_for_worker_global_scope(metadata, policy_container);
|
||
self.set_endpoints_list(ReportingEndpoint::parse_reporting_endpoints_header(
|
||
&metadata.final_url.clone(),
|
||
&metadata.headers,
|
||
));
|
||
self.globalscope.set_https_state(metadata.https_state);
|
||
}
|
||
}
|
||
|
||
impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-self>
|
||
fn Self_(&self) -> DomRoot<WorkerGlobalScope> {
|
||
DomRoot::from_ref(self)
|
||
}
|
||
|
||
/// <https://w3c.github.io/IndexedDB/#factory-interface>
|
||
fn IndexedDB(&self) -> DomRoot<IDBFactory> {
|
||
self.upcast::<GlobalScope>().get_indexeddb()
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-location>
|
||
fn Location(&self) -> DomRoot<WorkerLocation> {
|
||
self.location.or_init(|| {
|
||
WorkerLocation::new(
|
||
self,
|
||
self.worker_url.borrow().clone(),
|
||
CanGc::deprecated_note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-importscripts>
|
||
fn ImportScripts(
|
||
&self,
|
||
cx: &mut js::context::JSContext,
|
||
url_strings: Vec<TrustedScriptURLOrUSVString>,
|
||
) -> ErrorResult {
|
||
// https://html.spec.whatwg.org/multipage/#import-scripts-into-worker-global-scope
|
||
// Step 1: If worker global scope's type is "module", throw a TypeError exception.
|
||
if self.worker_type == WorkerType::Module {
|
||
return Err(Error::Type(
|
||
c"importScripts() is not allowed in module workers".to_owned(),
|
||
));
|
||
}
|
||
|
||
// Step 4: Let urlStrings be « ».
|
||
let mut urls = Vec::with_capacity(url_strings.len());
|
||
// Step 5: For each url of urls:
|
||
for url in url_strings {
|
||
// Step 3: Append the result of invoking the Get Trusted Type compliant string algorithm
|
||
// with TrustedScriptURL, this's relevant global object, url, "WorkerGlobalScope importScripts",
|
||
// and "script" to urlStrings.
|
||
let url = TrustedScriptURL::get_trusted_type_compliant_string(
|
||
cx,
|
||
self.upcast::<GlobalScope>(),
|
||
url,
|
||
"WorkerGlobalScope importScripts",
|
||
)?;
|
||
let url = self.worker_url.borrow().join(&url.str());
|
||
match url {
|
||
Ok(url) => urls.push(url),
|
||
Err(_) => return Err(Error::Syntax(None)),
|
||
};
|
||
}
|
||
|
||
for url in urls {
|
||
let global_scope = self.upcast::<GlobalScope>();
|
||
let request = RequestBuilder::new(
|
||
global_scope.webview_id(),
|
||
UrlWithBlobClaim::from_url_without_having_claimed_blob(url.clone()),
|
||
global_scope.get_referrer(),
|
||
)
|
||
.destination(Destination::Script)
|
||
.credentials_mode(CredentialsMode::Include)
|
||
.parser_metadata(ParserMetadata::NotParserInserted)
|
||
.use_url_credentials(true)
|
||
.with_global_scope(global_scope);
|
||
|
||
// https://html.spec.whatwg.org/multipage/#fetch-a-classic-worker-imported-script
|
||
let (url, bytes, muted_errors) = match load_whole_resource(
|
||
request,
|
||
&global_scope.resource_threads().sender(),
|
||
global_scope,
|
||
&WorkerCspProcessor {
|
||
global_scope: DomRoot::from_ref(global_scope),
|
||
},
|
||
cx,
|
||
) {
|
||
Err(_) => return Err(Error::Network(None)),
|
||
Ok((metadata, bytes, muted_errors)) => {
|
||
// Step 7: Check if response status is not an ok status
|
||
if !metadata.status.is_success() {
|
||
return Err(Error::Network(None));
|
||
}
|
||
|
||
// Step 7: Check if the MIME type is not a JavaScript MIME type
|
||
let not_a_javascript_mime_type =
|
||
!metadata.content_type.clone().is_some_and(|ct| {
|
||
let mime: Mime = ct.into_inner().into();
|
||
SCRIPT_JS_MIMES.contains(&mime.essence_str())
|
||
});
|
||
if not_a_javascript_mime_type {
|
||
return Err(Error::Network(None));
|
||
}
|
||
|
||
(metadata.final_url, bytes, muted_errors)
|
||
},
|
||
};
|
||
|
||
// Step 8. Let sourceText be the result of UTF-8 decoding bodyBytes.
|
||
let (source, _) = UTF_8.decode_with_bom_removal(&bytes);
|
||
|
||
// Step 9. Let mutedErrors be true if response was CORS-cross-origin, and false otherwise.
|
||
// Note: done inside load_whole_resource
|
||
|
||
// Step 10. Let script be the result of creating a classic script
|
||
// given sourceText, settingsObject, response's URL, the default script fetch options, and mutedErrors.
|
||
let script = self.globalscope.create_a_classic_script(
|
||
cx,
|
||
source,
|
||
url,
|
||
ScriptFetchOptions::default_classic_script(),
|
||
ErrorReporting::from(muted_errors),
|
||
Some(IntroductionType::WORKER),
|
||
1,
|
||
true,
|
||
);
|
||
|
||
// Run the classic script script, with rethrow errors set to true.
|
||
let result = self
|
||
.globalscope
|
||
.run_a_classic_script(cx, script, RethrowErrors::Yes);
|
||
|
||
if let Err(error) = result {
|
||
if self.is_closing() {
|
||
// Don't return JSFailed as we might not have
|
||
// any pending exceptions.
|
||
error!("evaluate_script failed (terminated)");
|
||
} else {
|
||
error!("evaluate_script failed");
|
||
return Err(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
// https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onerror
|
||
error_event_handler!(error, GetOnerror, SetOnerror);
|
||
|
||
// https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onlanguagechange
|
||
event_handler!(languagechange, GetOnlanguagechange, SetOnlanguagechange);
|
||
|
||
// https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onoffline
|
||
event_handler!(offline, GetOnoffline, SetOnoffline);
|
||
|
||
// https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-ononline
|
||
event_handler!(online, GetOnonline, SetOnonline);
|
||
|
||
// https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onrejectionhandled
|
||
event_handler!(
|
||
rejectionhandled,
|
||
GetOnrejectionhandled,
|
||
SetOnrejectionhandled
|
||
);
|
||
|
||
// https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onunhandledrejection
|
||
event_handler!(
|
||
unhandledrejection,
|
||
GetOnunhandledrejection,
|
||
SetOnunhandledrejection
|
||
);
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-worker-navigator>
|
||
fn Navigator(&self) -> DomRoot<WorkerNavigator> {
|
||
self.navigator
|
||
.or_init(|| WorkerNavigator::new(self, CanGc::deprecated_note()))
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dfn-Crypto>
|
||
fn Crypto(&self) -> DomRoot<Crypto> {
|
||
self.crypto
|
||
.or_init(|| Crypto::new(self.upcast::<GlobalScope>(), CanGc::deprecated_note()))
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-reporterror>
|
||
fn ReportError(&self, cx: JSContext, error: HandleValue, can_gc: CanGc) {
|
||
self.upcast::<GlobalScope>()
|
||
.report_an_exception(cx, error, can_gc);
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-windowbase64-btoa>
|
||
fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> {
|
||
base64_btoa(btoa)
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-windowbase64-atob>
|
||
fn Atob(&self, atob: DOMString) -> Fallible<DOMString> {
|
||
base64_atob(atob)
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout>
|
||
fn SetTimeout(
|
||
&self,
|
||
cx: &mut js::context::JSContext,
|
||
callback: TrustedScriptOrStringOrFunction,
|
||
timeout: i32,
|
||
args: Vec<HandleValue>,
|
||
) -> Fallible<i32> {
|
||
let callback = match callback {
|
||
TrustedScriptOrStringOrFunction::String(i) => {
|
||
TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
|
||
},
|
||
TrustedScriptOrStringOrFunction::TrustedScript(i) => {
|
||
TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
|
||
},
|
||
TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
|
||
};
|
||
self.upcast::<GlobalScope>().set_timeout_or_interval(
|
||
cx,
|
||
callback,
|
||
args,
|
||
Duration::from_millis(timeout.max(0) as u64),
|
||
IsInterval::NonInterval,
|
||
)
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-cleartimeout>
|
||
fn ClearTimeout(&self, handle: i32) {
|
||
self.upcast::<GlobalScope>()
|
||
.clear_timeout_or_interval(handle);
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval>
|
||
fn SetInterval(
|
||
&self,
|
||
cx: &mut js::context::JSContext,
|
||
callback: TrustedScriptOrStringOrFunction,
|
||
timeout: i32,
|
||
args: Vec<HandleValue>,
|
||
) -> Fallible<i32> {
|
||
let callback = match callback {
|
||
TrustedScriptOrStringOrFunction::String(i) => {
|
||
TimerCallback::StringTimerCallback(TrustedScriptOrString::String(i))
|
||
},
|
||
TrustedScriptOrStringOrFunction::TrustedScript(i) => {
|
||
TimerCallback::StringTimerCallback(TrustedScriptOrString::TrustedScript(i))
|
||
},
|
||
TrustedScriptOrStringOrFunction::Function(i) => TimerCallback::FunctionTimerCallback(i),
|
||
};
|
||
self.upcast::<GlobalScope>().set_timeout_or_interval(
|
||
cx,
|
||
callback,
|
||
args,
|
||
Duration::from_millis(timeout.max(0) as u64),
|
||
IsInterval::Interval,
|
||
)
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval>
|
||
fn ClearInterval(&self, handle: i32) {
|
||
self.ClearTimeout(handle);
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-queuemicrotask>
|
||
fn QueueMicrotask(&self, callback: Rc<VoidFunction>) {
|
||
self.enqueue_microtask(Microtask::User(UserMicrotask {
|
||
callback,
|
||
pipeline: self.pipeline_id(),
|
||
}));
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-createimagebitmap>
|
||
fn CreateImageBitmap(
|
||
&self,
|
||
realm: &mut CurrentRealm,
|
||
image: ImageBitmapSource,
|
||
options: &ImageBitmapOptions,
|
||
) -> Rc<Promise> {
|
||
ImageBitmap::create_image_bitmap(self.upcast(), image, 0, 0, None, None, options, realm)
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-createimagebitmap>
|
||
fn CreateImageBitmap_(
|
||
&self,
|
||
realm: &mut CurrentRealm,
|
||
image: ImageBitmapSource,
|
||
sx: i32,
|
||
sy: i32,
|
||
sw: i32,
|
||
sh: i32,
|
||
options: &ImageBitmapOptions,
|
||
) -> Rc<Promise> {
|
||
ImageBitmap::create_image_bitmap(
|
||
self.upcast(),
|
||
image,
|
||
sx,
|
||
sy,
|
||
Some(sw),
|
||
Some(sh),
|
||
options,
|
||
realm,
|
||
)
|
||
}
|
||
|
||
/// <https://fetch.spec.whatwg.org/#dom-global-fetch>
|
||
fn Fetch(
|
||
&self,
|
||
realm: &mut CurrentRealm,
|
||
input: RequestOrUSVString,
|
||
init: RootedTraceableBox<RequestInit>,
|
||
) -> Rc<Promise> {
|
||
Fetch(self.upcast(), input, init, realm)
|
||
}
|
||
|
||
/// <https://w3c.github.io/hr-time/#the-performance-attribute>
|
||
fn Performance(&self) -> DomRoot<Performance> {
|
||
self.performance.or_init(|| {
|
||
let global_scope = self.upcast::<GlobalScope>();
|
||
Performance::new(
|
||
global_scope,
|
||
self.navigation_start,
|
||
CanGc::deprecated_note(),
|
||
)
|
||
})
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-origin>
|
||
fn Origin(&self) -> USVString {
|
||
USVString(
|
||
self.upcast::<GlobalScope>()
|
||
.origin()
|
||
.immutable()
|
||
.ascii_serialization(),
|
||
)
|
||
}
|
||
|
||
/// <https://w3c.github.io/webappsec-secure-contexts/#dom-windoworworkerglobalscope-issecurecontext>
|
||
fn IsSecureContext(&self) -> bool {
|
||
self.upcast::<GlobalScope>().is_secure_context()
|
||
}
|
||
|
||
/// <https://html.spec.whatwg.org/multipage/#dom-structuredclone>
|
||
fn StructuredClone(
|
||
&self,
|
||
cx: JSContext,
|
||
value: HandleValue,
|
||
options: RootedTraceableBox<StructuredSerializeOptions>,
|
||
can_gc: CanGc,
|
||
retval: MutableHandleValue,
|
||
) -> Fallible<()> {
|
||
self.upcast::<GlobalScope>()
|
||
.structured_clone(cx, value, options, retval, can_gc)
|
||
}
|
||
|
||
/// <https://www.w3.org/TR/trusted-types/#dom-windoworworkerglobalscope-trustedtypes>
|
||
fn TrustedTypes(&self, cx: &mut js::context::JSContext) -> DomRoot<TrustedTypePolicyFactory> {
|
||
self.trusted_types.or_init(|| {
|
||
let global_scope = self.upcast::<GlobalScope>();
|
||
TrustedTypePolicyFactory::new(cx, global_scope)
|
||
})
|
||
}
|
||
}
|
||
|
||
impl WorkerGlobalScope {
|
||
pub(crate) fn new_script_pair(&self) -> (ScriptEventLoopSender, ScriptEventLoopReceiver) {
|
||
let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
|
||
if let Some(dedicated) = dedicated {
|
||
dedicated.new_script_pair()
|
||
} else if let Some(shared) = self.downcast::<SharedWorkerGlobalScope>() {
|
||
shared.new_script_pair()
|
||
} else {
|
||
panic!("need to implement a sender for ServiceWorker")
|
||
}
|
||
}
|
||
|
||
/// Process a single event as if it were the next event
|
||
/// in the queue for this worker event-loop.
|
||
/// Returns a boolean indicating whether further events should be processed.
|
||
pub(crate) fn process_event(
|
||
&self,
|
||
msg: CommonScriptMsg,
|
||
cx: &mut js::context::JSContext,
|
||
) -> bool {
|
||
if self.is_closing() {
|
||
return false;
|
||
}
|
||
match msg {
|
||
CommonScriptMsg::Task(_, task, _, _) => task.run_box(cx),
|
||
CommonScriptMsg::CollectReports(reports_chan) => {
|
||
let cx: JSContext = cx.into();
|
||
perform_memory_report(|ops| {
|
||
let reports = cx.get_reports(format!("url({})", self.get_url()), ops);
|
||
reports_chan.send(ProcessReports::new(reports));
|
||
});
|
||
},
|
||
CommonScriptMsg::ReportCspViolations(_, violations) => {
|
||
self.upcast::<GlobalScope>()
|
||
.report_csp_violations(violations, None, None);
|
||
},
|
||
}
|
||
true
|
||
}
|
||
|
||
pub(crate) fn close(&self) {
|
||
self.closing.store(true, Ordering::SeqCst);
|
||
self.upcast::<GlobalScope>()
|
||
.task_manager()
|
||
.cancel_all_tasks_and_ignore_future_tasks();
|
||
if let Some(factory) = self.upcast::<GlobalScope>().get_existing_indexeddb() {
|
||
factory.abort_pending_upgrades();
|
||
}
|
||
}
|
||
|
||
pub(super) fn init_debugger_global(
|
||
&self,
|
||
debugger_global: &DebuggerGlobalScope,
|
||
cx: &mut js::context::JSContext,
|
||
) {
|
||
let mut realm = enter_auto_realm(cx, self);
|
||
let cx = &mut realm.current_realm();
|
||
|
||
// Convert the debugger global’s reflector to a Value, wrapping it from its originating realm (debugger realm)
|
||
// into the active realm (debuggee realm) so that it can be passed across compartments.
|
||
rooted!(&in(cx) let mut wrapped_global: Value);
|
||
debugger_global.reflector().safe_to_jsval(
|
||
cx.into(),
|
||
wrapped_global.handle_mut(),
|
||
CanGc::from_cx(cx),
|
||
);
|
||
self.debugger_global.set(*wrapped_global);
|
||
}
|
||
|
||
pub(super) fn handle_devtools_message(
|
||
&self,
|
||
msg: DevtoolScriptControlMsg,
|
||
cx: &mut js::context::JSContext,
|
||
) {
|
||
match msg {
|
||
DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, _wants_updates) => {},
|
||
DevtoolScriptControlMsg::Eval(code, id, frame_actor_id, reply) => {
|
||
let debugger_global_handle = rooted_heap_handle(self, |this| &this.debugger_global);
|
||
let debugger_global =
|
||
root_from_handlevalue::<DebuggerGlobalScope>(debugger_global_handle, cx.into())
|
||
.expect("must be a debugger global scope");
|
||
|
||
debugger_global.fire_eval(
|
||
cx,
|
||
code.into(),
|
||
id,
|
||
Some(self.worker_id()),
|
||
frame_actor_id,
|
||
reply,
|
||
);
|
||
},
|
||
_ => debug!("got an unusable devtools control message inside the worker!"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[expect(unsafe_code)]
|
||
unsafe extern "C" fn interrupt_callback(cx: *mut RawJSContext) -> bool {
|
||
let in_realm_proof = AlreadyInRealm::assert_for_cx(unsafe { JSContext::from_ptr(cx) });
|
||
let global = unsafe { GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof)) };
|
||
|
||
// If we are running the debugger script, just exit immediately.
|
||
let Some(worker) = global.downcast::<WorkerGlobalScope>() else {
|
||
return false;
|
||
};
|
||
|
||
// A false response causes the script to terminate.
|
||
!worker.is_closing()
|
||
}
|
||
|
||
struct WorkerCspProcessor {
|
||
global_scope: DomRoot<GlobalScope>,
|
||
}
|
||
|
||
impl CspViolationsProcessor for WorkerCspProcessor {
|
||
fn process_csp_violations(&self, violations: Vec<Violation>) {
|
||
self.global_scope
|
||
.report_csp_violations(violations, None, None);
|
||
}
|
||
}
|