Skip to main content

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