Skip to main content

script/dom/closewatcher/
closewatcher.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 std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::gc::HandleObject;
10use script_bindings::cell::DomRefCell;
11use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
12use script_bindings::error::{Error, Fallible};
13use script_bindings::reflector::reflect_dom_object_with_proto;
14use script_bindings::root::DomRoot;
15
16use crate::dom::abortsignal::AbortAlgorithm;
17use crate::dom::bindings::codegen::Bindings::CloseWatcherBinding::{
18    CloseWatcherMethods, CloseWatcherOptions,
19};
20use crate::dom::bindings::codegen::DomTypeHolder::DomTypeHolder;
21use crate::dom::eventtarget::EventTarget;
22use crate::dom::internalclosewatcher::{InternalCloseWatcher, InternalCloseWatcherOwner};
23use crate::dom::window::Window;
24
25#[dom_struct]
26pub(crate) struct CloseWatcher {
27    eventtarget: EventTarget,
28    /// <https://html.spec.whatwg.org/multipage/#internal-close-watcher>
29    #[conditional_malloc_size_of]
30    internal_close_watcher: DomRefCell<Option<Rc<InternalCloseWatcher>>>,
31}
32
33impl CloseWatcher {
34    pub(crate) fn new_inherited() -> CloseWatcher {
35        Self {
36            eventtarget: EventTarget::new_inherited(),
37            internal_close_watcher: Default::default(),
38        }
39    }
40
41    pub(crate) fn new_with_proto(
42        cx: &mut JSContext,
43        window: &Window,
44        proto: Option<HandleObject>,
45    ) -> DomRoot<CloseWatcher> {
46        reflect_dom_object_with_proto(cx, Box::new(CloseWatcher::new_inherited()), window, proto)
47    }
48}
49
50impl CloseWatcherMethods<DomTypeHolder> for CloseWatcher {
51    /// <https://html.spec.whatwg.org/multipage/#dom-closewatcher>
52    fn Constructor(
53        cx: &mut JSContext,
54        window: &Window,
55        proto: Option<HandleObject>,
56        options: &CloseWatcherOptions,
57    ) -> Fallible<DomRoot<CloseWatcher>> {
58        // Step 1. If this's relevant global object's associated Document is not fully active, then
59        // throw an "InvalidStateError" DOMException.
60        if !window.Document().is_fully_active() {
61            return Err(Error::InvalidState(Some("Failed to construct 'CloseWatcher': CloseWatchers cannot be created in detached Windows.".into())));
62        }
63        // Step 2. Let closeWatcher be the result of establishing a close watcher given this's
64        // relevant global object, with:
65        // - cancelAction given canPreventClose being to return the result of firing an event named
66        //   cancel at this, with the cancelable attribute initialized to canPreventClose.
67        // - closeAction being to fire an event named close at this.
68        // - getEnabledState being to return true.
69        let close_watcher = CloseWatcher::new_with_proto(cx, window, proto);
70        let internal_close_watcher = InternalCloseWatcher::establish(
71            cx.no_gc(),
72            window,
73            &InternalCloseWatcherOwner::CloseWatcher(close_watcher.as_traced()),
74        );
75        // Step 3. If options["signal"] exists, then:
76        if let Some(signal) = options.signal.as_ref() {
77            // Step 3.1. If options["signal"] is aborted, then destroy closeWatcher.
78            if signal.aborted() {
79                internal_close_watcher.destroy(cx.no_gc())
80            }
81            // Step 3.2. Add the following steps to options["signal"]:
82            // Step 3.2.1. Destroy closeWatcher.
83            signal.add(&AbortAlgorithm::CloseWatcher(
84                internal_close_watcher.clone(),
85            ));
86        }
87        // Step 4. Set this's internal close watcher to closeWatcher.
88        close_watcher
89            .internal_close_watcher
90            .safe_borrow_mut(cx.no_gc())
91            .replace(internal_close_watcher);
92        Ok(close_watcher)
93    }
94
95    /// <https://html.spec.whatwg.org/multipage/#dom-closewatcher-requestclose>
96    fn RequestClose(&self, cx: &mut JSContext) {
97        // > The requestClose() method steps are to request to close this's internal close watcher
98        // > with false.
99        if let Some(internal) = &*self.internal_close_watcher.borrow() {
100            internal.request_to_close(cx, false);
101        }
102    }
103
104    /// <https://html.spec.whatwg.org/multipage/#dom-closewatcher-close>
105    fn Close(&self, cx: &mut JSContext) {
106        // > The close() method steps are to close this's internal close watcher.
107        if let Some(internal) = &*self.internal_close_watcher.borrow() {
108            internal.close(cx);
109        }
110    }
111
112    /// <https://html.spec.whatwg.org/multipage/#dom-closewatcher-destroy>
113    fn Destroy(&self, cx: &mut JSContext) {
114        // > The destroy() method steps are to destroy this's internal close watcher.
115        if let Some(internal) = &*self.internal_close_watcher.borrow() {
116            internal.destroy(cx.no_gc());
117        }
118    }
119
120    // <https://html.spec.whatwg.org/multipage/#handler-closewatcher-oncancel>
121    event_handler!(cancel, GetOncancel, SetOncancel);
122    // <https://html.spec.whatwg.org/multipage/#handler-closewatcher-onclose>
123    event_handler!(close, GetOnclose, SetOnclose);
124}