Merge pull request #2666 from mbrubeck/pinch

Separate "desktop" and "mobile" zoom calculations.
This commit is contained in:
Lars Bergstrom 2014-06-21 08:06:26 -05:00
commit 6c150724f4
13 changed files with 149 additions and 66 deletions

View file

@ -12,6 +12,7 @@ use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEve
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
use windowing::PinchZoomWindowEvent;
use azure::azure_hl::{SourceSurfaceMethods, Color};
use azure::azure_hl;
@ -30,9 +31,9 @@ use png;
use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet};
use servo_msg::compositor_msg::{LayerId, ReadyState, RenderState, ScrollPolicy, Scrollable};
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, NavigateMsg};
use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg};
use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg, WindowSizeData};
use servo_msg::constellation_msg;
use servo_util::geometry::{DevicePixel, PagePx, ScreenPx};
use servo_util::geometry::{DevicePixel, PagePx, ScreenPx, ViewportPx};
use servo_util::opts::Opts;
use servo_util::time::{profile, ProfilerChan};
use servo_util::{time, url};
@ -64,6 +65,13 @@ pub struct IOCompositor {
/// The application window size.
window_size: TypedSize2D<DevicePixel, uint>,
/// "Mobile-style" zoom that does not reflow the page.
viewport_zoom: ScaleFactor<PagePx, ViewportPx, f32>,
/// "Desktop-style" zoom that resizes the viewport to fit the window.
/// See `ViewportPx` docs in util/geom.rs for details.
page_zoom: ScaleFactor<ViewportPx, ScreenPx, f32>,
/// The device pixel ratio for this window.
hidpi_factor: ScaleFactor<ScreenPx, DevicePixel, f32>,
@ -82,9 +90,6 @@ pub struct IOCompositor {
/// Tracks whether we need to re-composite a page.
recomposite: bool,
/// Keeps track of the current zoom factor.
world_zoom: ScaleFactor<PagePx, ScreenPx, f32>,
/// Tracks whether the zoom action has happend recently.
zoom_action: bool,
@ -146,7 +151,8 @@ impl IOCompositor {
shutting_down: false,
done: false,
recomposite: false,
world_zoom: ScaleFactor(1.0),
page_zoom: ScaleFactor(1.0),
viewport_zoom: ScaleFactor(1.0),
zoom_action: false,
zoom_time: 0f64,
ready_state: Blank,
@ -417,10 +423,17 @@ impl IOCompositor {
self.window_size.as_f32() / self.device_pixels_per_page_px()
}
/// The size of the window in screen px.
fn send_window_size(&self) {
let dppx = self.page_zoom * self.device_pixels_per_screen_px();
let initial_viewport = self.window_size.as_f32() / dppx;
let visible_viewport = initial_viewport / self.viewport_zoom;
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ResizedWindowMsg(self.page_window()));
chan.send(ResizedWindowMsg(WindowSizeData {
device_pixel_ratio: dppx,
initial_viewport: initial_viewport,
visible_viewport: visible_viewport,
}));
}
fn set_layer_page_size(&mut self,
@ -549,6 +562,10 @@ impl IOCompositor {
self.on_zoom_window_event(magnification);
}
PinchZoomWindowEvent(magnification) => {
self.on_pinch_zoom_window_event(magnification);
}
NavigationWindowEvent(direction) => {
self.on_navigation_window_event(direction);
}
@ -647,7 +664,7 @@ impl IOCompositor {
}
fn device_pixels_per_page_px(&self) -> ScaleFactor<PagePx, DevicePixel, f32> {
self.world_zoom * self.device_pixels_per_screen_px()
self.viewport_zoom * self.page_zoom * self.device_pixels_per_screen_px()
}
fn update_zoom_transform(&mut self) {
@ -656,21 +673,26 @@ impl IOCompositor {
}
fn on_zoom_window_event(&mut self, magnification: f32) {
self.page_zoom = ScaleFactor((self.page_zoom.get() * magnification).max(1.0));
self.update_zoom_transform();
self.send_window_size();
}
fn on_pinch_zoom_window_event(&mut self, magnification: f32) {
self.zoom_action = true;
self.zoom_time = precise_time_s();
let old_world_zoom = self.world_zoom;
let old_viewport_zoom = self.viewport_zoom;
let window_size = self.window_size.as_f32();
// Determine zoom amount
self.world_zoom = ScaleFactor((self.world_zoom.get() * magnification).max(1.0));
let world_zoom = self.world_zoom;
self.viewport_zoom = ScaleFactor((self.viewport_zoom.get() * magnification).max(1.0));
let viewport_zoom = self.viewport_zoom;
self.update_zoom_transform();
// Scroll as needed
let page_delta = TypedPoint2D(
window_size.width.get() * (world_zoom.inv() - old_world_zoom.inv()).get() * 0.5,
window_size.height.get() * (world_zoom.inv() - old_world_zoom.inv()).get() * 0.5);
window_size.width.get() * (viewport_zoom.inv() - old_viewport_zoom.inv()).get() * 0.5,
window_size.height.get() * (viewport_zoom.inv() - old_viewport_zoom.inv()).get() * 0.5);
// TODO: modify delta to snap scroll to pixels.
let page_cursor = TypedPoint2D(-1f32, -1f32); // Make sure this hits the base layer
let page_window = self.page_window();

View file

@ -4,8 +4,9 @@
use compositing::*;
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, ResizedWindowMsg};
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, ResizedWindowMsg, WindowSizeData};
use servo_util::time::ProfilerChan;
use servo_util::time;
@ -33,7 +34,11 @@ impl NullCompositor {
// Tell the constellation about the initial fake size.
{
let ConstellationChan(ref chan) = constellation_chan;
chan.send(ResizedWindowMsg(TypedSize2D(640_f32, 480_f32)));
chan.send(ResizedWindowMsg(WindowSizeData {
initial_viewport: TypedSize2D(640_f32, 480_f32),
visible_viewport: TypedSize2D(640_f32, 480_f32),
device_pixel_ratio: ScaleFactor(1.0),
}));
}
compositor.handle_message(constellation_chan);

View file

@ -6,6 +6,7 @@ use compositing::{CompositorChan, LoadComplete, SetIds, SetLayerClipRect, Shutdo
use collections::hashmap::{HashMap, HashSet};
use geom::rect::{Rect, TypedRect};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use gfx::render_task;
use libc;
@ -19,7 +20,7 @@ use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failu
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
use servo_msg::constellation_msg::{NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg};
use servo_msg::constellation_msg::SubpageId;
use servo_msg::constellation_msg::{SubpageId, WindowSizeData};
use servo_msg::constellation_msg;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask;
@ -48,7 +49,7 @@ pub struct Constellation {
pending_frames: Vec<FrameChange>,
pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
pub profiler_chan: ProfilerChan,
pub window_size: TypedSize2D<PagePx, f32>,
pub window_size: WindowSizeData,
pub opts: Opts,
}
@ -261,7 +262,11 @@ impl Constellation {
pending_frames: vec!(),
pending_sizes: HashMap::new(),
profiler_chan: profiler_chan,
window_size: TypedSize2D(800_f32, 600_f32),
window_size: WindowSizeData {
visible_viewport: TypedSize2D(800_f32, 600_f32),
initial_viewport: TypedSize2D(800_f32, 600_f32),
device_pixel_ratio: ScaleFactor(1.0),
},
opts: opts_clone,
};
constellation.run();
@ -491,7 +496,11 @@ impl Constellation {
if !already_sent.contains(&pipeline.id) {
if is_active {
let ScriptChan(ref script_chan) = pipeline.script_chan;
script_chan.send(ResizeMsg(pipeline.id, rect.size));
script_chan.send(ResizeMsg(pipeline.id, WindowSizeData {
visible_viewport: rect.size,
initial_viewport: rect.size * ScaleFactor(1.0),
device_pixel_ratio: self.window_size.device_pixel_ratio,
}));
self.compositor_chan.send(SetLayerClipRect(pipeline.id,
LayerId::null(),
rect.to_untyped()));
@ -788,7 +797,7 @@ impl Constellation {
}
/// Called when the window is resized.
fn handle_resized_window_msg(&mut self, new_size: TypedSize2D<PagePx, f32>) {
fn handle_resized_window_msg(&mut self, new_size: WindowSizeData) {
let mut already_seen = HashSet::new();
for frame_tree in self.current_frame().iter() {
debug!("constellation sending resize message to active frame");

View file

@ -581,8 +581,12 @@ impl LayoutTask {
_ => false
};
let current_screen_size = Size2D(Au::from_page_px(data.window_size.width),
Au::from_page_px(data.window_size.height));
// TODO: Calculate the "actual viewport":
// http://www.w3.org/TR/css-device-adapt/#actual-viewport
let viewport_size = data.window_size.initial_viewport;
let current_screen_size = Size2D(Au::from_frac32_px(viewport_size.width.get()),
Au::from_frac32_px(viewport_size.height.get()));
if self.screen_size != current_screen_size {
all_style_damage = true
}

View file

@ -5,7 +5,6 @@
use compositing::CompositorChan;
use layout::layout_task::LayoutTask;
use geom::size::TypedSize2D;
use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
use gfx::render_task::{RenderChan, RenderTask};
use script::layout_interface::LayoutChan;
@ -13,9 +12,9 @@ use script::script_task::LoadMsg;
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
use script::script_task;
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId};
use servo_msg::constellation_msg::WindowSizeData;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::geometry::PagePx;
use servo_util::opts::Opts;
use servo_util::time::ProfilerChan;
use std::rc::Rc;
@ -113,7 +112,7 @@ impl Pipeline {
image_cache_task: ImageCacheTask,
resource_task: ResourceTask,
profiler_chan: ProfilerChan,
window_size: TypedSize2D<PagePx, f32>,
window_size: WindowSizeData,
opts: Opts,
url: Url)
-> Pipeline {

View file

@ -6,7 +6,7 @@
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass, MouseWindowMoveEventClass};
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
use windowing::{ScrollWindowEvent, ZoomWindowEvent, PinchZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use windowing::RefreshWindowEvent;
use windowing::{Forward, Back};
@ -240,19 +240,33 @@ impl Window {
MouseWindowMoveEventClass(TypedPoint2D(xpos as f32, ypos as f32)));
},
glfw::ScrollEvent(xpos, ypos) => {
let dx = (xpos as f32) * 30.0;
let dy = (ypos as f32) * 30.0;
match (window.get_key(glfw::KeyLeftControl),
window.get_key(glfw::KeyRightControl)) {
(glfw::Press, _) | (_, glfw::Press) => {
// Ctrl-Scrollwheel simulates a "pinch zoom" gesture.
if ypos < 0.0 {
self.event_queue.borrow_mut().push(PinchZoomWindowEvent(1.0/1.1));
} else if ypos > 0.0 {
self.event_queue.borrow_mut().push(PinchZoomWindowEvent(1.1));
}
},
_ => {
let dx = (xpos as f32) * 30.0;
let dy = (ypos as f32) * 30.0;
let (x, y) = window.get_cursor_pos();
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (backing_size, _) = window.get_framebuffer_size();
let (window_size, _) = window.get_size();
let hidpi = (backing_size as f32) / (window_size as f32);
let x = x as f32 * hidpi;
let y = y as f32 * hidpi;
let (x, y) = window.get_cursor_pos();
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (backing_size, _) = window.get_framebuffer_size();
let (window_size, _) = window.get_size();
let hidpi = (backing_size as f32) / (window_size as f32);
let x = x as f32 * hidpi;
let y = y as f32 * hidpi;
self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(dx, dy),
TypedPoint2D(x as i32, y as i32)));
}
}
self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(dx, dy),
TypedPoint2D(x as i32, y as i32)));
},
_ => {}
}
@ -298,7 +312,7 @@ impl Window {
self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1));
}
glfw::KeyMinus if mods.contains(glfw::Control) => { // Ctrl--
self.event_queue.borrow_mut().push(ZoomWindowEvent(0.90909090909));
self.event_queue.borrow_mut().push(ZoomWindowEvent(1.0/1.1));
}
glfw::KeyBackspace if mods.contains(glfw::Shift) => { // Shift-Backspace
self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward));

View file

@ -43,6 +43,8 @@ pub enum WindowEvent {
ScrollWindowEvent(TypedPoint2D<DevicePixel, f32>, TypedPoint2D<DevicePixel, i32>),
/// Sent when the user zooms.
ZoomWindowEvent(f32),
/// Simulated "pinch zoom" gesture for non-touch platforms (e.g. ctrl-scrollwheel).
PinchZoomWindowEvent(f32),
/// Sent when the user uses chrome navigation (i.e. backspace or shift-backspace).
NavigationWindowEvent(WindowNavigateMsg),
/// Sent when rendering is finished.

View file

@ -7,7 +7,8 @@
use geom::rect::Rect;
use geom::size::TypedSize2D;
use servo_util::geometry::PagePx;
use geom::scale_factor::ScaleFactor;
use servo_util::geometry::{DevicePixel, PagePx, ViewportPx};
use std::comm::{channel, Sender, Receiver};
use url::Url;
@ -34,6 +35,18 @@ pub struct Failure {
pub subpage_id: Option<SubpageId>,
}
pub struct WindowSizeData {
/// The size of the initial layout viewport, before parsing an
/// http://www.w3.org/TR/css-device-adapt/#initial-viewport
pub initial_viewport: TypedSize2D<ViewportPx, f32>,
/// The "viewing area" in page px. See `PagePx` documentation for details.
pub visible_viewport: TypedSize2D<PagePx, f32>,
/// The resolution of the window in dppx, not including any "pinch zoom" factor.
pub device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>,
}
/// Messages from the compositor and script to the constellation.
pub enum Msg {
ExitMsg,
@ -45,7 +58,7 @@ pub enum Msg {
LoadIframeUrlMsg(Url, PipelineId, SubpageId, IFrameSandboxState),
NavigateMsg(NavigationDirection),
RendererReadyMsg(PipelineId),
ResizedWindowMsg(TypedSize2D<PagePx, f32>),
ResizedWindowMsg(WindowSizeData),
}
/// Represents the two different ways to which a page can be navigated

View file

@ -10,17 +10,16 @@ use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::eventtarget::EventTarget;
use dom::window::Window;
use servo_msg::constellation_msg::WindowSizeData;
use servo_util::str::DOMString;
use servo_util::geometry::PagePx;
use std::cell::{Cell, RefCell};
use geom::point::Point2D;
use geom::size::TypedSize2D;
use time;
pub enum Event_ {
ResizeEvent(TypedSize2D<PagePx, f32>),
ResizeEvent(WindowSizeData),
ReflowEvent,
ClickEvent(uint, Point2D<f32>),
MouseDownEvent(uint, Point2D<f32>),

View file

@ -11,10 +11,10 @@ use dom::node::{Node, LayoutDataRef};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::TypedSize2D;
use libc::c_void;
use script_task::{ScriptChan};
use servo_util::geometry::{Au, PagePx};
use servo_msg::constellation_msg::WindowSizeData;
use servo_util::geometry::Au;
use std::cmp;
use std::comm::{channel, Receiver, Sender};
use style::Stylesheet;
@ -137,7 +137,7 @@ pub struct Reflow {
/// The channel through which messages can be sent back to the script task.
pub script_chan: ScriptChan,
/// The current window size.
pub window_size: TypedSize2D<PagePx, f32>,
pub window_size: WindowSizeData,
/// The channel that we send a notification to.
pub script_join_chan: Sender<()>,
/// Unique identifier

View file

@ -19,14 +19,12 @@ use layout_interface::UntrustedNodeAddress;
use script_task::ScriptChan;
use geom::point::Point2D;
use geom::size::TypedSize2D;
use js::rust::Cx;
use servo_msg::compositor_msg::PerformingLayout;
use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::ConstellationChan;
use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_net::resource_task::ResourceTask;
use servo_util::geometry::PagePx;
use servo_util::namespace::Null;
use servo_util::str::DOMString;
use std::cell::{Cell, RefCell, Ref, RefMut};
@ -62,7 +60,7 @@ pub struct Page {
damage: Traceable<RefCell<Option<DocumentDamage>>>,
/// The current size of the window, in pixels.
pub window_size: Untraceable<Cell<TypedSize2D<PagePx, f32>>>,
pub window_size: Untraceable<Cell<WindowSizeData>>,
js_info: Traceable<RefCell<Option<JSPageInfo>>>,
@ -75,7 +73,7 @@ pub struct Page {
next_subpage_id: Untraceable<Cell<SubpageId>>,
/// Pending resize event, if any.
pub resize_event: Untraceable<Cell<Option<TypedSize2D<PagePx, f32>>>>,
pub resize_event: Untraceable<Cell<Option<WindowSizeData>>>,
/// Pending scroll to fragment event, if any
pub fragment_node: Cell<Option<JS<Element>>>,
@ -119,7 +117,8 @@ impl IterablePage for Rc<Page> {
impl Page {
pub fn new(id: PipelineId, subpage_id: Option<SubpageId>,
layout_chan: LayoutChan,
window_size: TypedSize2D<PagePx, f32>, resource_task: ResourceTask,
window_size: WindowSizeData,
resource_task: ResourceTask,
constellation_chan: ConstellationChan,
js_context: Rc<Cx>) -> Page {
let js_info = JSPageInfo {

View file

@ -32,7 +32,6 @@ use layout_interface;
use page::{Page, IterablePage, Frame};
use geom::point::Point2D;
use geom::size::TypedSize2D;
use js::jsapi::JS_CallFunctionValue;
use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC};
use js::jsapi::{JSContext, JSRuntime};
@ -43,11 +42,11 @@ use js;
use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading};
use servo_msg::compositor_msg::{ScriptListener};
use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg};
use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg, WindowSizeData};
use servo_msg::constellation_msg;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::geometry::{PagePx, to_frac_px};
use servo_util::geometry::to_frac_px;
use servo_util::task::send_on_failure;
use std::cell::RefCell;
use std::comm::{channel, Sender, Receiver};
@ -76,13 +75,13 @@ pub enum ScriptMsg {
/// Sends a DOM event.
SendEventMsg(PipelineId, Event_),
/// Window resized. Sends a DOM event eventually, but first we combine events.
ResizeMsg(PipelineId, TypedSize2D<PagePx, f32>),
ResizeMsg(PipelineId, WindowSizeData),
/// Fires a JavaScript timeout.
FireTimerMsg(PipelineId, TimerId),
/// Notifies script that reflow is finished.
ReflowCompleteMsg(PipelineId, uint),
/// Notifies script that window has been resized but to not take immediate action.
ResizeInactiveMsg(PipelineId, TypedSize2D<PagePx, f32>),
ResizeInactiveMsg(PipelineId, WindowSizeData),
/// Notifies the script that a pipeline should be closed.
ExitPipelineMsg(PipelineId),
/// Notifies the script that a window associated with a particular pipeline should be closed.
@ -208,7 +207,7 @@ impl ScriptTask {
constellation_chan: ConstellationChan,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
window_size: TypedSize2D<PagePx, f32>)
window_size: WindowSizeData)
-> Rc<ScriptTask> {
let (js_runtime, js_context) = ScriptTask::new_rt_and_cx();
let page = Page::new(id, None, layout_chan, window_size,
@ -289,7 +288,7 @@ impl ScriptTask {
failure_msg: Failure,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask,
window_size: TypedSize2D<PagePx, f32>) {
window_size: WindowSizeData) {
let mut builder = TaskBuilder::new().named("ScriptTask");
let ConstellationChan(const_chan) = constellation_chan.clone();
send_on_failure(&mut builder, FailureMsg(failure_msg), const_chan);
@ -463,7 +462,7 @@ impl ScriptTask {
}
/// Window was resized, but this script was not active, so don't reflow yet
fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: TypedSize2D<PagePx, f32>) {
fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) {
let mut page = self.page.borrow_mut();
let page = page.find(id).expect("Received resize message for PipelineId not associated
with a page in the page tree. This is a bug.");

View file

@ -33,14 +33,32 @@ pub enum DevicePixel {}
/// `servo::windowing::WindowMethods::hidpi_factor`.
pub enum ScreenPx {}
/// One CSS "px" in the coordinate system of the "initial viewport":
/// http://www.w3.org/TR/css-device-adapt/#initial-viewport
///
/// ViewportPx is equal to ScreenPx times a "page zoom" factor controlled by the user. This is
/// the desktop-style "full page" zoom that enlarges content but then reflows the layout viewport
/// so it still exactly fits the visible area.
///
/// At the default zoom level of 100%, one PagePx is equal to one ScreenPx. However, if the
/// document is zoomed in or out then this scale may be larger or smaller.
pub enum ViewportPx {}
/// One CSS "px" in the root coordinate system for the content document.
///
///
/// PagePx is equal to ScreenPx multiplied by a "zoom" factor controlled by the user. At the
/// default zoom level of 100%, one PagePx is equal to one ScreenPx. However, if the document
/// is zoomed in or out then this scale may be larger or smaller.
/// PagePx is equal to ViewportPx multiplied by a "viewport zoom" factor controlled by the user.
/// This is the mobile-style "pinch zoom" that enlarges content without reflowing it. When the
/// viewport zoom is not equal to 1.0, then the layout viewport is no longer the same physical size
/// as the viewable area.
pub enum PagePx {}
// In summary, the hierarchy of pixel units and the factors to convert from one to the next:
//
// DevicePixel
// / hidpi_ratio => ScreenPx
// / desktop_zoom => ViewportPx
// / pinch_zoom => PagePx
// An Au is an "App Unit" and represents 1/60th of a CSS pixel. It was
// originally proposed in 2002 as a standard unit of measure in Gecko.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info.