diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-02-23 09:14:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-23 08:14:10 +0000 |
commit | 9c0561536d37f64c028d67648091a314b5b88f6f (patch) | |
tree | d9376aebb1970cb89adb76b610e7176a58e7c7da /components | |
parent | 4849ba901efab9304d71b316ec9e0d7e98e1993b (diff) | |
download | servo-9c0561536d37f64c028d67648091a314b5b88f6f.tar.gz servo-9c0561536d37f64c028d67648091a314b5b88f6f.zip |
script: Do not run layout in a thread (#31346)
* script: Do not run layout in a thread
Instead of spawning a thread for layout that almost always runs
synchronously with script, simply run layout in the script thread.
This is a resurrection of #28708, taking just the bits that remove the
layout thread. It's a complex change and thus is just a first step
toward cleaning up the interface between script and layout. Messages are
still passed from script to layout via a `process()` method and script
proxies some messages to layout from other threads as well.
Big changes:
1. Layout is created in the script thread on Document load, thus every
live document is guaranteed to have a layout. This isn't completely
hidden in the interface, but we can safely `unwrap()` on a Document's
layout.
2. Layout configuration is abstracted away into a LayoutConfig struct
and the LayoutFactory is a struct passed around by the Constellation.
This is to avoid having to monomorphize the entire script thread
for each layout.
3. Instead of having the Constellation block on the layout thread to
figure out the current epoch and whether there are pending web fonts
loading, updates are sent synchronously to the Constellation when
rendering to a screenshot. This practically only used by the WPT.
A couple tests start to fail, which is probably inevitable since removing
the layout thread has introduced timing changes in "exit after load" and
screenshot behavior.
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
* Update test expectations
* Fix some issues found during review
* Clarify some comments
* Address review comments
---------
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
Diffstat (limited to 'components')
39 files changed, 628 insertions, 1229 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 37ac7feb35a..612774a7675 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -37,9 +37,9 @@ use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; use script_traits::compositor::{HitTestInfo, ScrollTree}; use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent}; use script_traits::{ - AnimationState, AnimationTickType, CompositorHitTestResult, LayoutControlMsg, MouseButton, - MouseEventType, ScrollState, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta, - WindowSizeData, WindowSizeType, + AnimationState, AnimationTickType, CompositorHitTestResult, ConstellationControlMsg, + LayoutControlMsg, MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId, + UntrustedNodeAddress, WheelDelta, WindowSizeData, WindowSizeType, }; use servo_geometry::{DeviceIndependentPixel, FramebufferUintLength}; use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor}; @@ -207,11 +207,11 @@ pub struct IOCompositor<Window: WindowMethods + ?Sized> { /// Some XR devices want to run on the main thread. pub webxr_main_thread: webxr::MainThreadRegistry, - /// Map of the pending paint metrics per layout thread. - /// The layout thread for each specific pipeline expects the compositor to + /// Map of the pending paint metrics per Layout. + /// The Layout for each specific pipeline expects the compositor to /// paint frames with specific given IDs (epoch). Once the compositor paints /// these frames, it records the paint time for each of them and sends the - /// metric to the corresponding layout thread. + /// metric to the corresponding Layout. pending_paint_metrics: HashMap<PipelineId, Epoch>, /// The coordinates of the native window, its view and the screen. @@ -1606,9 +1606,11 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { }); if let Some(pipeline) = details.pipeline.as_ref() { - let _ = pipeline - .layout_chan - .send(LayoutControlMsg::SetScrollStates(scroll_states)); + let message = ConstellationControlMsg::ForLayoutFromConstellation( + LayoutControlMsg::SetScrollStates(scroll_states), + *pipeline_id, + ); + let _ = pipeline.script_chan.send(message); } } @@ -1792,10 +1794,9 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { }, ); - // If there are pending paint metrics, we check if any of the painted epochs is - // one of the ones that the paint metrics recorder is expecting . In that case, - // we get the current time, inform the layout thread about it and remove the - // pending metric from the list. + // If there are pending paint metrics, we check if any of the painted epochs is one of the + // ones that the paint metrics recorder is expecting. In that case, we get the current + // time, inform layout about it and remove the pending metric from the list. if !self.pending_paint_metrics.is_empty() { let paint_time = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -1809,7 +1810,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { .webrender .current_epoch(self.webrender_document, id.to_webrender()) { - // and check if it is the one the layout thread is expecting, + // and check if it is the one layout is expecting, let epoch = Epoch(epoch); if *pending_epoch != epoch { continue; @@ -1817,9 +1818,11 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> { // in which case, we remove it from the list of pending metrics, to_remove.push(id.clone()); if let Some(pipeline) = self.pipeline(*id) { - // and inform the layout thread with the measured paint time. - let msg = LayoutControlMsg::PaintMetric(epoch, paint_time); - if let Err(e) = pipeline.layout_chan.send(msg) { + // and inform layout with the measured paint time. + let message = LayoutControlMsg::PaintMetric(epoch, paint_time); + let message = + ConstellationControlMsg::ForLayoutFromConstellation(message, *id); + if let Err(e) = pipeline.script_chan.send(message) { warn!("Sending PaintMetric message to layout failed ({:?}).", e); } } diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml index 43a4c7d982d..2f2120070d3 100644 --- a/components/constellation/Cargo.toml +++ b/components/constellation/Cargo.toml @@ -29,7 +29,6 @@ gfx_traits = { workspace = true } http = { workspace = true } ipc-channel = { workspace = true } keyboard-types = { workspace = true } -layout_traits = { workspace = true } log = { workspace = true } media = { path = "../media" } metrics = { path = "../metrics" } @@ -37,6 +36,7 @@ msg = { workspace = true } net = { path = "../net" } net_traits = { workspace = true } profile_traits = { workspace = true } +script_layout_interface = { workspace = true } script_traits = { workspace = true } serde = { workspace = true } servo_config = { path = "../config" } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index e7ddb5a95f5..1acd379d7bf 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -13,8 +13,7 @@ //! //! * The set of all `Pipeline` objects. Each pipeline gives the //! constellation's view of a `Window`, with its script thread and -//! layout threads. Pipelines may share script threads, but not -//! layout threads. +//! layout. Pipelines may share script threads. //! //! * The set of all `BrowsingContext` objects. Each browsing context //! gives the constellation's view of a `WindowProxy`. @@ -45,7 +44,7 @@ // //! The constellation also maintains channels to threads, including: //! -//! * The script and layout threads. +//! * The script thread. //! * The graphics compositor. //! * The font cache, image cache, and resource manager, which load //! and cache shared fonts, images, or other resources. @@ -67,12 +66,8 @@ //! sender to send some data. Servo tries to achieve deadlock-freedom by using the following //! can-block-on relation: //! -//! * Layout can block on canvas -//! * Layout can block on font cache -//! * Layout can block on image cache //! * Constellation can block on compositor //! * Constellation can block on embedder -//! * Constellation can block on layout //! * Script can block on anything (other than script) //! * Blocking is transitive (if T1 can block on T2 and T2 can block on T3 then T1 can block on T3) //! * Nothing can block on itself! @@ -124,7 +119,6 @@ use ipc_channel::router::ROUTER; use ipc_channel::Error as IpcError; use keyboard_types::webdriver::Event as WebDriverInputEvent; use keyboard_types::KeyboardEvent; -use layout_traits::LayoutThreadFactory; use log::{debug, error, info, trace, warn}; use media::{GLPlayerThreads, WindowGLContext}; use msg::constellation_msg::{ @@ -138,17 +132,18 @@ use net_traits::request::{Referrer, RequestBuilder}; use net_traits::storage_thread::{StorageThreadMsg, StorageType}; use net_traits::{self, FetchResponseMsg, IpcSend, ResourceThreads}; use profile_traits::{mem, time}; +use script_layout_interface::{LayoutFactory, ScriptThreadFactory}; use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent}; use script_traits::{ webdriver_msg, AnimationState, AnimationTickType, AuxiliaryBrowsingContextLoadInfo, BroadcastMsg, CompositorEvent, ConstellationControlMsg, DiscardBrowsingContext, DocumentActivity, DocumentState, GamepadEvent, HistoryEntryReplacement, IFrameLoadInfo, - IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LayoutControlMsg, - LayoutMsg as FromLayoutMsg, LoadData, LoadOrigin, LogEntry, MediaSessionActionType, - MessagePortMsg, MouseEventType, PortMessageTask, SWManagerMsg, SWManagerSenders, - ScriptMsg as FromScriptMsg, ScriptThreadFactory, ScriptToConstellationChan, - ServiceWorkerManagerFactory, ServiceWorkerMsg, StructuredSerializedData, TimerSchedulerMsg, - UpdatePipelineIdReason, WebDriverCommandMsg, WindowSizeData, WindowSizeType, + IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LayoutMsg as FromLayoutMsg, + LoadData, LoadOrigin, LogEntry, MediaSessionActionType, MessagePortMsg, MouseEventType, + PortMessageTask, SWManagerMsg, SWManagerSenders, ScriptMsg as FromScriptMsg, + ScriptToConstellationChan, ServiceWorkerManagerFactory, ServiceWorkerMsg, + StructuredSerializedData, TimerSchedulerMsg, UpdatePipelineIdReason, WebDriverCommandMsg, + WindowSizeData, WindowSizeType, }; use serde::{Deserialize, Serialize}; use servo_config::{opts, pref}; @@ -270,7 +265,7 @@ struct BrowsingContextGroup { /// `LayoutThread` in the `layout` crate, and `ScriptThread` in /// the `script` crate). Script and layout communicate using a `Message` /// type. -pub struct Constellation<Message, LTF, STF, SWF> { +pub struct Constellation<STF, SWF> { /// An ipc-sender/threaded-receiver pair /// to facilitate installing pipeline namespaces in threads /// via a per-process installer. @@ -302,11 +297,16 @@ pub struct Constellation<Message, LTF, STF, SWF> { /// from the background hang monitor. background_hang_monitor_receiver: Receiver<Result<HangMonitorAlert, IpcError>>, - /// An IPC channel for layout threads to send messages to the constellation. - /// This is the layout threads' view of `layout_receiver`. + /// A factory for creating layouts. This allows customizing the kind + /// of layout created for a [`Constellation`] and prevents a circular crate + /// dependency between script and layout. + layout_factory: Arc<dyn LayoutFactory>, + + /// An IPC channel for layout to send messages to the constellation. + /// This is the layout's view of `layout_receiver`. layout_sender: IpcSender<FromLayoutMsg>, - /// A channel for the constellation to receive messages from layout threads. + /// A channel for the constellation to receive messages from layout. /// This is the constellation's view of `layout_sender`. layout_receiver: Receiver<Result<FromLayoutMsg, IpcError>>, @@ -455,7 +455,7 @@ pub struct Constellation<Message, LTF, STF, SWF> { random_pipeline_closure: Option<(ServoRng, f32)>, /// Phantom data that keeps the Rust type system happy. - phantom: PhantomData<(Message, LTF, STF, SWF)>, + phantom: PhantomData<(STF, SWF)>, /// Entry point to create and get channels to a WebGLThread. webgl_threads: Option<WebGLThreads>, @@ -575,7 +575,6 @@ impl WebDriverData { enum ReadyToSave { NoTopLevelBrowsingContext, PendingChanges, - WebFontNotLoaded, DocumentLoading, EpochMismatch, PipelineUnknown, @@ -610,15 +609,15 @@ where crossbeam_receiver } -impl<Message, LTF, STF, SWF> Constellation<Message, LTF, STF, SWF> +impl<STF, SWF> Constellation<STF, SWF> where - LTF: LayoutThreadFactory<Message = Message>, - STF: ScriptThreadFactory<Message = Message>, + STF: ScriptThreadFactory, SWF: ServiceWorkerManagerFactory, { /// Create a new constellation thread. pub fn start( state: InitialConstellationState, + layout_factory: Arc<dyn LayoutFactory>, initial_window_size: WindowSizeData, random_pipeline_closure_probability: Option<f32>, random_pipeline_closure_seed: Option<usize>, @@ -738,7 +737,7 @@ where wgpu_image_map: state.wgpu_image_map, }; - let mut constellation: Constellation<Message, LTF, STF, SWF> = Constellation { + let mut constellation: Constellation<STF, SWF> = Constellation { namespace_receiver, namespace_ipc_sender, script_sender: script_ipc_sender, @@ -749,6 +748,7 @@ where layout_sender: layout_ipc_sender, script_receiver: script_receiver, compositor_receiver: compositor_receiver, + layout_factory, layout_receiver: layout_receiver, network_listener_sender: network_listener_sender, network_listener_receiver: network_listener_receiver, @@ -1020,7 +1020,7 @@ where self.public_resource_threads.clone() }; - let result = Pipeline::spawn::<Message, LTF, STF>(InitialPipelineState { + let result = Pipeline::spawn::<STF>(InitialPipelineState { id: pipeline_id, browsing_context_id, top_level_browsing_context_id, @@ -1037,6 +1037,7 @@ where .background_hang_monitor_sender .clone(), layout_to_constellation_chan: self.layout_sender.clone(), + layout_factory: self.layout_factory.clone(), scheduler_chan: self.scheduler_ipc_sender.clone(), compositor_proxy: self.compositor_proxy.clone(), devtools_sender: self.devtools_sender.clone(), @@ -1652,11 +1653,11 @@ where FromScriptMsg::ScriptLoadedURLInIFrame(load_info) => { self.handle_script_loaded_url_in_iframe_msg(load_info); }, - FromScriptMsg::ScriptNewIFrame(load_info, response_sender) => { - self.handle_script_new_iframe(load_info, response_sender); + FromScriptMsg::ScriptNewIFrame(load_info) => { + self.handle_script_new_iframe(load_info); }, - FromScriptMsg::ScriptNewAuxiliary(load_info, response_sender) => { - self.handle_script_new_auxiliary(load_info, response_sender); + FromScriptMsg::ScriptNewAuxiliary(load_info) => { + self.handle_script_new_auxiliary(load_info); }, FromScriptMsg::ChangeRunningAnimationsState(animation_state) => { self.handle_change_running_animations_state(source_pipeline_id, animation_state) @@ -1737,6 +1738,13 @@ where FromScriptMsg::SetDocumentState(state) => { self.document_states.insert(source_pipeline_id, state); }, + FromScriptMsg::SetLayoutEpoch(epoch, response_sender) => { + if let Some(pipeline) = self.pipelines.get_mut(&source_pipeline_id) { + pipeline.layout_epoch = epoch; + } + + response_sender.send(true).unwrap_or_default(); + }, FromScriptMsg::GetClientWindow(response_sender) => { self.compositor_proxy .send(CompositorMsg::GetClientWindow(response_sender)); @@ -2560,6 +2568,8 @@ where } fn handle_exit(&mut self) { + debug!("Handling exit."); + // TODO: add a timer, which forces shutdown if threads aren't responsive. if self.shutting_down { return; @@ -2638,6 +2648,8 @@ where } fn handle_shutdown(&mut self) { + debug!("Handling shutdown."); + // At this point, there are no active pipelines, // so we can safely block on other threads, without worrying about deadlock. // Channels to receive signals when threads are done exiting. @@ -3233,11 +3245,7 @@ where }); } - fn handle_script_new_iframe( - &mut self, - load_info: IFrameLoadInfoWithData, - layout_sender: IpcSender<LayoutControlMsg>, - ) { + fn handle_script_new_iframe(&mut self, load_info: IFrameLoadInfoWithData) { let IFrameLoadInfo { parent_pipeline_id, new_pipeline_id, @@ -3274,7 +3282,6 @@ where top_level_browsing_context_id, None, script_sender, - layout_sender, self.compositor_proxy.clone(), is_parent_visible, load_info.load_data, @@ -3298,11 +3305,7 @@ where }); } - fn handle_script_new_auxiliary( - &mut self, - load_info: AuxiliaryBrowsingContextLoadInfo, - layout_sender: IpcSender<LayoutControlMsg>, - ) { + fn handle_script_new_auxiliary(&mut self, load_info: AuxiliaryBrowsingContextLoadInfo) { let AuxiliaryBrowsingContextLoadInfo { load_data, opener_pipeline_id, @@ -3337,7 +3340,6 @@ where new_top_level_browsing_context_id, Some(opener_browsing_context_id), script_sender, - layout_sender, self.compositor_proxy.clone(), is_opener_visible, load_data, @@ -4956,14 +4958,9 @@ where return ReadyToSave::PendingChanges; } - let (state_sender, state_receiver) = ipc::channel().expect("Failed to create IPC channel!"); - let (epoch_ipc_sender, epoch_ipc_receiver) = - ipc::channel().expect("Failed to create IPC channel!"); - - // Step through the fully active browsing contexts, checking that the script - // thread is idle, and that the current epoch of the layout thread - // matches what the compositor has painted. If all these conditions - // are met, then the output image should not change and a reftest + // Step through the fully active browsing contexts, checking that the script thread is idle, + // and that the current epoch of the layout matches what the compositor has painted. If all + // these conditions are met, then the output image should not change and a reftest // screenshot can safely be written. for browsing_context in self.fully_active_browsing_contexts_iter(top_level_browsing_context_id) @@ -4983,22 +4980,6 @@ where Some(pipeline) => pipeline, }; - // Check to see if there are any webfonts still loading. - // - // If GetWebFontLoadState returns false, either there are no - // webfonts loading, or there's a WebFontLoaded message waiting in - // script_chan's message queue. Therefore, we need to check this - // before we check whether the document is ready; otherwise, - // there's a race condition where a webfont has finished loading, - // but hasn't yet notified the document. - let msg = LayoutControlMsg::GetWebFontLoadState(state_sender.clone()); - if let Err(e) = pipeline.layout_chan.send(msg) { - warn!("Get web font failed ({})", e); - } - if state_receiver.recv().unwrap_or(true) { - return ReadyToSave::WebFontNotLoaded; - } - // See if this pipeline has reached idle script state yet. match self.document_states.get(&browsing_context.pipeline_id) { Some(&DocumentState::Idle) => {}, @@ -5017,25 +4998,14 @@ where continue; } - // Get the epoch that the compositor has drawn for this pipeline. + // Get the epoch that the compositor has drawn for this pipeline and then check if the + // last laid out epoch matches what the compositor has drawn. If they match (and script + // is idle) then this pipeline won't change again and can be considered stable. let compositor_epoch = pipeline_states.get(&browsing_context.pipeline_id); match compositor_epoch { Some(compositor_epoch) => { - // Synchronously query the layout thread to see if the current - // epoch matches what the compositor has drawn. If they match - // (and script is idle) then this pipeline won't change again - // and can be considered stable. - let message = LayoutControlMsg::GetCurrentEpoch(epoch_ipc_sender.clone()); - if let Err(e) = pipeline.layout_chan.send(message) { - warn!("Failed to send GetCurrentEpoch ({}).", e); - } - match epoch_ipc_receiver.recv() { - Err(e) => warn!("Failed to receive current epoch ({:?}).", e), - Ok(layout_thread_epoch) => { - if layout_thread_epoch != *compositor_epoch { - return ReadyToSave::EpochMismatch; - } - }, + if pipeline.layout_epoch != *compositor_epoch { + return ReadyToSave::EpochMismatch; } }, None => { diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 061cf218789..525fca57f79 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -5,7 +5,6 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::rc::Rc; -use std::sync::atomic::AtomicBool; use std::sync::Arc; use background_hang_monitor::HangMonitorRegister; @@ -16,13 +15,12 @@ use crossbeam_channel::{unbounded, Sender}; use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg}; use embedder_traits::EventLoopWaker; use gfx::font_cache_thread::FontCacheThread; +use gfx_traits::Epoch; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use ipc_channel::Error; -use layout_traits::LayoutThreadFactory; use log::{debug, error, warn}; use media::WindowGLContext; -use metrics::PaintTimeMetrics; use msg::constellation_msg::{ BackgroundHangMonitorControlMsg, BackgroundHangMonitorRegister, BrowsingContextId, HangMonitorAlert, HistoryStateId, PipelineId, PipelineNamespace, PipelineNamespaceId, @@ -32,10 +30,11 @@ use net::image_cache::ImageCacheImpl; use net_traits::image_cache::ImageCache; use net_traits::ResourceThreads; use profile_traits::{mem as profile_mem, time}; +use script_layout_interface::{LayoutFactory, ScriptThreadFactory}; use script_traits::{ AnimationState, ConstellationControlMsg, DiscardBrowsingContext, DocumentActivity, - InitialScriptState, LayoutControlMsg, LayoutMsg, LoadData, NewLayoutInfo, SWManagerMsg, - ScriptThreadFactory, ScriptToConstellationChan, TimerSchedulerMsg, WindowSizeData, + InitialScriptState, LayoutMsg, LoadData, NewLayoutInfo, SWManagerMsg, + ScriptToConstellationChan, TimerSchedulerMsg, WindowSizeData, }; use serde::{Deserialize, Serialize}; use servo_config::opts::{self, Opts}; @@ -47,10 +46,8 @@ use webrender_api::DocumentId; use crate::event_loop::EventLoop; use crate::sandboxing::{spawn_multiprocess, UnprivilegedContent}; -/// A `Pipeline` is the constellation's view of a `Document`. Each pipeline has an -/// event loop (executed by a script thread) and a layout thread. A script thread -/// may be responsible for many pipelines, but a layout thread is only responsible -/// for one. +/// A `Pipeline` is the constellation's view of a `Window`. Each pipeline has an event loop +/// (executed by a script thread). A script thread may be responsible for many pipelines. pub struct Pipeline { /// The ID of the pipeline. pub id: PipelineId, @@ -66,9 +63,6 @@ pub struct Pipeline { /// The event loop handling this pipeline. pub event_loop: Rc<EventLoop>, - /// A channel to layout, for performing reflows and shutdown. - pub layout_chan: IpcSender<LayoutControlMsg>, - /// A channel to the compositor. pub compositor_proxy: CompositorProxy, @@ -98,6 +92,10 @@ pub struct Pipeline { /// The title of this pipeline's document. pub title: String, + + /// The last compositor [`Epoch`] that was laid out in this pipeline if "exit after load" is + /// enabled. + pub layout_epoch: Epoch, } /// Initial setup data needed to construct a pipeline. @@ -133,9 +131,12 @@ pub struct InitialPipelineState { /// A channel for the background hang monitor to send messages to the constellation. pub background_hang_monitor_to_constellation_chan: IpcSender<HangMonitorAlert>, - /// A channel for the layout thread to send messages to the constellation. + /// A channel for the layout to send messages to the constellation. pub layout_to_constellation_chan: IpcSender<LayoutMsg>, + /// A fatory for creating layouts to be used by the ScriptThread. + pub layout_factory: Arc<dyn LayoutFactory>, + /// A channel to schedule timer events. pub scheduler_chan: IpcSender<TimerSchedulerMsg>, @@ -212,17 +213,12 @@ pub struct NewPipeline { } impl Pipeline { - /// Starts a layout thread, and possibly a script thread, in - /// a new process if requested. - pub fn spawn<Message, LTF, STF>(state: InitialPipelineState) -> Result<NewPipeline, Error> - where - LTF: LayoutThreadFactory<Message = Message>, - STF: ScriptThreadFactory<Message = Message>, - { + /// Possibly starts a script thread, in a new process if requested. + pub fn spawn<STF: ScriptThreadFactory>( + state: InitialPipelineState, + ) -> Result<NewPipeline, Error> { // Note: we allow channel creation to panic, since recovering from this // probably requires a general low-memory strategy. - let (pipeline_chan, pipeline_port) = ipc::channel().expect("Pipeline main chan"); - let (script_chan, bhm_control_chan) = match state.event_loop { Some(script_chan) => { let new_layout_info = NewLayoutInfo { @@ -233,7 +229,6 @@ impl Pipeline { opener: state.opener, load_data: state.load_data.clone(), window_size: state.window_size, - pipeline_port: pipeline_port, }; if let Err(e) = @@ -299,7 +294,6 @@ impl Pipeline { script_port: script_port, opts: (*opts::get()).clone(), prefs: prefs::pref_map().iter().collect(), - pipeline_port: pipeline_port, pipeline_namespace_id: state.pipeline_namespace_id, webrender_api_sender: state.webrender_api_sender, webrender_image_api_sender: state.webrender_image_api_sender, @@ -324,7 +318,11 @@ impl Pipeline { let register = state .background_monitor_register .expect("Couldn't start content, no background monitor has been initiated"); - unprivileged_pipeline_content.start_all::<Message, LTF, STF>(false, register); + unprivileged_pipeline_content.start_all::<STF>( + false, + state.layout_factory, + register, + ); None }; @@ -338,7 +336,6 @@ impl Pipeline { state.top_level_browsing_context_id, state.opener, script_chan, - pipeline_chan, state.compositor_proxy, state.prev_visibility, state.load_data, @@ -349,15 +346,13 @@ impl Pipeline { }) } - /// Creates a new `Pipeline`, after the script and layout threads have been - /// spawned. + /// Creates a new `Pipeline`, after the script has been spawned. pub fn new( id: PipelineId, browsing_context_id: BrowsingContextId, top_level_browsing_context_id: TopLevelBrowsingContextId, opener: Option<BrowsingContextId>, event_loop: Rc<EventLoop>, - layout_chan: IpcSender<LayoutControlMsg>, compositor_proxy: CompositorProxy, is_visible: bool, load_data: LoadData, @@ -368,7 +363,6 @@ impl Pipeline { top_level_browsing_context_id: top_level_browsing_context_id, opener: opener, event_loop: event_loop, - layout_chan: layout_chan, compositor_proxy: compositor_proxy, url: load_data.url.clone(), children: vec![], @@ -378,6 +372,7 @@ impl Pipeline { history_states: HashSet::new(), completely_loaded: false, title: String::new(), + layout_epoch: Epoch(0), }; pipeline.notify_visibility(is_visible); @@ -418,9 +413,6 @@ impl Pipeline { if let Err(e) = self.event_loop.send(msg) { warn!("Sending script exit message failed ({}).", e); } - if let Err(e) = self.layout_chan.send(LayoutControlMsg::ExitNow) { - warn!("Sending layout exit message failed ({}).", e); - } } /// Notify this pipeline of its activity. @@ -437,7 +429,6 @@ impl Pipeline { id: self.id.clone(), top_level_browsing_context_id: self.top_level_browsing_context_id.clone(), script_chan: self.event_loop.sender(), - layout_chan: self.layout_chan.clone(), } } @@ -504,7 +495,6 @@ pub struct UnprivilegedPipelineContent { script_port: IpcReceiver<ConstellationControlMsg>, opts: Opts, prefs: HashMap<String, PrefValue>, - pipeline_port: IpcReceiver<LayoutControlMsg>, pipeline_namespace_id: PipelineNamespaceId, webrender_api_sender: script_traits::WebrenderIpcSender, webrender_image_api_sender: net_traits::WebrenderIpcSender, @@ -516,29 +506,19 @@ pub struct UnprivilegedPipelineContent { } impl UnprivilegedPipelineContent { - pub fn start_all<Message, LTF, STF>( + pub fn start_all<STF: ScriptThreadFactory>( self, wait_for_completion: bool, + layout_factory: Arc<dyn LayoutFactory>, background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, - ) where - LTF: LayoutThreadFactory<Message = Message>, - STF: ScriptThreadFactory<Message = Message>, - { + ) { // Setup pipeline-namespace-installing for all threads in this process. // Idempotent in single-process mode. PipelineNamespace::set_installer_sender(self.namespace_request_sender); let image_cache = Arc::new(ImageCacheImpl::new(self.webrender_image_api_sender.clone())); - let paint_time_metrics = PaintTimeMetrics::new( - self.id, - self.time_profiler_chan.clone(), - self.layout_to_constellation_chan.clone(), - self.script_chan.clone(), - self.load_data.url.clone(), - ); let (content_process_shutdown_chan, content_process_shutdown_port) = unbounded(); - let layout_thread_busy_flag = Arc::new(AtomicBool::new(false)); - let layout_pair = STF::create( + STF::create( InitialScriptState { id: self.id, browsing_context_id: self.browsing_context_id, @@ -564,34 +544,15 @@ impl UnprivilegedPipelineContent { webxr_registry: self.webxr_registry, webrender_document: self.webrender_document, webrender_api_sender: self.webrender_api_sender.clone(), - layout_is_busy: layout_thread_busy_flag.clone(), player_context: self.player_context.clone(), inherited_secure_context: self.load_data.inherited_secure_context.clone(), }, + layout_factory, + self.font_cache_thread.clone(), self.load_data.clone(), self.user_agent, ); - LTF::create( - self.id, - self.top_level_browsing_context_id, - self.load_data.url, - self.parent_pipeline_id.is_some(), - layout_pair, - self.pipeline_port, - background_hang_monitor_register, - self.layout_to_constellation_chan, - self.script_chan, - image_cache, - self.font_cache_thread, - self.time_profiler_chan, - self.mem_profiler_chan, - self.webrender_api_sender, - paint_time_metrics, - layout_thread_busy_flag.clone(), - self.window_size, - ); - if wait_for_completion { match content_process_shutdown_port.recv() { Ok(()) => {}, diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index 83290031644..a0de645a544 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -111,7 +111,7 @@ fn create_face( &mut face, ) } else { - // This will trigger a synchronous file read in the layout thread, which we may want to + // This will trigger a synchronous file read during layout, which we may want to // revisit at some point. See discussion here: // // https://github.com/servo/servo/pull/20506#issuecomment-378838800 diff --git a/components/layout/context.rs b/components/layout/context.rs index e0a4450b41f..eb25b5bd51d 100644 --- a/components/layout/context.rs +++ b/components/layout/context.rs @@ -2,7 +2,7 @@ * 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/. */ -//! Data needed by the layout thread. +//! Data needed by layout. use std::cell::{RefCell, RefMut}; use std::collections::HashMap; diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index f22a0edb9a0..6884d4cbe6b 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -488,7 +488,7 @@ impl ImageFragmentInfo { } /// A fragment that represents an inline frame (iframe). This stores the frame ID so that the -/// size of this iframe can be communicated via the constellation to the iframe's own layout thread. +/// size of this iframe can be communicated via the constellation to the iframe's own layout. #[derive(Clone)] pub struct IframeFragmentInfo { /// The frame ID of this iframe. None if there is no nested browsing context. diff --git a/components/layout/query.rs b/components/layout/query.rs index d55ef89a6fe..2bb03e2a5ca 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -2,7 +2,7 @@ * 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/. */ -//! Utilities for querying the layout, as needed by the layout thread. +//! Utilities for querying the layout, as needed by layout. use std::cmp::{max, min}; use std::ops::Deref; diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 61c2a46b4dd..30ae7ce1725 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -112,7 +112,7 @@ pub(crate) struct DisplayListBuilder<'a> { pub display_list: &'a mut DisplayList, /// A recording of the sizes of iframes encountered when building this - /// display list. This information is forwarded to the layout thread for the + /// display list. This information is forwarded to layout for the /// iframe so that its layout knows how large the initial containing block / /// viewport is. iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index 8ec2f0b4570..9c39abda413 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -2,7 +2,7 @@ * 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/. */ -//! Utilities for querying the layout, as needed by the layout thread. +//! Utilities for querying the layout, as needed by layout. use std::collections::HashMap; use std::sync::{Arc, Mutex}; diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index dbe3db3e248..4da4f2d785a 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -22,7 +22,6 @@ gfx_traits = { workspace = true } histogram = "0.6.8" ipc-channel = { workspace = true } layout = { path = "../layout", package = "layout_2013" } -layout_traits = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } malloc_size_of = { path = "../malloc_size_of" } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 01b66e39274..74be9d0cfd9 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -5,20 +5,18 @@ // Work around https://github.com/rust-lang/rust/issues/62132 #![recursion_limit = "128"] -//! The layout thread. Performs layout on the DOM, builds display lists and sends them to be +//! Layout. Performs layout on the DOM, builds display lists and sends them to be //! painted. use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::process; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; -use std::time::Duration; -use std::{process, thread}; use app_units::Au; -use crossbeam_channel::{select, Receiver, Sender}; use embedder_traits::resources::{self, Resource}; use euclid::default::Size2D as UntypedSize2D; use euclid::{Point2D, Rect, Scale, Size2D}; @@ -28,7 +26,7 @@ use gfx::font_cache_thread::FontCacheThread; use gfx::{font, font_context}; use gfx_traits::{node_id_from_scroll_id, Epoch}; use histogram::Histogram; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use layout::construct::ConstructionResult; use layout::context::{ @@ -51,30 +49,25 @@ use layout::traversal::{ }; use layout::wrapper::LayoutNodeLayoutData; use layout::{layout_debug, layout_debug_scope, parallel, sequential, LayoutData}; -use layout_traits::LayoutThreadFactory; use lazy_static::lazy_static; use log::{debug, error, trace, warn}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric}; -use msg::constellation_msg::{ - BackgroundHangMonitor, BackgroundHangMonitorRegister, BrowsingContextId, HangAnnotation, - LayoutHangAnnotation, MonitoredComponentId, MonitoredComponentType, PipelineId, - TopLevelBrowsingContextId, -}; +use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; use parking_lot::RwLock; -use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan}; +use profile_traits::mem::{Report, ReportKind, ReportsChan}; use profile_traits::path; use profile_traits::time::{ self as profile_time, profile, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType, }; use script::layout_dom::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use script_layout_interface::message::{ - LayoutThreadInit, Msg, NodesFromPointQueryType, QueryMsg, Reflow, ReflowComplete, ReflowGoal, - ScriptReflow, + Msg, NodesFromPointQueryType, QueryMsg, Reflow, ReflowComplete, ReflowGoal, ScriptReflow, }; use script_layout_interface::rpc::{LayoutRPC, OffsetParentResponse, TextIndexResponse}; use script_layout_interface::wrapper_traits::LayoutNode; +use script_layout_interface::{Layout, LayoutConfig, LayoutFactory}; use script_traits::{ ConstellationControlMsg, DrawAPaintImageResult, IFrameSizeMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, PaintWorkletError, Painter, ScrollState, UntrustedNodeAddress, @@ -104,21 +97,17 @@ use style::stylesheets::{ UserAgentStylesheets, }; use style::stylist::Stylist; -use style::thread_state::{self, ThreadState}; use style::traversal::DomTraversal; use style::traversal_flags::TraversalFlags; use style_traits::{CSSPixel, DevicePixel, SpeculativePainter}; use url::Url; use webrender_api::{units, ColorF, HitTestFlags}; -/// Information needed by the layout thread. +/// Information needed by layout. pub struct LayoutThread { /// The ID of the pipeline that we belong to. id: PipelineId, - /// The ID of the top-level browsing context that we belong to. - top_level_browsing_context_id: TopLevelBrowsingContextId, - /// The URL of the pipeline that we belong to. url: ServoUrl, @@ -128,21 +117,9 @@ pub struct LayoutThread { /// Is the current reflow of an iframe, as opposed to a root window? is_iframe: bool, - /// The port on which we receive messages from the script thread. - port: Receiver<Msg>, - - /// The port on which we receive messages from the constellation. - pipeline_port: Receiver<LayoutControlMsg>, - - /// The port on which we receive messages from the font cache thread. - font_cache_receiver: Receiver<()>, - /// The channel on which the font cache can send messages to us. font_cache_sender: IpcSender<()>, - /// A means of communication with the background hang monitor. - background_hang_monitor: Box<dyn BackgroundHangMonitor>, - /// The channel on which messages can be sent to the constellation. constellation_chan: IpcSender<ConstellationMsg>, @@ -152,9 +129,6 @@ pub struct LayoutThread { /// The channel on which messages can be sent to the time profiler. time_profiler_chan: profile_time::ProfilerChan, - /// The channel on which messages can be sent to the memory profiler. - mem_profiler_chan: profile_mem::ProfilerChan, - /// Reference to the script thread image cache. image_cache: Arc<dyn ImageCache>, @@ -201,15 +175,12 @@ pub struct LayoutThread { /// Paint time metrics. paint_time_metrics: PaintTimeMetrics, - /// The time a layout query has waited before serviced by layout thread. + /// The time a layout query has waited before serviced by layout. layout_query_waiting_time: Histogram, /// The sizes of all iframes encountered during the last layout operation. last_iframe_sizes: RefCell<FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>>, - /// Flag that indicates if LayoutThread is busy handling a request. - busy: Arc<AtomicBool>, - /// Debug options, copied from configuration to this `LayoutThread` in order /// to avoid having to constantly access the thread-safe global options. debug: DebugOptions, @@ -218,81 +189,23 @@ pub struct LayoutThread { nonincremental_layout: bool, } -impl LayoutThreadFactory for LayoutThread { - type Message = Msg; - - /// Spawns a new layout thread. - fn create( - id: PipelineId, - top_level_browsing_context_id: TopLevelBrowsingContextId, - url: ServoUrl, - is_iframe: bool, - chan: (Sender<Msg>, Receiver<Msg>), - pipeline_port: IpcReceiver<LayoutControlMsg>, - background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, - constellation_chan: IpcSender<ConstellationMsg>, - script_chan: IpcSender<ConstellationControlMsg>, - image_cache: Arc<dyn ImageCache>, - font_cache_thread: FontCacheThread, - time_profiler_chan: profile_time::ProfilerChan, - mem_profiler_chan: profile_mem::ProfilerChan, - webrender_api_sender: WebrenderIpcSender, - paint_time_metrics: PaintTimeMetrics, - busy: Arc<AtomicBool>, - window_size: WindowSizeData, - ) { - thread::Builder::new() - .name(format!("Layout{}", id)) - .spawn(move || { - thread_state::initialize(ThreadState::LAYOUT); - - // In order to get accurate crash reports, we install the top-level bc id. - TopLevelBrowsingContextId::install(top_level_browsing_context_id); - - { - // Ensures layout thread is destroyed before we send shutdown message - let sender = chan.0; - - let background_hang_monitor = background_hang_monitor_register - .register_component( - MonitoredComponentId(id, MonitoredComponentType::Layout), - Duration::from_millis(1000), - Duration::from_millis(5000), - None, - ); - - let layout = LayoutThread::new( - id, - top_level_browsing_context_id, - url, - is_iframe, - chan.1, - pipeline_port, - background_hang_monitor, - constellation_chan, - script_chan, - image_cache, - font_cache_thread, - time_profiler_chan, - mem_profiler_chan.clone(), - webrender_api_sender, - paint_time_metrics, - busy, - window_size, - ); - - let reporter_name = format!("layout-reporter-{}", id); - mem_profiler_chan.run_with_memory_reporting( - || { - layout.start(); - }, - reporter_name, - sender, - Msg::CollectReports, - ); - } - }) - .expect("Thread spawning failed"); +pub struct LayoutFactoryImpl(); + +impl LayoutFactory for LayoutFactoryImpl { + fn create(&self, config: LayoutConfig) -> Box<dyn Layout> { + Box::new(LayoutThread::new( + config.id, + config.url, + config.is_iframe, + config.constellation_chan, + config.script_chan, + config.image_cache, + config.font_cache_thread, + config.time_profiler_chan, + config.webrender_api_sender, + config.paint_time_metrics, + config.window_size, + )) } } @@ -419,25 +332,49 @@ fn add_font_face_rules( } } +impl Layout for LayoutThread { + fn process(&mut self, msg: script_layout_interface::message::Msg) { + self.handle_request(Request::FromScript(msg)); + } + + fn handle_constellation_msg(&mut self, msg: script_traits::LayoutControlMsg) { + self.handle_request(Request::FromPipeline(msg)); + } + + fn handle_font_cache_msg(&mut self) { + self.handle_request(Request::FromFontCache); + } + + fn rpc(&self) -> Box<dyn script_layout_interface::rpc::LayoutRPC> { + Box::new(LayoutRPCImpl(self.rw_data.clone())) as Box<dyn LayoutRPC> + } + + fn waiting_for_web_fonts_to_load(&self) -> bool { + self.outstanding_web_fonts.load(Ordering::SeqCst) != 0 + } + + fn current_epoch(&self) -> Epoch { + self.epoch.get() + } +} +enum Request { + FromPipeline(LayoutControlMsg), + FromScript(Msg), + FromFontCache, +} + impl LayoutThread { - /// Creates a new `LayoutThread` structure. fn new( id: PipelineId, - top_level_browsing_context_id: TopLevelBrowsingContextId, url: ServoUrl, is_iframe: bool, - port: Receiver<Msg>, - pipeline_port: IpcReceiver<LayoutControlMsg>, - background_hang_monitor: Box<dyn BackgroundHangMonitor>, constellation_chan: IpcSender<ConstellationMsg>, script_chan: IpcSender<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, font_cache_thread: FontCacheThread, time_profiler_chan: profile_time::ProfilerChan, - mem_profiler_chan: profile_mem::ProfilerChan, webrender_api: WebrenderIpcSender, paint_time_metrics: PaintTimeMetrics, - busy: Arc<AtomicBool>, window_size: WindowSizeData, ) -> LayoutThread { // Let webrender know about this pipeline by sending an empty display list. @@ -450,31 +387,28 @@ impl LayoutThread { window_size.device_pixel_ratio, ); - // Proxy IPC messages from the pipeline to the layout thread. - let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(pipeline_port); - - // Ask the router to proxy IPC messages from the font cache thread to the layout thread. + // Ask the router to proxy IPC messages from the font cache thread to layout. let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap(); - let font_cache_receiver = - ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_font_cache_receiver); + let cloned_script_chan = script_chan.clone(); + ROUTER.add_route( + ipc_font_cache_receiver.to_opaque(), + Box::new(move |_message| { + let _ = + cloned_script_chan.send(ConstellationControlMsg::ForLayoutFromFontCache(id)); + }), + ); LayoutThread { id, - top_level_browsing_context_id: top_level_browsing_context_id, url, is_iframe, - port, - pipeline_port: pipeline_receiver, script_chan, - background_hang_monitor, constellation_chan: constellation_chan.clone(), time_profiler_chan, - mem_profiler_chan, registered_painters: RegisteredPaintersImpl(Default::default()), image_cache, font_cache_thread, first_reflow: Cell::new(true), - font_cache_receiver, font_cache_sender: ipc_font_cache_sender, parallel_flag: true, generation: Cell::new(0), @@ -507,25 +441,11 @@ impl LayoutThread { paint_time_metrics, layout_query_waiting_time: Histogram::new(), last_iframe_sizes: Default::default(), - busy, debug: opts::get().debug.clone(), nonincremental_layout: opts::get().nonincremental_layout, } } - /// Starts listening on the port. - fn start(mut self) { - let rw_data = self.rw_data.clone(); - let mut possibly_locked_rw_data = Some(rw_data.lock().unwrap()); - let mut rw_data = RwData { - rw_data: &rw_data, - possibly_locked_rw_data: &mut possibly_locked_rw_data, - }; - while self.handle_request(&mut rw_data) { - // Loop indefinitely. - } - } - // Create a layout context for use in building display lists, hit testing, &c. fn build_layout_context<'a>( &'a self, @@ -563,77 +483,35 @@ impl LayoutThread { } } - fn notify_activity_to_hang_monitor(&self, request: &Msg) { - let hang_annotation = match request { - Msg::AddStylesheet(..) => LayoutHangAnnotation::AddStylesheet, - Msg::RemoveStylesheet(..) => LayoutHangAnnotation::RemoveStylesheet, - Msg::SetQuirksMode(..) => LayoutHangAnnotation::SetQuirksMode, - Msg::Reflow(..) => LayoutHangAnnotation::Reflow, - Msg::GetRPC(..) => LayoutHangAnnotation::GetRPC, - Msg::CollectReports(..) => LayoutHangAnnotation::CollectReports, - Msg::PrepareToExit(..) => LayoutHangAnnotation::PrepareToExit, - Msg::ExitNow => LayoutHangAnnotation::ExitNow, - Msg::GetCurrentEpoch(..) => LayoutHangAnnotation::GetCurrentEpoch, - Msg::GetWebFontLoadState(..) => LayoutHangAnnotation::GetWebFontLoadState, - Msg::CreateLayoutThread(..) => LayoutHangAnnotation::CreateLayoutThread, - Msg::SetFinalUrl(..) => LayoutHangAnnotation::SetFinalUrl, - Msg::SetScrollStates(..) => LayoutHangAnnotation::SetScrollStates, - Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint, - Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart, - }; - self.background_hang_monitor - .notify_activity(HangAnnotation::Layout(hang_annotation)); - } - /// Receives and dispatches messages from the script and constellation threads - fn handle_request<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) -> bool { - enum Request { - FromPipeline(LayoutControlMsg), - FromScript(Msg), - FromFontCache, - } - - // Notify the background-hang-monitor we are waiting for an event. - self.background_hang_monitor.notify_wait(); - - let request = select! { - recv(self.pipeline_port) -> msg => Request::FromPipeline(msg.unwrap()), - recv(self.port) -> msg => Request::FromScript(msg.unwrap()), - recv(self.font_cache_receiver) -> msg => { msg.unwrap(); Request::FromFontCache } + fn handle_request<'a, 'b>(&mut self, request: Request) { + let rw_data = self.rw_data.clone(); + let mut possibly_locked_rw_data = Some(rw_data.lock().unwrap()); + let mut rw_data = RwData { + rw_data: &rw_data, + possibly_locked_rw_data: &mut possibly_locked_rw_data, }; - self.busy.store(true, Ordering::Relaxed); - let result = match request { - Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => self - .handle_request_helper( - Msg::SetScrollStates(new_scroll_states), - possibly_locked_rw_data, - ), - Request::FromPipeline(LayoutControlMsg::GetCurrentEpoch(sender)) => { - self.handle_request_helper(Msg::GetCurrentEpoch(sender), possibly_locked_rw_data) + match request { + Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => { + self.handle_request_helper(Msg::SetScrollStates(new_scroll_states), &mut rw_data) }, - Request::FromPipeline(LayoutControlMsg::GetWebFontLoadState(sender)) => self - .handle_request_helper(Msg::GetWebFontLoadState(sender), possibly_locked_rw_data), Request::FromPipeline(LayoutControlMsg::ExitNow) => { - self.handle_request_helper(Msg::ExitNow, possibly_locked_rw_data) + self.handle_request_helper(Msg::ExitNow, &mut rw_data); }, Request::FromPipeline(LayoutControlMsg::PaintMetric(epoch, paint_time)) => { self.paint_time_metrics.maybe_set_metric(epoch, paint_time); - true }, - Request::FromScript(msg) => self.handle_request_helper(msg, possibly_locked_rw_data), + Request::FromScript(msg) => self.handle_request_helper(msg, &mut rw_data), Request::FromFontCache => { - let _rw_data = possibly_locked_rw_data.lock(); + let _rw_data = rw_data.lock(); self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst); font_context::invalidate_font_caches(); self.script_chan .send(ConstellationControlMsg::WebFontLoaded(self.id)) .unwrap(); - true }, }; - self.busy.store(false, Ordering::Relaxed); - result } /// Receives and dispatches messages from other threads. @@ -641,9 +519,7 @@ impl LayoutThread { &mut self, request: Msg, possibly_locked_rw_data: &mut RwData<'a, 'b>, - ) -> bool { - self.notify_activity_to_hang_monitor(&request); - + ) { match request { Msg::AddStylesheet(stylesheet, before_stylesheet) => { let guard = stylesheet.shared_lock.read(); @@ -686,19 +562,6 @@ impl LayoutThread { Msg::CollectReports(reports_chan) => { self.collect_reports(reports_chan, possibly_locked_rw_data); }, - Msg::GetCurrentEpoch(sender) => { - let _rw_data = possibly_locked_rw_data.lock(); - sender.send(self.epoch.get()).unwrap(); - }, - Msg::GetWebFontLoadState(sender) => { - let _rw_data = possibly_locked_rw_data.lock(); - let outstanding_web_fonts = self.outstanding_web_fonts.load(Ordering::SeqCst); - sender.send(outstanding_web_fonts != 0).unwrap(); - }, - Msg::CreateLayoutThread(info) => self.create_layout_thread(info), - Msg::SetFinalUrl(final_url) => { - self.url = final_url; - }, Msg::RegisterPaint(name, mut properties, painter) => { debug!("Registering the painter"); let properties = properties @@ -716,22 +579,12 @@ impl LayoutThread { }; self.registered_painters.0.insert(name, registered_painter); }, - Msg::PrepareToExit(response_chan) => { - self.prepare_to_exit(response_chan); - return false; - }, // Receiving the Exit message at this stage only happens when layout is undergoing a "force exit". Msg::ExitNow => { debug!("layout: ExitNow received"); self.exit_now(); - return false; - }, - Msg::SetNavigationStart(time) => { - self.paint_time_metrics.set_navigation_start(time); }, } - - true } fn collect_reports<'a, 'b>( @@ -770,49 +623,7 @@ impl LayoutThread { reports_chan.send(reports); } - fn create_layout_thread(&self, info: LayoutThreadInit) { - LayoutThread::create( - info.id, - self.top_level_browsing_context_id, - info.url.clone(), - info.is_parent, - info.layout_pair, - info.pipeline_port, - info.background_hang_monitor_register, - info.constellation_chan, - info.script_chan, - info.image_cache, - self.font_cache_thread.clone(), - self.time_profiler_chan.clone(), - self.mem_profiler_chan.clone(), - self.webrender_api.clone(), - info.paint_time_metrics, - info.layout_is_busy, - info.window_size, - ); - } - - /// Enters a quiescent state in which no new messages will be processed until an `ExitNow` is - /// received. A pong is immediately sent on the given response channel. - fn prepare_to_exit(&mut self, response_chan: Sender<()>) { - response_chan.send(()).unwrap(); - loop { - match self.port.recv().unwrap() { - Msg::ExitNow => { - debug!("layout thread is exiting..."); - self.exit_now(); - break; - }, - Msg::CollectReports(_) => { - // Just ignore these messages at this point. - }, - _ => panic!("layout: unexpected message received after `PrepareToExitMsg`"), - } - } - } - - /// Shuts down the layout thread now. If there are any DOM nodes left, layout will now (safely) - /// crash. + /// Shuts down layout now. fn exit_now(&mut self) { // Drop the root flow explicitly to avoid holding style data, such as // rule nodes. The `Stylist` checks when it is dropped that all rule @@ -827,7 +638,6 @@ impl LayoutThread { ); self.root_flow.borrow_mut().take(); - self.background_hang_monitor.unregister(); } fn handle_add_stylesheet(&self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard) { @@ -916,9 +726,8 @@ impl LayoutThread { ); } - /// Update the recorded iframe sizes of the contents of this layout thread and - /// when these sizes changes, send a message to the constellation informing it - /// of the new sizes. + /// Update the recorded iframe sizes of the contents of layout and when these sizes changes, + /// send a message to the constellation informing it of the new sizes. fn update_iframe_sizes( &self, new_iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, @@ -1078,7 +887,7 @@ impl LayoutThread { ); } - /// The high-level routine that performs layout threads. + /// The high-level routine that performs layout. fn handle_reflow<'a, 'b>( &mut self, data: &mut ScriptReflowResult, diff --git a/components/layout_thread_2020/Cargo.toml b/components/layout_thread_2020/Cargo.toml index df18d6e0322..2a1e510630c 100644 --- a/components/layout_thread_2020/Cargo.toml +++ b/components/layout_thread_2020/Cargo.toml @@ -21,7 +21,6 @@ gfx = { path = "../gfx" } gfx_traits = { workspace = true } ipc-channel = { workspace = true } layout = { path = "../layout_2020", package = "layout_2020" } -layout_traits = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } malloc_size_of = { path = "../malloc_size_of" } diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index cddb4ac04ed..9e7bb76f31d 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -5,19 +5,17 @@ // Work around https://github.com/rust-lang/rust/issues/62132 #![recursion_limit = "128"] -//! The layout thread. Performs layout on the DOM, builds display lists and sends them to be +//! Layout. Performs layout on the DOM, builds display lists and sends them to be //! painted. use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::process; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; -use std::time::Duration; -use std::{process, thread}; use app_units::Au; -use crossbeam_channel::{select, Receiver, Sender}; use embedder_traits::resources::{self, Resource}; use euclid::default::Size2D as UntypedSize2D; use euclid::{Point2D, Rect, Scale, Size2D}; @@ -26,7 +24,7 @@ use fxhash::FxHashMap; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context; use gfx_traits::{node_id_from_scroll_id, Epoch}; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use layout::context::LayoutContext; use layout::display_list::{DisplayList, WebRenderImageInfo}; @@ -39,29 +37,24 @@ use layout::query::{ }; use layout::traversal::RecalcStyle; use layout::{layout_debug, BoxTree, FragmentTree}; -use layout_traits::LayoutThreadFactory; use lazy_static::lazy_static; use log::{debug, error, warn}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use metrics::{PaintTimeMetrics, ProfilerMetadataFactory, ProgressiveWebMetric}; -use msg::constellation_msg::{ - BackgroundHangMonitor, BackgroundHangMonitorRegister, BrowsingContextId, HangAnnotation, - LayoutHangAnnotation, MonitoredComponentId, MonitoredComponentType, PipelineId, - TopLevelBrowsingContextId, -}; +use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; +use msg::constellation_msg::{BrowsingContextId, PipelineId}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; use parking_lot::RwLock; -use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan}; +use profile_traits::mem::{Report, ReportKind, ReportsChan}; use profile_traits::path; use profile_traits::time::{ self as profile_time, profile, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType, }; use script::layout_dom::{ServoLayoutDocument, ServoLayoutElement, ServoLayoutNode}; use script_layout_interface::message::{ - LayoutThreadInit, Msg, NodesFromPointQueryType, QueryMsg, ReflowComplete, ReflowGoal, - ScriptReflow, + Msg, NodesFromPointQueryType, QueryMsg, ReflowComplete, ReflowGoal, ScriptReflow, }; use script_layout_interface::rpc::{LayoutRPC, OffsetParentResponse, TextIndexResponse}; +use script_layout_interface::{Layout, LayoutConfig, LayoutFactory}; use script_traits::{ ConstellationControlMsg, DrawAPaintImageResult, IFrameSizeMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, PaintWorkletError, Painter, ScrollState, UntrustedNodeAddress, @@ -88,21 +81,17 @@ use style::stylesheets::{ UserAgentStylesheets, }; use style::stylist::Stylist; -use style::thread_state::{self, ThreadState}; use style::traversal::DomTraversal; use style::traversal_flags::TraversalFlags; use style_traits::{CSSPixel, DevicePixel, SpeculativePainter}; use url::Url; use webrender_api::{units, HitTestFlags}; -/// Information needed by the layout thread. +/// Information needed by layout. pub struct LayoutThread { /// The ID of the pipeline that we belong to. id: PipelineId, - /// The ID of the top-level browsing context that we belong to. - top_level_browsing_context_id: TopLevelBrowsingContextId, - /// The URL of the pipeline that we belong to. url: ServoUrl, @@ -112,21 +101,9 @@ pub struct LayoutThread { /// Is the current reflow of an iframe, as opposed to a root window? is_iframe: bool, - /// The port on which we receive messages from the script thread. - port: Receiver<Msg>, - - /// The port on which we receive messages from the constellation. - pipeline_port: Receiver<LayoutControlMsg>, - - /// The port on which we receive messages from the font cache thread. - font_cache_receiver: Receiver<()>, - /// The channel on which the font cache can send messages to us. font_cache_sender: IpcSender<()>, - /// A means of communication with the background hang monitor. - background_hang_monitor: Box<dyn BackgroundHangMonitor>, - /// The channel on which messages can be sent to the constellation. constellation_chan: IpcSender<ConstellationMsg>, @@ -136,9 +113,6 @@ pub struct LayoutThread { /// The channel on which messages can be sent to the time profiler. time_profiler_chan: profile_time::ProfilerChan, - /// The channel on which messages can be sent to the memory profiler. - mem_profiler_chan: profile_mem::ProfilerChan, - /// Reference to the script thread image cache. image_cache: Arc<dyn ImageCache>, @@ -188,89 +162,28 @@ pub struct LayoutThread { /// The sizes of all iframes encountered during the last layout operation. last_iframe_sizes: RefCell<FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>>, - /// Flag that indicates if LayoutThread is busy handling a request. - busy: Arc<AtomicBool>, - /// Debug options, copied from configuration to this `LayoutThread` in order /// to avoid having to constantly access the thread-safe global options. debug: DebugOptions, } -impl LayoutThreadFactory for LayoutThread { - type Message = Msg; - - /// Spawns a new layout thread. - fn create( - id: PipelineId, - top_level_browsing_context_id: TopLevelBrowsingContextId, - url: ServoUrl, - is_iframe: bool, - chan: (Sender<Msg>, Receiver<Msg>), - pipeline_port: IpcReceiver<LayoutControlMsg>, - background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, - constellation_chan: IpcSender<ConstellationMsg>, - script_chan: IpcSender<ConstellationControlMsg>, - image_cache: Arc<dyn ImageCache>, - font_cache_thread: FontCacheThread, - time_profiler_chan: profile_time::ProfilerChan, - mem_profiler_chan: profile_mem::ProfilerChan, - webrender_api_sender: WebrenderIpcSender, - paint_time_metrics: PaintTimeMetrics, - busy: Arc<AtomicBool>, - window_size: WindowSizeData, - ) { - thread::Builder::new() - .name(format!("Layout{}", id)) - .spawn(move || { - thread_state::initialize(ThreadState::LAYOUT); - - // In order to get accurate crash reports, we install the top-level bc id. - TopLevelBrowsingContextId::install(top_level_browsing_context_id); - - { - // Ensures layout thread is destroyed before we send shutdown message - let sender = chan.0; - - let background_hang_monitor = background_hang_monitor_register - .register_component( - MonitoredComponentId(id, MonitoredComponentType::Layout), - Duration::from_millis(1000), - Duration::from_millis(5000), - None, - ); - - let layout = LayoutThread::new( - id, - top_level_browsing_context_id, - url, - is_iframe, - chan.1, - pipeline_port, - background_hang_monitor, - constellation_chan, - script_chan, - image_cache, - font_cache_thread, - time_profiler_chan, - mem_profiler_chan.clone(), - webrender_api_sender, - paint_time_metrics, - busy, - window_size, - ); - - let reporter_name = format!("layout-reporter-{}", id); - mem_profiler_chan.run_with_memory_reporting( - || { - layout.start(); - }, - reporter_name, - sender, - Msg::CollectReports, - ); - } - }) - .expect("Thread spawning failed"); +pub struct LayoutFactoryImpl(); + +impl LayoutFactory for LayoutFactoryImpl { + fn create(&self, config: LayoutConfig) -> Box<dyn Layout> { + Box::new(LayoutThread::new( + config.id, + config.url, + config.is_iframe, + config.constellation_chan, + config.script_chan, + config.image_cache, + config.font_cache_thread, + config.time_profiler_chan, + config.webrender_api_sender, + config.paint_time_metrics, + config.window_size, + )) } } @@ -396,26 +309,50 @@ fn add_font_face_rules( }) } } +impl Layout for LayoutThread { + fn process(&mut self, msg: script_layout_interface::message::Msg) { + self.handle_request(Request::FromScript(msg)); + } + + fn handle_constellation_msg(&mut self, msg: script_traits::LayoutControlMsg) { + self.handle_request(Request::FromPipeline(msg)); + } + + fn handle_font_cache_msg(&mut self) { + self.handle_request(Request::FromFontCache); + } + + fn rpc(&self) -> Box<dyn script_layout_interface::rpc::LayoutRPC> { + Box::new(LayoutRPCImpl(self.rw_data.clone())) as Box<dyn LayoutRPC> + } + + fn waiting_for_web_fonts_to_load(&self) -> bool { + self.outstanding_web_fonts.load(Ordering::SeqCst) != 0 + } + + fn current_epoch(&self) -> Epoch { + self.epoch.get() + } +} + +enum Request { + FromPipeline(LayoutControlMsg), + FromScript(Msg), + FromFontCache, +} impl LayoutThread { - /// Creates a new `LayoutThread` structure. fn new( id: PipelineId, - top_level_browsing_context_id: TopLevelBrowsingContextId, url: ServoUrl, is_iframe: bool, - port: Receiver<Msg>, - pipeline_port: IpcReceiver<LayoutControlMsg>, - background_hang_monitor: Box<dyn BackgroundHangMonitor>, constellation_chan: IpcSender<ConstellationMsg>, script_chan: IpcSender<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, font_cache_thread: FontCacheThread, time_profiler_chan: profile_time::ProfilerChan, - mem_profiler_chan: profile_mem::ProfilerChan, webrender_api_sender: WebrenderIpcSender, paint_time_metrics: PaintTimeMetrics, - busy: Arc<AtomicBool>, window_size: WindowSizeData, ) -> LayoutThread { // Let webrender know about this pipeline by sending an empty display list. @@ -430,31 +367,28 @@ impl LayoutThread { window_size.device_pixel_ratio, ); - // Proxy IPC messages from the pipeline to the layout thread. - let pipeline_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(pipeline_port); - - // Ask the router to proxy IPC messages from the font cache thread to the layout thread. + // Ask the router to proxy IPC messages from the font cache thread to layout. let (ipc_font_cache_sender, ipc_font_cache_receiver) = ipc::channel().unwrap(); - let font_cache_receiver = - ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_font_cache_receiver); + let cloned_script_chan = script_chan.clone(); + ROUTER.add_route( + ipc_font_cache_receiver.to_opaque(), + Box::new(move |_message| { + let _ = + cloned_script_chan.send(ConstellationControlMsg::ForLayoutFromFontCache(id)); + }), + ); LayoutThread { id, - top_level_browsing_context_id: top_level_browsing_context_id, url, is_iframe, - port, - pipeline_port: pipeline_receiver, constellation_chan, script_chan: script_chan.clone(), - background_hang_monitor, time_profiler_chan, - mem_profiler_chan, registered_painters: RegisteredPaintersImpl(Default::default()), image_cache, font_cache_thread, first_reflow: Cell::new(true), - font_cache_receiver, font_cache_sender: ipc_font_cache_sender, generation: Cell::new(0), outstanding_web_fonts: Arc::new(AtomicUsize::new(0)), @@ -484,24 +418,10 @@ impl LayoutThread { webrender_image_cache: Default::default(), paint_time_metrics: paint_time_metrics, last_iframe_sizes: Default::default(), - busy, debug: opts::get().debug.clone(), } } - /// Starts listening on the port. - fn start(mut self) { - let rw_data = self.rw_data.clone(); - let mut possibly_locked_rw_data = Some(rw_data.lock().unwrap()); - let mut rw_data = RwData { - rw_data: &rw_data, - possibly_locked_rw_data: &mut possibly_locked_rw_data, - }; - while self.handle_request(&mut rw_data) { - // Loop indefinitely. - } - } - // Create a layout context for use in building display lists, hit testing, &c. fn build_layout_context<'a>( &'a self, @@ -540,77 +460,35 @@ impl LayoutThread { } } - fn notify_activity_to_hang_monitor(&self, request: &Msg) { - let hang_annotation = match request { - Msg::AddStylesheet(..) => LayoutHangAnnotation::AddStylesheet, - Msg::RemoveStylesheet(..) => LayoutHangAnnotation::RemoveStylesheet, - Msg::SetQuirksMode(..) => LayoutHangAnnotation::SetQuirksMode, - Msg::Reflow(..) => LayoutHangAnnotation::Reflow, - Msg::GetRPC(..) => LayoutHangAnnotation::GetRPC, - Msg::CollectReports(..) => LayoutHangAnnotation::CollectReports, - Msg::PrepareToExit(..) => LayoutHangAnnotation::PrepareToExit, - Msg::ExitNow => LayoutHangAnnotation::ExitNow, - Msg::GetCurrentEpoch(..) => LayoutHangAnnotation::GetCurrentEpoch, - Msg::GetWebFontLoadState(..) => LayoutHangAnnotation::GetWebFontLoadState, - Msg::CreateLayoutThread(..) => LayoutHangAnnotation::CreateLayoutThread, - Msg::SetFinalUrl(..) => LayoutHangAnnotation::SetFinalUrl, - Msg::SetScrollStates(..) => LayoutHangAnnotation::SetScrollStates, - Msg::RegisterPaint(..) => LayoutHangAnnotation::RegisterPaint, - Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart, - }; - self.background_hang_monitor - .notify_activity(HangAnnotation::Layout(hang_annotation)); - } - /// Receives and dispatches messages from the script and constellation threads - fn handle_request<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) -> bool { - enum Request { - FromPipeline(LayoutControlMsg), - FromScript(Msg), - FromFontCache, - } - - // Notify the background-hang-monitor we are waiting for an event. - self.background_hang_monitor.notify_wait(); - - let request = select! { - recv(self.pipeline_port) -> msg => Request::FromPipeline(msg.unwrap()), - recv(self.port) -> msg => Request::FromScript(msg.unwrap()), - recv(self.font_cache_receiver) -> msg => { msg.unwrap(); Request::FromFontCache } + fn handle_request<'a, 'b>(&mut self, request: Request) { + let rw_data = self.rw_data.clone(); + let mut possibly_locked_rw_data = Some(rw_data.lock().unwrap()); + let mut rw_data = RwData { + rw_data: &rw_data, + possibly_locked_rw_data: &mut possibly_locked_rw_data, }; - self.busy.store(true, Ordering::Relaxed); - let result = match request { - Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => self - .handle_request_helper( - Msg::SetScrollStates(new_scroll_states), - possibly_locked_rw_data, - ), - Request::FromPipeline(LayoutControlMsg::GetCurrentEpoch(sender)) => { - self.handle_request_helper(Msg::GetCurrentEpoch(sender), possibly_locked_rw_data) + match request { + Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => { + self.handle_request_helper(Msg::SetScrollStates(new_scroll_states), &mut rw_data) }, - Request::FromPipeline(LayoutControlMsg::GetWebFontLoadState(sender)) => self - .handle_request_helper(Msg::GetWebFontLoadState(sender), possibly_locked_rw_data), Request::FromPipeline(LayoutControlMsg::ExitNow) => { - self.handle_request_helper(Msg::ExitNow, possibly_locked_rw_data) + self.handle_request_helper(Msg::ExitNow, &mut rw_data); }, Request::FromPipeline(LayoutControlMsg::PaintMetric(epoch, paint_time)) => { self.paint_time_metrics.maybe_set_metric(epoch, paint_time); - true }, - Request::FromScript(msg) => self.handle_request_helper(msg, possibly_locked_rw_data), + Request::FromScript(msg) => self.handle_request_helper(msg, &mut rw_data), Request::FromFontCache => { - let _rw_data = possibly_locked_rw_data.lock(); + let _rw_data = rw_data.lock(); self.outstanding_web_fonts.fetch_sub(1, Ordering::SeqCst); font_context::invalidate_font_caches(); self.script_chan .send(ConstellationControlMsg::WebFontLoaded(self.id)) .unwrap(); - true }, }; - self.busy.store(false, Ordering::Relaxed); - result } /// Receives and dispatches messages from other threads. @@ -618,9 +496,7 @@ impl LayoutThread { &mut self, request: Msg, possibly_locked_rw_data: &mut RwData<'a, 'b>, - ) -> bool { - self.notify_activity_to_hang_monitor(&request); - + ) { match request { Msg::AddStylesheet(stylesheet, before_stylesheet) => { let guard = stylesheet.shared_lock.read(); @@ -663,36 +539,10 @@ impl LayoutThread { Msg::CollectReports(reports_chan) => { self.collect_reports(reports_chan, possibly_locked_rw_data); }, - Msg::GetCurrentEpoch(sender) => { - let _rw_data = possibly_locked_rw_data.lock(); - sender.send(self.epoch.get()).unwrap(); - }, - Msg::GetWebFontLoadState(sender) => { - let _rw_data = possibly_locked_rw_data.lock(); - let outstanding_web_fonts = self.outstanding_web_fonts.load(Ordering::SeqCst); - sender.send(outstanding_web_fonts != 0).unwrap(); - }, - Msg::CreateLayoutThread(info) => self.create_layout_thread(info), - Msg::SetFinalUrl(final_url) => { - self.url = final_url; - }, Msg::RegisterPaint(_name, _properties, _painter) => {}, - Msg::PrepareToExit(response_chan) => { - self.prepare_to_exit(response_chan); - return false; - }, // Receiving the Exit message at this stage only happens when layout is undergoing a "force exit". - Msg::ExitNow => { - debug!("layout: ExitNow received"); - self.exit_now(); - return false; - }, - Msg::SetNavigationStart(time) => { - self.paint_time_metrics.set_navigation_start(time); - }, + Msg::ExitNow => {}, } - - true } fn collect_reports<'a, 'b>( @@ -724,51 +574,6 @@ impl LayoutThread { reports_chan.send(reports); } - fn create_layout_thread(&self, info: LayoutThreadInit) { - LayoutThread::create( - info.id, - self.top_level_browsing_context_id, - info.url.clone(), - info.is_parent, - info.layout_pair, - info.pipeline_port, - info.background_hang_monitor_register, - info.constellation_chan, - info.script_chan.clone(), - info.image_cache.clone(), - self.font_cache_thread.clone(), - self.time_profiler_chan.clone(), - self.mem_profiler_chan.clone(), - self.webrender_api.clone(), - info.paint_time_metrics, - info.layout_is_busy, - info.window_size, - ); - } - - /// Enters a quiescent state in which no new messages will be processed until an `ExitNow` is - /// received. A pong is immediately sent on the given response channel. - fn prepare_to_exit(&mut self, response_chan: Sender<()>) { - response_chan.send(()).unwrap(); - loop { - match self.port.recv().unwrap() { - Msg::ExitNow => { - debug!("layout thread is exiting..."); - self.exit_now(); - break; - }, - Msg::CollectReports(_) => { - // Just ignore these messages at this point. - }, - _ => panic!("layout: unexpected message received after `PrepareToExitMsg`"), - } - } - } - - fn exit_now(&mut self) { - self.background_hang_monitor.unregister(); - } - fn handle_add_stylesheet(&self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard) { // Find all font-face rules and notify the font cache of them. // GWTODO: Need to handle unloading web fonts. @@ -790,7 +595,7 @@ impl LayoutThread { self.stylist.set_quirks_mode(quirks_mode); } - /// The high-level routine that performs layout threads. + /// The high-level routine that performs layout. fn handle_reflow<'a, 'b>( &mut self, data: &mut ScriptReflowResult, @@ -1333,9 +1138,8 @@ impl LayoutThread { } } - /// Update the recorded iframe sizes of the contents of this layout thread and - /// when these sizes changes, send a message to the constellation informing it - /// of the new sizes. + /// Update the recorded iframe sizes of the contents of layout and when these sizes changes, + /// send a message to the constellation informing it of the new sizes. fn update_iframe_sizes( &self, new_iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, diff --git a/components/metrics/lib.rs b/components/metrics/lib.rs index f3ea4b26c51..971ad729441 100644 --- a/components/metrics/lib.rs +++ b/components/metrics/lib.rs @@ -261,7 +261,7 @@ impl ProgressiveWebMetric for InteractiveMetrics { // https://w3c.github.io/paint-timing/ pub struct PaintTimeMetrics { pending_metrics: RefCell<HashMap<Epoch, (Option<TimerMetadata>, bool)>>, - navigation_start: Option<u64>, + navigation_start: u64, first_paint: Cell<Option<u64>>, first_contentful_paint: Cell<Option<u64>>, pipeline_id: PipelineId, @@ -278,10 +278,11 @@ impl PaintTimeMetrics { constellation_chan: IpcSender<LayoutMsg>, script_chan: IpcSender<ConstellationControlMsg>, url: ServoUrl, + navigation_start: u64, ) -> PaintTimeMetrics { PaintTimeMetrics { pending_metrics: RefCell::new(HashMap::new()), - navigation_start: None, + navigation_start, first_paint: Cell::new(None), first_contentful_paint: Cell::new(None), pipeline_id, @@ -342,11 +343,8 @@ impl PaintTimeMetrics { } pub fn maybe_set_metric(&self, epoch: Epoch, paint_time: u64) { - if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() || - self.navigation_start.is_none() - { - // If we already set all paint metrics or we have not set navigation start yet, - // we just bail out. + if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() { + // If we already set all paint metrics we just bail out. return; } @@ -387,11 +385,11 @@ impl PaintTimeMetrics { impl ProgressiveWebMetric for PaintTimeMetrics { fn get_navigation_start(&self) -> Option<u64> { - self.navigation_start + Some(self.navigation_start) } fn set_navigation_start(&mut self, time: u64) { - self.navigation_start = Some(time); + self.navigation_start = time; } fn send_queued_constellation_msg(&self, name: ProgressiveWebMetricType, time: u64) { diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index 684b57c2e20..6a25aad9818 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -29,7 +29,7 @@ use webrender_api::{ImageData, ImageDescriptor, ImageDescriptorFlags, ImageForma /// TODO(gw): Remaining work on image cache: /// * Make use of the prefetch support in various parts of the code. /// * Profile time in GetImageIfAvailable - might be worth caching these -/// results per paint / layout thread. +/// results per paint / layout. /// /// MAYBE(Yoric): /// * For faster lookups, it might be useful to store the LoadKey in the diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 31e6c6b466a..cc29865bd69 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -53,6 +53,7 @@ euclid = { workspace = true } fnv = { workspace = true } fxhash = { workspace = true } gfx_traits = { workspace = true } +gfx = { path = "../gfx" } headers = { workspace = true } html5ever = { workspace = true } http = { workspace = true } diff --git a/components/script/dom/bindings/cell.rs b/components/script/dom/bindings/cell.rs index 124240e311e..a9fb9b9b9d3 100644 --- a/components/script/dom/bindings/cell.rs +++ b/components/script/dom/bindings/cell.rs @@ -30,7 +30,7 @@ pub struct DomRefCell<T> { impl<T> DomRefCell<T> { /// Return a reference to the contents. /// - /// For use in the layout thread only. + /// For use in layout only. #[allow(unsafe_code)] pub unsafe fn borrow_for_layout(&self) -> &T { assert_in_layout(); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 7de5d650bdd..512c3870b1f 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -320,7 +320,7 @@ pub struct Document { /// This field is set to the document itself for inert documents. /// <https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document> appropriate_template_contents_owner_document: MutNullableDom<Document>, - /// Information on elements needing restyle to ship over to the layout thread when the + /// Information on elements needing restyle to ship over to layout when the /// time comes. pending_restyles: DomRefCell<HashMap<Dom<Element>, NoTrace<PendingRestyle>>>, /// This flag will be true if layout suppressed a reflow attempt that was @@ -367,7 +367,7 @@ pub struct Document { spurious_animation_frames: Cell<u8>, /// Track the total number of elements in this DOM's tree. - /// This is sent to the layout thread every time a reflow is done; + /// This is sent to layout every time a reflow is done; /// layout uses this to determine if the gains from parallel layout will be worth the overhead. /// /// See also: <https://github.com/servo/servo/issues/10110> @@ -827,10 +827,9 @@ impl Document { let old_mode = self.quirks_mode.replace(new_mode); if old_mode != new_mode { - match self.window.layout_chan() { - Some(chan) => chan.send(Msg::SetQuirksMode(new_mode)).unwrap(), - None => warn!("Layout channel unavailable"), - } + let _ = self + .window + .with_layout(move |layout| layout.process(Msg::SetQuirksMode(new_mode))); } } @@ -2092,7 +2091,7 @@ impl Document { }, LoadType::PageSource(_) => { if self.has_browsing_context && self.is_fully_active() { - // Note: if the document is not fully active, the layout thread will have exited already. + // Note: if the document is not fully active, layout will have exited already. // The underlying problem might actually be that layout exits while it should be kept alive. // See https://github.com/servo/servo/issues/22507 @@ -2264,7 +2263,7 @@ impl Document { None => false, }; - // Note: if the document is not fully active, the layout thread will have exited already, + // Note: if the document is not fully active, layout will have exited already, // and this method will panic. // The underlying problem might actually be that layout exits while it should be kept alive. // See https://github.com/servo/servo/issues/22507 @@ -3441,12 +3440,11 @@ impl Document { /// Flushes the stylesheet list, and returns whether any stylesheet changed. pub fn flush_stylesheets_for_reflow(&self) -> bool { // NOTE(emilio): The invalidation machinery is used on the replicated - // list on the layout thread. + // list in layout. // // FIXME(emilio): This really should differentiate between CSSOM changes // and normal stylesheets additions / removals, because in the last case - // the layout thread already has that information and we could avoid - // dirtying the whole thing. + // layout already has that information and we could avoid dirtying the whole thing. let mut stylesheets = self.stylesheets.borrow_mut(); let have_changed = stylesheets.has_changed(); stylesheets.flush_without_invalidation(); @@ -3830,15 +3828,14 @@ impl Document { }) .cloned(); - match self.window.layout_chan() { - Some(chan) => chan - .send(Msg::AddStylesheet( - sheet.clone(), - insertion_point.as_ref().map(|s| s.sheet.clone()), - )) - .unwrap(), - None => return warn!("Layout channel unavailable"), - } + let cloned_stylesheet = sheet.clone(); + let insertion_point2 = insertion_point.clone(); + let _ = self.window.with_layout(move |layout| { + layout.process(Msg::AddStylesheet( + cloned_stylesheet, + insertion_point2.as_ref().map(|s| s.sheet.clone()), + )); + }); DocumentOrShadowRoot::add_stylesheet( owner, @@ -3851,15 +3848,15 @@ impl Document { /// Remove a stylesheet owned by `owner` from the list of document sheets. #[allow(crown::unrooted_must_root)] // Owner needs to be rooted already necessarily. - pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) { - match self.window.layout_chan() { - Some(chan) => chan.send(Msg::RemoveStylesheet(s.clone())).unwrap(), - None => return warn!("Layout channel unavailable"), - } + pub fn remove_stylesheet(&self, owner: &Element, stylesheet: &Arc<Stylesheet>) { + let cloned_stylesheet = stylesheet.clone(); + let _ = self + .window + .with_layout(|layout| layout.process(Msg::RemoveStylesheet(cloned_stylesheet))); DocumentOrShadowRoot::remove_stylesheet( owner, - s, + stylesheet, StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()), ) } diff --git a/components/script/dom/documentorshadowroot.rs b/components/script/dom/documentorshadowroot.rs index 9bbe435359b..27b6734d5d7 100644 --- a/components/script/dom/documentorshadowroot.rs +++ b/components/script/dom/documentorshadowroot.rs @@ -93,7 +93,7 @@ impl DocumentOrShadowRoot { return vec![]; }; - self.window.layout().nodes_from_point_response() + self.window.layout_rpc().nodes_from_point_response() } #[allow(unsafe_code)] diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index 7d546f154fb..42f3a575d4d 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -457,7 +457,7 @@ impl HTMLElementMethods for HTMLElement { window.layout_reflow(QueryMsg::ElementInnerTextQuery( node.to_trusted_node_address(), )); - DOMString::from(window.layout().element_inner_text()) + DOMString::from(window.layout_rpc().element_inner_text()) } // https://html.spec.whatwg.org/multipage/#the-innertext-idl-attribute diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index f973ddc73f8..d082e09d287 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -7,7 +7,6 @@ use std::cell::Cell; use bitflags::bitflags; use dom_struct::dom_struct; use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; -use ipc_channel::ipc; use js::rust::HandleObject; use msg::constellation_msg::{BrowsingContextId, PipelineId, TopLevelBrowsingContextId}; use profile_traits::ipc as ProfiledIpc; @@ -200,8 +199,6 @@ impl HTMLIFrameElement { match pipeline_type { PipelineType::InitialAboutBlank => { - let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap(); - self.about_blank_pipeline_id.set(Some(new_pipeline_id)); let load_info = IFrameLoadInfoWithData { @@ -213,7 +210,7 @@ impl HTMLIFrameElement { }; global_scope .script_to_constellation_chan() - .send(ScriptMsg::ScriptNewIFrame(load_info, pipeline_sender)) + .send(ScriptMsg::ScriptNewIFrame(load_info)) .unwrap(); let new_layout_info = NewLayoutInfo { @@ -223,7 +220,6 @@ impl HTMLIFrameElement { top_level_browsing_context_id: top_level_browsing_context_id, opener: None, load_data: load_data, - pipeline_port: pipeline_receiver, window_size, }; diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 681f8e0ef4b..8d7561114ff 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -197,7 +197,7 @@ //! Layout code can access the DOM through the //! [`LayoutDom`](bindings/root/struct.LayoutDom.html) smart pointer. This does not //! keep the DOM object alive; we ensure that no DOM code (Garbage Collection -//! in particular) runs while the layout thread is accessing the DOM. +//! in particular) runs while layout is accessing the DOM. //! //! Methods accessible to layout are implemented on `LayoutDom<Foo>` using //! `LayoutFooHelpers` traits. diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index cbdb3412cc4..e44d62f7778 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -10,7 +10,7 @@ use std::default::Default; use std::io::{stderr, stdout, Write}; use std::ptr::NonNull; use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use std::{cmp, env, mem}; @@ -26,7 +26,7 @@ use dom_struct::dom_struct; use embedder_traits::{EmbedderMsg, PromptDefinition, PromptOrigin, PromptResult}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; use euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; -use ipc_channel::ipc::IpcSender; +use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::conversions::ToJSValConvertible; use js::jsapi::{GCReason, Heap, JSAutoRealm, JSObject, StackFormat, JSPROP_ENUMERATE, JS_GC}; @@ -47,13 +47,13 @@ use num_traits::ToPrimitive; use parking_lot::Mutex as ParkMutex; use profile_traits::ipc as ProfiledIpc; use profile_traits::mem::ProfilerChan as MemProfilerChan; -use profile_traits::time::{ProfilerChan as TimeProfilerChan, ProfilerMsg}; +use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_layout_interface::message::{Msg, QueryMsg, Reflow, ReflowGoal, ScriptReflow}; use script_layout_interface::rpc::{ ContentBoxResponse, ContentBoxesResponse, LayoutRPC, NodeScrollIdResponse, ResolvedStyleResponse, TextIndexResponse, }; -use script_layout_interface::{PendingImageState, TrustedNodeAddress}; +use script_layout_interface::{Layout, PendingImageState, TrustedNodeAddress}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData, ScriptMsg, @@ -237,19 +237,6 @@ pub struct Window { #[ignore_malloc_size_of = "Rc<T> is hard"] js_runtime: DomRefCell<Option<Rc<Runtime>>>, - /// A handle for communicating messages to the layout thread. - /// - /// This channel shouldn't be accessed directly, but through `Window::layout_chan()`, - /// which returns `None` if there's no layout thread anymore. - #[ignore_malloc_size_of = "channels are hard"] - #[no_trace] - layout_chan: Sender<Msg>, - - /// A handle to perform RPC calls into the layout, quickly. - #[ignore_malloc_size_of = "trait objects are hard"] - #[no_trace] - layout_rpc: Box<dyn LayoutRPC + Send + 'static>, - /// The current size of the window, in pixels. #[no_trace] window_size: Cell<WindowSizeData>, @@ -339,10 +326,6 @@ pub struct Window { /// It is used to avoid sending idle message more than once, which is unneccessary. has_sent_idle_message: Cell<bool>, - /// Flag that indicates if the layout thread is busy handling a request. - #[ignore_malloc_size_of = "Arc<T> is hard"] - layout_is_busy: Arc<AtomicBool>, - /// Emits notifications when there is a relayout. relayout_event: bool, @@ -519,8 +502,8 @@ impl Window { } pub fn pending_image_notification(&self, response: PendingImageResponse) { - //XXXjdm could be more efficient to send the responses to the layout thread, - // rather than making the layout thread talk to the image cache to + //XXXjdm could be more efficient to send the responses to layout, + // rather than making layout talk to the image cache to // obtain the same data. let mut images = self.pending_layout_images.borrow_mut(); let nodes = images.entry(response.id); @@ -1676,7 +1659,7 @@ impl Window { // objects removed from the tree that haven't been collected // yet). There should not be any such DOM nodes with layout // data, but if there are, then when they are dropped, they - // will attempt to send a message to the closed layout thread. + // will attempt to send a message to layout. // This causes memory safety issues, because the DOM node uses // the layout channel from its window, and the window has // already been GC'd. For nodes which do not have a live @@ -1803,18 +1786,14 @@ impl Window { ScriptThread::handle_tick_all_animations_for_testing(pipeline_id); } - /// Reflows the page unconditionally if possible and not suppressed. This - /// method will wait for the layout thread to complete (but see the `TODO` - /// below). If there is no window size yet, the page is presumed invisible - /// and no reflow is performed. If reflow is suppressed, no reflow will be - /// performed for ForDisplay goals. - /// - /// TODO(pcwalton): Only wait for style recalc, since we have - /// off-main-thread layout. + /// Reflows the page unconditionally if possible and not suppressed. This method will wait for + /// the layout to complete. If there is no window size yet, the page is presumed invisible and + /// no reflow is performed. If reflow is suppressed, no reflow will be performed for ForDisplay + /// goals. /// /// Returns true if layout actually happened, false otherwise. #[allow(unsafe_code)] - pub fn force_reflow( + pub(crate) fn force_reflow( &self, reflow_goal: ReflowGoal, reason: ReflowReason, @@ -1905,14 +1884,7 @@ impl Window { animations: document.animations().sets.clone(), }; - match self.layout_chan() { - Some(layout_chan) => layout_chan - .send(Msg::Reflow(reflow)) - .expect("Layout thread disconnected"), - None => return false, - }; - - debug!("script: layout forked"); + let _ = self.with_layout(move |layout| layout.process(Msg::Reflow(reflow))); let complete = match join_port.try_recv() { Err(TryRecvError::Empty) => { @@ -1921,11 +1893,11 @@ impl Window { }, Ok(reflow_complete) => reflow_complete, Err(TryRecvError::Disconnected) => { - panic!("Layout thread failed while script was waiting for a result."); + panic!("Layout failed while script was waiting for a result."); }, }; - debug!("script: layout joined"); + debug!("script: layout complete"); // Pending reflows require display, so only reset the pending reflow count if this reflow // was to be displayed. @@ -1968,17 +1940,12 @@ impl Window { } document.update_animations_post_reflow(); + self.update_constellation_epoch(); true } - /// Reflows the page if it's possible to do so and the page is dirty. This - /// method will wait for the layout thread to complete (but see the `TODO` - /// below). If there is no window size yet, the page is presumed invisible - /// and no reflow is performed. - /// - /// TODO(pcwalton): Only wait for style recalc, since we have - /// off-main-thread layout. + /// Reflows the page if it's possible to do so and the page is dirty. /// /// Returns true if layout actually happened, false otherwise. /// This return value is useful for script queries, that wait for a lock @@ -2032,12 +1999,24 @@ impl Window { elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) }); + let pending_web_fonts = self + .with_layout(move |layout| layout.waiting_for_web_fonts_to_load()) + .unwrap(); + let has_sent_idle_message = self.has_sent_idle_message.get(); let is_ready_state_complete = document.ReadyState() == DocumentReadyState::Complete; - let pending_images = self.pending_layout_images.borrow().is_empty(); + let pending_images = !self.pending_layout_images.borrow().is_empty(); - if !has_sent_idle_message && is_ready_state_complete && !reftest_wait && pending_images + if !has_sent_idle_message && + is_ready_state_complete && + !reftest_wait && + !pending_images && + !pending_web_fonts { + debug!( + "{:?}: Sending DocumentState::Idle to Constellation", + self.pipeline_id() + ); let event = ScriptMsg::SetDocumentState(DocumentState::Idle); self.send_to_constellation(event); self.has_sent_idle_message.set(true); @@ -2047,13 +2026,28 @@ impl Window { issued_reflow } - pub fn layout_reflow(&self, query_msg: QueryMsg) -> bool { - if self.layout_is_busy.load(Ordering::Relaxed) { - let url = self.get_url().into_string(); - self.time_profiler_chan() - .send(ProfilerMsg::BlockedLayoutQuery(url)); + /// If writing a screenshot, synchronously update the layout epoch that it set + /// in the constellation. + pub(crate) fn update_constellation_epoch(&self) { + if !self.prepare_for_screenshot { + return; } + let epoch = self + .with_layout(move |layout| layout.current_epoch()) + .unwrap(); + + debug!( + "{:?}: Updating constellation epoch: {epoch:?}", + self.pipeline_id() + ); + let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!"); + let event = ScriptMsg::SetLayoutEpoch(epoch, sender); + self.send_to_constellation(event); + receiver.recv().unwrap(); + } + + pub fn layout_reflow(&self, query_msg: QueryMsg) -> bool { self.reflow( ReflowGoal::LayoutQuery(query_msg, time::precise_time_ns()), ReflowReason::Query, @@ -2069,18 +2063,18 @@ impl Window { )) { return None; } - self.layout_rpc.resolved_font_style() + self.layout_rpc().resolved_font_style() } - pub fn layout(&self) -> &dyn LayoutRPC { - &*self.layout_rpc + pub fn layout_rpc(&self) -> Box<dyn LayoutRPC> { + self.with_layout(|layout| layout.rpc()).unwrap() } pub fn content_box_query(&self, node: &Node) -> Option<UntypedRect<Au>> { if !self.layout_reflow(QueryMsg::ContentBoxQuery(node.to_opaque())) { return None; } - let ContentBoxResponse(rect) = self.layout_rpc.content_box(); + let ContentBoxResponse(rect) = self.layout_rpc().content_box(); rect } @@ -2088,7 +2082,7 @@ impl Window { if !self.layout_reflow(QueryMsg::ContentBoxesQuery(node.to_opaque())) { return vec![]; } - let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); + let ContentBoxesResponse(rects) = self.layout_rpc().content_boxes(); rects } @@ -2096,7 +2090,7 @@ impl Window { if !self.layout_reflow(QueryMsg::ClientRectQuery(node.to_opaque())) { return Rect::zero(); } - self.layout_rpc.node_geometry().client_rect + self.layout_rpc().node_geometry().client_rect } /// Find the scroll area of the given node, if it is not None. If the node @@ -2106,7 +2100,7 @@ impl Window { if !self.layout_reflow(QueryMsg::ScrollingAreaQuery(opaque)) { return Rect::zero(); } - self.layout_rpc.scrolling_area().client_rect + self.layout_rpc().scrolling_area().client_rect } pub fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32, LayoutPixel> { @@ -2129,7 +2123,7 @@ impl Window { .borrow_mut() .insert(node.to_opaque(), Vector2D::new(x_ as f32, y_ as f32)); - let NodeScrollIdResponse(scroll_id) = self.layout_rpc.node_scroll_id(); + let NodeScrollIdResponse(scroll_id) = self.layout_rpc().node_scroll_id(); // Step 12 self.perform_a_scroll( @@ -2150,7 +2144,7 @@ impl Window { if !self.layout_reflow(QueryMsg::ResolvedStyleQuery(element, pseudo, property)) { return DOMString::new(); } - let ResolvedStyleResponse(resolved) = self.layout_rpc.resolved_style(); + let ResolvedStyleResponse(resolved) = self.layout_rpc().resolved_style(); DOMString::from(resolved) } @@ -2161,7 +2155,7 @@ impl Window { if !self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery(browsing_context)) { return None; } - self.layout_rpc.inner_window_dimensions() + self.layout_rpc().inner_window_dimensions() } #[allow(unsafe_code)] @@ -2172,7 +2166,7 @@ impl Window { // FIXME(nox): Layout can reply with a garbage value which doesn't // actually correspond to an element, that's unsound. - let response = self.layout_rpc.offset_parent(); + let response = self.layout_rpc().offset_parent(); let element = response.node_address.and_then(|parent_node_address| { let node = unsafe { from_untrusted_node_address(parent_node_address) }; DomRoot::downcast(node) @@ -2188,7 +2182,7 @@ impl Window { if !self.layout_reflow(QueryMsg::TextIndexQuery(node.to_opaque(), point_in_node)) { return TextIndexResponse(None); } - self.layout_rpc.text_index() + self.layout_rpc().text_index() } #[allow(unsafe_code)] @@ -2313,12 +2307,8 @@ impl Window { self.Document().url() } - pub fn layout_chan(&self) -> Option<&Sender<Msg>> { - if self.is_alive() { - Some(&self.layout_chan) - } else { - None - } + pub fn with_layout<'a, T>(&self, call: impl FnOnce(&mut dyn Layout) -> T) -> Result<T, ()> { + ScriptThread::with_layout(self.pipeline_id(), call) } pub fn windowproxy_handler(&self) -> WindowProxyHandler { @@ -2539,7 +2529,6 @@ impl Window { constellation_chan: ScriptToConstellationChan, control_chan: IpcSender<ConstellationControlMsg>, scheduler_chan: IpcSender<TimerSchedulerMsg>, - layout_chan: Sender<Msg>, pipelineid: PipelineId, parent_info: Option<PipelineId>, window_size: WindowSizeData, @@ -2552,7 +2541,6 @@ impl Window { microtask_queue: Rc<MicrotaskQueue>, webrender_document: DocumentId, webrender_api_sender: WebrenderIpcSender, - layout_is_busy: Arc<AtomicBool>, relayout_event: bool, prepare_for_screenshot: bool, unminify_js: bool, @@ -2565,11 +2553,6 @@ impl Window { gpu_id_hub: Arc<ParkMutex<Identities>>, inherited_secure_context: Option<bool>, ) -> DomRoot<Self> { - let layout_rpc: Box<dyn LayoutRPC + Send> = { - let (rpc_send, rpc_recv) = unbounded(); - layout_chan.send(Msg::GetRPC(rpc_send)).unwrap(); - rpc_recv.recv().unwrap() - }; let error_reporter = CSSErrorReporter { pipelineid, script_chan: Arc::new(Mutex::new(control_chan)), @@ -2615,8 +2598,6 @@ impl Window { bluetooth_extra_permission_data: BluetoothExtraPermissionData::new(), page_clip_rect: Cell::new(MaxRect::max_rect()), resize_event: Default::default(), - layout_chan, - layout_rpc, window_size: Cell::new(window_size), current_viewport: Cell::new(Rect::zero()), suppress_reflow: Cell::new(true), @@ -2640,7 +2621,6 @@ impl Window { exists_mut_observer: Cell::new(false), webrender_api_sender, has_sent_idle_message: Cell::new(false), - layout_is_busy, relayout_event, prepare_for_screenshot, unminify_js, diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs index 886d63dc17c..b0a40118e1f 100644 --- a/components/script/dom/windowproxy.rs +++ b/components/script/dom/windowproxy.rs @@ -323,7 +323,6 @@ impl WindowProxy { new_pipeline_id: new_pipeline_id, }; - let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap(); let new_layout_info = NewLayoutInfo { parent_info: None, new_pipeline_id: new_pipeline_id, @@ -331,10 +330,9 @@ impl WindowProxy { top_level_browsing_context_id: new_top_level_browsing_context_id, opener: Some(self.browsing_context_id), load_data: load_data, - pipeline_port: pipeline_receiver, window_size: window.window_size(), }; - let constellation_msg = ScriptMsg::ScriptNewAuxiliary(load_info, pipeline_sender); + let constellation_msg = ScriptMsg::ScriptNewAuxiliary(load_info); window.send_to_constellation(constellation_msg); ScriptThread::process_attach_layout(new_layout_info, document.origin().clone()); let msg = EmbedderMsg::WebViewOpened(new_top_level_browsing_context_id); diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs index b039fc12cd0..f96b41e17cc 100644 --- a/components/script/dom/worklet.rs +++ b/components/script/dom/worklet.rs @@ -234,7 +234,7 @@ impl PendingTasksStruct { /// when a worklet adds a module. It is dropped when the script thread /// is dropped, and asks each of the worklet threads to quit. /// -/// The layout thread can end up blocking on the primary worklet thread +/// Layout can end up blocking on the primary worklet thread /// (e.g. when invoking a paint callback), so it is important to avoid /// deadlock by making sure the primary worklet thread doesn't end up /// blocking waiting on layout. In particular, since the constellation diff --git a/components/script/layout_image.rs b/components/script/layout_image.rs index a7e1d6fa06c..c9fed246773 100644 --- a/components/script/layout_image.rs +++ b/components/script/layout_image.rs @@ -2,10 +2,9 @@ * 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/. */ -//! Infrastructure to initiate network requests for images needed by the layout -//! thread. The script thread needs to be responsible for them because there's -//! no guarantee that the responsible nodes will still exist in the future if the -//! layout thread holds on to them during asynchronous operations. +//! Infrastructure to initiate network requests for images needed by layout. The script thread needs +//! to be responsible for them because there's no guarantee that the responsible nodes will still +//! exist in the future if layout holds on to them during asynchronous operations. use std::sync::{Arc, Mutex}; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 2ac9b1f05f5..06dd6613ab2 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -2,8 +2,8 @@ * 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/. */ -//! The script thread is the thread that owns the DOM in memory, runs JavaScript, and spawns parsing -//! and layout threads. It's in charge of processing events for all same-origin pages in a frame +//! The script thread is the thread that owns the DOM in memory, runs JavaScript, and triggers +//! layout. It's in charge of processing events for all same-origin pages in a frame //! tree, and manages the entire lifetime of pages in the frame tree from initial request to //! teardown. //! @@ -41,6 +41,7 @@ use devtools_traits::{ use embedder_traits::EmbedderMsg; use euclid::default::{Point2D, Rect}; use euclid::Vector2D; +use gfx::font_cache_thread::FontCacheThread; use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader}; use html5ever::{local_name, namespace_url, ns}; use hyper_serde::Serde; @@ -56,10 +57,9 @@ use media::WindowGLContext; use metrics::{PaintTimeMetrics, MAX_TASK_NS}; use mime::{self, Mime}; use msg::constellation_msg::{ - BackgroundHangMonitor, BackgroundHangMonitorExitSignal, BackgroundHangMonitorRegister, - BrowsingContextId, HangAnnotation, HistoryStateId, MonitoredComponentId, - MonitoredComponentType, PipelineId, PipelineNamespace, ScriptHangAnnotation, - TopLevelBrowsingContextId, + BackgroundHangMonitor, BackgroundHangMonitorExitSignal, BrowsingContextId, HangAnnotation, + HistoryStateId, MonitoredComponentId, MonitoredComponentType, PipelineId, PipelineNamespace, + ScriptHangAnnotation, TopLevelBrowsingContextId, }; use net_traits::image_cache::{ImageCache, PendingImageResponse}; use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestBuilder}; @@ -72,7 +72,8 @@ use parking_lot::Mutex; use percent_encoding::percent_decode; use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan}; use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; -use script_layout_interface::message::{self, LayoutThreadInit, Msg, ReflowGoal}; +use script_layout_interface::message::{Msg, ReflowGoal}; +use script_layout_interface::{Layout, LayoutConfig, LayoutFactory, ScriptThreadFactory}; use script_traits::webdriver_msg::WebDriverScriptCommand; use script_traits::CompositorEvent::{ CompositionEvent, GamepadEvent, IMEDismissedEvent, KeyboardEvent, MouseButtonEvent, @@ -81,8 +82,8 @@ use script_traits::CompositorEvent::{ use script_traits::{ AnimationTickType, CompositorEvent, ConstellationControlMsg, DiscardBrowsingContext, DocumentActivity, EventResult, HistoryEntryReplacement, InitialScriptState, JsEvalResult, - LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType, - NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptThreadFactory, + LayoutControlMsg, LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, + MouseEventType, NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptToConstellationChan, StructuredSerializedData, TimerSchedulerMsg, TouchEventType, TouchId, UntrustedNodeAddress, UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, WindowSizeData, WindowSizeType, @@ -202,9 +203,6 @@ struct InProgressLoad { /// The current window size associated with this pipeline. #[no_trace] window_size: WindowSizeData, - /// Channel to the layout thread associated with this pipeline. - #[no_trace] - layout_chan: Sender<message::Msg>, /// The activity level of the document (inactive, active or fully active). #[no_trace] activity: DocumentActivity, @@ -222,8 +220,6 @@ struct InProgressLoad { navigation_start_precise: u64, /// For cancelling the fetch canceller: FetchCanceller, - /// Flag for sharing with the layout thread that is not yet created. - layout_is_busy: Arc<AtomicBool>, /// If inheriting the security context inherited_secure_context: Option<bool>, } @@ -236,11 +232,9 @@ impl InProgressLoad { top_level_browsing_context_id: TopLevelBrowsingContextId, parent_info: Option<PipelineId>, opener: Option<BrowsingContextId>, - layout_chan: Sender<message::Msg>, window_size: WindowSizeData, url: ServoUrl, origin: MutableOrigin, - layout_is_busy: Arc<AtomicBool>, inherited_secure_context: Option<bool>, ) -> InProgressLoad { let duration = SystemTime::now() @@ -248,18 +242,12 @@ impl InProgressLoad { .unwrap_or_default(); let navigation_start = duration.as_millis(); let navigation_start_precise = precise_time_ns(); - layout_chan - .send(message::Msg::SetNavigationStart( - navigation_start_precise as u64, - )) - .unwrap(); InProgressLoad { pipeline_id: id, browsing_context_id: browsing_context_id, top_level_browsing_context_id: top_level_browsing_context_id, parent_info: parent_info, opener: opener, - layout_chan: layout_chan, window_size: window_size, activity: DocumentActivity::FullyActive, is_visible: true, @@ -268,7 +256,6 @@ impl InProgressLoad { navigation_start: navigation_start as u64, navigation_start_precise: navigation_start_precise, canceller: Default::default(), - layout_is_busy: layout_is_busy, inherited_secure_context: inherited_secure_context, } } @@ -554,9 +541,6 @@ pub struct ScriptThread { /// A queue of tasks to be executed in this script-thread. task_queue: TaskQueue<MainThreadScriptMsg>, - /// A handle to register associated layout threads for hang-monitoring. - #[no_trace] - background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, /// The dedicated means of communication with the background-hang-monitor for this script-thread. #[no_trace] background_hang_monitor: Box<dyn BackgroundHangMonitor>, @@ -596,7 +580,7 @@ pub struct ScriptThread { #[no_trace] control_chan: IpcSender<ConstellationControlMsg>, - /// The port on which the constellation and layout threads can communicate with the + /// The port on which the constellation and layout can communicate with the /// script thread. #[no_trace] control_port: Receiver<ConstellationControlMsg>, @@ -605,10 +589,14 @@ pub struct ScriptThread { #[no_trace] script_sender: IpcSender<(PipelineId, ScriptMsg)>, - /// A sender for new layout threads to communicate to the constellation. + /// A sender for layout to communicate to the constellation. #[no_trace] layout_to_constellation_chan: IpcSender<LayoutMsg>, + /// The font cache thread to use for layout that happens in this [`ScriptThread`]. + #[no_trace] + font_cache_thread: FontCacheThread, + /// The port on which we receive messages from the image cache #[no_trace] image_cache_port: Receiver<ImageCacheMsg>, @@ -738,6 +726,14 @@ pub struct ScriptThread { // Secure context inherited_secure_context: Option<bool>, + + /// The layouts that we control. + #[no_trace] + layouts: RefCell<HashMap<PipelineId, Box<dyn Layout>>>, + + /// A factory for making new layouts. This allows layout to depend on script. + #[no_trace] + layout_factory: Arc<dyn LayoutFactory>, } struct BHMExitSignal { @@ -791,21 +787,18 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> { } impl ScriptThreadFactory for ScriptThread { - type Message = message::Msg; - fn create( state: InitialScriptState, + layout_factory: Arc<dyn LayoutFactory>, + font_cache_thread: FontCacheThread, load_data: LoadData, user_agent: Cow<'static, str>, - ) -> (Sender<message::Msg>, Receiver<message::Msg>) { + ) { let (script_chan, script_port) = unbounded(); - - let (sender, receiver) = unbounded(); - let layout_chan = sender.clone(); thread::Builder::new() .name(format!("Script{:?}", state.id)) .spawn(move || { - thread_state::initialize(ThreadState::SCRIPT); + thread_state::initialize(ThreadState::SCRIPT | ThreadState::LAYOUT); PipelineNamespace::install(state.pipeline_namespace_id); TopLevelBrowsingContextId::install(state.top_level_browsing_context_id); let roots = RootCollection::new(); @@ -818,10 +811,15 @@ impl ScriptThreadFactory for ScriptThread { let secure = load_data.inherited_secure_context.clone(); let mem_profiler_chan = state.mem_profiler_chan.clone(); let window_size = state.window_size; - let layout_is_busy = state.layout_is_busy.clone(); - let script_thread = - ScriptThread::new(state, script_port, script_chan.clone(), user_agent); + let script_thread = ScriptThread::new( + state, + script_port, + script_chan.clone(), + layout_factory, + font_cache_thread, + user_agent, + ); SCRIPT_THREAD_ROOT.with(|root| { root.set(Some(&script_thread as *const _)); @@ -836,11 +834,9 @@ impl ScriptThreadFactory for ScriptThread { top_level_browsing_context_id, parent_info, opener, - layout_chan, window_size, load_data.url.clone(), origin, - layout_is_busy, secure, ); script_thread.pre_page_load(new_load, load_data); @@ -860,12 +856,26 @@ impl ScriptThreadFactory for ScriptThread { failsafe.neuter(); }) .expect("Thread spawning failed"); - - (sender, receiver) } } impl ScriptThread { + pub fn with_layout<'a, T>( + pipeline_id: PipelineId, + call: impl FnOnce(&mut dyn Layout) -> T, + ) -> Result<T, ()> { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = unsafe { &*root.get().unwrap() }; + let mut layouts = script_thread.layouts.borrow_mut(); + if let Some(ref mut layout) = layouts.get_mut(&pipeline_id) { + Ok(call(&mut ***layout)) + } else { + warn!("No layout found for {}", pipeline_id); + Err(()) + } + }) + } + pub fn runtime_handle() -> ParentRuntime { SCRIPT_THREAD_ROOT.with(|root| { let script_thread = unsafe { &*root.get().unwrap() }; @@ -1201,12 +1211,8 @@ impl ScriptThread { }, }; - match window.layout_chan() { - Some(chan) => chan - .send(Msg::RegisterPaint(name, properties, painter)) - .unwrap(), - None => warn!("Layout channel unavailable"), - } + let _ = window + .with_layout(|layout| layout.process(Msg::RegisterPaint(name, properties, painter))); } pub fn push_new_element_queue() { @@ -1292,6 +1298,8 @@ impl ScriptThread { state: InitialScriptState, port: Receiver<MainThreadScriptMsg>, chan: Sender<MainThreadScriptMsg>, + layout_factory: Arc<dyn LayoutFactory>, + font_cache_thread: FontCacheThread, user_agent: Cow<'static, str>, ) -> ScriptThread { let opts = opts::get(); @@ -1351,7 +1359,6 @@ impl ScriptThread { task_queue, - background_hang_monitor_register: state.background_hang_monitor_register, background_hang_monitor, closing, @@ -1394,6 +1401,7 @@ impl ScriptThread { mutation_observers: Default::default(), layout_to_constellation_chan: state.layout_to_constellation_chan, + font_cache_thread, webgl_chan: state.webgl_chan, webxr_registry: state.webxr_registry, @@ -1426,6 +1434,8 @@ impl ScriptThread { gpu_id_hub: Arc::new(Mutex::new(Identities::new())), webgpu_port: RefCell::new(None), inherited_secure_context: state.inherited_secure_context, + layouts: Default::default(), + layout_factory, } } @@ -1847,6 +1857,8 @@ impl ScriptThread { ExitFullScreen(id, ..) => Some(id), MediaSessionAction(..) => None, SetWebGPUPort(..) => None, + ForLayoutFromConstellation(_, id) => Some(id), + ForLayoutFromFontCache(id) => Some(id), }, MixedMessage::FromDevtools(_) => None, MixedMessage::FromScript(ref inner_msg) => match *inner_msg { @@ -2081,9 +2093,23 @@ impl ScriptThread { msg @ ConstellationControlMsg::ExitScriptThread => { panic!("should have handled {:?} already", msg) }, + ConstellationControlMsg::ForLayoutFromConstellation(msg, pipeline_id) => { + self.handle_layout_message(msg, pipeline_id) + }, + ConstellationControlMsg::ForLayoutFromFontCache(pipeline_id) => { + self.handle_font_cache(pipeline_id) + }, } } + fn handle_layout_message(&self, msg: LayoutControlMsg, pipeline_id: PipelineId) { + let _ = Self::with_layout(pipeline_id, |layout| layout.handle_constellation_msg(msg)); + } + + fn handle_font_cache(&self, pipeline_id: PipelineId) { + let _ = Self::with_layout(pipeline_id, |layout| layout.handle_font_cache_msg()); + } + fn handle_msg_from_webgpu_server(&self, msg: WebGPUMsg) { match msg { WebGPUMsg::FreeAdapter(id) => self.gpu_id_hub.lock().kill_adapter_id(id), @@ -2509,55 +2535,8 @@ impl ScriptThread { opener, load_data, window_size, - pipeline_port, } = new_layout_info; - let layout_pair = unbounded(); - let layout_chan = layout_pair.0.clone(); - - let layout_is_busy = Arc::new(AtomicBool::new(false)); - - let msg = message::Msg::CreateLayoutThread(LayoutThreadInit { - id: new_pipeline_id, - url: load_data.url.clone(), - is_parent: false, - layout_pair: layout_pair, - pipeline_port: pipeline_port, - background_hang_monitor_register: self.background_hang_monitor_register.clone(), - constellation_chan: self.layout_to_constellation_chan.clone(), - script_chan: self.control_chan.clone(), - image_cache: self.image_cache.clone(), - paint_time_metrics: PaintTimeMetrics::new( - new_pipeline_id, - self.time_profiler_chan.clone(), - self.layout_to_constellation_chan.clone(), - self.control_chan.clone(), - load_data.url.clone(), - ), - layout_is_busy: layout_is_busy.clone(), - window_size, - }); - - // Pick a layout thread, any layout thread - let current_layout_chan: Option<Sender<Msg>> = self - .documents - .borrow() - .iter() - .next() - .and_then(|(_, document)| document.window().layout_chan().cloned()) - .or_else(|| { - self.incomplete_loads - .borrow() - .first() - .map(|load| load.layout_chan.clone()) - }); - - match current_layout_chan { - None => panic!("Layout attached to empty script thread."), - // Tell the layout thread factory to actually spawn the thread. - Some(layout_chan) => layout_chan.send(msg).unwrap(), - }; - // Kick off the fetch for the new resource. let new_load = InProgressLoad::new( new_pipeline_id, @@ -2565,11 +2544,9 @@ impl ScriptThread { top_level_browsing_context_id, parent_info, opener, - layout_chan, window_size, load_data.url.clone(), origin, - layout_is_busy.clone(), load_data.inherited_secure_context.clone(), ); if load_data.url.as_str() == "about:blank" { @@ -2926,60 +2903,37 @@ impl ScriptThread { /// Handles a request to exit a pipeline and shut down layout. fn handle_exit_pipeline_msg(&self, id: PipelineId, discard_bc: DiscardBrowsingContext) { - debug!("Exiting pipeline {}.", id); + debug!("{id}: Starting pipeline exit."); self.closed_pipelines.borrow_mut().insert(id); - // Check if the exit message is for an in progress load. - let idx = self - .incomplete_loads - .borrow() - .iter() - .position(|load| load.pipeline_id == id); - - let document = self.documents.borrow_mut().remove(id); - // Abort the parser, if any, // to prevent any further incoming networking messages from being handled. - if let Some(document) = document.as_ref() { + let document = self.documents.borrow_mut().remove(id); + if let Some(document) = document { + // We should never have a pipeline that's still an incomplete load, but also has a Document. + debug_assert!(!self + .incomplete_loads + .borrow() + .iter() + .any(|load| load.pipeline_id == id)); + if let Some(parser) = document.get_current_parser() { parser.abort(); } - } - - // We should never have a pipeline that's still an incomplete load, - // but also has a Document. - debug_assert!(idx.is_none() || document.is_none()); - - // Remove any incomplete load. - let chan = if let Some(idx) = idx { - let load = self.incomplete_loads.borrow_mut().remove(idx); - load.layout_chan.clone() - } else if let Some(ref document) = document { - match document.window().layout_chan() { - Some(chan) => chan.clone(), - None => return warn!("Layout channel unavailable"), - } - } else { - return warn!("Exiting nonexistant pipeline {}.", id); - }; - // We shut down layout before removing the document, - // since layout might still be in the middle of laying it out. - debug!("preparing to shut down layout for page {}", id); - let (response_chan, response_port) = unbounded(); - chan.send(message::Msg::PrepareToExit(response_chan)).ok(); - let _ = response_port.recv(); + debug!("{id}: Shutting down layout"); + let _ = document.window().with_layout(|layout| { + layout.process(Msg::ExitNow); + }); - debug!("shutting down layout for page {}", id); - chan.send(message::Msg::ExitNow).ok(); - self.script_sender - .send((id, ScriptMsg::PipelineExited)) - .ok(); + debug!("{id}: Sending PipelineExited message to constellation"); + self.script_sender + .send((id, ScriptMsg::PipelineExited)) + .ok(); - // Now that layout is shut down, it's OK to remove the document. - if let Some(document) = document { // Clear any active animations and unroot all of the associated DOM objects. + debug!("{id}: Clearing animations"); document.animations().clear(); // We don't want to dispatch `mouseout` event pointing to non-existing element @@ -2995,10 +2949,12 @@ impl ScriptThread { if discard_bc == DiscardBrowsingContext::Yes { window.discard_browsing_context(); } + + debug!("{id}: Clearing JavaScript runtime"); window.clear_js_runtime(); } - debug!("Exited pipeline {}.", id); + debug!("{id}: Finished pipeline exit"); } /// Handles a request to exit the script thread and shut down layout. @@ -3045,7 +3001,7 @@ impl ScriptThread { }); } - /// Handles when layout thread finishes all animation in one tick + /// Handles when layout finishes all animation in one tick fn handle_tick_all_animations(&self, id: PipelineId, tick_type: AnimationTickType) { let document = match self.documents.borrow().find_document(id) { Some(document) => document, @@ -3255,13 +3211,6 @@ impl ScriptThread { fn load(&self, metadata: Metadata, incomplete: InProgressLoad) -> DomRoot<ServoParser> { let final_url = metadata.final_url.clone(); { - // send the final url to the layout thread. - incomplete - .layout_chan - .send(message::Msg::SetFinalUrl(final_url.clone())) - .unwrap(); - - // update the pipeline url self.script_sender .send(( incomplete.pipeline_id, @@ -3304,6 +3253,33 @@ impl ScriptThread { self.websocket_task_source(incomplete.pipeline_id), ); + let paint_time_metrics = PaintTimeMetrics::new( + incomplete.pipeline_id, + self.time_profiler_chan.clone(), + self.layout_to_constellation_chan.clone(), + self.control_chan.clone(), + final_url.clone(), + incomplete.navigation_start_precise, + ); + + let layout_config = LayoutConfig { + id: incomplete.pipeline_id, + url: final_url.clone(), + is_iframe: incomplete.parent_info.is_some(), + constellation_chan: self.layout_to_constellation_chan.clone(), + script_chan: self.control_chan.clone(), + image_cache: self.image_cache.clone(), + font_cache_thread: self.font_cache_thread.clone(), + time_profiler_chan: self.time_profiler_chan.clone(), + webrender_api_sender: self.webrender_api_sender.clone(), + paint_time_metrics, + window_size: incomplete.window_size.clone(), + }; + self.layouts.borrow_mut().insert( + incomplete.pipeline_id, + self.layout_factory.create(layout_config), + ); + // Create the window and document objects. let window = Window::new( self.js_runtime.clone(), @@ -3319,7 +3295,6 @@ impl ScriptThread { script_to_constellation_chan, self.control_chan.clone(), self.scheduler_chan.clone(), - incomplete.layout_chan, incomplete.pipeline_id, incomplete.parent_info, incomplete.window_size, @@ -3332,7 +3307,6 @@ impl ScriptThread { self.microtask_queue.clone(), self.webrender_document, self.webrender_api_sender.clone(), - incomplete.layout_is_busy, self.relayout_event, self.prepare_for_screenshot, self.unminify_js, diff --git a/components/servo/lib.rs b/components/servo/lib.rs index eb423ea4685..c21c6af4ede 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -75,6 +75,7 @@ use profile::{mem as profile_mem, time as profile_time}; use profile_traits::{mem, time}; use script::serviceworker_manager::ServiceWorkerManager; use script::JSEngineSetup; +use script_layout_interface::LayoutFactory; use script_traits::{ScriptToConstellationChan, WindowSizeData}; use servo_config::{opts, pref, prefs}; use servo_media::player::context::GlContext; @@ -417,9 +418,8 @@ where device_pixel_ratio: Scale::new(device_pixel_ratio), }; - // Create the constellation, which maintains the engine - // pipelines, including the script and layout threads, as well - // as the navigation context. + // Create the constellation, which maintains the engine pipelines, including script and + // layout, as well as the navigation context. let constellation_chan = create_constellation( user_agent, opts.config_dir.clone(), @@ -986,24 +986,18 @@ fn create_constellation( wgpu_image_map, }; - let start_constellation_chan = if opts::get().legacy_layout { - Constellation::< - script_layout_interface::message::Msg, - layout_thread_2013::LayoutThread, - script::script_thread::ScriptThread, - script::serviceworker_manager::ServiceWorkerManager, - >::start + let layout_factory: Arc<dyn LayoutFactory> = if opts::get().legacy_layout { + Arc::new(layout_thread_2013::LayoutFactoryImpl()) } else { - Constellation::< - script_layout_interface::message::Msg, - layout_thread_2020::LayoutThread, - script::script_thread::ScriptThread, - script::serviceworker_manager::ServiceWorkerManager, - >::start + Arc::new(layout_thread_2020::LayoutFactoryImpl()) }; - start_constellation_chan( + Constellation::< + script::script_thread::ScriptThread, + script::serviceworker_manager::ServiceWorkerManager, + >::start( initial_state, + layout_factory, initial_window_size, opts.random_pipeline_closure_probability, opts.random_pipeline_closure_seed, @@ -1129,21 +1123,17 @@ pub fn run_content_process(token: String) { set_logger(content.script_to_constellation_chan().clone()); let background_hang_monitor_register = content.register_with_background_hang_monitor(); - if opts::get().legacy_layout { - content.start_all::<script_layout_interface::message::Msg, - layout_thread_2013::LayoutThread, - script::script_thread::ScriptThread>( - true, - background_hang_monitor_register, - ); + let layout_factory: Arc<dyn LayoutFactory> = if opts::get().legacy_layout { + Arc::new(layout_thread_2013::LayoutFactoryImpl()) } else { - content.start_all::<script_layout_interface::message::Msg, - layout_thread_2020::LayoutThread, - script::script_thread::ScriptThread>( - true, - background_hang_monitor_register, - ); - } + Arc::new(layout_thread_2020::LayoutFactoryImpl()) + }; + + content.start_all::<script::script_thread::ScriptThread>( + true, + layout_factory, + background_hang_monitor_register, + ); }, UnprivilegedContent::ServiceWorker(content) => { content.start::<ServiceWorkerManager>(); diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index 81ef4ace3b9..b3810344439 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -20,8 +20,8 @@ use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId}; use net_traits::image::base::Image; use net_traits::NetToCompositorMsg; use script_traits::{ - AnimationState, ConstellationControlMsg, EventResult, LayoutControlMsg, MouseButton, - MouseEventType, ScriptToCompositorMsg, + AnimationState, ConstellationControlMsg, EventResult, MouseButton, MouseEventType, + ScriptToCompositorMsg, }; use style_traits::CSSPixel; use webrender_api::units::{DeviceIntPoint, DeviceIntSize}; @@ -132,7 +132,7 @@ pub enum CompositorMsg { /// Required to allow WGL GLContext sharing in Windows. Dispatch(Box<dyn Fn() + Send>), /// Indicates to the compositor that it needs to record the time when the frame with - /// the given ID (epoch) is painted and report it to the layout thread of the given + /// the given ID (epoch) is painted and report it to the layout of the given /// pipeline ID. PendingPaintMetric(PipelineId, Epoch), /// The load of a page has completed @@ -165,7 +165,6 @@ pub struct CompositionPipeline { pub id: PipelineId, pub top_level_browsing_context_id: TopLevelBrowsingContextId, pub script_chan: IpcSender<ConstellationControlMsg>, - pub layout_chan: IpcSender<LayoutControlMsg>, } pub enum FontToCompositorMsg { diff --git a/components/shared/layout/Cargo.toml b/components/shared/layout/Cargo.toml deleted file mode 100644 index 88b04842f6c..00000000000 --- a/components/shared/layout/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "layout_traits" -version = "0.0.1" -authors = ["The Servo Project Developers"] -license = "MPL-2.0" -edition = "2018" -publish = false - -[lib] -name = "layout_traits" -path = "lib.rs" - -[dependencies] -crossbeam-channel = { workspace = true } -gfx = { path = "../../gfx" } -ipc-channel = { workspace = true } -metrics = { path = "../../metrics" } -msg = { workspace = true } -net_traits = { workspace = true } -profile_traits = { workspace = true } -script_traits = { workspace = true } -servo_url = { path = "../../url" } diff --git a/components/shared/layout/lib.rs b/components/shared/layout/lib.rs deleted file mode 100644 index 1e1783e489f..00000000000 --- a/components/shared/layout/lib.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* 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/. */ - -#![deny(unsafe_code)] - -// This module contains traits in layout used generically -// in the rest of Servo. -// The traits are here instead of in layout so -// that these modules won't have to depend on layout. - -use std::sync::atomic::AtomicBool; -use std::sync::Arc; - -use crossbeam_channel::{Receiver, Sender}; -use gfx::font_cache_thread::FontCacheThread; -use ipc_channel::ipc::{IpcReceiver, IpcSender}; -use metrics::PaintTimeMetrics; -use msg::constellation_msg::{ - BackgroundHangMonitorRegister, PipelineId, TopLevelBrowsingContextId, -}; -use net_traits::image_cache::ImageCache; -use profile_traits::{mem, time}; -use script_traits::{ - ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, WebrenderIpcSender, - WindowSizeData, -}; -use servo_url::ServoUrl; - -// A static method creating a layout thread -// Here to remove the compositor -> layout dependency -pub trait LayoutThreadFactory { - type Message; - fn create( - id: PipelineId, - top_level_browsing_context_id: TopLevelBrowsingContextId, - url: ServoUrl, - is_iframe: bool, - chan: (Sender<Self::Message>, Receiver<Self::Message>), - pipeline_port: IpcReceiver<LayoutControlMsg>, - background_hang_monitor: Box<dyn BackgroundHangMonitorRegister>, - constellation_chan: IpcSender<ConstellationMsg>, - script_chan: IpcSender<ConstellationControlMsg>, - image_cache: Arc<dyn ImageCache>, - font_cache_thread: FontCacheThread, - time_profiler_chan: time::ProfilerChan, - mem_profiler_chan: mem::ProfilerChan, - webrender_api_sender: WebrenderIpcSender, - paint_time_metrics: PaintTimeMetrics, - busy: Arc<AtomicBool>, - window_size: WindowSizeData, - ); -} diff --git a/components/shared/msg/constellation_msg.rs b/components/shared/msg/constellation_msg.rs index 46c947de67b..45f43b612b7 100644 --- a/components/shared/msg/constellation_msg.rs +++ b/components/shared/msg/constellation_msg.rs @@ -459,7 +459,6 @@ pub enum LayoutHangAnnotation { Reflow, GetRPC, CollectReports, - PrepareToExit, ExitNow, GetCurrentEpoch, GetWebFontLoadState, @@ -468,7 +467,6 @@ pub enum LayoutHangAnnotation { SetScrollStates, UpdateScrollStateFromScript, RegisterPaint, - SetNavigationStart, } #[derive(Clone, Copy, Debug, Deserialize, Serialize)] @@ -611,7 +609,6 @@ impl fmt::Debug for HangProfile { #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum MonitoredComponentType { - Layout, Script, } diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 7ade170fe77..f39089cf3a0 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -18,14 +18,13 @@ pub mod webdriver_msg; use std::borrow::Cow; use std::collections::{HashMap, VecDeque}; use std::fmt; -use std::sync::atomic::AtomicBool; use std::sync::Arc; use bitflags::bitflags; use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; use compositor::ScrollTreeNodeId; -use crossbeam_channel::{Receiver, RecvTimeoutError, Sender}; +use crossbeam_channel::{RecvTimeoutError, Sender}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use embedder_traits::{CompositorEventVariant, Cursor}; use euclid::default::Point2D; @@ -110,19 +109,14 @@ impl UntrustedNodeAddress { } } -/// Messages sent to the layout thread from the constellation and/or compositor. +/// Messages sent to layout from the constellation and/or compositor. #[derive(Debug, Deserialize, Serialize)] pub enum LayoutControlMsg { - /// Requests that this layout thread exit. + /// Requests that this layout clean up before exit. ExitNow, - /// Requests the current epoch (layout counter) from this layout. - GetCurrentEpoch(IpcSender<Epoch>), /// Tells layout about the new scrolling offsets of each scrollable stacking context. SetScrollStates(Vec<ScrollState>), - /// Requests the current load state of Web fonts. `true` is returned if fonts are still loading - /// and `false` is returned if all fonts have loaded. - GetWebFontLoadState(IpcSender<bool>), - /// Send the paint time for a specific epoch to the layout thread. + /// Send the paint time for a specific epoch to layout. PaintMetric(Epoch, u64), } @@ -233,8 +227,6 @@ pub struct NewLayoutInfo { pub load_data: LoadData, /// Information about the initial window size. pub window_size: WindowSizeData, - /// A port on which layout can receive messages from the pipeline. - pub pipeline_port: IpcReceiver<LayoutControlMsg>, } /// When a pipeline is closed, should its browsing context be discarded too? @@ -293,7 +285,7 @@ pub enum ConstellationControlMsg { /// Sends the final response to script thread for fetching after all redirections /// have been resolved NavigationResponse(PipelineId, FetchResponseMsg), - /// Gives a channel and ID to a layout thread, as well as the ID of that layout's parent + /// Gives a channel and ID to a layout, as well as the ID of that layout's parent AttachLayout(NewLayoutInfo), /// Window resized. Sends a DOM event eventually, but first we combine events. Resize(PipelineId, WindowSizeData, WindowSizeType), @@ -401,6 +393,10 @@ pub enum ConstellationControlMsg { MediaSessionAction(PipelineId, MediaSessionActionType), /// Notifies script thread that WebGPU server has started SetWebGPUPort(IpcReceiver<WebGPUMsg>), + /// A mesage for a layout from the constellation. + ForLayoutFromConstellation(LayoutControlMsg, PipelineId), + /// A message for a layout from the font cache. + ForLayoutFromFontCache(PipelineId), } impl fmt::Debug for ConstellationControlMsg { @@ -439,6 +435,8 @@ impl fmt::Debug for ConstellationControlMsg { ExitFullScreen(..) => "ExitFullScreen", MediaSessionAction(..) => "MediaSessionAction", SetWebGPUPort(..) => "SetWebGPUPort", + ForLayoutFromConstellation(..) => "ForLayoutFromConstellation", + ForLayoutFromFontCache(..) => "ForLayoutFromFontCache", }; write!(formatter, "ConstellationControlMsg::{}", variant) } @@ -664,7 +662,7 @@ pub struct InitialScriptState { pub script_to_constellation_chan: ScriptToConstellationChan, /// A handle to register script-(and associated layout-)threads for hang monitoring. pub background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, - /// A sender for the layout thread to communicate to the constellation. + /// A sender layout to communicate to the constellation. pub layout_to_constellation_chan: IpcSender<LayoutMsg>, /// A channel to schedule timer events. pub scheduler_chan: IpcSender<TimerSchedulerMsg>, @@ -694,25 +692,10 @@ pub struct InitialScriptState { pub webrender_document: DocumentId, /// FIXME(victor): The Webrender API sender in this constellation's pipeline pub webrender_api_sender: WebrenderIpcSender, - /// Flag to indicate if the layout thread is busy handling a request. - pub layout_is_busy: Arc<AtomicBool>, /// Application window's GL Context for Media player pub player_context: WindowGLContext, } -/// This trait allows creating a `ScriptThread` without depending on the `script` -/// crate. -pub trait ScriptThreadFactory { - /// Type of message sent from script to layout. - type Message; - /// Create a `ScriptThread`. - fn create( - state: InitialScriptState, - load_data: LoadData, - user_agent: Cow<'static, str>, - ) -> (Sender<Self::Message>, Receiver<Self::Message>); -} - /// This trait allows creating a `ServiceWorkerManager` without depending on the `script` /// crate. pub trait ServiceWorkerManagerFactory { diff --git a/components/shared/script/script_msg.rs b/components/shared/script/script_msg.rs index 5abef781743..8f55db70a12 100644 --- a/components/shared/script/script_msg.rs +++ b/components/shared/script/script_msg.rs @@ -29,8 +29,8 @@ use webrender_api::units::{DeviceIntPoint, DeviceIntSize}; use crate::{ AnimationState, AuxiliaryBrowsingContextLoadInfo, BroadcastMsg, DocumentState, - IFrameLoadInfoWithData, LayoutControlMsg, LoadData, MessagePortMsg, PortMessageTask, - StructuredSerializedData, WindowSizeType, WorkerGlobalScopeInit, WorkerScriptLoadOrigin, + IFrameLoadInfoWithData, LoadData, MessagePortMsg, PortMessageTask, StructuredSerializedData, + WindowSizeType, WorkerGlobalScopeInit, WorkerScriptLoadOrigin, }; /// An iframe sizing operation. @@ -220,16 +220,15 @@ pub enum ScriptMsg { /// A load has been requested in an IFrame. ScriptLoadedURLInIFrame(IFrameLoadInfoWithData), /// A load of the initial `about:blank` has been completed in an IFrame. - ScriptNewIFrame(IFrameLoadInfoWithData, IpcSender<LayoutControlMsg>), + ScriptNewIFrame(IFrameLoadInfoWithData), /// Script has opened a new auxiliary browsing context. - ScriptNewAuxiliary( - AuxiliaryBrowsingContextLoadInfo, - IpcSender<LayoutControlMsg>, - ), + ScriptNewAuxiliary(AuxiliaryBrowsingContextLoadInfo), /// Mark a new document as active ActivateDocument, /// Set the document state for a pipeline (used by screenshot / reftests) SetDocumentState(DocumentState), + /// Update the layout epoch in the constellation (used by screenshot / reftests). + SetLayoutEpoch(Epoch, IpcSender<bool>), /// Update the pipeline Url, which can change after redirections. SetFinalUrl(ServoUrl), /// Script has handled a touch event, and either prevented or allowed default actions. @@ -311,6 +310,7 @@ impl fmt::Debug for ScriptMsg { ScriptNewAuxiliary(..) => "ScriptNewAuxiliary", ActivateDocument => "ActivateDocument", SetDocumentState(..) => "SetDocumentState", + SetLayoutEpoch(..) => "SetLayoutEpoch", SetFinalUrl(..) => "SetFinalUrl", TouchEventProcessed(..) => "TouchEventProcessed", LogEntry(..) => "LogEntry", diff --git a/components/shared/script_layout/Cargo.toml b/components/shared/script_layout/Cargo.toml index 4055e337e42..40c92e96c8e 100644 --- a/components/shared/script_layout/Cargo.toml +++ b/components/shared/script_layout/Cargo.toml @@ -16,6 +16,7 @@ atomic_refcell = { workspace = true } canvas_traits = { workspace = true } crossbeam-channel = { workspace = true } euclid = { workspace = true } +gfx = { path = "../../gfx" } gfx_traits = { workspace = true } html5ever = { workspace = true } ipc-channel = { workspace = true } diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 0db42229678..933484fc1fa 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -13,15 +13,25 @@ pub mod rpc; pub mod wrapper_traits; use std::any::Any; +use std::borrow::Cow; use std::sync::atomic::AtomicIsize; +use std::sync::Arc; use atomic_refcell::AtomicRefCell; use canvas_traits::canvas::{CanvasId, CanvasMsg}; +use gfx::font_cache_thread::FontCacheThread; +use gfx_traits::Epoch; use ipc_channel::ipc::IpcSender; use libc::c_void; use malloc_size_of_derive::MallocSizeOf; -use net_traits::image_cache::PendingImageId; -use script_traits::UntrustedNodeAddress; +use metrics::PaintTimeMetrics; +use msg::constellation_msg::PipelineId; +use net_traits::image_cache::{ImageCache, PendingImageId}; +use profile_traits::time; +use script_traits::{ + ConstellationControlMsg, InitialScriptState, LayoutControlMsg, LayoutMsg, LoadData, + UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData, +}; use servo_url::{ImmutableOrigin, ServoUrl}; use style::data::ElementData; use webrender_api::ImageKey; @@ -162,3 +172,57 @@ pub struct PendingImage { pub struct HTMLMediaData { pub current_frame: Option<(ImageKey, i32, i32)>, } + +pub struct LayoutConfig { + pub id: PipelineId, + pub url: ServoUrl, + pub is_iframe: bool, + pub constellation_chan: IpcSender<LayoutMsg>, + pub script_chan: IpcSender<ConstellationControlMsg>, + pub image_cache: Arc<dyn ImageCache>, + pub font_cache_thread: FontCacheThread, + pub time_profiler_chan: time::ProfilerChan, + pub webrender_api_sender: WebrenderIpcSender, + pub paint_time_metrics: PaintTimeMetrics, + pub window_size: WindowSizeData, +} + +pub trait LayoutFactory: Send + Sync { + fn create(&self, config: LayoutConfig) -> Box<dyn Layout>; +} + +pub trait Layout { + /// Process a single message from script. + fn process(&mut self, msg: message::Msg); + + /// Handle a single message from the Constellation. + fn handle_constellation_msg(&mut self, msg: LayoutControlMsg); + + /// Handle a a single mesasge from the FontCacheThread. + fn handle_font_cache_msg(&mut self); + + /// Return the interface used for scipt queries. + /// TODO: Make this part of the the Layout interface itself now that the + /// layout thread has been removed. + fn rpc(&self) -> Box<dyn rpc::LayoutRPC>; + + /// Whether or not this layout is waiting for fonts from loaded stylesheets to finish loading. + fn waiting_for_web_fonts_to_load(&self) -> bool; + + /// The currently laid out Epoch that this Layout has finished. + fn current_epoch(&self) -> Epoch; +} + +/// This trait is part of `layout_traits` because it depends on both `script_traits` and also +/// `LayoutFactory` from this crate. If it was in `script_traits` there would be a circular +/// dependency. +pub trait ScriptThreadFactory { + /// Create a `ScriptThread`. + fn create( + state: InitialScriptState, + layout_factory: Arc<dyn LayoutFactory>, + font_cache_thread: FontCacheThread, + load_data: LoadData, + user_agent: Cow<'static, str>, + ); +} diff --git a/components/shared/script_layout/message.rs b/components/shared/script_layout/message.rs index a83557757a4..d07272821b0 100644 --- a/components/shared/script_layout/message.rs +++ b/components/shared/script_layout/message.rs @@ -2,26 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::sync::atomic::AtomicBool; -use std::sync::Arc; - use app_units::Au; -use crossbeam_channel::{Receiver, Sender}; +use crossbeam_channel::Sender; use euclid::default::{Point2D, Rect}; -use gfx_traits::Epoch; -use ipc_channel::ipc::{IpcReceiver, IpcSender}; use malloc_size_of_derive::MallocSizeOf; -use metrics::PaintTimeMetrics; -use msg::constellation_msg::{BackgroundHangMonitorRegister, BrowsingContextId, PipelineId}; -use net_traits::image_cache::ImageCache; +use msg::constellation_msg::BrowsingContextId; use profile_traits::mem::ReportsChan; -use script_traits::{ - ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, Painter, ScrollState, - WindowSizeData, -}; +use script_traits::{Painter, ScrollState, WindowSizeData}; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; -use servo_url::{ImmutableOrigin, ServoUrl}; +use servo_url::ImmutableOrigin; use style::animation::DocumentAnimationSet; use style::context::QuirksMode; use style::dom::OpaqueNode; @@ -52,42 +42,19 @@ pub enum Msg { /// Get an RPC interface. GetRPC(Sender<Box<dyn LayoutRPC + Send>>), - /// Requests that the layout thread measure its memory usage. The resulting reports are sent back + /// Requests that layout measure its memory usage. The resulting reports are sent back /// via the supplied channel. CollectReports(ReportsChan), - /// Requests that the layout thread enter a quiescent state in which no more messages are - /// accepted except `ExitMsg`. A response message will be sent on the supplied channel when - /// this happens. - PrepareToExit(Sender<()>), - - /// Requests that the layout thread immediately shut down. There must be no more nodes left after + /// Requests that layout immediately shut down. There must be no more nodes left after /// this, or layout will crash. ExitNow, - /// Get the last epoch counter for this layout thread. - GetCurrentEpoch(IpcSender<Epoch>), - - /// Asks the layout thread whether any Web fonts have yet to load (if true, loads are pending; - /// false otherwise). - GetWebFontLoadState(IpcSender<bool>), - - /// Creates a new layout thread. - /// - /// This basically exists to keep the script-layout dependency one-way. - CreateLayoutThread(LayoutThreadInit), - - /// Set the final Url. - SetFinalUrl(ServoUrl), - /// Tells layout about the new scrolling offsets of each scrollable stacking context. SetScrollStates(Vec<ScrollState>), /// Tells layout that script has added some paint worklet modules. RegisterPaint(Atom, Vec<Atom>, Box<dyn Painter>), - - /// Send to layout the precise time when the navigation started. - SetNavigationStart(u64), } #[derive(Debug, PartialEq)] @@ -218,21 +185,6 @@ pub struct ScriptReflow { pub animations: DocumentAnimationSet, } -pub struct LayoutThreadInit { - pub id: PipelineId, - pub url: ServoUrl, - pub is_parent: bool, - pub layout_pair: (Sender<Msg>, Receiver<Msg>), - pub pipeline_port: IpcReceiver<LayoutControlMsg>, - pub background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, - pub constellation_chan: IpcSender<ConstellationMsg>, - pub script_chan: IpcSender<ConstellationControlMsg>, - pub image_cache: Arc<dyn ImageCache>, - pub paint_time_metrics: PaintTimeMetrics, - pub layout_is_busy: Arc<AtomicBool>, - pub window_size: WindowSizeData, -} - /// A pending restyle. #[derive(Debug, MallocSizeOf)] pub struct PendingRestyle { diff --git a/components/style/context.rs b/components/style/context.rs index f38963539b9..8f717eca61f 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -479,7 +479,7 @@ impl<E: TElement> SequentialTask<E> { /// Executes this task. pub fn execute(self) { use self::SequentialTask::*; - debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT); + debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); match self { Unused(_) => unreachable!(), #[cfg(feature = "gecko")] @@ -555,7 +555,7 @@ where E: TElement, { fn drop(&mut self) { - debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT); + debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); for task in self.0.drain(..) { task.execute() } |