aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock21
-rw-r--r--components/compositing/compositor.rs37
-rw-r--r--components/constellation/Cargo.toml2
-rw-r--r--components/constellation/constellation.rs132
-rw-r--r--components/constellation/pipeline.rs101
-rw-r--r--components/gfx/platform/freetype/font.rs2
-rw-r--r--components/layout/context.rs2
-rw-r--r--components/layout/fragment.rs2
-rw-r--r--components/layout/query.rs2
-rw-r--r--components/layout_2020/display_list/mod.rs2
-rw-r--r--components/layout_2020/query.rs2
-rw-r--r--components/layout_thread/Cargo.toml1
-rw-r--r--components/layout_thread/lib.rs361
-rw-r--r--components/layout_thread_2020/Cargo.toml1
-rw-r--r--components/layout_thread_2020/lib.rs364
-rw-r--r--components/metrics/lib.rs16
-rw-r--r--components/net/image_cache.rs2
-rw-r--r--components/script/Cargo.toml1
-rw-r--r--components/script/dom/bindings/cell.rs2
-rw-r--r--components/script/dom/document.rs49
-rw-r--r--components/script/dom/documentorshadowroot.rs2
-rw-r--r--components/script/dom/htmlelement.rs2
-rw-r--r--components/script/dom/htmliframeelement.rs6
-rw-r--r--components/script/dom/mod.rs2
-rw-r--r--components/script/dom/window.rs150
-rw-r--r--components/script/dom/windowproxy.rs4
-rw-r--r--components/script/dom/worklet.rs2
-rw-r--r--components/script/layout_image.rs7
-rw-r--r--components/script/script_thread.rs276
-rw-r--r--components/servo/lib.rs52
-rw-r--r--components/shared/compositing/lib.rs7
-rw-r--r--components/shared/layout/Cargo.toml22
-rw-r--r--components/shared/layout/lib.rs53
-rw-r--r--components/shared/msg/constellation_msg.rs3
-rw-r--r--components/shared/script/lib.rs41
-rw-r--r--components/shared/script/script_msg.rs14
-rw-r--r--components/shared/script_layout/Cargo.toml1
-rw-r--r--components/shared/script_layout/lib.rs68
-rw-r--r--components/shared/script_layout/message.rs60
-rw-r--r--components/style/context.rs4
-rw-r--r--tests/unit/metrics/paint_time.rs8
-rw-r--r--tests/wpt/meta-legacy-layout/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini2
-rw-r--r--tests/wpt/meta/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini2
-rw-r--r--tests/wpt/mozilla/meta-legacy-layout/mozilla/detached_layout.html.ini3
44 files changed, 638 insertions, 1255 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7b257dccde8..bf76bcf88ed 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -886,7 +886,6 @@ dependencies = [
"http",
"ipc-channel",
"keyboard-types",
- "layout_traits",
"log",
"media",
"metrics",
@@ -894,6 +893,7 @@ dependencies = [
"net",
"net_traits",
"profile_traits",
+ "script_layout_interface",
"script_traits",
"serde",
"servo_config",
@@ -3150,7 +3150,6 @@ dependencies = [
"histogram",
"ipc-channel",
"layout_2013",
- "layout_traits",
"lazy_static",
"log",
"malloc_size_of",
@@ -3190,7 +3189,6 @@ dependencies = [
"gfx_traits",
"ipc-channel",
"layout_2020",
- "layout_traits",
"lazy_static",
"log",
"malloc_size_of",
@@ -3214,21 +3212,6 @@ dependencies = [
]
[[package]]
-name = "layout_traits"
-version = "0.0.1"
-dependencies = [
- "crossbeam-channel",
- "gfx",
- "ipc-channel",
- "metrics",
- "msg",
- "net_traits",
- "profile_traits",
- "script_traits",
- "servo_url",
-]
-
-[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4919,6 +4902,7 @@ dependencies = [
"euclid",
"fnv",
"fxhash",
+ "gfx",
"gfx_traits",
"headers",
"html5ever",
@@ -4998,6 +4982,7 @@ dependencies = [
"canvas_traits",
"crossbeam-channel",
"euclid",
+ "gfx",
"gfx_traits",
"html5ever",
"ipc-channel",
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()
}
diff --git a/tests/unit/metrics/paint_time.rs b/tests/unit/metrics/paint_time.rs
index ac8ade3842c..65316e09d16 100644
--- a/tests/unit/metrics/paint_time.rs
+++ b/tests/unit/metrics/paint_time.rs
@@ -28,11 +28,12 @@ fn test_paint_metrics_construction() {
layout_sender,
script_sender,
ServoUrl::parse("about:blank").unwrap(),
+ 0,
);
assert_eq!(
(&paint_time_metrics).get_navigation_start(),
- None,
- "navigation start is None"
+ Some(0),
+ "navigation start is set properly"
);
assert_eq!(
paint_time_metrics.get_first_paint(),
@@ -57,6 +58,7 @@ fn test_common(display_list_is_contentful: bool, epoch: Epoch) -> PaintTimeMetri
layout_sender,
script_sender,
ServoUrl::parse("about:blank").unwrap(),
+ 0,
);
let dummy_profiler_metadata_factory = DummyProfilerMetadataFactory {};
@@ -66,8 +68,6 @@ fn test_common(display_list_is_contentful: bool, epoch: Epoch) -> PaintTimeMetri
display_list_is_contentful,
);
- // Should not set any metric until navigation start is set.
- paint_time_metrics.maybe_set_metric(epoch, 0);
assert_eq!(
paint_time_metrics.get_first_paint(),
None,
diff --git a/tests/wpt/meta-legacy-layout/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini b/tests/wpt/meta-legacy-layout/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini
deleted file mode 100644
index 7c64c626527..00000000000
--- a/tests/wpt/meta-legacy-layout/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[adopt-while-parsing-001.html]
- expected: TIMEOUT
diff --git a/tests/wpt/meta/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini b/tests/wpt/meta/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini
deleted file mode 100644
index 7c64c626527..00000000000
--- a/tests/wpt/meta/html/the-xhtml-syntax/parsing-xhtml-documents/adopt-while-parsing-001.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[adopt-while-parsing-001.html]
- expected: TIMEOUT
diff --git a/tests/wpt/mozilla/meta-legacy-layout/mozilla/detached_layout.html.ini b/tests/wpt/mozilla/meta-legacy-layout/mozilla/detached_layout.html.ini
new file mode 100644
index 00000000000..bc10807d4c2
--- /dev/null
+++ b/tests/wpt/mozilla/meta-legacy-layout/mozilla/detached_layout.html.ini
@@ -0,0 +1,3 @@
+[detached_layout.html]
+ [Detached layout doesn't panic]
+ expected: FAIL