playwright_rs/protocol/web_storage.rs
1//! WebStorage: per-origin `localStorage` / `sessionStorage` access.
2//!
3//! Obtained via [`Page::local_storage`](crate::protocol::Page::local_storage) /
4//! [`Page::session_storage`](crate::protocol::Page::session_storage). Reads and
5//! writes the current origin's storage directly through the Page channel (not via
6//! `page.evaluate`), matching playwright-python's `WebStorage`.
7//!
8//! ```no_run
9//! # use playwright_rs::Playwright;
10//! # async fn ex() -> playwright_rs::Result<()> {
11//! # let pw = Playwright::launch().await?;
12//! # let browser = pw.chromium().launch().await?;
13//! # let page = browser.new_page().await?;
14//! page.goto("https://example.com", None).await?;
15//! let storage = page.local_storage();
16//! storage.set_item("token", "abc123").await?;
17//! assert_eq!(storage.get_item("token").await?, Some("abc123".to_string()));
18//! # Ok(())
19//! # }
20//! ```
21//!
22//! See: <https://playwright.dev/docs/api/class-webstorage>
23
24use crate::error::Result;
25use crate::server::channel::Channel;
26use serde_json::json;
27
28/// Which storage area a [`WebStorage`] handle targets.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum WebStorageKind {
31 /// `window.localStorage` — persists across sessions for the origin.
32 Local,
33 /// `window.sessionStorage` — cleared when the tab/context closes.
34 Session,
35}
36
37impl WebStorageKind {
38 pub(crate) fn as_str(self) -> &'static str {
39 match self {
40 WebStorageKind::Local => "local",
41 WebStorageKind::Session => "session",
42 }
43 }
44}
45
46/// Read/write access to a page's `localStorage` or `sessionStorage` for the
47/// current origin.
48///
49/// Obtained from [`Page::local_storage`](crate::protocol::Page::local_storage)
50/// and [`Page::session_storage`](crate::protocol::Page::session_storage).
51///
52/// See: <https://playwright.dev/docs/api/class-webstorage>
53#[derive(Clone)]
54pub struct WebStorage {
55 channel: Channel,
56 kind: WebStorageKind,
57}
58
59impl std::fmt::Debug for WebStorage {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("WebStorage")
62 .field("kind", &self.kind)
63 .finish_non_exhaustive()
64 }
65}
66
67impl WebStorage {
68 pub(crate) fn new(channel: Channel, kind: WebStorageKind) -> Self {
69 Self { channel, kind }
70 }
71
72 /// Returns the value for `name`, or `None` if the key is not set.
73 ///
74 /// # Errors
75 ///
76 /// Returns error if:
77 /// - The page has been closed
78 /// - Communication with the browser process fails
79 ///
80 /// See: <https://playwright.dev/docs/api/class-webstorage#web-storage-get-item>
81 pub async fn get_item(&self, name: &str) -> Result<Option<String>> {
82 #[derive(serde::Deserialize)]
83 struct R {
84 #[serde(default)]
85 value: Option<String>,
86 }
87 let r: R = self
88 .channel
89 .send(
90 "webStorageGetItem",
91 json!({ "kind": self.kind.as_str(), "name": name }),
92 )
93 .await?;
94 Ok(r.value)
95 }
96
97 /// Sets `name` to `value`.
98 ///
99 /// # Errors
100 ///
101 /// Returns error if:
102 /// - The page has been closed
103 /// - Communication with the browser process fails
104 ///
105 /// See: <https://playwright.dev/docs/api/class-webstorage#web-storage-set-item>
106 pub async fn set_item(&self, name: &str, value: &str) -> Result<()> {
107 self.channel
108 .send_no_result(
109 "webStorageSetItem",
110 json!({ "kind": self.kind.as_str(), "name": name, "value": value }),
111 )
112 .await
113 }
114
115 /// Removes `name` from storage (no-op if absent).
116 ///
117 /// # Errors
118 ///
119 /// Returns error if:
120 /// - The page has been closed
121 /// - Communication with the browser process fails
122 ///
123 /// See: <https://playwright.dev/docs/api/class-webstorage#web-storage-remove-item>
124 pub async fn remove_item(&self, name: &str) -> Result<()> {
125 self.channel
126 .send_no_result(
127 "webStorageRemoveItem",
128 json!({ "kind": self.kind.as_str(), "name": name }),
129 )
130 .await
131 }
132
133 /// Removes all entries from this storage area.
134 ///
135 /// # Errors
136 ///
137 /// Returns error if:
138 /// - The page has been closed
139 /// - Communication with the browser process fails
140 ///
141 /// See: <https://playwright.dev/docs/api/class-webstorage#web-storage-clear>
142 pub async fn clear(&self) -> Result<()> {
143 self.channel
144 .send_no_result("webStorageClear", json!({ "kind": self.kind.as_str() }))
145 .await
146 }
147
148 /// Returns all `(name, value)` entries currently in this storage area.
149 ///
150 /// # Errors
151 ///
152 /// Returns error if:
153 /// - The page has been closed
154 /// - Communication with the browser process fails
155 ///
156 /// See: <https://playwright.dev/docs/api/class-webstorage#web-storage-items>
157 pub async fn items(&self) -> Result<Vec<(String, String)>> {
158 #[derive(serde::Deserialize)]
159 struct Item {
160 name: String,
161 value: String,
162 }
163 #[derive(serde::Deserialize)]
164 struct R {
165 items: Vec<Item>,
166 }
167 let r: R = self
168 .channel
169 .send("webStorageItems", json!({ "kind": self.kind.as_str() }))
170 .await?;
171 Ok(r.items.into_iter().map(|i| (i.name, i.value)).collect())
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::WebStorageKind;
178
179 #[test]
180 fn kind_as_str_maps_each_variant() {
181 assert_eq!(WebStorageKind::Local.as_str(), "local");
182 assert_eq!(WebStorageKind::Session.as_str(), "session");
183 }
184}