diff options
author | yvt <i@yvt.jp> | 2021-07-10 17:24:27 +0900 |
---|---|---|
committer | yvt <i@yvt.jp> | 2021-07-10 17:55:42 +0900 |
commit | 01a7de50ab1843d85295f9dccad7f4c099e7208c (patch) | |
tree | ee53fb6e8889deb7b880ee969e6c662e6128d210 /components/script/dom/dedicatedworkerglobalscope.rs | |
parent | ff8d2cdbbfc7a9dc7f38b7dd47cb350fde39388f (diff) | |
parent | 94b613fbdaa2b98f2179fc0bbda13c64e6fa0d38 (diff) | |
download | servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.tar.gz servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.zip |
Merge remote-tracking branch 'upstream/master' into feat-cow-infra
`tests/wpt/web-platform-tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html`
was reverted to the upstream version.
Diffstat (limited to 'components/script/dom/dedicatedworkerglobalscope.rs')
-rw-r--r-- | components/script/dom/dedicatedworkerglobalscope.rs | 838 |
1 files changed, 550 insertions, 288 deletions
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 80b7982d35b..e1c67cfafee 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -1,60 +1,79 @@ /* 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 http://mozilla.org/MPL/2.0/. */ - -use devtools; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::devtools; +use crate::dom::abstractworker::{SimpleWorkerErrorHandler, WorkerScriptMsg}; +use crate::dom::abstractworkerglobalscope::{run_worker_event_loop, WorkerEventLoopMethods}; +use crate::dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; +use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; +use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions; +use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType; +use crate::dom::bindings::error::{ErrorInfo, ErrorResult}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots}; +use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::structuredclone; +use crate::dom::bindings::trace::RootedTraceableBox; +use crate::dom::errorevent::ErrorEvent; +use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; +use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; +use crate::dom::identityhub::Identities; +use crate::dom::messageevent::MessageEvent; +use crate::dom::worker::{TrustedWorkerAddress, Worker}; +use crate::dom::workerglobalscope::WorkerGlobalScope; +use crate::fetch::load_whole_resource; +use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; +use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent; +use crate::script_runtime::{ + new_child_runtime, CommonScriptMsg, ContextForRequestInterrupt, JSContext as SafeJSContext, + Runtime, ScriptChan, ScriptPort, +}; +use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue}; +use crate::task_source::networking::NetworkingTaskSource; +use crate::task_source::TaskSourceName; +use crossbeam_channel::{unbounded, Receiver, Sender}; use devtools_traits::DevtoolScriptControlMsg; -use dom::abstractworker::{SharedRt, SimpleWorkerErrorHandler, WorkerScriptMsg}; -use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; -use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; -use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; -use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; -use dom::bindings::error::{ErrorInfo, ErrorResult}; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{Root, RootCollection}; -use dom::bindings::reflector::DomObject; -use dom::bindings::str::DOMString; -use dom::bindings::structuredclone::StructuredCloneData; -use dom::globalscope::GlobalScope; -use dom::messageevent::MessageEvent; -use dom::worker::{TrustedWorkerAddress, WorkerErrorHandler, WorkerMessageHandler}; -use dom::workerglobalscope::WorkerGlobalScope; use dom_struct::dom_struct; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::IpcReceiver; use ipc_channel::router::ROUTER; -use js::jsapi::{HandleValue, JS_SetInterruptCallback}; -use js::jsapi::{JSAutoCompartment, JSContext}; +use js::jsapi::JS_AddInterruptCallback; +use js::jsapi::{Heap, JSContext, JSObject}; use js::jsval::UndefinedValue; -use js::rust::Runtime; -use msg::constellation_msg::FrameId; -use net_traits::{IpcSend, load_whole_resource}; -use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; -use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; -use script_runtime::ScriptThreadEventCategory::WorkerEvent; -use script_traits::{TimerEvent, TimerSource, WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; +use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue}; +use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId}; +use net_traits::image_cache::ImageCache; +use net_traits::request::{CredentialsMode, Destination, ParserMetadata}; +use net_traits::request::{Referrer, RequestBuilder, RequestMode}; +use net_traits::IpcSend; +use parking_lot::Mutex; +use script_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; use servo_rand::random; use servo_url::{MutableOrigin, ServoUrl}; use std::mem::replace; -use std::sync::{Arc, Mutex}; use std::sync::atomic::AtomicBool; -use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; -use std::thread; -use style::thread_state; +use std::sync::Arc; +use std::thread::{self, JoinHandle}; +use style::thread_state::{self, ThreadState}; /// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular /// value for the duration of this object's lifetime. This ensures that the related Worker /// object only lives as long as necessary (ie. while events are being executed), while /// providing a reference that can be cloned freely. -struct AutoWorkerReset<'a> { +pub struct AutoWorkerReset<'a> { workerscope: &'a DedicatedWorkerGlobalScope, old_worker: Option<TrustedWorkerAddress>, } impl<'a> AutoWorkerReset<'a> { - fn new(workerscope: &'a DedicatedWorkerGlobalScope, - worker: TrustedWorkerAddress) - -> AutoWorkerReset<'a> { + fn new( + workerscope: &'a DedicatedWorkerGlobalScope, + worker: TrustedWorkerAddress, + ) -> AutoWorkerReset<'a> { AutoWorkerReset { workerscope: workerscope, old_worker: replace(&mut *workerscope.worker.borrow_mut(), Some(worker)), @@ -68,82 +87,224 @@ impl<'a> Drop for AutoWorkerReset<'a> { } } -enum MixedMessage { - FromWorker((TrustedWorkerAddress, WorkerScriptMsg)), - FromScheduler((TrustedWorkerAddress, TimerEvent)), - FromDevtools(DevtoolScriptControlMsg) +/// Messages sent from the owning global. +pub enum DedicatedWorkerControlMsg { + /// Shutdown the worker. + Exit, +} + +pub enum DedicatedWorkerScriptMsg { + /// Standard message from a worker. + CommonWorker(TrustedWorkerAddress, WorkerScriptMsg), + /// Wake-up call from the task queue. + WakeUp, +} + +pub enum MixedMessage { + FromWorker(DedicatedWorkerScriptMsg), + FromDevtools(DevtoolScriptControlMsg), + FromControl(DedicatedWorkerControlMsg), +} + +impl QueuedTaskConversion for DedicatedWorkerScriptMsg { + fn task_source_name(&self) -> Option<&TaskSourceName> { + let common_worker_msg = match self { + DedicatedWorkerScriptMsg::CommonWorker(_, common_worker_msg) => common_worker_msg, + _ => return None, + }; + let script_msg = match common_worker_msg { + WorkerScriptMsg::Common(ref script_msg) => script_msg, + _ => return None, + }; + match script_msg { + CommonScriptMsg::Task(_category, _boxed, _pipeline_id, source_name) => { + Some(&source_name) + }, + _ => None, + } + } + + fn pipeline_id(&self) -> Option<PipelineId> { + // Workers always return None, since the pipeline_id is only used to check for document activity, + // and this check does not apply to worker event-loops. + None + } + + fn into_queued_task(self) -> Option<QueuedTask> { + let (worker, common_worker_msg) = match self { + DedicatedWorkerScriptMsg::CommonWorker(worker, common_worker_msg) => { + (worker, common_worker_msg) + }, + _ => return None, + }; + let script_msg = match common_worker_msg { + WorkerScriptMsg::Common(script_msg) => script_msg, + _ => return None, + }; + let (category, boxed, pipeline_id, task_source) = match script_msg { + CommonScriptMsg::Task(category, boxed, pipeline_id, task_source) => { + (category, boxed, pipeline_id, task_source) + }, + _ => return None, + }; + Some((Some(worker), category, boxed, pipeline_id, task_source)) + } + + fn from_queued_task(queued_task: QueuedTask) -> Self { + let (worker, category, boxed, pipeline_id, task_source) = queued_task; + let script_msg = CommonScriptMsg::Task(category, boxed, pipeline_id, task_source); + DedicatedWorkerScriptMsg::CommonWorker(worker.unwrap(), WorkerScriptMsg::Common(script_msg)) + } + + fn inactive_msg() -> Self { + // Inactive is only relevant in the context of a browsing-context event-loop. + panic!("Workers should never receive messages marked as inactive"); + } + + fn wake_up_msg() -> Self { + DedicatedWorkerScriptMsg::WakeUp + } + + fn is_wake_up(&self) -> bool { + match self { + DedicatedWorkerScriptMsg::WakeUp => true, + _ => false, + } + } } +unsafe_no_jsmanaged_fields!(TaskQueue<DedicatedWorkerScriptMsg>); + // https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope #[dom_struct] pub struct DedicatedWorkerGlobalScope { workerglobalscope: WorkerGlobalScope, - #[ignore_heap_size_of = "Defined in std"] - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, - #[ignore_heap_size_of = "Defined in std"] - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - #[ignore_heap_size_of = "Defined in std"] - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, - #[ignore_heap_size_of = "Trusted<T> has unclear ownership like JS<T>"] - worker: DOMRefCell<Option<TrustedWorkerAddress>>, - #[ignore_heap_size_of = "Can't measure trait objects"] + #[ignore_malloc_size_of = "Defined in std"] + task_queue: TaskQueue<DedicatedWorkerScriptMsg>, + #[ignore_malloc_size_of = "Defined in std"] + own_sender: Sender<DedicatedWorkerScriptMsg>, + #[ignore_malloc_size_of = "Trusted<T> has unclear ownership like Dom<T>"] + worker: DomRefCell<Option<TrustedWorkerAddress>>, + #[ignore_malloc_size_of = "Can't measure trait objects"] /// Sender to the parent thread. - parent_sender: Box<ScriptChan + Send>, + parent_sender: Box<dyn ScriptChan + Send>, + #[ignore_malloc_size_of = "Arc"] + image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, + /// A receiver of control messages, + /// currently only used to signal shutdown. + #[ignore_malloc_size_of = "Channels are hard"] + control_receiver: Receiver<DedicatedWorkerControlMsg>, +} + +impl WorkerEventLoopMethods for DedicatedWorkerGlobalScope { + type WorkerMsg = DedicatedWorkerScriptMsg; + type ControlMsg = DedicatedWorkerControlMsg; + type Event = MixedMessage; + + fn task_queue(&self) -> &TaskQueue<DedicatedWorkerScriptMsg> { + &self.task_queue + } + + fn handle_event(&self, event: MixedMessage) -> bool { + self.handle_mixed_message(event) + } + + fn handle_worker_post_event(&self, worker: &TrustedWorkerAddress) -> Option<AutoWorkerReset> { + let ar = AutoWorkerReset::new(&self, worker.clone()); + Some(ar) + } + + fn from_control_msg(&self, msg: DedicatedWorkerControlMsg) -> MixedMessage { + MixedMessage::FromControl(msg) + } + + fn from_worker_msg(&self, msg: DedicatedWorkerScriptMsg) -> MixedMessage { + MixedMessage::FromWorker(msg) + } + + fn from_devtools_msg(&self, msg: DevtoolScriptControlMsg) -> MixedMessage { + MixedMessage::FromDevtools(msg) + } + + fn control_receiver(&self) -> &Receiver<DedicatedWorkerControlMsg> { + &self.control_receiver + } } impl DedicatedWorkerGlobalScope { - fn new_inherited(init: WorkerGlobalScopeInit, - worker_url: ServoUrl, - from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, - runtime: Runtime, - parent_sender: Box<ScriptChan + Send>, - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, - closing: Arc<AtomicBool>) - -> DedicatedWorkerGlobalScope { + fn new_inherited( + init: WorkerGlobalScopeInit, + worker_name: DOMString, + worker_type: WorkerType, + worker_url: ServoUrl, + from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, + runtime: Runtime, + parent_sender: Box<dyn ScriptChan + Send>, + own_sender: Sender<DedicatedWorkerScriptMsg>, + receiver: Receiver<DedicatedWorkerScriptMsg>, + closing: Arc<AtomicBool>, + image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, + gpu_id_hub: Arc<Mutex<Identities>>, + control_receiver: Receiver<DedicatedWorkerControlMsg>, + ) -> DedicatedWorkerGlobalScope { DedicatedWorkerGlobalScope { - workerglobalscope: WorkerGlobalScope::new_inherited(init, - worker_url, - runtime, - from_devtools_receiver, - timer_event_chan, - Some(closing)), - receiver: receiver, + workerglobalscope: WorkerGlobalScope::new_inherited( + init, + worker_name, + worker_type, + worker_url, + runtime, + from_devtools_receiver, + closing, + gpu_id_hub, + ), + task_queue: TaskQueue::new(receiver, own_sender.clone()), own_sender: own_sender, - timer_event_port: timer_event_port, parent_sender: parent_sender, - worker: DOMRefCell::new(None), + worker: DomRefCell::new(None), + image_cache: image_cache, + browsing_context, + control_receiver, } } #[allow(unsafe_code)] - pub fn new(init: WorkerGlobalScopeInit, - worker_url: ServoUrl, - from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, - runtime: Runtime, - parent_sender: Box<ScriptChan + Send>, - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, - timer_event_chan: IpcSender<TimerEvent>, - timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>, - closing: Arc<AtomicBool>) - -> Root<DedicatedWorkerGlobalScope> { + pub fn new( + init: WorkerGlobalScopeInit, + worker_name: DOMString, + worker_type: WorkerType, + worker_url: ServoUrl, + from_devtools_receiver: Receiver<DevtoolScriptControlMsg>, + runtime: Runtime, + parent_sender: Box<dyn ScriptChan + Send>, + own_sender: Sender<DedicatedWorkerScriptMsg>, + receiver: Receiver<DedicatedWorkerScriptMsg>, + closing: Arc<AtomicBool>, + image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, + gpu_id_hub: Arc<Mutex<Identities>>, + control_receiver: Receiver<DedicatedWorkerControlMsg>, + ) -> DomRoot<DedicatedWorkerGlobalScope> { let cx = runtime.cx(); - let scope = box DedicatedWorkerGlobalScope::new_inherited(init, - worker_url, - from_devtools_receiver, - runtime, - parent_sender, - own_sender, - receiver, - timer_event_chan, - timer_event_port, - closing); - unsafe { - DedicatedWorkerGlobalScopeBinding::Wrap(cx, scope) - } + let scope = Box::new(DedicatedWorkerGlobalScope::new_inherited( + init, + worker_name, + worker_type, + worker_url, + from_devtools_receiver, + runtime, + parent_sender, + own_sender, + receiver, + closing, + image_cache, + browsing_context, + gpu_id_hub, + control_receiver, + )); + unsafe { DedicatedWorkerGlobalScopeBinding::Wrap(SafeJSContext::from_ptr(cx), scope) } } pub fn origin(&self) -> MutableOrigin { @@ -151,233 +312,318 @@ impl DedicatedWorkerGlobalScope { } #[allow(unsafe_code)] - pub fn run_worker_scope(init: WorkerGlobalScopeInit, - worker_url: ServoUrl, - from_devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>, - worker_rt_for_mainthread: Arc<Mutex<Option<SharedRt>>>, - worker: TrustedWorkerAddress, - parent_sender: Box<ScriptChan + Send>, - own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>, - receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>, - worker_load_origin: WorkerScriptLoadOrigin, - closing: Arc<AtomicBool>) { + // https://html.spec.whatwg.org/multipage/#run-a-worker + pub fn run_worker_scope( + init: WorkerGlobalScopeInit, + worker_url: ServoUrl, + from_devtools_receiver: IpcReceiver<DevtoolScriptControlMsg>, + worker: TrustedWorkerAddress, + parent_sender: Box<dyn ScriptChan + Send>, + own_sender: Sender<DedicatedWorkerScriptMsg>, + receiver: Receiver<DedicatedWorkerScriptMsg>, + worker_load_origin: WorkerScriptLoadOrigin, + worker_name: String, + worker_type: WorkerType, + closing: Arc<AtomicBool>, + image_cache: Arc<dyn ImageCache>, + browsing_context: Option<BrowsingContextId>, + gpu_id_hub: Arc<Mutex<Identities>>, + control_receiver: Receiver<DedicatedWorkerControlMsg>, + context_sender: Sender<ContextForRequestInterrupt>, + ) -> JoinHandle<()> { let serialized_worker_url = worker_url.to_string(); let name = format!("WebWorker for {}", serialized_worker_url); - let top_level_frame_id = FrameId::installed(); - - thread::Builder::new().name(name).spawn(move || { - thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER); + let top_level_browsing_context_id = TopLevelBrowsingContextId::installed(); + let current_global = GlobalScope::current().expect("No current global object"); + let origin = current_global.origin().immutable().clone(); + let referrer = current_global.get_referrer(); + let parent = current_global.runtime_handle(); + let current_global_https_state = current_global.get_https_state(); + + thread::Builder::new() + .name(name) + .spawn(move || { + thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER); + + if let Some(top_level_browsing_context_id) = top_level_browsing_context_id { + TopLevelBrowsingContextId::install(top_level_browsing_context_id); + } - if let Some(top_level_frame_id) = top_level_frame_id { - FrameId::install(top_level_frame_id); - } + let roots = RootCollection::new(); + let _stack_roots = ThreadLocalStackRoots::new(&roots); + + let WorkerScriptLoadOrigin { + referrer_url, + referrer_policy, + pipeline_id, + } = worker_load_origin; + + let referrer = referrer_url + .map(|url| Referrer::ReferrerUrl(url)) + .unwrap_or(referrer); + + let request = RequestBuilder::new(worker_url.clone(), referrer) + .destination(Destination::Worker) + .mode(RequestMode::SameOrigin) + .credentials_mode(CredentialsMode::CredentialsSameOrigin) + .parser_metadata(ParserMetadata::NotParserInserted) + .use_url_credentials(true) + .pipeline_id(Some(pipeline_id)) + .referrer_policy(referrer_policy) + .origin(origin); + + let runtime = unsafe { + let task_source = NetworkingTaskSource( + Box::new(WorkerThreadWorkerChan { + sender: own_sender.clone(), + worker: worker.clone(), + }), + pipeline_id, + ); + new_child_runtime(parent, Some(task_source)) + }; + + let _ = context_sender.send(ContextForRequestInterrupt::new(runtime.cx())); + + let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded(); + ROUTER.route_ipc_receiver_to_crossbeam_sender( + from_devtools_receiver, + devtools_mpsc_chan, + ); + + let global = DedicatedWorkerGlobalScope::new( + init, + DOMString::from_string(worker_name), + worker_type, + worker_url, + devtools_mpsc_port, + runtime, + parent_sender.clone(), + own_sender, + receiver, + closing, + image_cache, + browsing_context, + gpu_id_hub, + control_receiver, + ); + // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter + // registration (#6631), so we instead use a random number and cross our fingers. + let scope = global.upcast::<WorkerGlobalScope>(); + let global_scope = global.upcast::<GlobalScope>(); + + global_scope.set_https_state(current_global_https_state); + + let (metadata, bytes) = match load_whole_resource( + request, + &global_scope.resource_threads().sender(), + &global_scope, + ) { + Err(_) => { + println!("error loading script {}", serialized_worker_url); + parent_sender + .send(CommonScriptMsg::Task( + WorkerEvent, + Box::new(SimpleWorkerErrorHandler::new(worker)), + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )) + .unwrap(); + return; + }, + Ok((metadata, bytes)) => (metadata, bytes), + }; + scope.set_url(metadata.final_url); + global_scope.set_https_state(metadata.https_state); + let source = String::from_utf8_lossy(&bytes); + + unsafe { + // Handle interrupt requests + JS_AddInterruptCallback(*scope.get_cx(), Some(interrupt_callback)); + } - let roots = RootCollection::new(); - let _stack_roots_tls = StackRootTLS::new(&roots); - - let WorkerScriptLoadOrigin { referrer_url, referrer_policy, pipeline_id } = worker_load_origin; - - let request = RequestInit { - url: worker_url.clone(), - type_: RequestType::Script, - destination: Destination::Worker, - credentials_mode: CredentialsMode::Include, - use_url_credentials: true, - origin: worker_url, - pipeline_id: pipeline_id, - referrer_url: referrer_url, - referrer_policy: referrer_policy, - .. RequestInit::default() - }; - - let (metadata, bytes) = match load_whole_resource(request, - &init.resource_threads.sender()) { - Err(_) => { - println!("error loading script {}", serialized_worker_url); - parent_sender.send(CommonScriptMsg::RunnableMsg(WorkerEvent, - box SimpleWorkerErrorHandler::new(worker))).unwrap(); + if scope.is_closing() { return; } - Ok((metadata, bytes)) => (metadata, bytes) - }; - let url = metadata.final_url; - let source = String::from_utf8_lossy(&bytes); - - let runtime = unsafe { new_rt_and_cx() }; - *worker_rt_for_mainthread.lock().unwrap() = Some(SharedRt::new(&runtime)); - - let (devtools_mpsc_chan, devtools_mpsc_port) = channel(); - ROUTER.route_ipc_receiver_to_mpsc_sender(from_devtools_receiver, devtools_mpsc_chan); - - let (timer_tx, timer_rx) = channel(); - let (timer_ipc_chan, timer_ipc_port) = ipc::channel().unwrap(); - let worker_for_route = worker.clone(); - ROUTER.add_route(timer_ipc_port.to_opaque(), box move |message| { - let event = message.to().unwrap(); - timer_tx.send((worker_for_route.clone(), event)).unwrap(); - }); - - let global = DedicatedWorkerGlobalScope::new( - init, url, devtools_mpsc_port, runtime, - parent_sender.clone(), own_sender, receiver, - timer_ipc_chan, timer_rx, closing); - // FIXME(njn): workers currently don't have a unique ID suitable for using in reporter - // registration (#6631), so we instead use a random number and cross our fingers. - let scope = global.upcast::<WorkerGlobalScope>(); - - unsafe { - // Handle interrupt requests - JS_SetInterruptCallback(scope.runtime(), Some(interrupt_callback)); - } - if scope.is_closing() { - return; - } - - { - let _ar = AutoWorkerReset::new(&global, worker.clone()); - scope.execute_script(DOMString::from(source)); - } - - let reporter_name = format!("dedicated-worker-reporter-{}", random::<u64>()); - scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| { - // https://html.spec.whatwg.org/multipage/#event-loop-processing-model - // Step 1 - while let Ok(event) = global.receive_event() { - if scope.is_closing() { - break; - } - // Step 3 - global.handle_event(event); - // Step 6 + { let _ar = AutoWorkerReset::new(&global, worker.clone()); - global.upcast::<WorkerGlobalScope>().perform_a_microtask_checkpoint(); + scope.execute_script(DOMString::from(source)); } - }, reporter_name, parent_sender, CommonScriptMsg::CollectReports); - }).expect("Thread spawning failed"); + + let reporter_name = format!("dedicated-worker-reporter-{}", random::<u64>()); + scope + .upcast::<GlobalScope>() + .mem_profiler_chan() + .run_with_memory_reporting( + || { + // Step 29, Run the responsible event loop specified + // by inside settings until it is destroyed. + // The worker processing model remains on this step + // until the event loop is destroyed, + // which happens after the closing flag is set to true. + while !scope.is_closing() { + run_worker_event_loop(&*global, Some(&worker)); + } + }, + reporter_name, + parent_sender, + CommonScriptMsg::CollectReports, + ); + scope.clear_js_runtime(); + }) + .expect("Thread spawning failed") + } + + pub fn image_cache(&self) -> Arc<dyn ImageCache> { + self.image_cache.clone() } - pub fn script_chan(&self) -> Box<ScriptChan + Send> { - box WorkerThreadWorkerChan { + pub fn script_chan(&self) -> Box<dyn ScriptChan + Send> { + Box::new(WorkerThreadWorkerChan { sender: self.own_sender.clone(), worker: self.worker.borrow().as_ref().unwrap().clone(), - } + }) } - pub fn new_script_pair(&self) -> (Box<ScriptChan + Send>, Box<ScriptPort + Send>) { - let (tx, rx) = channel(); - let chan = box SendableWorkerScriptChan { + pub fn new_script_pair(&self) -> (Box<dyn ScriptChan + Send>, Box<dyn ScriptPort + Send>) { + let (tx, rx) = unbounded(); + let chan = Box::new(SendableWorkerScriptChan { sender: tx, worker: self.worker.borrow().as_ref().unwrap().clone(), - }; - (chan, box rx) - } - - pub fn process_event(&self, msg: CommonScriptMsg) { - self.handle_script_event(WorkerScriptMsg::Common(msg)); - } - - #[allow(unsafe_code)] - fn receive_event(&self) -> Result<MixedMessage, RecvError> { - let scope = self.upcast::<WorkerGlobalScope>(); - let worker_port = &self.receiver; - let timer_event_port = &self.timer_event_port; - let devtools_port = scope.from_devtools_receiver(); - - let sel = Select::new(); - let mut worker_handle = sel.handle(worker_port); - let mut timer_event_handle = sel.handle(timer_event_port); - let mut devtools_handle = sel.handle(devtools_port); - unsafe { - worker_handle.add(); - timer_event_handle.add(); - if scope.from_devtools_sender().is_some() { - devtools_handle.add(); - } - } - let ret = sel.wait(); - if ret == worker_handle.id() { - Ok(MixedMessage::FromWorker(try!(worker_port.recv()))) - } else if ret == timer_event_handle.id() { - Ok(MixedMessage::FromScheduler(try!(timer_event_port.recv()))) - } else if ret == devtools_handle.id() { - Ok(MixedMessage::FromDevtools(try!(devtools_port.recv()))) - } else { - panic!("unexpected select result!") - } + }); + (chan, Box::new(rx)) } fn handle_script_event(&self, msg: WorkerScriptMsg) { match msg { - WorkerScriptMsg::DOMMessage(data) => { + WorkerScriptMsg::DOMMessage { origin, data } => { let scope = self.upcast::<WorkerGlobalScope>(); let target = self.upcast(); - let _ac = JSAutoCompartment::new(scope.get_cx(), - scope.reflector().get_jsobject().get()); - rooted!(in(scope.get_cx()) let mut message = UndefinedValue()); - data.read(scope.upcast(), message.handle_mut()); - MessageEvent::dispatch_jsval(target, scope.upcast(), message.handle()); + let _ac = enter_realm(self); + rooted!(in(*scope.get_cx()) let mut message = UndefinedValue()); + if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut()) + { + MessageEvent::dispatch_jsval( + target, + scope.upcast(), + message.handle(), + Some(&origin.ascii_serialization()), + None, + ports, + ); + } else { + MessageEvent::dispatch_error(target, scope.upcast()); + } }, - WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => { - runnable.handler() + WorkerScriptMsg::Common(msg) => { + self.upcast::<WorkerGlobalScope>().process_event(msg); }, - WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => { - let scope = self.upcast::<WorkerGlobalScope>(); - let cx = scope.get_cx(); - let path_seg = format!("url({})", scope.get_url()); - let reports = get_reports(cx, path_seg); - reports_chan.send(reports); - } } } - fn handle_event(&self, event: MixedMessage) { - match event { - MixedMessage::FromDevtools(msg) => { - match msg { - DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => - devtools::handle_evaluate_js(self.upcast(), string, sender), - DevtoolScriptControlMsg::GetCachedMessages(pipe_id, message_types, sender) => - devtools::handle_get_cached_messages(pipe_id, message_types, sender), - DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) => - devtools::handle_wants_live_notifications(self.upcast(), bool_val), - _ => debug!("got an unusable devtools control message inside the worker!"), - } + fn handle_mixed_message(&self, msg: MixedMessage) -> bool { + // FIXME(#26324): `self.worker` is None in devtools messages. + match msg { + MixedMessage::FromDevtools(msg) => match msg { + DevtoolScriptControlMsg::EvaluateJS(_pipe_id, string, sender) => { + devtools::handle_evaluate_js(self.upcast(), string, sender) + }, + DevtoolScriptControlMsg::WantsLiveNotifications(_pipe_id, bool_val) => { + devtools::handle_wants_live_notifications(self.upcast(), bool_val) + }, + _ => debug!("got an unusable devtools control message inside the worker!"), }, - MixedMessage::FromScheduler((linked_worker, timer_event)) => { - match timer_event { - TimerEvent(TimerSource::FromWorker, id) => { - let _ar = AutoWorkerReset::new(self, linked_worker); - let scope = self.upcast::<WorkerGlobalScope>(); - scope.handle_fire_timer(id); - }, - TimerEvent(_, _) => { - panic!("A worker received a TimerEvent from a window.") - } - } - } - MixedMessage::FromWorker((linked_worker, msg)) => { + MixedMessage::FromWorker(DedicatedWorkerScriptMsg::CommonWorker( + linked_worker, + msg, + )) => { let _ar = AutoWorkerReset::new(self, linked_worker); self.handle_script_event(msg); - } + }, + MixedMessage::FromWorker(DedicatedWorkerScriptMsg::WakeUp) => {}, + MixedMessage::FromControl(DedicatedWorkerControlMsg::Exit) => { + return false; + }, } + true } + // https://html.spec.whatwg.org/multipage/#runtime-script-errors-2 + #[allow(unsafe_code)] pub fn forward_error_to_worker_object(&self, error_info: ErrorInfo) { let worker = self.worker.borrow().as_ref().unwrap().clone(); - // TODO: Should use the DOM manipulation task source. + let pipeline_id = self.upcast::<GlobalScope>().pipeline_id(); + let task = Box::new(task!(forward_error_to_worker_object: move || { + let worker = worker.root(); + let global = worker.global(); + + // Step 1. + let event = ErrorEvent::new( + &global, + atom!("error"), + EventBubbles::DoesNotBubble, + EventCancelable::Cancelable, + error_info.message.as_str().into(), + error_info.filename.as_str().into(), + error_info.lineno, + error_info.column, + HandleValue::null(), + ); + let event_status = + event.upcast::<Event>().fire(worker.upcast::<EventTarget>()); + + // Step 2. + if event_status == EventStatus::NotCanceled { + global.report_an_error(error_info, HandleValue::null()); + } + })); self.parent_sender - .send(CommonScriptMsg::RunnableMsg(WorkerEvent, - box WorkerErrorHandler::new(worker, error_info))) + .send(CommonScriptMsg::Task( + WorkerEvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )) .unwrap(); } + + // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage + fn post_message_impl( + &self, + cx: SafeJSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>, + ) -> ErrorResult { + let data = structuredclone::write(cx, message, Some(transfer))?; + let worker = self.worker.borrow().as_ref().unwrap().clone(); + let global_scope = self.upcast::<GlobalScope>(); + let pipeline_id = global_scope.pipeline_id(); + let task = Box::new(task!(post_worker_message: move || { + Worker::handle_message(worker, data); + })); + self.parent_sender + .send(CommonScriptMsg::Task( + WorkerEvent, + task, + Some(pipeline_id), + TaskSourceName::DOMManipulation, + )) + .expect("Sending to parent failed"); + Ok(()) + } + + pub(crate) fn browsing_context(&self) -> Option<BrowsingContextId> { + self.browsing_context + } } #[allow(unsafe_code)] unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool { + let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx)); + let global = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof)); let worker = - Root::downcast::<WorkerGlobalScope>(GlobalScope::from_context(cx)) - .expect("global is not a worker scope"); + DomRoot::downcast::<WorkerGlobalScope>(global).expect("global is not a worker scope"); assert!(worker.is::<DedicatedWorkerGlobalScope>()); // A false response causes the script to terminate @@ -385,16 +631,32 @@ unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool { } impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope { - #[allow(unsafe_code)] - // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage - unsafe fn PostMessage(&self, cx: *mut JSContext, message: HandleValue) -> ErrorResult { - let data = try!(StructuredCloneData::write(cx, message)); - let worker = self.worker.borrow().as_ref().unwrap().clone(); - self.parent_sender - .send(CommonScriptMsg::RunnableMsg(WorkerEvent, - box WorkerMessageHandler::new(worker, data))) - .unwrap(); - Ok(()) + /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage + fn PostMessage( + &self, + cx: SafeJSContext, + message: HandleValue, + transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>, + ) -> ErrorResult { + self.post_message_impl(cx, message, transfer) + } + + /// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage + fn PostMessage_( + &self, + cx: SafeJSContext, + message: HandleValue, + options: RootedTraceableBox<PostMessageOptions>, + ) -> ErrorResult { + let mut rooted = CustomAutoRooter::new( + options + .transfer + .iter() + .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get()) + .collect(), + ); + let guard = CustomAutoRooterGuard::new(*cx, &mut rooted); + self.post_message_impl(cx, message, guard) } // https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close |