script/dom/
storage.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4use 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    // https://html.spec.whatwg.org/multipage/#dom-storage-length
66    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    // https://html.spec.whatwg.org/multipage/#dom-storage-key
81    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    // https://html.spec.whatwg.org/multipage/#dom-storage-getitem
97    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    // https://html.spec.whatwg.org/multipage/#dom-storage-setitem
114    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    // https://html.spec.whatwg.org/multipage/#dom-storage-removeitem
144    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    // https://html.spec.whatwg.org/multipage/#dom-storage-clear
163    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    // https://html.spec.whatwg.org/multipage/#the-storage-interface:supported-property-names
180    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    // check-tidy: no specs after this line
200    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    /// <https://html.spec.whatwg.org/multipage/#send-a-storage-notification>
215    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    /// <https://html.spec.whatwg.org/multipage/#send-a-storage-notification>
233    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}