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