1use dom_struct::dom_struct;
6use js::context::NoGC;
7use profile_traits::generic_channel;
8use script_bindings::reflector::{Reflector, reflect_dom_object};
9use servo_base::generic_channel::{GenericSend, SendResult};
10use servo_base::id::WebViewId;
11use servo_constellation_traits::ScriptToConstellationMessage;
12use servo_url::{ImmutableOrigin, ServoUrl};
13use storage_traits::webstorage_thread::{WebStorageThreadMsg, WebStorageType};
14
15use crate::dom::bindings::codegen::Bindings::StorageBinding::StorageMethods;
16use crate::dom::bindings::error::{Error, ErrorResult};
17use crate::dom::bindings::inheritance::Castable;
18use crate::dom::bindings::refcounted::Trusted;
19use crate::dom::bindings::reflector::DomGlobal;
20use crate::dom::bindings::root::DomRoot;
21use crate::dom::bindings::str::DOMString;
22use crate::dom::event::{Event, EventBubbles, EventCancelable};
23use crate::dom::storageevent::StorageEvent;
24use crate::dom::window::Window;
25use crate::script_runtime::CanGc;
26
27#[dom_struct]
28pub(crate) struct Storage {
29 reflector_: Reflector,
30 #[no_trace]
31 storage_type: WebStorageType,
32}
33
34impl Storage {
35 fn new_inherited(storage_type: WebStorageType) -> Storage {
36 Storage {
37 reflector_: Reflector::new(),
38 storage_type,
39 }
40 }
41
42 pub(crate) fn new(
43 global: &Window,
44 storage_type: WebStorageType,
45 can_gc: CanGc,
46 ) -> DomRoot<Storage> {
47 reflect_dom_object(
48 Box::new(Storage::new_inherited(storage_type)),
49 global,
50 can_gc,
51 )
52 }
53
54 fn webview_id(&self) -> WebViewId {
55 self.global().as_window().window_proxy().webview_id()
56 }
57
58 fn get_url(&self) -> ServoUrl {
59 self.global().get_url()
60 }
61
62 fn get_immutable_origin(&self) -> ImmutableOrigin {
63 self.global().origin().immutable().clone()
64 }
65
66 fn send_storage_msg(&self, msg: WebStorageThreadMsg) -> SendResult {
67 GenericSend::send(self.global().storage_threads(), msg)
68 }
69}
70
71impl StorageMethods<crate::DomTypeHolder> for Storage {
72 fn Length(&self) -> u32 {
74 let (sender, receiver) =
75 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
76
77 self.send_storage_msg(WebStorageThreadMsg::Length(
78 sender,
79 self.storage_type,
80 self.webview_id(),
81 self.get_immutable_origin(),
82 ))
83 .unwrap();
84 receiver.recv().unwrap() as u32
85 }
86
87 fn Key(&self, index: u32) -> Option<DOMString> {
89 let (sender, receiver) =
90 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
91
92 self.send_storage_msg(WebStorageThreadMsg::Key(
93 sender,
94 self.storage_type,
95 self.webview_id(),
96 self.get_immutable_origin(),
97 index,
98 ))
99 .unwrap();
100 receiver.recv().unwrap().map(DOMString::from)
101 }
102
103 fn GetItem(&self, name: DOMString) -> Option<DOMString> {
105 let (sender, receiver) =
106 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
107 let name = String::from(name);
108
109 let msg = WebStorageThreadMsg::GetItem(
110 sender,
111 self.storage_type,
112 self.webview_id(),
113 self.get_immutable_origin(),
114 name,
115 );
116 self.send_storage_msg(msg).unwrap();
117 receiver.recv().unwrap().map(DOMString::from)
118 }
119
120 fn SetItem(&self, name: DOMString, value: DOMString) -> ErrorResult {
122 let (sender, receiver) =
123 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
124 let name = String::from(name);
125 let value = String::from(value);
126
127 let msg = WebStorageThreadMsg::SetItem(
128 sender,
129 self.storage_type,
130 self.webview_id(),
131 self.get_immutable_origin(),
132 name.clone(),
133 value.clone(),
134 );
135 self.send_storage_msg(msg).unwrap();
136 match receiver.recv().unwrap() {
137 Err(_) => Err(Error::QuotaExceeded {
138 quota: None,
139 requested: None,
140 }),
141 Ok((changed, old_value)) => {
142 if changed {
143 self.broadcast_change_notification(Some(name), old_value, Some(value));
144 }
145 Ok(())
146 },
147 }
148 }
149
150 fn RemoveItem(&self, name: DOMString) {
152 let (sender, receiver) =
153 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
154 let name = String::from(name);
155
156 let msg = WebStorageThreadMsg::RemoveItem(
157 sender,
158 self.storage_type,
159 self.webview_id(),
160 self.get_immutable_origin(),
161 name.clone(),
162 );
163 self.send_storage_msg(msg).unwrap();
164 if let Some(old_value) = receiver.recv().unwrap() {
165 self.broadcast_change_notification(Some(name), Some(old_value), None);
166 }
167 }
168
169 fn Clear(&self) {
171 let (sender, receiver) =
172 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
173
174 self.send_storage_msg(WebStorageThreadMsg::Clear(
175 sender,
176 self.storage_type,
177 self.webview_id(),
178 self.get_immutable_origin(),
179 ))
180 .unwrap();
181 if receiver.recv().unwrap() {
182 self.broadcast_change_notification(None, None, None);
183 }
184 }
185
186 fn SupportedPropertyNames(&self, _: &NoGC) -> Vec<DOMString> {
188 let time_profiler = self.global().time_profiler_chan().clone();
189 let (sender, receiver) = generic_channel::channel(time_profiler).unwrap();
190
191 self.send_storage_msg(WebStorageThreadMsg::Keys(
192 sender,
193 self.storage_type,
194 self.webview_id(),
195 self.get_immutable_origin(),
196 ))
197 .unwrap();
198 receiver
199 .recv()
200 .unwrap()
201 .into_iter()
202 .map(DOMString::from)
203 .collect()
204 }
205
206 fn NamedGetter(&self, name: DOMString) -> Option<DOMString> {
208 self.GetItem(name)
209 }
210
211 fn NamedSetter(&self, name: DOMString, value: DOMString) -> ErrorResult {
212 self.SetItem(name, value)
213 }
214
215 fn NamedDeleter(&self, name: DOMString) {
216 self.RemoveItem(name);
217 }
218}
219
220impl Storage {
221 fn broadcast_change_notification(
223 &self,
224 key: Option<String>,
225 old_value: Option<String>,
226 new_value: Option<String>,
227 ) {
228 let storage = self.storage_type;
229 let url = self.get_url();
230 let msg = ScriptToConstellationMessage::BroadcastStorageEvent(
231 storage, url, key, old_value, new_value,
232 );
233 self.global()
234 .script_to_constellation_chan()
235 .send(msg)
236 .unwrap();
237 }
238
239 pub(crate) fn queue_storage_event(
241 &self,
242 url: ServoUrl,
243 key: Option<String>,
244 old_value: Option<String>,
245 new_value: Option<String>,
246 ) {
247 let global = self.global();
248 let this = Trusted::new(self);
249 global.task_manager().dom_manipulation_task_source().queue(
250 task!(send_storage_notification: move |cx| {
251 let this = this.root();
252 let global = this.global();
253 let event = StorageEvent::new(
254 global.as_window(),
255 atom!("storage"),
256 EventBubbles::DoesNotBubble,
257 EventCancelable::NotCancelable,
258 key.map(DOMString::from),
259 old_value.map(DOMString::from),
260 new_value.map(DOMString::from),
261 DOMString::from(url.into_string()),
262 Some(&this),
263 CanGc::from_cx(cx)
264 );
265 event.upcast::<Event>().fire(cx, global.upcast());
266 }),
267 );
268 }
269}