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