Outside of compositor, store window size in CSS px

This fixes an issue where the CSS viewport was too large on high-DPI displays
because it was set to the window size in device pixels, instead of px.  This
patch ensures that the window size is converted from device pixels to px
before being passed to script/layout code.

The Window trait now exposes the window size in both device pixels and
density-independent screen coordinates, with clearer method names.
This commit is contained in:
Matt Brubeck 2014-06-06 12:45:14 -07:00
parent e98b03f581
commit 89327aa5be
15 changed files with 104 additions and 85 deletions

View file

@ -235,12 +235,12 @@ DONE_net = $(B)src/components/net/libnet.dummy
DEPS_net = $(CRATE_net) $(SRC_net) $(DONE_SUBMODULES) $(DONE_util)
RFLAGS_msg = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES))
RFLAGS_msg = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util
SRC_msg = $(call rwildcard,$(S)src/components/msg/,*.rs)
CRATE_msg = $(S)src/components/msg/msg.rs
DONE_msg = $(B)src/components/msg/libmsg.dummy
DEPS_msg = $(CRATE_msg) $(SRC_msg) $(DONE_SUBMODULES)
DEPS_msg = $(CRATE_msg) $(SRC_msg) $(DONE_SUBMODULES) $(DONE_util)
RFLAGS_gfx = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util -L $(B)src/components/style -L $(B)src/components/net -L $(B)src/components/msg -L$(B)src/components/macros
SRC_gfx = $(call rwildcard,$(S)src/components/gfx/,*.rs)

View file

