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/. */
4use 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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-length>
72    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-key>
87    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-getitem>
103    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-setitem>
120    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-removeitem>
150    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    /// <https://html.spec.whatwg.org/multipage/#dom-storage-clear>
169    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    /// <https://html.spec.whatwg.org/multipage/#the-storage-interface:supported-property-names>
186    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    // check-tidy: no specs after this line
206    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    /// <https://html.spec.whatwg.org/multipage/#send-a-storage-notification>
221    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    /// <https://html.spec.whatwg.org/multipage/#send-a-storage-notification>
239    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}