diff --git a/Cargo.lock b/Cargo.lock index e2503d2cde4..391c553a00b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1925,6 +1925,7 @@ dependencies = [ "servo_malloc_size_of", "servo_url", "strum_macros", + "stylo", "stylo_traits", "url", "webdriver", diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 1816cf05373..65d94641322 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -172,6 +172,7 @@ use crate::browsingcontext::{ AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator, NewBrowsingContextInfo, }; +use crate::constellation_webview::ConstellationWebView; use crate::event_loop::EventLoop; use crate::pipeline::{InitialPipelineState, Pipeline}; use crate::process_manager::ProcessManager; @@ -229,18 +230,6 @@ struct WebrenderWGPU { wgpu_image_map: WGPUImageMap, } -/// Servo supports multiple top-level browsing contexts or “webviews”, so `Constellation` needs to -/// store webview-specific data for bookkeeping. -struct WebView { - /// The currently focused browsing context in this webview for key events. - /// The focused pipeline is the current entry of the focused browsing - /// context. - focused_browsing_context_id: BrowsingContextId, - - /// The joint session history for this webview. - session_history: JointSessionHistory, -} - /// A browsing context group. /// /// @@ -324,7 +313,7 @@ pub struct Constellation { compositor_proxy: CompositorProxy, /// Bookkeeping data for all webviews in the constellation. - webviews: WebViewManager, + webviews: WebViewManager, /// Channels for the constellation to send messages to the public /// resource-related threads. There are two groups of resource threads: one @@ -895,6 +884,16 @@ where if self.shutting_down { return; } + + let Some(theme) = self + .webviews + .get(webview_id) + .map(ConstellationWebView::theme) + else { + warn!("Tried to create Pipeline for uknown WebViewId: {webview_id:?}"); + return; + }; + debug!( "{}: Creating new pipeline in {}", pipeline_id, browsing_context_id @@ -973,6 +972,7 @@ where time_profiler_chan: self.time_profiler_chan.clone(), mem_profiler_chan: self.mem_profiler_chan.clone(), viewport_details: initial_viewport_details, + theme, event_loop, load_data, prev_throttled: throttled, @@ -1436,8 +1436,8 @@ where size_type, ); }, - EmbedderToConstellationMessage::ThemeChange(theme) => { - self.handle_theme_change(theme); + EmbedderToConstellationMessage::ThemeChange(webview_id, theme) => { + self.handle_theme_change(webview_id, theme); }, EmbedderToConstellationMessage::TickAnimation(webview_ids) => { self.handle_tick_animation(webview_ids) @@ -3142,13 +3142,8 @@ where // Register this new top-level browsing context id as a webview and set // its focused browsing context to be itself. - self.webviews.add( - webview_id, - WebView { - focused_browsing_context_id: browsing_context_id, - session_history: JointSessionHistory::new(), - }, - ); + self.webviews + .add(webview_id, ConstellationWebView::new(browsing_context_id)); // https://html.spec.whatwg.org/multipage/#creating-a-new-browsing-context-group let mut new_bc_group: BrowsingContextGroup = Default::default(); @@ -3554,10 +3549,7 @@ where self.pipelines.insert(new_pipeline_id, pipeline); self.webviews.add( new_webview_id, - WebView { - focused_browsing_context_id: new_browsing_context_id, - session_history: JointSessionHistory::new(), - }, + ConstellationWebView::new(new_browsing_context_id), ); // https://html.spec.whatwg.org/multipage/#bcg-append @@ -5623,18 +5615,31 @@ where } } - /// Handle theme change events from the embedder and forward them to the script thread + /// Handle theme change events from the embedder and forward them to all appropriate `ScriptThread`s. #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn handle_theme_change(&mut self, theme: Theme) { + fn handle_theme_change(&mut self, webview_id: WebViewId, theme: Theme) { + let Some(webview) = self.webviews.get_mut(webview_id) else { + warn!("Received theme change request for uknown WebViewId: {webview_id:?}"); + return; + }; + if !webview.set_theme(theme) { + return; + } + for pipeline in self.pipelines.values() { - let msg = ScriptThreadMessage::ThemeChange(pipeline.id, theme); - if let Err(err) = pipeline.event_loop.send(msg) { + if pipeline.webview_id != webview_id { + continue; + } + if let Err(error) = pipeline + .event_loop + .send(ScriptThreadMessage::ThemeChange(pipeline.id, theme)) + { warn!( - "{}: Failed to send theme change event to pipeline ({:?}).", - pipeline.id, err + "{}: Failed to send theme change event to pipeline ({error:?}).", + pipeline.id, ); } } diff --git a/components/constellation/constellation_webview.rs b/components/constellation/constellation_webview.rs new file mode 100644 index 00000000000..16c19e629fb --- /dev/null +++ b/components/constellation/constellation_webview.rs @@ -0,0 +1,45 @@ +/* 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 base::id::BrowsingContextId; +use embedder_traits::Theme; + +use crate::session_history::JointSessionHistory; + +/// The `Constellation`'s view of a `WebView` in the embedding layer. This tracks all of the +/// `Constellation` state for this `WebView`. +pub(crate) struct ConstellationWebView { + /// The currently focused browsing context in this webview for key events. + /// The focused pipeline is the current entry of the focused browsing + /// context. + pub focused_browsing_context_id: BrowsingContextId, + + /// The joint session history for this webview. + pub session_history: JointSessionHistory, + + /// The [`Theme`] that this [`ConstellationWebView`] uses. This is communicated to all + /// `ScriptThread`s so that they know how to render the contents of a particular `WebView. + theme: Theme, +} + +impl ConstellationWebView { + pub(crate) fn new(focused_browsing_context_id: BrowsingContextId) -> Self { + Self { + focused_browsing_context_id, + session_history: JointSessionHistory::new(), + theme: Theme::Light, + } + } + + /// Set the [`Theme`] on this [`ConstellationWebView`] returning true if the theme changed. + pub(crate) fn set_theme(&mut self, new_theme: Theme) -> bool { + let old_theme = std::mem::replace(&mut self.theme, new_theme); + old_theme != self.theme + } + + /// Get the [`Theme`] of this [`ConstellationWebView`]. + pub(crate) fn theme(&self) -> Theme { + self.theme + } +} diff --git a/components/constellation/lib.rs b/components/constellation/lib.rs index c24543743b7..8927888aa93 100644 --- a/components/constellation/lib.rs +++ b/components/constellation/lib.rs @@ -9,6 +9,7 @@ mod tracing; mod browsingcontext; mod constellation; +mod constellation_webview; mod event_loop; mod logging; mod pipeline; diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 556ef9bd60f..a0bf106ea06 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -25,7 +25,7 @@ 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, ViewportDetails}; +use embedder_traits::{AnimationState, FocusSequenceNumber, Theme, ViewportDetails}; use fonts::{SystemFontServiceProxy, SystemFontServiceProxySender}; use ipc_channel::Error; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -61,7 +61,7 @@ pub struct Pipeline { /// The ID of the browsing context that contains this Pipeline. pub browsing_context_id: BrowsingContextId, - /// The ID of the top-level browsing context that contains this Pipeline. + /// The [`WebViewId`] of the `WebView` that contains this Pipeline. pub webview_id: WebViewId, pub opener: Option, @@ -170,6 +170,9 @@ pub struct InitialPipelineState { /// The initial [`ViewportDetails`] to use when starting this new [`Pipeline`]. pub viewport_details: ViewportDetails, + /// The initial [`Theme`] to use when starting this new [`Pipeline`]. + pub theme: Theme, + /// The ID of the pipeline namespace for this script thread. pub pipeline_namespace_id: PipelineNamespaceId, @@ -224,6 +227,7 @@ impl Pipeline { opener: state.opener, load_data: state.load_data.clone(), viewport_details: state.viewport_details, + theme: state.theme, }; if let Err(e) = script_chan.send(ScriptThreadMessage::AttachLayout(new_layout_info)) @@ -280,6 +284,7 @@ impl Pipeline { time_profiler_chan: state.time_profiler_chan, mem_profiler_chan: state.mem_profiler_chan, viewport_details: state.viewport_details, + theme: state.theme, script_chan: script_chan.clone(), load_data: state.load_data.clone(), script_port, @@ -494,6 +499,7 @@ pub struct UnprivilegedPipelineContent { time_profiler_chan: time::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan, viewport_details: ViewportDetails, + theme: Theme, script_chan: IpcSender, load_data: LoadData, script_port: IpcReceiver, @@ -544,6 +550,7 @@ impl UnprivilegedPipelineContent { memory_profiler_sender: self.mem_profiler_chan.clone(), devtools_server_sender: self.devtools_ipc_sender, viewport_details: self.viewport_details, + theme: self.theme, pipeline_namespace_id: self.pipeline_namespace_id, content_process_shutdown_sender: content_process_shutdown_chan, webgl_chan: self.webgl_chan, diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index 3452c9a6e4c..4aecb00834c 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -15,7 +15,7 @@ use base::Epoch; use base::id::{PipelineId, WebViewId}; use compositing_traits::CrossProcessCompositorApi; use constellation_traits::ScrollState; -use embedder_traits::{UntrustedNodeAddress, ViewportDetails}; +use embedder_traits::{Theme, UntrustedNodeAddress, ViewportDetails}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Scale, Size2D, Vector2D}; use fnv::FnvHashMap; @@ -503,8 +503,7 @@ impl LayoutThread { Scale::new(config.viewport_details.hidpi_scale_factor.get()), Box::new(LayoutFontMetricsProvider(config.font_context.clone())), ComputedValues::initial_values_with_font_override(font), - // TODO: obtain preferred color scheme from embedder - PrefersColorScheme::Light, + config.theme.into(), ); LayoutThread { @@ -951,7 +950,8 @@ impl LayoutThread { size_did_change || pixel_ratio_did_change } - fn theme_did_change(&self, theme: PrefersColorScheme) -> bool { + fn theme_did_change(&self, theme: Theme) -> bool { + let theme: PrefersColorScheme = theme.into(); theme != self.device().color_scheme() } @@ -959,7 +959,7 @@ impl LayoutThread { fn update_device( &mut self, viewport_details: ViewportDetails, - theme: PrefersColorScheme, + theme: Theme, guards: &StylesheetGuards, ) { let device = Device::new( @@ -969,7 +969,7 @@ impl LayoutThread { Scale::new(viewport_details.hidpi_scale_factor.get()), Box::new(LayoutFontMetricsProvider(self.font_context.clone())), self.stylist.device().default_computed_values().to_arc(), - theme, + theme.into(), ); // Preserve any previously computed root font size. diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 7de3d4977b1..0ccf0e93b11 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -223,6 +223,7 @@ impl HTMLIFrameElement { old_pipeline_id, sandbox: sandboxed, viewport_details, + theme: window.theme(), }; window .as_global_scope() @@ -238,6 +239,7 @@ impl HTMLIFrameElement { opener: None, load_data, viewport_details, + theme: window.theme(), }; self.pipeline_id.set(Some(new_pipeline_id)); @@ -250,6 +252,7 @@ impl HTMLIFrameElement { old_pipeline_id, sandbox: sandboxed, viewport_details, + theme: window.theme(), }; window .as_global_scope() diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 24e694b4f06..f75ae4b4cb3 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -80,7 +80,6 @@ use style::dom::OpaqueNode; use style::error_reporting::{ContextualParseError, ParseErrorReporter}; use style::properties::PropertyId; use style::properties::style_structs::Font; -use style::queries::values::PrefersColorScheme; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::UrlExtraData; @@ -269,7 +268,7 @@ pub(crate) struct Window { /// Platform theme. #[no_trace] - theme: Cell, + theme: Cell, /// Parent id associated with this page, if any. #[no_trace] @@ -2739,13 +2738,13 @@ impl Window { self.viewport_details.get() } + /// Get the theme of this [`Window`]. + pub(crate) fn theme(&self) -> Theme { + self.theme.get() + } + /// Handle a theme change request, triggering a reflow is any actual change occured. pub(crate) fn handle_theme_change(&self, new_theme: Theme) { - let new_theme = match new_theme { - Theme::Light => PrefersColorScheme::Light, - Theme::Dark => PrefersColorScheme::Dark, - }; - if self.theme.get() == new_theme { return; } @@ -3033,6 +3032,7 @@ impl Window { player_context: WindowGLContext, #[cfg(feature = "webgpu")] gpu_id_hub: Arc, inherited_secure_context: Option, + theme: Theme, ) -> DomRoot { let error_reporter = CSSErrorReporter { pipelineid: pipeline_id, @@ -3118,7 +3118,7 @@ impl Window { throttled: Cell::new(false), layout_marker: DomRefCell::new(Rc::new(Cell::new(true))), current_event: DomRefCell::new(None), - theme: Cell::new(PrefersColorScheme::Light), + theme: Cell::new(theme), trusted_types: Default::default(), }); diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs index dc02f9feb49..a8decee24ed 100644 --- a/components/script/dom/windowproxy.rs +++ b/components/script/dom/windowproxy.rs @@ -329,6 +329,9 @@ impl WindowProxy { opener: Some(self.browsing_context_id), load_data, viewport_details: window.viewport_details(), + // Use the current `WebView`'s theme initially, but the embedder may + // change this later. + theme: window.theme(), }; ScriptThread::process_attach_layout(new_layout_info, document.origin().clone()); // TODO: if noopener is false, copy the sessionStorage storage area of the creator origin. diff --git a/components/script/navigation.rs b/components/script/navigation.rs index 17cff5bab4a..54f2db77d48 100644 --- a/components/script/navigation.rs +++ b/components/script/navigation.rs @@ -12,7 +12,7 @@ use base::cross_process_instant::CrossProcessInstant; use base::id::{BrowsingContextId, PipelineId, WebViewId}; use constellation_traits::LoadData; use crossbeam_channel::Sender; -use embedder_traits::ViewportDetails; +use embedder_traits::{Theme, ViewportDetails}; use http::header; use net_traits::request::{ CredentialsMode, InsecureRequestsPolicy, RedirectMode, RequestBuilder, RequestMode, @@ -159,6 +159,9 @@ pub(crate) struct InProgressLoad { /// this load. #[no_trace] pub(crate) url_list: Vec, + /// The [`Theme`] to use for this page, once it loads. + #[no_trace] + pub(crate) theme: Theme, } impl InProgressLoad { @@ -171,6 +174,7 @@ impl InProgressLoad { parent_info: Option, opener: Option, viewport_details: ViewportDetails, + theme: Theme, origin: MutableOrigin, load_data: LoadData, ) -> InProgressLoad { @@ -189,6 +193,7 @@ impl InProgressLoad { canceller: Default::default(), load_data, url_list: vec![url], + theme, } } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index e0309298f3d..4565c02dcb9 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -403,14 +403,20 @@ impl ScriptThreadFactory for ScriptThread { WebViewId::install(state.webview_id); let roots = RootCollection::new(); let _stack_roots = ThreadLocalStackRoots::new(&roots); - let id = state.id; - let browsing_context_id = state.browsing_context_id; - let webview_id = state.webview_id; - let parent_info = state.parent_info; - let opener = state.opener; let memory_profiler_sender = state.memory_profiler_sender.clone(); - let viewport_details = state.viewport_details; + let in_progress_load = InProgressLoad::new( + state.id, + state.browsing_context_id, + state.webview_id, + state.parent_info, + state.opener, + state.viewport_details, + state.theme, + MutableOrigin::new(load_data.url.origin()), + load_data, + ); + let reporter_name = format!("script-reporter-{:?}", state.id); let script_thread = ScriptThread::new(state, layout_factory, system_font_service); SCRIPT_THREAD_ROOT.with(|root| { @@ -419,19 +425,8 @@ impl ScriptThreadFactory for ScriptThread { let mut failsafe = ScriptMemoryFailsafe::new(&script_thread); - let origin = MutableOrigin::new(load_data.url.origin()); - script_thread.pre_page_load(InProgressLoad::new( - id, - browsing_context_id, - webview_id, - parent_info, - opener, - viewport_details, - origin, - load_data, - )); + script_thread.pre_page_load(in_progress_load); - let reporter_name = format!("script-reporter-{:?}", id); memory_profiler_sender.run_with_memory_reporting( || { script_thread.start(CanGc::note()); @@ -2435,6 +2430,7 @@ impl ScriptThread { opener, load_data, viewport_details, + theme, } = new_layout_info; // Kick off the fetch for the new resource. @@ -2446,6 +2442,7 @@ impl ScriptThread { parent_info, opener, viewport_details, + theme, origin, load_data, ); @@ -3189,6 +3186,7 @@ impl ScriptThread { time_profiler_chan: self.senders.time_profiler_sender.clone(), compositor_api: self.compositor_api.clone(), viewport_details: incomplete.viewport_details, + theme: incomplete.theme, }; // Create the window and document objects. @@ -3228,6 +3226,7 @@ impl ScriptThread { #[cfg(feature = "webgpu")] self.gpu_id_hub.clone(), incomplete.load_data.inherited_secure_context, + incomplete.theme, ); let _realm = enter_realm(&*window); diff --git a/components/servo/tests/webview.rs b/components/servo/tests/webview.rs index 41900015b94..d7cb9a965e0 100644 --- a/components/servo/tests/webview.rs +++ b/components/servo/tests/webview.rs @@ -17,14 +17,21 @@ use std::rc::Rc; use anyhow::ensure; use common::{ServoTest, run_api_tests}; use servo::{ - JSValue, JavaScriptEvaluationError, LoadStatus, WebView, WebViewBuilder, WebViewDelegate, + JSValue, JavaScriptEvaluationError, LoadStatus, Theme, WebView, WebViewBuilder, WebViewDelegate, }; +use url::Url; #[derive(Default)] struct WebViewDelegateImpl { url_changed: Cell, } +impl WebViewDelegateImpl { + pub(crate) fn reset(&self) { + self.url_changed.set(false); + } +} + impl WebViewDelegate for WebViewDelegateImpl { fn notify_url_changed(&self, _webview: servo::WebView, _url: url::Url) { self.url_changed.set(true); @@ -128,10 +135,40 @@ fn test_create_webview_and_immediately_drop_webview_before_shutdown( Ok(()) } +fn test_theme_change(servo_test: &ServoTest) -> Result<(), anyhow::Error> { + let delegate = Rc::new(WebViewDelegateImpl::default()); + let webview = WebViewBuilder::new(servo_test.servo()) + .delegate(delegate.clone()) + .url(Url::parse("data:text/html,page one").unwrap()) + .build(); + + let is_dark_theme_script = "window.matchMedia('(prefers-color-scheme: dark)').matches"; + + // The default theme is "light". + let result = evaluate_javascript(servo_test, webview.clone(), is_dark_theme_script); + ensure!(result == Ok(JSValue::Boolean(false))); + + // Changing the theme updates the current page. + webview.notify_theme_change(Theme::Dark); + let result = evaluate_javascript(servo_test, webview.clone(), is_dark_theme_script); + ensure!(result == Ok(JSValue::Boolean(true))); + + delegate.reset(); + webview.load(Url::parse("data:text/html,page two").unwrap()); + servo_test.spin(move || Ok(!delegate.url_changed.get()))?; + + // The theme persists after a navigation. + let result = evaluate_javascript(servo_test, webview.clone(), is_dark_theme_script); + ensure!(result == Ok(JSValue::Boolean(true))); + + Ok(()) +} + fn main() { run_api_tests!( test_create_webview, test_evaluate_javascript_basic, + test_theme_change, // This test needs to be last, as it tests creating and dropping // a WebView right before shutdown. test_create_webview_and_immediately_drop_webview_before_shutdown diff --git a/components/servo/webview.rs b/components/servo/webview.rs index 10786ad8b69..426a9d86128 100644 --- a/components/servo/webview.rs +++ b/components/servo/webview.rs @@ -395,7 +395,10 @@ impl WebView { pub fn notify_theme_change(&self, theme: Theme) { self.inner() .constellation_proxy - .send(EmbedderToConstellationMessage::ThemeChange(theme)) + .send(EmbedderToConstellationMessage::ThemeChange( + self.id(), + theme, + )) } pub fn load(&self, url: Url) { diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs index fa391f93859..b0fbcd097d4 100644 --- a/components/shared/constellation/from_script_message.rs +++ b/components/shared/constellation/from_script_message.rs @@ -16,7 +16,7 @@ use canvas_traits::canvas::{CanvasId, CanvasMsg}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use embedder_traits::{ AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError, - JavaScriptEvaluationId, MediaSessionEvent, TouchEventResult, ViewportDetails, + JavaScriptEvaluationId, MediaSessionEvent, Theme, TouchEventResult, ViewportDetails, WebDriverMessageId, }; use euclid::default::Size2D as UntypedSize2D; @@ -417,6 +417,8 @@ pub struct IFrameLoadInfoWithData { pub sandbox: IFrameSandboxState, /// The initial viewport size for this iframe. pub viewport_details: ViewportDetails, + /// The [`Theme`] to use within this iframe. + pub theme: Theme, } /// Resources required by workerglobalscopes diff --git a/components/shared/constellation/lib.rs b/components/shared/constellation/lib.rs index 005295000c9..fb00d4afdbf 100644 --- a/components/shared/constellation/lib.rs +++ b/components/shared/constellation/lib.rs @@ -55,7 +55,7 @@ pub enum EmbedderToConstellationMessage { /// Inform the Constellation that a `WebView`'s [`ViewportDetails`] have changed. ChangeViewportDetails(WebViewId, ViewportDetails, WindowSizeType), /// Inform the constellation of a theme change. - ThemeChange(Theme), + ThemeChange(WebViewId, Theme), /// Requests that the constellation instruct script/layout to try to layout again and tick /// animations. TickAnimation(Vec), diff --git a/components/shared/embedder/Cargo.toml b/components/shared/embedder/Cargo.toml index 4904adf6447..801a300bad4 100644 --- a/components/shared/embedder/Cargo.toml +++ b/components/shared/embedder/Cargo.toml @@ -35,6 +35,7 @@ serde = { workspace = true } servo_url = { path = "../../url" } strum_macros = { workspace = true } stylo_traits = { workspace = true } +stylo = { workspace = true } url = { workspace = true } webdriver = { workspace = true } webrender_api = { workspace = true } diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index 575c8f54ff6..0fe1150b099 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -34,6 +34,7 @@ use pixels::Image; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use servo_url::ServoUrl; use strum_macros::IntoStaticStr; +use style::queries::values::PrefersColorScheme; use style_traits::CSSPixel; use url::Url; use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel}; @@ -598,6 +599,16 @@ pub enum Theme { /// Dark theme. Dark, } + +impl From for PrefersColorScheme { + fn from(value: Theme) -> Self { + match value { + Theme::Light => PrefersColorScheme::Light, + Theme::Dark => PrefersColorScheme::Dark, + } + } +} + // The type of MediaSession action. /// #[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)] diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 29acc51765c..a8188d16ccd 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -68,6 +68,8 @@ pub struct NewLayoutInfo { pub load_data: LoadData, /// Initial [`ViewportDetails`] for this layout. pub viewport_details: ViewportDetails, + /// The [`Theme`] of the new layout. + pub theme: Theme, } /// When a pipeline is closed, should its browsing context be discarded too? @@ -321,6 +323,8 @@ pub struct InitialScriptState { pub devtools_server_sender: Option>, /// Initial [`ViewportDetails`] for the frame that is initiating this `ScriptThread`. pub viewport_details: ViewportDetails, + /// Initial [`Theme`] for the frame that is initiating this `ScriptThread`. + pub theme: Theme, /// The ID of the pipeline namespace for this script thread. pub pipeline_namespace_id: PipelineNamespaceId, /// A ping will be sent on this channel once the script thread shuts down. diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 96670e60084..5cc12608dbd 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -20,7 +20,7 @@ use base::Epoch; use base::id::{BrowsingContextId, PipelineId, WebViewId}; use compositing_traits::CrossProcessCompositorApi; use constellation_traits::{LoadData, ScrollState}; -use embedder_traits::{UntrustedNodeAddress, ViewportDetails}; +use embedder_traits::{Theme, UntrustedNodeAddress, ViewportDetails}; use euclid::default::{Point2D, Rect}; use fnv::FnvHashMap; use fonts::{FontContext, SystemFontServiceProxy}; @@ -46,7 +46,6 @@ use style::invalidation::element::restyle_hints::RestyleHint; use style::media_queries::Device; use style::properties::PropertyId; use style::properties::style_structs::Font; -use style::queries::values::PrefersColorScheme; use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot}; use style::stylesheets::Stylesheet; use webrender_api::ImageKey; @@ -182,6 +181,7 @@ pub struct LayoutConfig { pub time_profiler_chan: time::ProfilerChan, pub compositor_api: CrossProcessCompositorApi, pub viewport_details: ViewportDetails, + pub theme: Theme, } pub trait LayoutFactory: Send + Sync { @@ -428,7 +428,7 @@ pub struct ReflowRequest { /// The set of image animations. pub node_to_image_animation_map: FxHashMap, /// The theme for the window - pub theme: PrefersColorScheme, + pub theme: Theme, /// The node highlighted by the devtools, if any pub highlighted_dom_node: Option, }