diff options
Diffstat (limited to 'components/constellation')
-rw-r--r-- | components/constellation/Cargo.toml | 2 | ||||
-rw-r--r-- | components/constellation/constellation.rs | 132 | ||||
-rw-r--r-- | components/constellation/pipeline.rs | 101 |
3 files changed, 83 insertions, 152 deletions
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(()) => {}, |