Add embedder event for preferred color scheme and respond to it in the LayoutThread (#34532)

* respond to winit platform theme changed event and send it to the layout thread

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* refactoring viewport and theme change handling functions based on feedback

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* fixing issues reported by test-tidy

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* update stylo in order to use color_scheme function on Device

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

---------

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>
Co-authored-by: lazypassion <25536767+lazypassion@users.noreply.github.com>
This commit is contained in:
arthmis 2024-12-12 01:17:02 -05:00 committed by GitHub
parent dfcbb18a8b
commit 26f61103d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 144 additions and 43 deletions

32
Cargo.lock generated
View file

@ -1617,7 +1617,7 @@ dependencies = [
[[package]]
name = "dom"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"bitflags 2.6.0",
"malloc_size_of",
@ -1867,7 +1867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@ -4076,7 +4076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
"windows-targets 0.48.5",
]
[[package]]
@ -4248,7 +4248,7 @@ dependencies = [
[[package]]
name = "malloc_size_of"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"app_units",
"cssparser",
@ -6198,7 +6198,7 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.26.0"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"bitflags 2.6.0",
"cssparser",
@ -6486,7 +6486,7 @@ dependencies = [
[[package]]
name = "servo_arc"
version = "0.4.0"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"serde",
"stable_deref_trait",
@ -6495,7 +6495,7 @@ dependencies = [
[[package]]
name = "servo_atoms"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"string_cache",
"string_cache_codegen",
@ -6868,7 +6868,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_prefs"
version = "0.1.0"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
[[package]]
name = "strck"
@ -6927,7 +6927,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "style"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"app_units",
"arrayvec",
@ -6985,7 +6985,7 @@ dependencies = [
[[package]]
name = "style_config"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"lazy_static",
]
@ -6993,7 +6993,7 @@ dependencies = [
[[package]]
name = "style_derive"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"darling",
"proc-macro2",
@ -7023,7 +7023,7 @@ dependencies = [
[[package]]
name = "style_traits"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"app_units",
"bitflags 2.6.0",
@ -7177,7 +7177,7 @@ dependencies = [
"fastrand",
"once_cell",
"rustix",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@ -7386,7 +7386,7 @@ dependencies = [
[[package]]
name = "to_shmem"
version = "0.1.0"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"cssparser",
"servo_arc",
@ -7399,7 +7399,7 @@ dependencies = [
[[package]]
name = "to_shmem_derive"
version = "0.1.0"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#35e0fa29e93a7e85f44d4ad0610748634f7979b5"
source = "git+https://github.com/servo/stylo?branch=2024-12-04#098f1bef3e231bbf86038c0a2b1ddc31031dc422"
dependencies = [
"darling",
"proc-macro2",
@ -8451,7 +8451,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]

View file

@ -14,8 +14,8 @@ use keyboard_types::{CompositionEvent, KeyboardEvent};
use libc::c_void;
use net::protocols::ProtocolRegistry;
use script_traits::{
GamepadEvent, MediaSessionActionType, MouseButton, TouchEventType, TouchId, TraversalDirection,
WheelDelta,
GamepadEvent, MediaSessionActionType, MouseButton, Theme, TouchEventType, TouchId,
TraversalDirection, WheelDelta,
};
use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize, DeviceIndependentPixel};
use servo_url::ServoUrl;
@ -55,6 +55,8 @@ pub enum EmbedderEvent {
Refresh,
/// Sent when the window is resized.
WindowResize,
/// Sent when the platform theme changes.
ThemeChange(Theme),
/// Sent when a navigation request from script is allowed/refused.
AllowNavigationResponse(PipelineId, bool),
/// Sent when a new URL is to be loaded.
@ -141,6 +143,7 @@ impl Debug for EmbedderEvent {
EmbedderEvent::Idle => write!(f, "Idle"),
EmbedderEvent::Refresh => write!(f, "Refresh"),
EmbedderEvent::WindowResize => write!(f, "Resize"),
EmbedderEvent::ThemeChange(..) => write!(f, "ThemeChange"),
EmbedderEvent::Keyboard(..) => write!(f, "Keyboard"),
EmbedderEvent::IMEComposition(..) => write!(f, "IMEComposition"),
EmbedderEvent::AllowNavigationResponse(..) => write!(f, "AllowNavigationResponse"),

View file

@ -143,7 +143,7 @@ use script_traits::{
LoadData, LoadOrigin, LogEntry, MediaSessionActionType, MessagePortMsg, MouseEventType,
PortMessageTask, SWManagerMsg, SWManagerSenders, ScriptMsg as FromScriptMsg,
ScriptToConstellationChan, ServiceWorkerManagerFactory, ServiceWorkerMsg,
StructuredSerializedData, TimerSchedulerMsg, TraversalDirection, UpdatePipelineIdReason,
StructuredSerializedData, Theme, TimerSchedulerMsg, TraversalDirection, UpdatePipelineIdReason,
WebDriverCommandMsg, WindowSizeData, WindowSizeType,
};
use serde::{Deserialize, Serialize};
@ -1503,6 +1503,9 @@ where
FromCompositorMsg::WindowSize(top_level_browsing_context_id, new_size, size_type) => {
self.handle_window_size_msg(top_level_browsing_context_id, new_size, size_type);
},
FromCompositorMsg::ThemeChange(theme) => {
self.handle_theme_change(theme);
},
FromCompositorMsg::TickAnimation(pipeline_id, tick_type) => {
self.handle_tick_animation(pipeline_id, tick_type)
},
@ -5477,6 +5480,23 @@ where
}
}
/// Handle theme change events from the embedder and forward them to the script thread
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
)]
fn handle_theme_change(&mut self, theme: Theme) {
for pipeline in self.pipelines.values() {
let msg = ConstellationControlMsg::ThemeChange(pipeline.id, theme);
if let Err(err) = pipeline.event_loop.send(msg) {
warn!(
"{}: Failed to send theme change event to pipeline ({:?}).",
pipeline.id, err
);
}
}
}
// Handle switching from fullscreen mode
#[cfg_attr(
feature = "tracing",

View file

@ -72,6 +72,7 @@ mod from_compositor {
Self::ClearCache => target!("ClearCache"),
Self::TraverseHistory(..) => target!("TraverseHistory"),
Self::WindowSize(..) => target!("WindowSize"),
Self::ThemeChange(..) => target!("ThemeChange"),
Self::TickAnimation(..) => target!("TickAnimation"),
Self::WebDriverCommand(..) => target!("WebDriverCommand"),
Self::Reload(..) => target!("Reload"),

View file

@ -734,7 +734,13 @@ impl LayoutThread {
};
let had_used_viewport_units = self.stylist.device().used_viewport_units();
let viewport_size_changed = self.handle_viewport_change(data.window_size, &guards);
let viewport_size_changed = self.viewport_did_change(data.window_size);
let theme_changed = self.theme_did_change(data.theme);
if viewport_size_changed || theme_changed {
self.update_device(data.window_size, data.theme, &guards);
}
if viewport_size_changed && had_used_viewport_units {
if let Some(mut data) = root_element.mutate_data() {
data.hint.insert(RestyleHint::recascade_subtree());
@ -1093,25 +1099,34 @@ impl LayoutThread {
}
}
/// Update layout given a new viewport. Returns true if the viewport changed or false if it didn't.
fn handle_viewport_change(
&mut self,
window_size_data: WindowSizeData,
guards: &StylesheetGuards,
) -> bool {
// If the viewport size and device pixel ratio has not changed, do not make any changes.
let au_viewport_size = Size2D::new(
fn viewport_did_change(&mut self, window_size_data: WindowSizeData) -> bool {
let new_pixel_ratio = window_size_data.device_pixel_ratio.get();
let new_viewport_size = Size2D::new(
Au::from_f32_px(window_size_data.initial_viewport.width),
Au::from_f32_px(window_size_data.initial_viewport.height),
);
if self.stylist.device().au_viewport_size() == au_viewport_size &&
self.stylist.device().device_pixel_ratio().get() ==
window_size_data.device_pixel_ratio.get()
{
return false;
// TODO: eliminate self.viewport_size in favour of using self.device.au_viewport_size()
self.viewport_size = new_viewport_size;
let device = self.stylist.device();
let size_did_change = device.au_viewport_size() != new_viewport_size;
let pixel_ratio_did_change = device.device_pixel_ratio().get() != new_pixel_ratio;
size_did_change || pixel_ratio_did_change
}
fn theme_did_change(&self, theme: PrefersColorScheme) -> bool {
theme != self.device().color_scheme()
}
/// Update layout given a new viewport. Returns true if the viewport changed or false if it didn't.
fn update_device(
&mut self,
window_size_data: WindowSizeData,
theme: PrefersColorScheme,
guards: &StylesheetGuards,
) {
let device = Device::new(
MediaType::screen(),
self.stylist.quirks_mode(),
@ -1119,8 +1134,7 @@ impl LayoutThread {
Scale::new(window_size_data.device_pixel_ratio.get()),
Box::new(LayoutFontMetricsProvider(self.font_context.clone())),
self.stylist.device().default_computed_values().to_arc(),
// TODO: obtain preferred color scheme from embedder
PrefersColorScheme::Light,
theme,
);
// Preserve any previously computed root font size.
@ -1129,9 +1143,6 @@ impl LayoutThread {
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, guards);
self.stylist
.force_stylesheet_origins_dirty(sheet_origins_affected_by_device_change);
self.viewport_size = au_viewport_size;
true
}
}

View file

@ -57,7 +57,7 @@ use script_layout_interface::{
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use script_traits::{
ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData, ScriptMsg,
ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerSchedulerMsg,
ScriptToConstellationChan, ScrollState, StructuredSerializedData, Theme, TimerSchedulerMsg,
WindowSizeData, WindowSizeType,
};
use selectors::attr::CaseSensitivity;
@ -180,6 +180,7 @@ pub enum ReflowReason {
UpdateTheRendering,
Viewport,
WorkletLoaded,
ThemeChange,
}
#[dom_struct]
@ -223,6 +224,10 @@ pub struct Window {
#[no_trace]
unhandled_resize_event: DomRefCell<Option<(WindowSizeData, WindowSizeType)>>,
/// Platform theme.
#[no_trace]
theme: Cell<Theme>,
/// Parent id associated with this page, if any.
#[no_trace]
parent_info: Option<PipelineId>,
@ -1887,6 +1892,11 @@ impl Window {
.or_else(|| document.GetDocumentElement())
.map(|root| root.upcast::<Node>().to_trusted_node_address());
let theme = match self.theme.get() {
Theme::Light => style::queries::values::PrefersColorScheme::Light,
Theme::Dark => style::queries::values::PrefersColorScheme::Dark,
};
// Send new document and relevant styles to layout.
let reflow = ScriptReflow {
reflow_info: Reflow {
@ -1903,6 +1913,7 @@ impl Window {
pending_restyles,
animation_timeline_value: document.current_animation_timeline_value(),
animations: document.animations().sets.clone(),
theme,
};
self.layout.borrow_mut().reflow(reflow);
@ -2353,6 +2364,10 @@ impl Window {
self.window_size.get()
}
pub fn set_theme(&self, theme: Theme) {
self.theme.set(theme);
}
pub fn get_url(&self) -> ServoUrl {
self.Document().url()
}
@ -2728,6 +2743,7 @@ impl Window {
throttled: Cell::new(false),
layout_marker: DomRefCell::new(Rc::new(Cell::new(true))),
current_event: DomRefCell::new(None),
theme: Cell::new(Theme::Light),
});
unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) }

View file

@ -85,8 +85,8 @@ use script_traits::{
EventResult, HistoryEntryReplacement, InitialScriptState, JsEvalResult, LayoutMsg, LoadData,
LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo, Painter,
ProgressiveWebMetricType, ScriptMsg, ScriptToConstellationChan, ScrollState,
StructuredSerializedData, TimerSchedulerMsg, TouchEventType, TouchId, UntrustedNodeAddress,
UpdatePipelineIdReason, WheelDelta, WindowSizeData, WindowSizeType,
StructuredSerializedData, Theme, TimerSchedulerMsg, TouchEventType, TouchId,
UntrustedNodeAddress, UpdatePipelineIdReason, WheelDelta, WindowSizeData, WindowSizeType,
};
use servo_atoms::Atom;
use servo_config::opts;
@ -2021,6 +2021,7 @@ impl ScriptThread {
.parent_info
.or(Some(new_layout_info.new_pipeline_id)),
Resize(id, ..) => Some(id),
ThemeChange(id, ..) => Some(id),
ResizeInactive(id, ..) => Some(id),
UnloadDocument(id) => Some(id),
ExitPipeline(id, ..) => Some(id),
@ -2265,6 +2266,9 @@ impl ScriptThread {
ConstellationControlMsg::ResizeInactive(id, new_size) => {
self.handle_resize_inactive_msg(id, new_size)
},
ConstellationControlMsg::ThemeChange(_, theme) => {
self.handle_theme_change(theme);
},
ConstellationControlMsg::GetTitle(pipeline_id) => {
self.handle_get_title_msg(pipeline_id)
},
@ -2856,6 +2860,15 @@ impl ScriptThread {
})
}
fn handle_theme_change(&self, theme: Theme) {
let docs = self.documents.borrow();
for (_, document) in docs.iter() {
let window = document.window();
window.set_theme(theme);
window.force_reflow(ReflowGoal::Full, ReflowReason::ThemeChange, None);
}
}
// exit_fullscreen creates a new JS promise object, so we need to have entered a realm
fn handle_exit_fullscreen(&self, id: PipelineId, can_gc: CanGc) {
let document = self.documents.borrow().find_document(id);

View file

@ -665,6 +665,15 @@ where
EmbedderEvent::WindowResize => {
return self.compositor.on_resize_window_event();
},
EmbedderEvent::ThemeChange(theme) => {
let msg = ConstellationMsg::ThemeChange(theme);
if let Err(e) = self.constellation_chan.send(msg) {
warn!(
"Sending platform theme change to constellation failed ({:?}).",
e
)
}
},
EmbedderEvent::InvalidateNativeSurface => {
self.compositor.invalidate_native_surface();
},

View file

@ -12,7 +12,7 @@ use embedder_traits::Cursor;
use ipc_channel::ipc::IpcSender;
use keyboard_types::{CompositionEvent, KeyboardEvent};
use script_traits::{
AnimationTickType, CompositorEvent, GamepadEvent, LogEntry, MediaSessionActionType,
AnimationTickType, CompositorEvent, GamepadEvent, LogEntry, MediaSessionActionType, Theme,
TraversalDirection, WebDriverCommandMsg, WindowSizeData, WindowSizeType,
};
use servo_url::ServoUrl;
@ -46,6 +46,8 @@ pub enum ConstellationMsg {
TraverseHistory(TopLevelBrowsingContextId, TraversalDirection),
/// Inform the constellation of a window being resized.
WindowSize(TopLevelBrowsingContextId, WindowSizeData, WindowSizeType),
/// Inform the constellation of a theme change.
ThemeChange(Theme),
/// Requests that the constellation instruct layout to begin a new tick of the animation.
TickAnimation(PipelineId, AnimationTickType),
/// Dispatch a webdriver command
@ -110,6 +112,7 @@ impl ConstellationMsg {
LoadUrl(..) => "LoadUrl",
TraverseHistory(..) => "TraverseHistory",
WindowSize(..) => "WindowSize",
ThemeChange(..) => "ThemeChange",
TickAnimation(..) => "TickAnimation",
WebDriverCommand(..) => "WebDriverCommand",
Reload(..) => "Reload",

View file

@ -284,6 +284,8 @@ pub enum ConstellationControlMsg {
AttachLayout(NewLayoutInfo),
/// Window resized. Sends a DOM event eventually, but first we combine events.
Resize(PipelineId, WindowSizeData, WindowSizeType),
/// Theme changed.
ThemeChange(PipelineId, Theme),
/// Notifies script that window has been resized but to not take immediate action.
ResizeInactive(PipelineId, WindowSizeData),
/// Window switched from fullscreen mode.
@ -398,6 +400,7 @@ impl fmt::Debug for ConstellationControlMsg {
NavigationResponse(..) => "NavigationResponse",
AttachLayout(..) => "AttachLayout",
Resize(..) => "Resize",
ThemeChange(..) => "ThemeChange",
ResizeInactive(..) => "ResizeInactive",
UnloadDocument(..) => "UnloadDocument",
ExitPipeline(..) => "ExitPipeline",
@ -776,6 +779,15 @@ pub enum WindowSizeType {
Resize,
}
/// The type of platform theme.
#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
pub enum Theme {
/// Light theme.
Light,
/// Dark theme.
Dark,
}
/// Messages to the constellation originating from the WebDriver server.
#[derive(Debug, Deserialize, Serialize)]
pub enum WebDriverCommandMsg {

View file

@ -48,6 +48,7 @@ use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::Device;
use style::properties::style_structs::Font;
use style::properties::PropertyId;
use style::queries::values::PrefersColorScheme;
use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
use style::stylesheets::Stylesheet;
use style::Atom;
@ -426,6 +427,8 @@ pub struct ScriptReflow {
pub animation_timeline_value: f64,
/// The set of animations for this document.
pub animations: DocumentAnimationSet,
/// The theme for the window
pub theme: PrefersColorScheme,
}
/// A pending restyle.

View file

@ -487,6 +487,15 @@ impl WindowPortsMethods for Window {
.push(EmbedderEvent::WindowResize);
}
},
winit::event::WindowEvent::ThemeChanged(theme) => {
let theme = match theme {
winit::window::Theme::Light => servo::script_traits::Theme::Light,
winit::window::Theme::Dark => servo::script_traits::Theme::Dark,
};
self.event_queue
.borrow_mut()
.push(EmbedderEvent::ThemeChange(theme));
},
_ => {},
}
}

View file

@ -199,6 +199,7 @@ mod to_servo {
Self::Idle => target!("Idle"),
Self::Refresh => target!("Refresh"),
Self::WindowResize => target!("WindowResize"),
Self::ThemeChange(..) => target!("ThemeChange"),
Self::AllowNavigationResponse(..) => target!("AllowNavigationResponse"),
Self::LoadUrl(..) => target!("LoadUrl"),
Self::MouseWindowEventClass(..) => target!("MouseWindowEventClass"),