Skip to main content

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