1use std::collections::HashSet;
6use std::rc::Rc;
7
8use base::id::{BrowsingContextId, HistoryStateId, PipelineId, WebViewId};
9use compositing_traits::{CompositionPipeline, CompositorMsg, CompositorProxy};
10use constellation_traits::{LoadData, ServiceWorkerManagerFactory};
11use embedder_traits::{AnimationState, FocusSequenceNumber};
12use ipc_channel::Error;
13use layout_api::ScriptThreadFactory;
14use log::{debug, error, warn};
15use script_traits::{
16 DiscardBrowsingContext, DocumentActivity, NewPipelineInfo, ScriptThreadMessage,
17};
18use servo_url::ServoUrl;
19
20use crate::Constellation;
21use crate::event_loop::EventLoop;
22
23pub struct Pipeline {
26 pub id: PipelineId,
28
29 pub browsing_context_id: BrowsingContextId,
31
32 pub webview_id: WebViewId,
34
35 pub opener: Option<BrowsingContextId>,
36
37 pub event_loop: Rc<EventLoop>,
39
40 pub compositor_proxy: CompositorProxy,
42
43 pub url: ServoUrl,
47
48 pub animation_state: AnimationState,
51
52 pub children: Vec<BrowsingContextId>,
54
55 pub load_data: LoadData,
57
58 pub history_state_id: Option<HistoryStateId>,
60
61 pub history_states: HashSet<HistoryStateId>,
63
64 pub completely_loaded: bool,
66
67 pub title: String,
69
70 pub focus_sequence: FocusSequenceNumber,
71}
72
73impl Pipeline {
74 pub(crate) fn spawn<STF: ScriptThreadFactory, SWF: ServiceWorkerManagerFactory>(
76 new_pipeline_info: NewPipelineInfo,
77 event_loop: Rc<EventLoop>,
78 constellation: &Constellation<STF, SWF>,
79 throttled: bool,
80 ) -> Result<Self, Error> {
81 if let Err(error) = event_loop.send(ScriptThreadMessage::SpawnPipeline(
82 new_pipeline_info.clone(),
83 )) {
84 error!("Could not spawn Pipeline in EventLoop: {error}");
85 return Err(error);
86 }
87
88 Ok(Self::new_already_spawned(
89 new_pipeline_info.new_pipeline_id,
90 new_pipeline_info.browsing_context_id,
91 new_pipeline_info.webview_id,
92 new_pipeline_info.opener,
93 event_loop,
94 constellation.compositor_proxy.clone(),
95 throttled,
96 new_pipeline_info.load_data,
97 ))
98 }
99
100 #[allow(clippy::too_many_arguments)]
102 pub fn new_already_spawned(
103 id: PipelineId,
104 browsing_context_id: BrowsingContextId,
105 webview_id: WebViewId,
106 opener: Option<BrowsingContextId>,
107 event_loop: Rc<EventLoop>,
108 compositor_proxy: CompositorProxy,
109 throttled: bool,
110 load_data: LoadData,
111 ) -> Self {
112 let pipeline = Self {
113 id,
114 browsing_context_id,
115 webview_id,
116 opener,
117 event_loop,
118 compositor_proxy,
119 url: load_data.url.clone(),
120 children: vec![],
121 animation_state: AnimationState::NoAnimationsPresent,
122 load_data,
123 history_state_id: None,
124 history_states: HashSet::new(),
125 completely_loaded: false,
126 title: String::new(),
127 focus_sequence: FocusSequenceNumber::default(),
128 };
129 pipeline.set_throttled(throttled);
130 pipeline
131 }
132
133 pub fn send_exit_message_to_script(&self, discard_bc: DiscardBrowsingContext) {
137 debug!("{:?} Sending exit message to script", self.id);
138
139 if let Err(error) = self.event_loop.send(ScriptThreadMessage::ExitPipeline(
142 self.webview_id,
143 self.id,
144 discard_bc,
145 )) {
146 warn!("Sending script exit message failed ({error}).");
147 }
148 }
149
150 pub fn set_activity(&self, activity: DocumentActivity) {
152 let msg = ScriptThreadMessage::SetDocumentActivity(self.id, activity);
153 if let Err(e) = self.event_loop.send(msg) {
154 warn!("Sending activity message failed ({}).", e);
155 }
156 }
157
158 pub fn to_sendable(&self) -> CompositionPipeline {
160 CompositionPipeline {
161 id: self.id,
162 webview_id: self.webview_id,
163 }
164 }
165
166 pub fn add_child(&mut self, browsing_context_id: BrowsingContextId) {
168 self.children.push(browsing_context_id);
169 }
170
171 pub fn remove_child(&mut self, browsing_context_id: BrowsingContextId) {
173 match self
174 .children
175 .iter()
176 .position(|id| *id == browsing_context_id)
177 {
178 None => {
179 warn!(
180 "Pipeline remove child already removed ({:?}).",
181 browsing_context_id
182 )
183 },
184 Some(index) => {
185 self.children.remove(index);
186 },
187 }
188 }
189
190 pub fn set_throttled(&self, throttled: bool) {
193 let script_msg = ScriptThreadMessage::SetThrottled(self.webview_id, self.id, throttled);
194 let compositor_msg = CompositorMsg::SetThrottled(self.webview_id, self.id, throttled);
195 let err = self.event_loop.send(script_msg);
196 if let Err(e) = err {
197 warn!("Sending SetThrottled to script failed ({}).", e);
198 }
199 self.compositor_proxy.send(compositor_msg);
200 }
201}