script/dom/
visualviewport.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::cell::Cell;
6
7use dom_struct::dom_struct;
8use euclid::{Rect, Scale, Size2D};
9use script_bindings::codegen::GenericBindings::VisualViewportBinding::VisualViewportMethods;
10use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
11use script_bindings::num::Finite;
12use script_bindings::root::{Dom, DomRoot};
13use script_bindings::script_runtime::CanGc;
14use style_traits::CSSPixel;
15use webrender_api::units::DevicePixel;
16
17use crate::dom::bindings::reflector::reflect_dom_object;
18use crate::dom::eventtarget::EventTarget;
19use crate::dom::window::Window;
20
21/// <https://drafts.csswg.org/cssom-view/#the-visualviewport-interface>
22#[dom_struct]
23pub(crate) struct VisualViewport {
24    eventtarget: EventTarget,
25
26    /// The associated [`Window`] for this [`VisualViewport`],
27    window: Dom<Window>,
28
29    /// The rectangle bound of the [`VisualViewport`], relative to the layout viewport.
30    #[no_trace]
31    viewport_rect: Cell<Rect<f32, CSSPixel>>,
32
33    /// The scale factor of [`VisualViewport`], which is also commonly known as pinch-zoom.
34    /// <https://drafts.csswg.org/cssom-view/#scale-factor>
35    #[no_trace]
36    scale: Cell<Scale<f32, DevicePixel, DevicePixel>>,
37}
38
39impl VisualViewport {
40    fn new_inherited(
41        window: &Window,
42        viewport_rect: Rect<f32, CSSPixel>,
43        scale: Scale<f32, DevicePixel, DevicePixel>,
44    ) -> Self {
45        Self {
46            eventtarget: EventTarget::new_inherited(),
47            window: Dom::from_ref(window),
48            viewport_rect: Cell::new(viewport_rect),
49            scale: Cell::new(scale),
50        }
51    }
52
53    /// The initial visual viewport based on a layout viewport relative to the initial containing block, where
54    /// the dimension would be the same as layout viewport leaving the offset and the scale to its default value.
55    pub(crate) fn new_from_layout_viewport(
56        window: &Window,
57        viewport_size: Size2D<f32, CSSPixel>,
58        can_gc: CanGc,
59    ) -> DomRoot<Self> {
60        reflect_dom_object(
61            Box::new(Self::new_inherited(
62                window,
63                Rect::from_size(viewport_size),
64                Scale::identity(),
65            )),
66            window,
67            can_gc,
68        )
69    }
70}
71
72impl VisualViewportMethods<crate::DomTypeHolder> for VisualViewport {
73    /// <https://drafts.csswg.org/cssom-view/#dom-visualviewport-offsetleft>
74    fn OffsetLeft(&self) -> Finite<f64> {
75        // > 1. If the visual viewport’s associated document is not fully active, return 0.
76        if !self.window.Document().is_fully_active() {
77            return Finite::wrap(0.);
78        }
79
80        // > 2. Otherwise, return the offset of the left edge of the visual viewport from the left edge of the
81        // >    layout viewport.
82        Finite::wrap(self.viewport_rect.get().min_x() as f64)
83    }
84
85    /// <https://drafts.csswg.org/cssom-view/#dom-visualviewport-offsettop>
86    fn OffsetTop(&self) -> Finite<f64> {
87        // > 1. If the visual viewport’s associated document is not fully active, return 0.
88        if !self.window.Document().is_fully_active() {
89            return Finite::wrap(0.);
90        }
91
92        // > 2. Otherwise, return the offset of the top edge of the visual viewport from the top edge of the
93        // >    layout viewport.
94        Finite::wrap(self.viewport_rect.get().min_y() as f64)
95    }
96
97    /// <https://drafts.csswg.org/cssom-view/#dom-visualviewport-pageleft>
98    fn PageLeft(&self) -> Finite<f64> {
99        // > 1. If the visual viewport’s associated document is not fully active, return 0.
100        if !self.window.Document().is_fully_active() {
101            return Finite::wrap(0.);
102        }
103
104        // > 2. Otherwise, return the offset of the left edge of the visual viewport from the left edge of the
105        // >    initial containing block of the layout viewport’s document.
106        let page_left = self.viewport_rect.get().min_x() + self.window.scroll_offset().x;
107        Finite::wrap(page_left as f64)
108    }
109
110    /// <https://drafts.csswg.org/cssom-view/#dom-visualviewport-pagetop>
111    fn PageTop(&self) -> Finite<f64> {
112        // > 1. If the visual viewport’s associated document is not fully active, return 0.
113        if !self.window.Document().is_fully_active() {
114            return Finite::wrap(0.);
115        }
116
117        // > 2. Otherwise, return the offset of the top edge of the visual viewport from the top edge of the
118        // >    initial containing block of the layout viewport’s document.
119        let page_top = self.viewport_rect.get().min_y() + self.window.scroll_offset().y;
120        Finite::wrap(page_top as f64)
121    }
122
123    /// <https://drafts.csswg.org/cssom-view/#dom-visualviewport-width>
124    fn Width(&self) -> Finite<f64> {
125        // > 1. If the visual viewport’s associated document is not fully active, return 0.
126        if !self.window.Document().is_fully_active() {
127            return Finite::wrap(0.);
128        }
129
130        // > 2. Otherwise, return the width of the visual viewport excluding the width of any rendered vertical
131        // >    classic scrollbar that is fixed to the visual viewport.
132        // TODO(#41341): when classic scrollbar is implemented, exclude it's size from visual viewport width.
133        Finite::wrap(self.viewport_rect.get().width() as f64)
134    }
135
136    /// <https://drafts.csswg.org/cssom-view/#dom-visualviewport-height>
137    fn Height(&self) -> Finite<f64> {
138        // > 1. If the visual viewport’s associated document is not fully active, return 0.
139        if !self.window.Document().is_fully_active() {
140            return Finite::wrap(0.);
141        }
142
143        // > 2. Otherwise, return the height of the visual viewport excluding the height of any rendered horizontal
144        // >    classic scrollbar that is fixed to the visual viewport.
145        // TODO(#41341): when classic scrollbar is implemented, exclude it's size from visual viewport height.
146        Finite::wrap(self.viewport_rect.get().height() as f64)
147    }
148
149    /// <https://drafts.csswg.org/cssom-view/#dom-visualviewport-scale>
150    fn Scale(&self) -> Finite<f64> {
151        // > 1. If the visual viewport’s associated document is not fully active, return 0.
152        if !self.window.Document().is_fully_active() {
153            return Finite::wrap(0.);
154        }
155
156        // > 2. If there is no output device, return 1 and abort these steps.
157        // TODO(#41341): check for output device.
158
159        // > 3. Otherwise, return the visual viewport’s scale factor.
160        Finite::wrap(self.scale.get().get() as f64)
161    }
162
163    // <https://drafts.csswg.org/cssom-view/#dom-visualviewport-onresize>
164    event_handler!(resize, GetOnresize, SetOnresize);
165
166    // <https://drafts.csswg.org/cssom-view/#dom-visualviewport-onscroll>
167    event_handler!(scroll, GetOnscroll, SetOnscroll);
168
169    // <https://drafts.csswg.org/cssom-view/#dom-visualviewport-onscrollend>
170    event_handler!(scrollend, GetOnscrollend, SetOnscrollend);
171}