1use super::page_core::Page;
2use crate::error::Result;
3use crate::network::cookies::{Cookie, CookieManager, SetCookieParams};
4use crate::storage::manager::{StorageManager, StorageType};
5use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct PageSession {
13 pub url: String,
15 pub cookies: Vec<Cookie>,
17 pub local_storage: HashMap<String, String>,
19 pub session_storage: HashMap<String, String>,
21 pub timestamp: u64,
23}
24
25impl PageSession {
26 pub fn new(url: String) -> Self {
28 Self {
29 url,
30 cookies: Vec::new(),
31 local_storage: HashMap::new(),
32 session_storage: HashMap::new(),
33 timestamp: std::time::SystemTime::now()
34 .duration_since(std::time::UNIX_EPOCH)
35 .unwrap_or_default()
36 .as_secs(),
37 }
38 }
39
40 pub fn to_json(&self) -> Result<String> {
42 Ok(serde_json::to_string_pretty(self)?)
43 }
44
45 pub fn from_json(json: &str) -> Result<Self> {
47 Ok(serde_json::from_str(json)?)
48 }
49
50 pub fn save_to_file(&self, path: &std::path::Path) -> Result<()> {
52 let json = self.to_json()?;
53 std::fs::write(path, json)?;
54 Ok(())
55 }
56
57 pub fn load_from_file(path: &std::path::Path) -> Result<Self> {
59 let json = std::fs::read_to_string(path)?;
60 Self::from_json(&json)
61 }
62}
63
64#[async_trait]
66pub trait PageSessionManager {
67 async fn export_session(self: &Arc<Self>) -> Result<PageSession>;
69
70 async fn import_session(self: &Arc<Self>, session: &PageSession) -> Result<()>;
73
74 async fn export_session_to_file(
76 self: &Arc<Self>,
77 path: &std::path::Path,
78 ) -> Result<PageSession>;
79
80 async fn import_session_from_file(self: &Arc<Self>, path: &std::path::Path) -> Result<()>;
82}
83
84#[async_trait]
85impl PageSessionManager for Page {
86 async fn export_session(self: &Arc<Self>) -> Result<PageSession> {
87 let main_frame = self.main_frame().await?;
89 let url = main_frame.url().await?;
90
91 let cookies = self.get_cookies(None).await?;
93
94 let local_storage_items = self.get_storage_items(StorageType::Local).await?;
96 let local_storage: HashMap<String, String> = local_storage_items
97 .into_iter()
98 .map(|item| (item.key, item.value))
99 .collect();
100
101 let session_storage_items = self.get_storage_items(StorageType::Session).await?;
103 let session_storage: HashMap<String, String> = session_storage_items
104 .into_iter()
105 .map(|item| (item.key, item.value))
106 .collect();
107
108 Ok(PageSession {
109 url,
110 cookies,
111 local_storage,
112 session_storage,
113 timestamp: std::time::SystemTime::now()
114 .duration_since(std::time::UNIX_EPOCH)
115 .unwrap_or_default()
116 .as_secs(),
117 })
118 }
119
120 async fn import_session(self: &Arc<Self>, session: &PageSession) -> Result<()> {
121 self.navigate(&session.url).await?;
123
124 self.clear_browser_cookies().await?;
126 self.clear_storage(StorageType::Local).await?;
127 self.clear_storage(StorageType::Session).await?;
128
129 for cookie in &session.cookies {
131 let set_cookie_params = SetCookieParams {
132 name: cookie.name.clone(),
133 value: cookie.value.clone(),
134 url: Some(session.url.clone()),
135 domain: Some(cookie.domain.clone()),
136 path: Some(cookie.path.clone()),
137 secure: Some(cookie.secure),
138 http_only: Some(cookie.http_only),
139 same_site: cookie.same_site.clone(),
140 expires: Some(cookie.expires),
141 priority: Some(cookie.priority.clone()),
142 };
143 self.set_cookie(set_cookie_params).await?;
144 }
145
146 for (key, value) in &session.local_storage {
148 self.set_storage_item(StorageType::Local, key, value)
149 .await?;
150 }
151
152 for (key, value) in &session.session_storage {
154 self.set_storage_item(StorageType::Session, key, value)
155 .await?;
156 }
157
158 Ok(())
159 }
160
161 async fn export_session_to_file(
162 self: &Arc<Self>,
163 path: &std::path::Path,
164 ) -> Result<PageSession> {
165 let session = self.export_session().await?;
166 session.save_to_file(path)?;
167 Ok(session)
168 }
169
170 async fn import_session_from_file(self: &Arc<Self>, path: &std::path::Path) -> Result<()> {
171 let session = PageSession::load_from_file(path)?;
172 self.import_session(&session).await?;
173 Ok(())
174 }
175}
176
177#[async_trait]
179pub trait PageSessionSnapshot {
180 async fn snapshot(self: &Arc<Self>) -> Result<PageSession>;
182
183 async fn restore(self: &Arc<Self>, snapshot: &PageSession) -> Result<()>;
185
186 async fn clone_session_to(self: &Arc<Self>, target: &Arc<Page>) -> Result<()>;
188}
189
190#[async_trait]
191impl PageSessionSnapshot for Page {
192 async fn snapshot(self: &Arc<Self>) -> Result<PageSession> {
193 self.export_session().await
194 }
195
196 async fn restore(self: &Arc<Self>, snapshot: &PageSession) -> Result<()> {
197 self.import_session(snapshot).await
198 }
199
200 async fn clone_session_to(self: &Arc<Self>, target: &Arc<Page>) -> Result<()> {
201 let session = self.export_session().await?;
202 target.import_session(&session).await?;
203 Ok(())
204 }
205}