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