1use std::cmp::PartialEq;
6use std::fmt;
7
8use base::id::{BrowsingContextId, HistoryStateId, PipelineId, WebViewId};
9use constellation_traits::LoadData;
10use embedder_traits::ViewportDetails;
11use log::debug;
12use servo_url::ServoUrl;
13
14use crate::browsingcontext::NewBrowsingContextInfo;
15
16#[derive(Debug)]
19pub struct JointSessionHistory {
20 pub past: Vec<SessionHistoryDiff>,
23
24 pub future: Vec<SessionHistoryDiff>,
27}
28
29impl JointSessionHistory {
30 pub fn new() -> JointSessionHistory {
31 JointSessionHistory {
32 past: Vec::new(),
33 future: Vec::new(),
34 }
35 }
36
37 pub fn history_length(&self) -> usize {
38 self.past.len() + 1 + self.future.len()
39 }
40
41 pub fn push_diff(&mut self, diff: SessionHistoryDiff) -> Vec<SessionHistoryDiff> {
42 debug!("pushing a past entry; removing future");
43 self.past.push(diff);
44 std::mem::take(&mut self.future)
45 }
46
47 pub fn replace_reloader(&mut self, old_reloader: NeedsToReload, new_reloader: NeedsToReload) {
48 for diff in self.past.iter_mut().chain(self.future.iter_mut()) {
49 diff.replace_reloader(&old_reloader, &new_reloader);
50 }
51 }
52
53 pub fn replace_history_state(
54 &mut self,
55 pipeline_id: PipelineId,
56 history_state_id: HistoryStateId,
57 url: ServoUrl,
58 ) {
59 if let Some(SessionHistoryDiff::Pipeline {
60 new_history_state_id,
61 new_url,
62 ..
63 }) = self.past.iter_mut().find(|diff| match diff {
64 SessionHistoryDiff::Pipeline {
65 pipeline_reloader: NeedsToReload::No(id),
66 ..
67 } => pipeline_id == *id,
68 _ => false,
69 }) {
70 *new_history_state_id = history_state_id;
71 *new_url = url.clone();
72 }
73
74 if let Some(SessionHistoryDiff::Pipeline {
75 old_history_state_id,
76 old_url,
77 ..
78 }) = self.future.iter_mut().find(|diff| match diff {
79 SessionHistoryDiff::Pipeline {
80 pipeline_reloader: NeedsToReload::No(id),
81 ..
82 } => pipeline_id == *id,
83 _ => false,
84 }) {
85 *old_history_state_id = Some(history_state_id);
86 *old_url = url;
87 }
88 }
89
90 pub fn remove_entries_for_browsing_context(&mut self, context_id: BrowsingContextId) {
91 debug!("{}: Removing entries for browsing context", context_id);
92 self.past.retain(|diff| match diff {
93 SessionHistoryDiff::BrowsingContext {
94 browsing_context_id,
95 ..
96 } => *browsing_context_id != context_id,
97 _ => true,
98 });
99 self.future.retain(|diff| match diff {
100 SessionHistoryDiff::BrowsingContext {
101 browsing_context_id,
102 ..
103 } => *browsing_context_id != context_id,
104 _ => true,
105 });
106 }
107}
108
109#[derive(Debug)]
112pub struct SessionHistoryChange {
113 pub browsing_context_id: BrowsingContextId,
115
116 pub webview_id: WebViewId,
118
119 pub new_pipeline_id: PipelineId,
121
122 pub replace: Option<NeedsToReload>,
124
125 pub new_browsing_context_info: Option<NewBrowsingContextInfo>,
128
129 pub viewport_details: ViewportDetails,
131}
132
133#[expect(clippy::large_enum_variant)]
136#[derive(Clone, Debug)]
137pub enum NeedsToReload {
138 No(PipelineId),
140 Yes(PipelineId, LoadData),
143}
144
145impl fmt::Display for NeedsToReload {
146 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
147 match *self {
148 NeedsToReload::No(pipeline_id) => write!(fmt, "Alive({})", pipeline_id),
149 NeedsToReload::Yes(pipeline_id, ..) => write!(fmt, "Dead({})", pipeline_id),
150 }
151 }
152}
153
154impl NeedsToReload {
155 pub fn alive_pipeline_id(&self) -> Option<PipelineId> {
156 match *self {
157 NeedsToReload::No(pipeline_id) => Some(pipeline_id),
158 NeedsToReload::Yes(..) => None,
159 }
160 }
161}
162
163impl PartialEq for NeedsToReload {
165 fn eq(&self, other: &NeedsToReload) -> bool {
166 match *self {
167 NeedsToReload::No(pipeline_id) => match *other {
168 NeedsToReload::No(other_pipeline_id) => pipeline_id == other_pipeline_id,
169 _ => false,
170 },
171 NeedsToReload::Yes(pipeline_id, _) => match *other {
172 NeedsToReload::Yes(other_pipeline_id, _) => pipeline_id == other_pipeline_id,
173 _ => false,
174 },
175 }
176 }
177}
178
179#[expect(clippy::large_enum_variant)]
182#[derive(Debug)]
183pub enum SessionHistoryDiff {
184 BrowsingContext {
186 browsing_context_id: BrowsingContextId,
188 old_reloader: NeedsToReload,
190 new_reloader: NeedsToReload,
192 },
193 Pipeline {
195 pipeline_reloader: NeedsToReload,
197 old_history_state_id: Option<HistoryStateId>,
199 old_url: ServoUrl,
201 new_history_state_id: HistoryStateId,
203 new_url: ServoUrl,
205 },
206 Hash {
207 pipeline_reloader: NeedsToReload,
208 old_url: ServoUrl,
209 new_url: ServoUrl,
210 },
211}
212
213impl SessionHistoryDiff {
214 pub fn alive_old_pipeline(&self) -> Option<PipelineId> {
216 match *self {
217 SessionHistoryDiff::BrowsingContext {
218 ref old_reloader, ..
219 } => match *old_reloader {
220 NeedsToReload::No(pipeline_id) => Some(pipeline_id),
221 NeedsToReload::Yes(..) => None,
222 },
223 _ => None,
224 }
225 }
226
227 pub fn alive_new_pipeline(&self) -> Option<PipelineId> {
229 match *self {
230 SessionHistoryDiff::BrowsingContext {
231 ref new_reloader, ..
232 } => match *new_reloader {
233 NeedsToReload::No(pipeline_id) => Some(pipeline_id),
234 NeedsToReload::Yes(..) => None,
235 },
236 _ => None,
237 }
238 }
239
240 pub fn replace_reloader(
242 &mut self,
243 replaced_reloader: &NeedsToReload,
244 reloader: &NeedsToReload,
245 ) {
246 match *self {
247 SessionHistoryDiff::BrowsingContext {
248 ref mut old_reloader,
249 ref mut new_reloader,
250 ..
251 } => {
252 if *old_reloader == *replaced_reloader {
253 *old_reloader = reloader.clone();
254 }
255 if *new_reloader == *replaced_reloader {
256 *new_reloader = reloader.clone();
257 }
258 },
259 SessionHistoryDiff::Pipeline {
260 ref mut pipeline_reloader,
261 ..
262 } => {
263 if *pipeline_reloader == *replaced_reloader {
264 *pipeline_reloader = reloader.clone();
265 }
266 },
267 SessionHistoryDiff::Hash {
268 ref mut pipeline_reloader,
269 ..
270 } => {
271 if *pipeline_reloader == *replaced_reloader {
272 *pipeline_reloader = reloader.clone();
273 }
274 },
275 }
276 }
277}