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