1use std::collections::HashSet;
6use std::rc::Rc;
7
8use devtools_traits::WorkerId;
9use embedder_traits::{AnimationState, FocusSequenceNumber};
10use layout_api::ScriptThreadFactory;
11use log::{debug, error, warn};
12use paint_api::{CompositionPipeline, PaintMessage, PaintProxy};
13use rustc_hash::FxHashSet;
14use script_traits::{
15 DiscardBrowsingContext, DocumentActivity, NewPipelineInfo, ScriptThreadMessage,
16};
17use servo_base::generic_channel::SendError;
18use servo_base::id::{BrowsingContextId, HistoryStateId, PipelineId, WebViewId};
19use servo_constellation_traits::{LoadData, ServiceWorkerManagerFactory};
20use servo_url::ServoUrl;
21
22use crate::Constellation;
23use crate::event_loop::EventLoop;
24
25pub struct Pipeline {
28 pub id: PipelineId,
30
31 pub browsing_context_id: BrowsingContextId,
33
34 pub webview_id: WebViewId,
36
37 pub opener: Option<BrowsingContextId>,
38
39 pub event_loop: Rc<EventLoop>,
41
42 pub paint_proxy: PaintProxy,
44
45 pub url: ServoUrl,
49
50 pub animation_state: AnimationState,
53
54 pub document_callbacks_active: bool,
56
57 pub worker_callbacks_active: FxHashSet<WorkerId>,
59
60 pub last_callbacks_active_sent_to_paint: bool,
62
63 pub children: Vec<BrowsingContextId>,
65
66 pub load_data: LoadData,
68
69 pub history_state_id: Option<HistoryStateId>,
71
72 pub history_states: HashSet<HistoryStateId>,
74
75 pub completely_loaded: bool,
77
78 pub title: String,
80
81 pub focus_sequence: FocusSequenceNumber,
82}
83
84impl Pipeline {
85 pub(crate) fn spawn<STF: ScriptThreadFactory, SWF: ServiceWorkerManagerFactory>(
87 new_pipeline_info: NewPipelineInfo,
88 event_loop: Rc<EventLoop>,
89 constellation: &Constellation<STF, SWF>,
90 throttled: bool,
91 ) -> Result<Self, SendError> {
92 if let Err(error) = event_loop.send(ScriptThreadMessage::SpawnPipeline(
93 new_pipeline_info.clone(),
94 )) {
95 error!("Could not spawn Pipeline in EventLoop: {error}");
96 return Err(error);
97 }
98
99 Ok(Self::new_already_spawned(
100 new_pipeline_info.new_pipeline_id,
101 new_pipeline_info.browsing_context_id,
102 new_pipeline_info.webview_id,
103 new_pipeline_info.opener,
104 event_loop,
105 constellation.paint_proxy.clone(),
106 throttled,
107 new_pipeline_info.load_data,
108 ))
109 }
110
111 #[expect(clippy::too_many_arguments)]
113 pub fn new_already_spawned(
114 id: PipelineId,
115 browsing_context_id: BrowsingContextId,
116 webview_id: WebViewId,
117 opener: Option<BrowsingContextId>,
118 event_loop: Rc<EventLoop>,
119 paint_proxy: PaintProxy,
120 throttled: bool,
121 load_data: LoadData,
122 ) -> Self {
123 let pipeline = Self {
124 id,
125 browsing_context_id,
126 webview_id,
127 opener,
128 event_loop,
129 paint_proxy,
130 url: load_data.url.clone(),
131 children: vec![],
132 animation_state: AnimationState::NoAnimationsPresent,
133 document_callbacks_active: false,
134 worker_callbacks_active: FxHashSet::default(),
135 last_callbacks_active_sent_to_paint: false,
136 load_data,
137 history_state_id: None,
138 history_states: HashSet::new(),
139 completely_loaded: false,
140 title: String::new(),
141 focus_sequence: FocusSequenceNumber::default(),
142 };
143 pipeline.set_throttled(throttled);
144 pipeline
145 }
146
147 pub fn send_exit_message_to_script(&self, discard_bc: DiscardBrowsingContext) {
151 debug!("{:?} Sending exit message to script", self.id);
152
153 if let Err(error) = self.event_loop.send(ScriptThreadMessage::ExitPipeline(
156 self.webview_id,
157 self.id,
158 discard_bc,
159 )) {
160 warn!("Sending script exit message failed ({error}).");
161 }
162 }
163
164 pub fn set_activity(&self, activity: DocumentActivity) {
166 let msg = ScriptThreadMessage::SetDocumentActivity(self.id, activity);
167 if let Err(e) = self.event_loop.send(msg) {
168 warn!("Sending activity message failed ({}).", e);
169 }
170 }
171
172 pub fn to_sendable(&self) -> CompositionPipeline {
174 CompositionPipeline {
175 id: self.id,
176 webview_id: self.webview_id,
177 }
178 }
179
180 pub fn add_child(&mut self, browsing_context_id: BrowsingContextId) {
182 self.children.push(browsing_context_id);
183 }
184
185 pub fn remove_child(&mut self, browsing_context_id: BrowsingContextId) {
187 match self
188 .children
189 .iter()
190 .position(|id| *id == browsing_context_id)
191 {
192 None => {
193 warn!(
194 "Pipeline remove child already removed ({:?}).",
195 browsing_context_id
196 )
197 },
198 Some(index) => {
199 self.children.remove(index);
200 },
201 }
202 }
203
204 pub fn set_throttled(&self, throttled: bool) {
207 let script_msg = ScriptThreadMessage::SetThrottled(self.webview_id, self.id, throttled);
208 let paint_message = PaintMessage::SetThrottled(self.webview_id, self.id, throttled);
209 let err = self.event_loop.send(script_msg);
210 if let Err(e) = err {
211 warn!("Sending SetThrottled to script failed ({}).", e);
212 }
213 self.paint_proxy.send(paint_message);
214 }
215}