@ -128,7 +128,7 @@ impl IOCompositor {
// TODO: There should be no initial layer tree until the renderer creates one from the display
// list. This is only here because we don't have that logic in the renderer yet.
let root_layer = Rc::new(ContainerLayer());
let window_size = window.size();
let window_size = window.framebuffer_size();
let hidpi_factor = window.hidpi_factor();
IOCompositor {
@ -138,8 +138,8 @@ impl IOCompositor {
context: rendergl::init_render_context(),
root_layer: root_layer.clone(),
root_pipeline: None,
scene: Scene(ContainerLayerKind(root_layer), window_size.to_untyped(), identity()),
window_size: window_size.as_uint(),
scene: Scene(ContainerLayerKind(root_layer), window_size.as_f32().to_untyped(), identity()),
window_size: window_size,
hidpi_factor: hidpi_factor,
graphics_context: CompositorTask::create_graphics_context(),
composite_ready: false,
@ -176,10 +176,7 @@ impl IOCompositor {
fn run (&mut self) {
// Tell the constellation about the initial window size.
{
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ResizedWindowMsg(self.window_size.to_untyped()));
}
self.send_window_size();
// Enter the main event loop.
while !self.done {
@ -339,13 +336,8 @@ impl IOCompositor {
self.root_pipeline = Some(frame_tree.pipeline.clone());
// Initialize the new constellation channel by sending it the root window size.
let window_size = self.window.size().as_uint();
{
let ConstellationChan(ref chan) = new_constellation_chan;
chan.send(ResizedWindowMsg(window_size.to_untyped()));
}
self.constellation_chan = new_constellation_chan;
self.send_window_size();
}
fn create_root_compositor_layer_if_necessary(&mut self,
@ -425,6 +417,12 @@ 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 ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ResizedWindowMsg(self.page_window()));
}
fn set_layer_page_size(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
@ -527,8 +525,8 @@ impl IOCompositor {
self.recomposite = true;
}
ResizeWindowEvent(width, height) => {
self.on_resize_window_event(width, height);
ResizeWindowEvent(size) => {
self.on_resize_window_event(size);
}
LoadUrlWindowEvent(url_string) => {
@ -574,22 +572,20 @@ impl IOCompositor {
}
}
fn on_resize_window_event(&mut self, width: uint, height: uint) {
let new_size: TypedSize2D<DevicePixel, uint> = TypedSize2D(width, height);
if self.window_size != new_size {
debug!("osmain: window resized to {:u}x{:u}", width, height);
self.window_size = new_size;
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ResizedWindowMsg(new_size.to_untyped()))
} else {
debug!("osmain: dropping window resize since size is still {:u}x{:u}", width, height);
}
fn on_resize_window_event(&mut self, new_size: TypedSize2D<DevicePixel, uint>) {
// A size change could also mean a resolution change.
let new_hidpi_factor = self.window.hidpi_factor();
if self.hidpi_factor != new_hidpi_factor {
self.hidpi_factor = new_hidpi_factor;
self.update_zoom_transform();
}
if self.window_size != new_size {
debug!("osmain: window resized to {:?}", new_size);
self.window_size = new_size;
self.send_window_size();
} else {
debug!("osmain: dropping window resize since size is still {:?}", new_size);
}
}
fn on_load_url_window_event(&mut self, url_string: String) {
@ -717,7 +713,7 @@ impl IOCompositor {
profile(time::CompositingCategory, self.profiler_chan.clone(), || {
debug!("compositor: compositing");
// Adjust the layer dimensions as necessary to correspond to the size of the window.
self.scene.size = self.window.size().to_untyped();
self.scene.size = self.window_size.as_f32().to_untyped();
// Render the scene.
match self.compositor_layer {
Some(ref mut layer) => {

View file

@ -4,7 +4,7 @@
use compositing::*;
use geom::size::Size2D;
use geom::size::TypedSize2D;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, ResizedWindowMsg};
use servo_util::time::ProfilerChan;
use servo_util::time;
@ -33,7 +33,7 @@ impl NullCompositor {
// Tell the constellation about the initial fake size.
{
let ConstellationChan(ref chan) = constellation_chan;
chan.send(ResizedWindowMsg(Size2D(640u, 480u)));
chan.send(ResizedWindowMsg(TypedSize2D(640_f32, 480_f32)));
}
compositor.handle_message(constellation_chan);

View file

@ -5,8 +5,8 @@
use compositing::{CompositorChan, LoadComplete, SetIds, SetLayerClipRect, ShutdownComplete};
use collections::hashmap::{HashMap, HashSet};
use geom::rect::Rect;
use geom::size::Size2D;
use geom::rect::{Rect, TypedRect};
use geom::size::TypedSize2D;
use gfx::render_task;
use libc;
use pipeline::{Pipeline, CompositionPipeline};
@ -24,6 +24,7 @@ use servo_msg::constellation_msg;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask;
use servo_net::resource_task;
use servo_util::geometry::PagePx;
use servo_util::opts::Opts;
use servo_util::time::ProfilerChan;
use servo_util::url::parse_url;
@ -45,9 +46,9 @@ pub struct Constellation {
navigation_context: NavigationContext,
next_pipeline_id: PipelineId,
pending_frames: Vec<FrameChange>,
pending_sizes: HashMap<(PipelineId, SubpageId), Rect<f32>>,
pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
pub profiler_chan: ProfilerChan,
pub window_size: Size2D<uint>,
pub window_size: TypedSize2D<PagePx, f32>,
pub opts: Opts,
}
@ -63,7 +64,7 @@ struct ChildFrameTree {
frame_tree: Rc<FrameTree>,
/// Clipping rect representing the size and position, in page coordinates, of the visible
/// region of the child frame relative to the parent.
pub rect: Option<Rect<f32>>,
pub rect: Option<TypedRect<PagePx, f32>>,
}
pub struct SendableFrameTree {
@ -73,7 +74,7 @@ pub struct SendableFrameTree {
pub struct SendableChildFrameTree {
pub frame_tree: SendableFrameTree,
pub rect: Option<Rect<f32>>,
pub rect: Option<TypedRect<PagePx, f32>>,
}
enum ReplaceResult {
@ -260,7 +261,7 @@ impl Constellation {
pending_frames: vec!(),
pending_sizes: HashMap::new(),
profiler_chan: profiler_chan,
window_size: Size2D(800u, 600u),
window_size: TypedSize2D(800_f32, 600_f32),
opts: opts_clone,
};
constellation.run();
@ -320,7 +321,7 @@ impl Constellation {
// all frame trees in the navigation context containing the subframe.
FrameRectMsg(pipeline_id, subpage_id, rect) => {
debug!("constellation got frame rect message");
self.handle_frame_rect_msg(pipeline_id, subpage_id, rect);
self.handle_frame_rect_msg(pipeline_id, subpage_id, Rect::from_untyped(&rect));
}
LoadIframeUrlMsg(url, source_pipeline_id, subpage_id, sandbox) => {
debug!("constellation got iframe URL load message");
@ -464,8 +465,9 @@ impl Constellation {
self.pipelines.insert(pipeline_wrapped.id, pipeline_wrapped);
}
fn handle_frame_rect_msg(&mut self, pipeline_id: PipelineId, subpage_id: SubpageId, rect: Rect<f32>) {
debug!("Received frame rect {} from {:?}, {:?}", rect, pipeline_id, subpage_id);
fn handle_frame_rect_msg(&mut self, pipeline_id: PipelineId, subpage_id: SubpageId,
rect: TypedRect<PagePx, f32>) {
debug!("Received frame rect {:?} from {:?}, {:?}", rect, pipeline_id, subpage_id);
let mut already_sent = HashSet::new();
// Returns true if a child frame tree's subpage id matches the given subpage id
@ -483,20 +485,16 @@ impl Constellation {
// if it hasn't been already. Optionally inform the compositor if
// resize happens immediately.
let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
child_frame_tree.rect = Some(rect.clone());
child_frame_tree.rect = Some(rect);
// NOTE: work around borrowchk issues
let pipeline = &child_frame_tree.frame_tree.pipeline;
if !already_sent.contains(&pipeline.id) {
let Size2D { width, height } = rect.size;
if is_active {
let ScriptChan(ref script_chan) = pipeline.script_chan;
script_chan.send(ResizeMsg(pipeline.id, Size2D {
width: width as uint,
height: height as uint
}));
script_chan.send(ResizeMsg(pipeline.id, rect.size));
self.compositor_chan.send(SetLayerClipRect(pipeline.id,
LayerId::null(),
rect));
rect.to_untyped()));
} else {
already_sent.insert(pipeline.id);
}
@ -790,7 +788,7 @@ impl Constellation {
}
/// Called when the window is resized.
fn handle_resized_window_msg(&mut self, new_size: Size2D<uint>) {
fn handle_resized_window_msg(&mut self, new_size: TypedSize2D<PagePx, f32>) {
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,8 @@ impl LayoutTask {
_ => false
};
let current_screen_size = Size2D(Au::from_px(data.window_size.width as int),
Au::from_px(data.window_size.height as int));
let current_screen_size = Size2D(Au::from_page_px(data.window_size.width),
Au::from_page_px(data.window_size.height));
if self.screen_size != current_screen_size {
all_style_damage = true
}

View file

@ -5,7 +5,7 @@
use compositing::CompositorChan;
use layout::layout_task::LayoutTask;
use geom::size::Size2D;
use geom::size::TypedSize2D;
use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
use gfx::render_task::{RenderChan, RenderTask};
use script::layout_interface::LayoutChan;
@ -15,6 +15,7 @@ use script::script_task;
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId};
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;
@ -112,7 +113,7 @@ impl Pipeline {
image_cache_task: ImageCacheTask,
resource_task: ResourceTask,
profiler_chan: ProfilerChan,
window_size: Size2D<uint>,
window_size: TypedSize2D<PagePx, f32>,
opts: Opts,
url: Url)
-> Pipeline {

View file

@ -145,9 +145,15 @@ impl WindowMethods<Application> for Window {
wrapped_window
}
/// Returns the size of the window.
fn size(&self) -> TypedSize2D<DevicePixel, f32> {
/// Returns the size of the window in hardware pixels.
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
let (width, height) = self.glfw_window.get_framebuffer_size();
TypedSize2D(width as uint, height as uint)
}
/// Returns the size of the window in density-independent "px" units.
fn size(&self) -> TypedSize2D<ScreenPx, f32> {
let (width, height) = self.glfw_window.get_size();
TypedSize2D(width as f32, height as f32)
}
@ -196,9 +202,9 @@ impl WindowMethods<Application> for Window {
}
fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
let (backing_size, _) = self.glfw_window.get_framebuffer_size();
let (window_size, _) = self.glfw_window.get_size();
ScaleFactor((backing_size as f32) / (window_size as f32))
let backing_size = self.framebuffer_size().width.get();
let window_size = self.size().width.get();
ScaleFactor((backing_size as f32) / window_size)
}
}
@ -211,7 +217,8 @@ impl Window {
}
},
glfw::FramebufferSizeEvent(width, height) => {
self.event_queue.borrow_mut().push(ResizeWindowEvent(width as uint, height as uint));
self.event_queue.borrow_mut().push(
ResizeWindowEvent(TypedSize2D(width as uint, height as uint)));
},
glfw::RefreshEvent => {
self.event_queue.borrow_mut().push(RefreshWindowEvent);

View file

@ -144,9 +144,14 @@ impl WindowMethods<Application> for Window {
wrapped_window
}
/// Returns the size of the window.
fn size(&self) -> TypedSize2D<DevicePixel, f32> {
TypedSize2D(glut::get(WindowWidth) as f32, glut::get(WindowHeight) as f32)
/// Returns the size of the window in hardware pixels.
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
TypedSize2D(glut::get(WindowWidth) as uint, glut::get(WindowHeight) as uint)
}
/// Returns the size of the window in density-independent "px" units.
fn size(&self) -> TypedSize2D<ScreenPx, f32> {
self.framebuffer_size().as_f32() / self.hidpi_factor()
}
/// Presents the window to the screen (perhaps by page flipping).

View file

@ -32,7 +32,7 @@ pub enum WindowEvent {
/// Sent when part of the window is marked dirty and needs to be redrawn.
RefreshWindowEvent,
/// Sent when the window is resized.
ResizeWindowEvent(uint, uint),
ResizeWindowEvent(TypedSize2D<DevicePixel, uint>),
/// Sent when a new URL is to be loaded.
LoadUrlWindowEvent(String),
/// Sent when a mouse hit test is to be performed.
@ -59,8 +59,10 @@ pub trait ApplicationMethods {
pub trait WindowMethods<A> {
/// Creates a new window.
fn new(app: &A, is_foreground: bool) -> Rc<Self>;
/// Returns the size of the window.
fn size(&self) -> TypedSize2D<DevicePixel, f32>;
/// Returns the size of the window in hardware pixels.
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint>;
/// Returns the size of the window in density-independent "px" units.
fn size(&self) -> TypedSize2D<ScreenPx, f32>;
/// Presents the window to the screen (perhaps by page flipping).
fn present(&self);

View file

@ -6,7 +6,8 @@
/// coupling between these two components
use geom::rect::Rect;
use geom::size::Size2D;
use geom::size::TypedSize2D;
use servo_util::geometry::PagePx;
use std::comm::{channel, Sender, Receiver};
use url::Url;
@ -44,7 +45,7 @@ pub enum Msg {
LoadIframeUrlMsg(Url, PipelineId, SubpageId, IFrameSandboxState),
NavigateMsg(NavigationDirection),
RendererReadyMsg(PipelineId),
ResizedWindowMsg(Size2D<uint>),
ResizedWindowMsg(TypedSize2D<PagePx, f32>),
}
/// Represents the two different ways to which a page can be navigated

View file

@ -11,6 +11,7 @@ extern crate azure;
extern crate geom;
extern crate layers;
extern crate serialize;
extern crate servo_util = "util";
extern crate std;
extern crate url;

View file

@ -10,14 +10,16 @@ use dom::bindings::error::Fallible;
use dom::eventtarget::EventTarget;
use dom::window::Window;
use servo_util::str::DOMString;
use servo_util::geometry::PagePx;
use std::cell::Cell;
use geom::point::Point2D;
use geom::size::TypedSize2D;
use time;
pub enum Event_ {
ResizeEvent(uint, uint),
ResizeEvent(TypedSize2D<PagePx, f32>),
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::Size2D;
use geom::size::TypedSize2D;
use libc::c_void;
use script_task::{ScriptChan};
use servo_util::geometry::Au;
use servo_util::geometry::{Au, PagePx};
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: Size2D<uint>,
pub window_size: TypedSize2D<PagePx, f32>,
/// The channel that we send a notification to.
pub script_join_chan: Sender<()>,
/// Unique identifier

View file

@ -34,7 +34,7 @@ use layout_interface::UntrustedNodeAddress;
use layout_interface;
use geom::point::Point2D;
use geom::size::Size2D;
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};
@ -49,7 +49,7 @@ use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg};
use servo_msg::constellation_msg;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::geometry::to_frac_px;
use servo_util::geometry::{PagePx, to_frac_px};
use servo_util::task::send_on_failure;
use servo_util::namespace::Null;
use servo_util::str::DOMString;
@ -80,13 +80,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, Size2D<uint>),
ResizeMsg(PipelineId, TypedSize2D<PagePx, f32>),
/// 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, Size2D<uint>),
ResizeInactiveMsg(PipelineId, TypedSize2D<PagePx, f32>),
/// 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.
@ -145,7 +145,7 @@ pub struct Page {
damage: Traceable<RefCell<Option<DocumentDamage>>>,
/// The current size of the window, in pixels.
window_size: Untraceable<Cell<Size2D<uint>>>,
window_size: Untraceable<Cell<TypedSize2D<PagePx, f32>>>,
js_info: Traceable<RefCell<Option<JSPageInfo>>>,
@ -158,7 +158,7 @@ pub struct Page {
next_subpage_id: Untraceable<Cell<SubpageId>>,
/// Pending resize event, if any.
resize_event: Untraceable<Cell<Option<Size2D<uint>>>>,
resize_event: Untraceable<Cell<Option<TypedSize2D<PagePx, f32>>>>,
/// Pending scroll to fragment event, if any
fragment_node: Cell<Option<JS<Element>>>,
@ -201,7 +201,7 @@ impl IterablePage for Rc<Page> {
impl Page {
fn new(id: PipelineId, subpage_id: Option<SubpageId>,
layout_chan: LayoutChan,
window_size: Size2D<uint>, resource_task: ResourceTask,
window_size: TypedSize2D<PagePx, f32>, resource_task: ResourceTask,
constellation_chan: ConstellationChan,
js_context: Rc<Cx>) -> Page {
let js_info = JSPageInfo {
@ -609,7 +609,7 @@ impl ScriptTask {
constellation_chan: ConstellationChan,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
window_size: Size2D<uint>)
window_size: TypedSize2D<PagePx, f32>)
-> Rc<ScriptTask> {
let (js_runtime, js_context) = ScriptTask::new_rt_and_cx();
let page = Page::new(id, None, layout_chan, window_size,
@ -690,7 +690,7 @@ impl ScriptTask {
failure_msg: Failure,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask,
window_size: Size2D<uint>) {
window_size: TypedSize2D<PagePx, f32>) {
let mut builder = TaskBuilder::new().named("ScriptTask");
let ConstellationChan(const_chan) = constellation_chan.clone();
send_on_failure(&mut builder, FailureMsg(failure_msg), const_chan);
@ -737,8 +737,8 @@ impl ScriptTask {
}
}
for (id, Size2D { width, height }) in resizes.move_iter() {
self.handle_event(id, ResizeEvent(width, height));
for (id, size) in resizes.move_iter() {
self.handle_event(id, ResizeEvent(size));
}
// Store new resizes, and gather all other events.
@ -868,7 +868,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: Size2D<uint>) {
fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: TypedSize2D<PagePx, f32>) {
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.");
@ -1063,12 +1063,12 @@ impl ScriptTask {
/// TODO: Actually perform DOM event dispatch.
fn handle_event(&self, pipeline_id: PipelineId, event: Event_) {
match event {
ResizeEvent(new_width, new_height) => {
debug!("script got resize event: {:u}, {:u}", new_width, new_height);
ResizeEvent(new_size) => {
debug!("script got resize event: {:?}", new_size);
let window = {
let page = get_page(&*self.page.borrow(), pipeline_id);
page.window_size.deref().set(Size2D(new_width, new_height));
page.window_size.deref().set(new_size);
let frame = page.frame();
if frame.is_some() {

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::length::Length;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
@ -178,6 +179,11 @@ impl Au {
NumCast::from(px * 60).unwrap()
}
#[inline]
pub fn from_page_px(px: Length<PagePx, f32>) -> Au {
NumCast::from(px.get() * 60f32).unwrap()
}
#[inline]
pub fn to_nearest_px(&self) -> int {
let Au(s) = *self;