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