From f4dd2960b89bcc6bae3f9dae5be8964c64c1c2c7 Mon Sep 17 00:00:00 2001 From: Jonathan Schwender <55576758+jschwe@users.noreply.github.com> Date: Tue, 2 Sep 2025 08:33:44 +0200 Subject: [PATCH] Add direct script to embedder channel (#39039) This PR **removes** `ScriptToConstellationMessage::ForwardToEmbedder`, and replaces it with an explicit `ScriptToEmbedderChannel`. This new channel is based on `GenericCallback` and in single-process mode will directly send the message the to the embedder and wake it. In multi-process mode, the message is routed via the ROUTER, since waking is only possible from the same process currently. This means in multi-process mode there are likely no direct perf benefits, since we still need to hop the message over the ROUTER (instead of over the constellation). In single-process mode we can directly send the message to the embedder, which should provide a noticable latency improvement in all cases where script is blocked waiting on the embedder to reply. This does not change the way the embedder receives messages - the receiving end is unchanged. ## How was sending messages to the embedder working before? 1. Script wraps it's message to the embedder in `ScriptToConstellationMessage::ForwardToEmbedder` and sends it to constellation. 2. The [constellation event loop] receives the message in [handle_request] 3. If deserialization fails, [an error is logged and the message is ignored] 4. Since our message came from script, it is handle in [handle_request_from_script] 5. The message is logged with trace log level 6. If the pipeline is closed, [a warning is logged and the message ignored] 7. The wrapped `EmbedderMsg` [is forwarded to the embedder]. Sending the message also invokes `wake()` on the embedder eventloop waker. [constellation event loop]: https://github.com/servo/servo/blob/2e1b2e72605e5cf944f5f2db1f8ad7546e911b3b/components/constellation/constellation.rs#L755 [handle request]: https://github.com/servo/servo/blob/2e1b2e72605e5cf944f5f2db1f8ad7546e911b3b/components/constellation/constellation.rs#L1182 [an error is logged and the message is ignored]: https://github.com/servo/servo/blob/2e1b2e72605e5cf944f5f2db1f8ad7546e911b3b/components/constellation/constellation.rs#L1252 [handle_request_from_script]: https://github.com/servo/servo/blob/main/components/constellation/constellation.rs#L1590 [a warning is logged and the message ignored]: https://github.com/servo/servo/blob/2e1b2e72605e5cf944f5f2db1f8ad7546e911b3b/components/constellation/constellation.rs#L1599 [is forwarded to the embedder]: https://github.com/servo/servo/blob/2e1b2e72605e5cf944f5f2db1f8ad7546e911b3b/components/constellation/constellation.rs#L1701 Testing: Communication between Script and Embedder is extensive, so this should be covered by existing tests. Signed-off-by: Jonathan Schwender --- Cargo.lock | 1 + components/constellation/constellation.rs | 12 ++-- components/constellation/pipeline.rs | 10 ++- components/constellation/tracing.rs | 71 ------------------- components/script/clipboard_provider.rs | 17 ++--- components/script/dom/debuggerglobalscope.rs | 4 +- .../script/dom/dedicatedworkerglobalscope.rs | 1 + .../script/dom/dissimilaroriginwindow.rs | 1 + .../script/dom/document_event_handler.rs | 8 +-- components/script/dom/globalscope.rs | 15 +++- .../script/dom/html/htmlinputelement.rs | 6 +- .../script/dom/html/htmltextareaelement.rs | 6 +- components/script/dom/window.rs | 13 ++-- components/script/dom/workerglobalscope.rs | 2 + components/script/dom/workletglobalscope.rs | 5 +- components/script/messaging.rs | 5 ++ components/script/script_thread.rs | 7 ++ .../constellation/from_script_message.rs | 10 +-- components/shared/embedder/lib.rs | 33 ++++++++- components/shared/script/Cargo.toml | 1 + components/shared/script/lib.rs | 5 +- 21 files changed, 120 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac1226d931c..30c721e36a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7461,6 +7461,7 @@ dependencies = [ "euclid", "ipc-channel", "keyboard-types", + "log", "malloc_size_of_derive", "media", "net_traits", diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index fe9b5cfda7b..5dfb421ce21 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -134,8 +134,8 @@ use embedder_traits::{ AnimationState, CompositorHitTestResult, EmbedderMsg, EmbedderProxy, FocusId, FocusSequenceNumber, InputEvent, JSValue, JavaScriptEvaluationError, JavaScriptEvaluationId, KeyboardEvent, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState, - MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg, - WebDriverCommandResponse, WebDriverLoadStatus, WebDriverScriptCommand, + MouseButton, MouseButtonAction, MouseButtonEvent, ScriptToEmbedderChan, Theme, ViewportDetails, + WebDriverCommandMsg, WebDriverCommandResponse, WebDriverLoadStatus, WebDriverScriptCommand, }; use euclid::Size2D; use euclid::default::Size2D as UntypedSize2D; @@ -968,6 +968,10 @@ where self.public_resource_threads.clone() }; + let embedder_chan = self.embedder_proxy.sender.clone(); + let eventloop_waker = self.embedder_proxy.event_loop_waker.clone(); + let script_to_embedder_chan = ScriptToEmbedderChan::new(embedder_chan, eventloop_waker); + let result = Pipeline::spawn::(InitialPipelineState { id: pipeline_id, browsing_context_id, @@ -978,6 +982,7 @@ where sender: self.script_sender.clone(), pipeline_id, }, + script_to_embedder_chan, namespace_request_sender: self.namespace_ipc_sender.clone(), pipeline_namespace_id: self.next_pipeline_namespace_id(), background_monitor_register: self.background_monitor_register.clone(), @@ -1702,9 +1707,6 @@ where self.broadcast_channels .schedule_broadcast(router_id, message); }, - ScriptToConstellationMessage::ForwardToEmbedder(embedder_msg) => { - self.embedder_proxy.send(embedder_msg); - }, ScriptToConstellationMessage::PipelineExited => { self.handle_pipeline_exited(source_pipeline_id); }, diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index d897a916bcd..ad78dcef5a0 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -27,7 +27,9 @@ use constellation_traits::{LoadData, SWManagerMsg, ScriptToConstellationChan}; use crossbeam_channel::{Sender, unbounded}; use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg}; use embedder_traits::user_content_manager::UserContentManager; -use embedder_traits::{AnimationState, FocusSequenceNumber, Theme, ViewportDetails}; +use embedder_traits::{ + AnimationState, FocusSequenceNumber, ScriptToEmbedderChan, Theme, ViewportDetails, +}; use fonts::{SystemFontServiceProxy, SystemFontServiceProxySender}; use ipc_channel::Error; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -131,6 +133,9 @@ pub struct InitialPipelineState { /// A channel to the associated constellation. pub script_to_constellation_chan: ScriptToConstellationChan, + /// A channel to the embedder. + pub script_to_embedder_chan: ScriptToEmbedderChan, + /// A sender to request pipeline namespace ids. pub namespace_request_sender: GenericSender, @@ -277,6 +282,7 @@ impl Pipeline { parent_pipeline_id: state.parent_pipeline_id, opener: state.opener, script_to_constellation_chan: state.script_to_constellation_chan.clone(), + script_to_embedder_chan: state.script_to_embedder_chan, namespace_request_sender: state.namespace_request_sender, background_hang_monitor_to_constellation_chan: state .background_hang_monitor_to_constellation_chan @@ -476,6 +482,7 @@ pub struct UnprivilegedPipelineContent { opener: Option, namespace_request_sender: GenericSender, script_to_constellation_chan: ScriptToConstellationChan, + script_to_embedder_chan: ScriptToEmbedderChan, background_hang_monitor_to_constellation_chan: IpcSender, bhm_control_port: Option>, devtools_ipc_sender: Option>, @@ -530,6 +537,7 @@ impl UnprivilegedPipelineContent { constellation_sender: self.script_chan.clone(), constellation_receiver: self.script_port, pipeline_to_constellation_sender: self.script_to_constellation_chan.clone(), + pipeline_to_embedder_sender: self.script_to_embedder_chan, background_hang_monitor_register: background_hang_monitor_register.clone(), #[cfg(feature = "bluetooth")] bluetooth_sender: self.bluetooth_thread, diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs index 1f081eb813a..3cb28ad5af9 100644 --- a/components/constellation/tracing.rs +++ b/components/constellation/tracing.rs @@ -110,8 +110,6 @@ mod from_compositor { } mod from_script { - use embedder_traits::LoadStatus; - use super::LogTarget; macro_rules! target { @@ -141,7 +139,6 @@ mod from_script { target!("RemoveBroadcastChannelNameInRouter") }, Self::ScheduleBroadcast(..) => target!("ScheduleBroadcast"), - Self::ForwardToEmbedder(msg) => msg.log_target(), Self::BroadcastStorageEvent(..) => target!("BroadcastStorageEvent"), Self::ChangeRunningAnimationsState(..) => target!("ChangeRunningAnimationsState"), Self::CreateCanvasPaintThread(..) => target!("CreateCanvasPaintThread"), @@ -188,72 +185,4 @@ mod from_script { } } } - - impl LogTarget for embedder_traits::EmbedderMsg { - fn log_target(&self) -> &'static str { - macro_rules! target_variant { - ($name:literal) => { - target!("ForwardToEmbedder(" $name ")") - }; - } - match self { - Self::Status(..) => target_variant!("Status"), - Self::ChangePageTitle(..) => target_variant!("ChangePageTitle"), - Self::MoveTo(..) => target_variant!("MoveTo"), - Self::ResizeTo(..) => target_variant!("ResizeTo"), - Self::ShowSimpleDialog(..) => target_variant!("ShowSimpleDialog"), - Self::RequestAuthentication(..) => target_variant!("RequestAuthentication"), - Self::ShowContextMenu(..) => target_variant!("ShowContextMenu"), - Self::AllowNavigationRequest(..) => target_variant!("AllowNavigationRequest"), - Self::AllowOpeningWebView(..) => target_variant!("AllowOpeningWebView"), - Self::WebViewClosed(..) => target_variant!("WebViewClosed"), - Self::WebViewFocused(..) => target_variant!("WebViewFocused"), - Self::WebViewBlurred => target_variant!("WebViewBlurred"), - Self::WebResourceRequested(..) => target_variant!("WebResourceRequested"), - Self::AllowUnload(..) => target_variant!("AllowUnload"), - Self::Keyboard(..) => target_variant!("Keyboard"), - Self::ClearClipboard(..) => target_variant!("ClearClipboard"), - Self::GetClipboardText(..) => target_variant!("GetClipboardText"), - Self::SetClipboardText(..) => target_variant!("SetClipboardText"), - Self::SetCursor(..) => target_variant!("SetCursor"), - Self::NewFavicon(..) => target_variant!("NewFavicon"), - Self::HistoryChanged(..) => target_variant!("HistoryChanged"), - Self::HistoryTraversalComplete(..) => target_variant!("HistoryTraversalComplete"), - Self::GetWindowRect(..) => target_variant!("GetWindowRect"), - Self::GetScreenMetrics(..) => target_variant!("GetScreenMetrics"), - Self::NotifyFullscreenStateChanged(..) => { - target_variant!("NotifyFullscreenStateChanged") - }, - Self::NotifyLoadStatusChanged(_, LoadStatus::Started) => { - target_variant!("NotifyLoadStatusChanged(LoadStatus::Started)") - }, - Self::NotifyLoadStatusChanged(_, LoadStatus::HeadParsed) => { - target_variant!("NotifyLoadStatusChanged(LoadStatus::HeadParsed)") - }, - Self::NotifyLoadStatusChanged(_, LoadStatus::Complete) => { - target_variant!("NotifyLoadStatusChanged(LoadStatus::Complete") - }, - Self::Panic(..) => target_variant!("Panic"), - Self::GetSelectedBluetoothDevice(..) => { - target_variant!("GetSelectedBluetoothDevice") - }, - Self::SelectFiles(..) => target_variant!("SelectFiles"), - Self::PromptPermission(..) => target_variant!("PromptPermission"), - Self::ShowIME(..) => target_variant!("ShowIME"), - Self::HideIME(..) => target_variant!("HideIME"), - Self::ReportProfile(..) => target_variant!("ReportProfile"), - Self::MediaSessionEvent(..) => target_variant!("MediaSessionEvent"), - Self::OnDevtoolsStarted(..) => target_variant!("OnDevtoolsStarted"), - Self::RequestDevtoolsConnection(..) => target_variant!("RequestDevtoolsConnection"), - Self::PlayGamepadHapticEffect(..) => target_variant!("PlayGamepadHapticEffect"), - Self::StopGamepadHapticEffect(..) => target_variant!("StopGamepadHapticEffect"), - Self::ShutdownComplete => target_variant!("ShutdownComplete"), - Self::ShowNotification(..) => target_variant!("ShowNotification"), - Self::ShowFormControl(..) => target_variant!("ShowFormControl"), - Self::FinishJavaScriptEvaluation(..) => { - target_variant!("FinishJavaScriptEvaluation") - }, - } - } - } } diff --git a/components/script/clipboard_provider.rs b/components/script/clipboard_provider.rs index 4c814942f60..b589efdbd7b 100644 --- a/components/script/clipboard_provider.rs +++ b/components/script/clipboard_provider.rs @@ -3,8 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use base::id::WebViewId; -use constellation_traits::{ScriptToConstellationChan, ScriptToConstellationMessage}; -use embedder_traits::EmbedderMsg; +use embedder_traits::{EmbedderMsg, ScriptToEmbedderChan}; use ipc_channel::ipc::channel; use malloc_size_of_derive::MallocSizeOf; @@ -19,25 +18,21 @@ pub trait ClipboardProvider { #[derive(MallocSizeOf)] pub(crate) struct EmbedderClipboardProvider { - pub constellation_sender: ScriptToConstellationChan, + pub embedder_sender: ScriptToEmbedderChan, pub webview_id: WebViewId, } impl ClipboardProvider for EmbedderClipboardProvider { fn get_text(&mut self) -> Result { let (tx, rx) = channel().unwrap(); - self.constellation_sender - .send(ScriptToConstellationMessage::ForwardToEmbedder( - EmbedderMsg::GetClipboardText(self.webview_id, tx), - )) + self.embedder_sender + .send(EmbedderMsg::GetClipboardText(self.webview_id, tx)) .unwrap(); rx.recv().unwrap() } fn set_text(&mut self, s: String) { - self.constellation_sender - .send(ScriptToConstellationMessage::ForwardToEmbedder( - EmbedderMsg::SetClipboardText(self.webview_id, s), - )) + self.embedder_sender + .send(EmbedderMsg::SetClipboardText(self.webview_id, s)) .unwrap(); } } diff --git a/components/script/dom/debuggerglobalscope.rs b/components/script/dom/debuggerglobalscope.rs index ca5c3090b06..c362f08616b 100644 --- a/components/script/dom/debuggerglobalscope.rs +++ b/components/script/dom/debuggerglobalscope.rs @@ -8,8 +8,8 @@ use base::id::{Index, PipelineId, PipelineNamespaceId}; use constellation_traits::ScriptToConstellationChan; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, SourceInfo, WorkerId}; use dom_struct::dom_struct; -use embedder_traits::JavaScriptEvaluationError; use embedder_traits::resources::{self, Resource}; +use embedder_traits::{JavaScriptEvaluationError, ScriptToEmbedderChan}; use ipc_channel::ipc::IpcSender; use js::jsval::UndefinedValue; use js::rust::wrappers::JS_DefineDebuggerObject; @@ -66,6 +66,7 @@ impl DebuggerGlobalScope { mem_profiler_chan: mem::ProfilerChan, time_profiler_chan: time::ProfilerChan, script_to_constellation_chan: ScriptToConstellationChan, + script_to_embedder_chan: ScriptToEmbedderChan, resource_threads: ResourceThreads, #[cfg(feature = "webgpu")] gpu_id_hub: std::sync::Arc, can_gc: CanGc, @@ -77,6 +78,7 @@ impl DebuggerGlobalScope { mem_profiler_chan, time_profiler_chan, script_to_constellation_chan, + script_to_embedder_chan, resource_threads, MutableOrigin::new(ImmutableOrigin::new_opaque()), ServoUrl::parse_with_base(None, "about:internal/debugger") diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 432abc3686e..7ce86e7b412 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -438,6 +438,7 @@ impl DedicatedWorkerGlobalScope { init.mem_profiler_chan.clone(), init.time_profiler_chan.clone(), init.script_to_constellation_chan.clone(), + init.script_to_embedder_chan.clone(), init.resource_threads.clone(), #[cfg(feature = "webgpu")] gpu_id_hub.clone(), diff --git a/components/script/dom/dissimilaroriginwindow.rs b/components/script/dom/dissimilaroriginwindow.rs index eb0d8bce663..881042286b9 100644 --- a/components/script/dom/dissimilaroriginwindow.rs +++ b/components/script/dom/dissimilaroriginwindow.rs @@ -58,6 +58,7 @@ impl DissimilarOriginWindow { global_to_clone_from.mem_profiler_chan().clone(), global_to_clone_from.time_profiler_chan().clone(), global_to_clone_from.script_to_constellation_chan().clone(), + global_to_clone_from.script_to_embedder_chan().clone(), global_to_clone_from.resource_threads().clone(), global_to_clone_from.origin().clone(), global_to_clone_from.creation_url().clone(), diff --git a/components/script/dom/document_event_handler.rs b/components/script/dom/document_event_handler.rs index 6cc5a984b19..4bf5ee54e34 100644 --- a/components/script/dom/document_event_handler.rs +++ b/components/script/dom/document_event_handler.rs @@ -1345,10 +1345,10 @@ impl DocumentEventHandler { }, ClipboardEventType::Paste => { let (sender, receiver) = ipc::channel().unwrap(); - self.window - .send_to_constellation(ScriptToConstellationMessage::ForwardToEmbedder( - EmbedderMsg::GetClipboardText(self.window.webview_id(), sender), - )); + self.window.send_to_embedder(EmbedderMsg::GetClipboardText( + self.window.webview_id(), + sender, + )); let text_contents = receiver .recv() .map(Result::unwrap_or_default) diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index a07eab246a5..39a85a654ff 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -26,7 +26,7 @@ use content_security_policy::CspList; use crossbeam_channel::Sender; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg}; use dom_struct::dom_struct; -use embedder_traits::{EmbedderMsg, JavaScriptEvaluationError}; +use embedder_traits::{EmbedderMsg, JavaScriptEvaluationError, ScriptToEmbedderChan}; use fonts::FontContext; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; @@ -254,6 +254,11 @@ pub(crate) struct GlobalScope { #[no_trace] script_to_constellation_chan: ScriptToConstellationChan, + /// A handle for communicating messages to the Embedder. + #[ignore_malloc_size_of = "channels are hard"] + #[no_trace] + script_to_embedder_chan: ScriptToEmbedderChan, + /// in_error_reporting_mode: Cell, @@ -741,6 +746,7 @@ impl GlobalScope { mem_profiler_chan: profile_mem::ProfilerChan, time_profiler_chan: profile_time::ProfilerChan, script_to_constellation_chan: ScriptToConstellationChan, + script_to_embedder_chan: ScriptToEmbedderChan, resource_threads: ResourceThreads, origin: MutableOrigin, creation_url: ServoUrl, @@ -770,6 +776,7 @@ impl GlobalScope { mem_profiler_chan, time_profiler_chan, script_to_constellation_chan, + script_to_embedder_chan, in_error_reporting_mode: Default::default(), resource_threads, timers: OnceCell::default(), @@ -2477,8 +2484,12 @@ impl GlobalScope { &self.script_to_constellation_chan } + pub(crate) fn script_to_embedder_chan(&self) -> &ScriptToEmbedderChan { + &self.script_to_embedder_chan + } + pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) { - self.send_to_constellation(ScriptToConstellationMessage::ForwardToEmbedder(msg)); + self.script_to_embedder_chan().send(msg).unwrap(); } pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) { diff --git a/components/script/dom/html/htmlinputelement.rs b/components/script/dom/html/htmlinputelement.rs index cd2a437e824..d0830dffc63 100644 --- a/components/script/dom/html/htmlinputelement.rs +++ b/components/script/dom/html/htmlinputelement.rs @@ -456,10 +456,10 @@ impl HTMLInputElement { prefix: Option, document: &Document, ) -> HTMLInputElement { - let constellation_sender = document + let embedder_sender = document .window() .as_global_scope() - .script_to_constellation_chan() + .script_to_embedder_chan() .clone(); HTMLInputElement { htmlelement: HTMLElement::new_inherited_with_state( @@ -478,7 +478,7 @@ impl HTMLInputElement { Single, DOMString::new(), EmbedderClipboardProvider { - constellation_sender, + embedder_sender, webview_id: document.webview_id(), }, None, diff --git a/components/script/dom/html/htmltextareaelement.rs b/components/script/dom/html/htmltextareaelement.rs index 2757d26da2a..d6ff11de9e1 100644 --- a/components/script/dom/html/htmltextareaelement.rs +++ b/components/script/dom/html/htmltextareaelement.rs @@ -141,10 +141,10 @@ impl HTMLTextAreaElement { prefix: Option, document: &Document, ) -> HTMLTextAreaElement { - let constellation_sender = document + let embedder_sender = document .window() .as_global_scope() - .script_to_constellation_chan() + .script_to_embedder_chan() .clone(); HTMLTextAreaElement { htmlelement: HTMLElement::new_inherited_with_state( @@ -158,7 +158,7 @@ impl HTMLTextAreaElement { Lines::Multiple, DOMString::new(), EmbedderClipboardProvider { - constellation_sender, + embedder_sender, webview_id: document.webview_id(), }, None, diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index a99d062ab8f..990ae915eaa 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -35,9 +35,9 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarker use dom_struct::dom_struct; use embedder_traits::user_content_manager::{UserContentManager, UserScript}; use embedder_traits::{ - AlertResponse, ConfirmResponse, EmbedderMsg, PromptResponse, SimpleDialog, Theme, - UntrustedNodeAddress, ViewportDetails, WebDriverJSError, WebDriverJSResult, - WebDriverLoadStatus, + AlertResponse, ConfirmResponse, EmbedderMsg, PromptResponse, ScriptToEmbedderChan, + SimpleDialog, Theme, UntrustedNodeAddress, ViewportDetails, WebDriverJSError, + WebDriverJSResult, WebDriverLoadStatus, }; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D}; use euclid::{Point2D, Scale, Size2D, Vector2D}; @@ -2995,7 +2995,10 @@ impl Window { } pub(crate) fn send_to_embedder(&self, msg: EmbedderMsg) { - self.send_to_constellation(ScriptToConstellationMessage::ForwardToEmbedder(msg)); + self.as_global_scope() + .script_to_embedder_chan() + .send(msg) + .unwrap(); } pub(crate) fn send_to_constellation(&self, msg: ScriptToConstellationMessage) { @@ -3100,6 +3103,7 @@ impl Window { time_profiler_chan: TimeProfilerChan, devtools_chan: Option>, constellation_chan: ScriptToConstellationChan, + embedder_chan: ScriptToEmbedderChan, control_chan: GenericSender, pipeline_id: PipelineId, parent_info: Option, @@ -3139,6 +3143,7 @@ impl Window { mem_profiler_chan, time_profiler_chan, constellation_chan, + embedder_chan, resource_threads, origin, creation_url, diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 76dc7af1aa1..82115b1dce3 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -90,6 +90,7 @@ pub(crate) fn prepare_workerscope_init( 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(), @@ -185,6 +186,7 @@ impl WorkerGlobalScope { init.mem_profiler_chan, init.time_profiler_chan, init.script_to_constellation_chan, + init.script_to_embedder_chan, init.resource_threads, MutableOrigin::new(init.origin), init.creation_url, diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index b095de4b6f4..b719a46767a 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -10,7 +10,7 @@ use constellation_traits::{ScriptToConstellationChan, ScriptToConstellationMessa use crossbeam_channel::Sender; use devtools_traits::ScriptToDevtoolsControlMsg; use dom_struct::dom_struct; -use embedder_traits::JavaScriptEvaluationError; +use embedder_traits::{JavaScriptEvaluationError, ScriptToEmbedderChan}; use ipc_channel::ipc::IpcSender; use js::jsval::UndefinedValue; use net_traits::ResourceThreads; @@ -100,6 +100,7 @@ impl WorkletGlobalScope { init.mem_profiler_chan.clone(), init.time_profiler_chan.clone(), script_to_constellation_chan, + init.to_embedder_sender.clone(), init.resource_threads.clone(), MutableOrigin::new(ImmutableOrigin::new_opaque()), base_url.clone(), @@ -198,6 +199,8 @@ pub(crate) struct WorkletGlobalScopeInit { pub(crate) devtools_chan: Option>, /// Messages to send to constellation pub(crate) to_constellation_sender: GenericSender<(PipelineId, ScriptToConstellationMessage)>, + /// Messages to send to the Embedder + pub(crate) to_embedder_sender: ScriptToEmbedderChan, /// The image cache pub(crate) image_cache: Arc, /// Identity manager for WebGPU resources diff --git a/components/script/messaging.rs b/components/script/messaging.rs index 6b81d8f3c7e..d9f6d2f285b 100644 --- a/components/script/messaging.rs +++ b/components/script/messaging.rs @@ -16,6 +16,7 @@ use bluetooth_traits::BluetoothRequest; use constellation_traits::ScriptToConstellationMessage; use crossbeam_channel::{Receiver, SendError, Sender, select}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg}; +use embedder_traits::ScriptToEmbedderChan; use ipc_channel::ipc::IpcSender; use net_traits::FetchResponseMsg; use net_traits::image_cache::ImageCacheResponseMessage; @@ -344,6 +345,10 @@ pub(crate) struct ScriptThreadSenders { pub(crate) pipeline_to_constellation_sender: GenericSender<(PipelineId, ScriptToConstellationMessage)>, + /// A channel to send messages to the Embedder. + #[no_trace] + pub(crate) pipeline_to_embedder_sender: ScriptToEmbedderChan, + /// The shared [`IpcSender`] which is sent to the `ImageCache` when requesting an image. The /// messages on this channel are routed to crossbeam [`Sender`] on the router thread, which /// in turn sends messages to [`ScriptThreadReceivers::image_cache_receiver`]. diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 22367eb859b..eba18757f67 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -781,6 +781,10 @@ impl ScriptThread { .senders .pipeline_to_constellation_sender .clone(), + to_embedder_sender: script_thread + .senders + .pipeline_to_embedder_sender + .clone(), image_cache, #[cfg(feature = "webgpu")] gpu_id_hub: script_thread.gpu_id_hub.clone(), @@ -941,6 +945,7 @@ impl ScriptThread { bluetooth_sender: state.bluetooth_sender, constellation_sender: state.constellation_sender, pipeline_to_constellation_sender: state.pipeline_to_constellation_sender.sender.clone(), + pipeline_to_embedder_sender: state.pipeline_to_embedder_sender.clone(), image_cache_sender: ipc_image_cache_sender, time_profiler_sender: state.time_profiler_sender, memory_profiler_sender: state.memory_profiler_sender, @@ -966,6 +971,7 @@ impl ScriptThread { senders.memory_profiler_sender.clone(), senders.time_profiler_sender.clone(), script_to_constellation_chan, + senders.pipeline_to_embedder_sender.clone(), state.resource_threads.clone(), #[cfg(feature = "webgpu")] gpu_id_hub.clone(), @@ -3312,6 +3318,7 @@ impl ScriptThread { self.senders.time_profiler_sender.clone(), self.senders.devtools_server_sender.clone(), script_to_constellation_chan, + self.senders.pipeline_to_embedder_sender.clone(), self.senders.constellation_sender.clone(), incomplete.pipeline_id, incomplete.parent_info, diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs index d62c70b2783..ff80c761b0d 100644 --- a/components/shared/constellation/from_script_message.rs +++ b/components/shared/constellation/from_script_message.rs @@ -17,9 +17,9 @@ use canvas_traits::canvas::{CanvasId, CanvasMsg}; use compositing_traits::CrossProcessCompositorApi; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use embedder_traits::{ - AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError, - JavaScriptEvaluationId, MediaSessionEvent, Theme, TouchEventResult, ViewportDetails, - WebDriverMessageId, + AnimationState, FocusSequenceNumber, JSValue, JavaScriptEvaluationError, + JavaScriptEvaluationId, MediaSessionEvent, ScriptToEmbedderChan, Theme, TouchEventResult, + ViewportDetails, WebDriverMessageId, }; use euclid::default::Size2D as UntypedSize2D; use fonts_traits::SystemFontServiceProxySender; @@ -444,6 +444,8 @@ pub struct WorkerGlobalScopeInit { pub from_devtools_sender: Option>, /// Messages to send to constellation pub script_to_constellation_chan: ScriptToConstellationChan, + /// Messages to send to the Embedder + pub script_to_embedder_chan: ScriptToEmbedderChan, /// The worker id pub worker_id: WorkerId, /// The pipeline id @@ -525,8 +527,6 @@ pub enum ScriptToConstellationMessage { /// Broadcast a message to all same-origin broadcast channels, /// excluding the source of the broadcast. ScheduleBroadcast(BroadcastChannelRouterId, BroadcastChannelMsg), - /// Forward a message to the embedder. - ForwardToEmbedder(EmbedderMsg), /// Broadcast a storage event to every same-origin pipeline. /// The strings are key, old value and new value. BroadcastStorageEvent( diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index ab092a2a4a8..d53757d04e9 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -21,7 +21,7 @@ use std::ops::Range; use std::path::PathBuf; use std::sync::Arc; -use base::generic_channel::GenericSender; +use base::generic_channel::{GenericCallback, GenericSender, SendResult}; use base::id::{PipelineId, WebViewId}; use crossbeam_channel::Sender; use euclid::{Point2D, Scale, Size2D}; @@ -1081,3 +1081,34 @@ pub struct RgbColor { pub green: u8, pub blue: u8, } + +/// A Script to Embedder Channel +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct ScriptToEmbedderChan(GenericCallback); + +impl ScriptToEmbedderChan { + /// Create a new Channel allowing script to send messages to the Embedder + pub fn new( + embedder_chan: Sender, + waker: Box, + ) -> ScriptToEmbedderChan { + let embedder_callback = GenericCallback::new(move |embedder_msg| { + let msg = match embedder_msg { + Ok(embedder_msg) => embedder_msg, + Err(err) => { + log::warn!("Script to Embedder message error: {err}"); + return; + }, + }; + let _ = embedder_chan.send(msg); + waker.wake(); + }) + .expect("Failed to create channel"); + ScriptToEmbedderChan(embedder_callback) + } + + /// Send a message to and wake the Embedder + pub fn send(&self, msg: EmbedderMsg) -> SendResult { + self.0.send(msg) + } +} diff --git a/components/shared/script/Cargo.toml b/components/shared/script/Cargo.toml index f0b9d9ac675..ca5433e4445 100644 --- a/components/shared/script/Cargo.toml +++ b/components/shared/script/Cargo.toml @@ -44,3 +44,4 @@ stylo_traits = { workspace = true } webgpu_traits = { workspace = true, optional = true } webrender_api = { workspace = true } webxr-api = { workspace = true, features = ["ipc"] } +log = "0.4.27" diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index e5051a069af..e16f835c47e 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -30,7 +30,7 @@ use devtools_traits::ScriptToDevtoolsControlMsg; use embedder_traits::user_content_manager::UserContentManager; use embedder_traits::{ CompositorHitTestResult, FocusSequenceNumber, InputEvent, JavaScriptEvaluationId, - MediaSessionActionType, Theme, ViewportDetails, WebDriverScriptCommand, + MediaSessionActionType, ScriptToEmbedderChan, Theme, ViewportDetails, WebDriverScriptCommand, }; use euclid::{Rect, Scale, Size2D, UnknownUnit}; use ipc_channel::ipc::{IpcReceiver, IpcSender}; @@ -321,6 +321,9 @@ pub struct InitialScriptState { pub constellation_receiver: GenericReceiver, /// A channel on which messages can be sent to the constellation from script. pub pipeline_to_constellation_sender: ScriptToConstellationChan, + /// A channel which allows script to send messages directly to the Embedder + /// This will pump the embedder event loop. + pub pipeline_to_embedder_sender: ScriptToEmbedderChan, /// A handle to register script-(and associated layout-)threads for hang monitoring. pub background_hang_monitor_register: Box, /// A channel to the resource manager thread.