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 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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-length>
70    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-key>
85    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-getitem>
101    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-setitem>
118    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-removeitem>
148    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-clear>
167    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    /// <https://html.spec.whatwg.org/multipage/#the-storage-interface:supported-property-names>
184    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    // check-tidy: no specs after this line
204    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    /// <https://html.spec.whatwg.org/multipage/#send-a-storage-notification>
219    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    /// <https://html.spec.whatwg.org/multipage/#send-a-storage-notification>
237    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}