diff options
-rw-r--r-- | components/gfx/display_list/mod.rs | 10 | ||||
-rw-r--r-- | components/gfx/paint_context.rs | 75 | ||||
-rw-r--r-- | components/gfx/paint_task.rs | 4 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 3 | ||||
-rw-r--r-- | components/script/dom/htmlimageelement.rs | 9 | ||||
-rw-r--r-- | components/script/dom/window.rs | 19 | ||||
-rw-r--r-- | components/script/script_task.rs | 39 |
7 files changed, 108 insertions, 51 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 7273c73d254..3f03ecf88ef 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -705,9 +705,9 @@ impl StackingContext { inverse_transform.m21, inverse_transform.m22, inverse_transform.m41, inverse_transform.m42); - let tile_size = Size2D::new(paint_context.screen_rect.size.width as f32, - paint_context.screen_rect.size.height as f32); - let tile_rect = Rect::new(Point2D::zero(), tile_size); + let tile_size = Size2D::new(paint_context.screen_rect.as_f32().size.width, + paint_context.screen_rect.as_f32().size.height); + let tile_rect = Rect::new(Point2D::zero(), tile_size).to_untyped(); let tile_rect = inverse_transform_2d.transform_rect(&tile_rect); // Optimize the display list to throw out out-of-bounds display items and so forth. @@ -1493,11 +1493,11 @@ impl DisplayItem { transform.translate(stacking_context.bounds .origin .x - .to_nearest_pixel(pixels_per_px) as AzFloat, + .to_nearest_pixel(pixels_per_px.get()) as AzFloat, stacking_context.bounds .origin .y - .to_nearest_pixel(pixels_per_px) as AzFloat, + .to_nearest_pixel(pixels_per_px.get()) as AzFloat, 0.0); stacking_context.optimize_and_draw_into_context(paint_context, &new_transform, diff --git a/components/gfx/paint_context.rs b/components/gfx/paint_context.rs index 039ffa15918..b579688e33f 100644 --- a/components/gfx/paint_context.rs +++ b/components/gfx/paint_context.rs @@ -20,7 +20,8 @@ use display_list::{BLUR_INFLATION_FACTOR, BorderRadii, BoxShadowClipMode, Clippi use display_list::{TextDisplayItem}; use euclid::matrix2d::Matrix2D; use euclid::point::Point2D; -use euclid::rect::Rect; +use euclid::rect::{Rect, TypedRect}; +use euclid::scale_factor::ScaleFactor; use euclid::side_offsets::SideOffsets2D; use euclid::size::Size2D; use filters; @@ -35,7 +36,7 @@ use std::{f32, mem, ptr}; use style::computed_values::{border_style, filter, image_rendering, mix_blend_mode}; use text::TextRun; use text::glyph::CharIndex; -use util::geometry::{self, MAX_RECT, ZERO_POINT, ZERO_RECT}; +use util::geometry::{self, MAX_RECT, PagePx, ScreenPx, ZERO_POINT, ZERO_RECT}; use util::opts; use util::range::Range; @@ -43,9 +44,9 @@ pub struct PaintContext<'a> { pub draw_target: DrawTarget, pub font_context: &'a mut Box<FontContext>, /// The rectangle that this context encompasses in page coordinates. - pub page_rect: Rect<f32>, + pub page_rect: TypedRect<PagePx, f32>, /// The rectangle that this context encompasses in screen coordinates (pixels). - pub screen_rect: Rect<usize>, + pub screen_rect: TypedRect<ScreenPx, usize>, /// The clipping rect for the stacking context as a whole. pub clip_rect: Option<Rect<Au>>, /// The current transient clipping region, if any. A "transient clipping region" is the @@ -119,8 +120,8 @@ struct CornerOrigin { } impl<'a> PaintContext<'a> { - pub fn screen_pixels_per_px(&self) -> f32 { - self.screen_rect.size.width as f32 / self.page_rect.size.width + pub fn screen_pixels_per_px(&self) -> ScaleFactor<PagePx, ScreenPx, f32> { + self.screen_rect.as_f32().size.width / self.page_rect.size.width } pub fn draw_target(&self) -> &DrawTarget { @@ -269,10 +270,12 @@ impl<'a> PaintContext<'a> { pub fn clear(&self) { let pattern = ColorPattern::new(color::transparent()); - let rect = Rect::new(Point2D::new(self.page_rect.origin.x as AzFloat, - self.page_rect.origin.y as AzFloat), - Size2D::new(self.screen_rect.size.width as AzFloat, - self.screen_rect.size.height as AzFloat)); + let page_rect = self.page_rect.to_untyped(); + let screen_rect = self.screen_rect.to_untyped(); + let rect = Rect::new(Point2D::new(page_rect.origin.x as AzFloat, + page_rect.origin.y as AzFloat), + Size2D::new(screen_rect.size.width as AzFloat, + screen_rect.size.height as AzFloat)); let mut draw_options = DrawOptions::new(1.0, CompositionOp::Over, AntialiasMode::None); draw_options.set_composition_op(CompositionOp::Source); self.draw_target.make_current(); @@ -1577,7 +1580,7 @@ impl<'a> PaintContext<'a> { // smallest possible rectangle that encompasses all the paint. let side_inflation = blur_radius * BLUR_INFLATION_FACTOR; let tile_box_bounds = - geometry::f32_rect_to_au_rect(self.page_rect).intersection(box_bounds) + geometry::f32_rect_to_au_rect(self.page_rect.to_untyped()).intersection(box_bounds) .unwrap_or(ZERO_RECT) .inflate(side_inflation, side_inflation); TemporaryDrawTarget::from_bounds(&self.draw_target, &tile_box_bounds) @@ -1635,14 +1638,14 @@ impl<'a> PaintContext<'a> { } pub trait ToAzurePoint { - fn to_nearest_azure_point(&self, pixels_per_px: f32) -> Point2D<AzFloat>; + fn to_nearest_azure_point(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Point2D<AzFloat>; fn to_azure_point(&self) -> Point2D<AzFloat>; } impl ToAzurePoint for Point2D<Au> { - fn to_nearest_azure_point(&self, pixels_per_px: f32) -> Point2D<AzFloat> { - Point2D::new(self.x.to_nearest_pixel(pixels_per_px) as AzFloat, - self.y.to_nearest_pixel(pixels_per_px) as AzFloat) + fn to_nearest_azure_point(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Point2D<AzFloat> { + Point2D::new(self.x.to_nearest_pixel(pixels_per_px.get()) as AzFloat, + self.y.to_nearest_pixel(pixels_per_px.get()) as AzFloat) } fn to_azure_point(&self) -> Point2D<AzFloat> { Point2D::new(self.x.to_f32_px(), self.y.to_f32_px()) @@ -1650,8 +1653,8 @@ impl ToAzurePoint for Point2D<Au> { } pub trait ToAzureRect { - fn to_nearest_azure_rect(&self, pixels_per_px: f32) -> Rect<AzFloat>; - fn to_nearest_non_empty_azure_rect(&self, pixels_per_px: f32) -> Rect<AzFloat>; + fn to_nearest_azure_rect(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Rect<AzFloat>; + fn to_nearest_non_empty_azure_rect(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Rect<AzFloat>; fn to_azure_rect(&self) -> Rect<AzFloat>; } @@ -1659,7 +1662,7 @@ impl ToAzureRect for Rect<Au> { /// Round rects to pixel coordinates, maintaining the invariant of non-overlap, /// assuming that before rounding rects don't overlap. - fn to_nearest_azure_rect(&self, pixels_per_px: f32) -> Rect<AzFloat> { + fn to_nearest_azure_rect(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Rect<AzFloat> { // Rounding the top left corner to the nearest pixel with the size rounded // to the nearest pixel multiple would violate the non-overlap condition, // e.g. @@ -1679,7 +1682,7 @@ impl ToAzureRect for Rect<Au> { /// 10px×0.6px at 0px,28.56px -> 10px×0px at 0px,29px /// Instead round the top left to the nearest pixel and the size to the nearest pixel /// multiple. It's possible for non-overlapping rects after this rounding to overlap. - fn to_nearest_non_empty_azure_rect(&self, pixels_per_px: f32) -> Rect<AzFloat> { + fn to_nearest_non_empty_azure_rect(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Rect<AzFloat> { Rect::new(self.origin.to_nearest_azure_point(pixels_per_px), self.size.to_nearest_azure_size(pixels_per_px)) } @@ -1690,13 +1693,13 @@ impl ToAzureRect for Rect<Au> { } pub trait ToNearestAzureSize { - fn to_nearest_azure_size(&self, pixels_per_px: f32) -> Size2D<AzFloat>; + fn to_nearest_azure_size(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Size2D<AzFloat>; } impl ToNearestAzureSize for Size2D<Au> { - fn to_nearest_azure_size(&self, pixels_per_px: f32) -> Size2D<AzFloat> { - Size2D::new(self.width.to_nearest_pixel(pixels_per_px) as AzFloat, - self.height.to_nearest_pixel(pixels_per_px) as AzFloat) + fn to_nearest_azure_size(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Size2D<AzFloat> { + Size2D::new(self.width.to_nearest_pixel(pixels_per_px.get()) as AzFloat, + self.height.to_nearest_pixel(pixels_per_px.get()) as AzFloat) } } @@ -1727,26 +1730,26 @@ impl ToAzureIntSize for Size2D<AzFloat> { } trait ToSideOffsetsPixels { - fn to_float_pixels(&self, pixels_per_px: f32) -> SideOffsets2D<AzFloat>; + fn to_float_pixels(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> SideOffsets2D<AzFloat>; } impl ToSideOffsetsPixels for SideOffsets2D<Au> { - fn to_float_pixels(&self, pixels_per_px: f32) -> SideOffsets2D<AzFloat> { - SideOffsets2D::new(self.top.to_nearest_pixel(pixels_per_px) as AzFloat, - self.right.to_nearest_pixel(pixels_per_px) as AzFloat, - self.bottom.to_nearest_pixel(pixels_per_px) as AzFloat, - self.left.to_nearest_pixel(pixels_per_px) as AzFloat) + fn to_float_pixels(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> SideOffsets2D<AzFloat> { + SideOffsets2D::new(self.top.to_nearest_pixel(pixels_per_px.get()) as AzFloat, + self.right.to_nearest_pixel(pixels_per_px.get()) as AzFloat, + self.bottom.to_nearest_pixel(pixels_per_px.get()) as AzFloat, + self.left.to_nearest_pixel(pixels_per_px.get()) as AzFloat) } } trait ToRadiiPixels { - fn to_radii_pixels(&self, pixels_per_px: f32) -> BorderRadii<AzFloat>; + fn to_radii_pixels(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> BorderRadii<AzFloat>; } impl ToRadiiPixels for BorderRadii<Au> { - fn to_radii_pixels(&self, pixels_per_px: f32) -> BorderRadii<AzFloat> { + fn to_radii_pixels(&self, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> BorderRadii<AzFloat> { let to_nearest_px = |x: Au| -> AzFloat { - x.to_nearest_pixel(pixels_per_px) as AzFloat + x.to_nearest_pixel(pixels_per_px.get()) as AzFloat }; BorderRadii { @@ -1846,17 +1849,17 @@ trait DrawTargetExtensions { fn create_rectangular_border_path(&self, outer_rect: &Rect<Au>, inner_rect: &Rect<Au>, - pixels_per_px: f32) -> Path; + pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Path; /// Creates and returns a path that represents a rectangle. - fn create_rectangular_path(&self, rect: &Rect<Au>, pixels_per_px: f32) -> Path; + fn create_rectangular_path(&self, rect: &Rect<Au>, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Path; } impl DrawTargetExtensions for DrawTarget { fn create_rectangular_border_path(&self, outer_rect: &Rect<Au>, inner_rect: &Rect<Au>, - pixels_per_px: f32) -> Path { + pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Path { // +-----------+ // |2 |1 // | | @@ -1885,7 +1888,7 @@ impl DrawTargetExtensions for DrawTarget { path_builder.finish() } - fn create_rectangular_path(&self, rect: &Rect<Au>, pixels_per_px: f32) -> Path { + fn create_rectangular_path(&self, rect: &Rect<Au>, pixels_per_px: ScaleFactor<PagePx, ScreenPx, f32>) -> Path { // Explicitly round to the nearest non-empty rect because when drawing // box-shadow the rect height can be between 0.5px & 1px and could // otherwise round to an empty rect. diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs index 331c25a0be2..cd996c5b8f2 100644 --- a/components/gfx/paint_task.rs +++ b/components/gfx/paint_task.rs @@ -681,8 +681,8 @@ impl WorkerThread { let mut paint_context = PaintContext { draw_target: draw_target.clone(), font_context: &mut self.font_context, - page_rect: tile.page_rect, - screen_rect: tile.screen_rect, + page_rect: Rect::from_untyped(&tile.page_rect), + screen_rect: Rect::from_untyped(&tile.screen_rect), clip_rect: None, transient_clip: None, layer_kind: layer_kind, diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index df5c84d3778..48f6ed39032 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -79,6 +79,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::rc::Rc; use std::sync::Arc; +use std::sync::atomic::AtomicBool; use std::sync::mpsc::{Receiver, Sender}; use string_cache::{Atom, Namespace}; use style::properties::PropertyDeclarationBlock; @@ -246,7 +247,7 @@ impl<A: JSTraceable, B: JSTraceable> JSTraceable for (A, B) { } -no_jsmanaged_fields!(bool, f32, f64, String, Url); +no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool); no_jsmanaged_fields!(usize, u8, u16, u32, u64); no_jsmanaged_fields!(isize, i8, i16, i32, i64); no_jsmanaged_fields!(Sender<T>); diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 098938c8285..e05a50e6027 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -110,13 +110,16 @@ impl HTMLImageElement { let trusted_node = Trusted::new(window.get_cx(), self, window.script_chan()); let (responder_sender, responder_receiver) = ipc::channel().unwrap(); let script_chan = window.script_chan(); + let wrapper = window.get_runnable_wrapper(); ROUTER.add_route(responder_receiver.to_opaque(), box move |message| { // Return the image via a message to the script task, which marks the element // as dirty and triggers a reflow. let image_response = message.to().unwrap(); - script_chan.send(CommonScriptMsg::RunnableMsg(UpdateReplacedElement, - box ImageResponseHandlerRunnable::new( - trusted_node.clone(), image_response))).unwrap(); + let runnable = ImageResponseHandlerRunnable::new( + trusted_node.clone(), image_response); + let runnable = wrapper.wrap_runnable(runnable); + script_chan.send(CommonScriptMsg::RunnableMsg( + UpdateReplacedElement, runnable)).unwrap(); }); image_cache.request_image(img_url, diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 93b8741f243..e296cea77d5 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -53,7 +53,7 @@ use num::traits::ToPrimitive; use page::Page; use profile_traits::mem; use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64}; -use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg}; +use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg, RunnableWrapper}; use script_task::{SendableMainThreadScriptChan, MainThreadScriptChan, MainThreadTimerEventChan}; use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource}; use selectors::parser::PseudoElement; @@ -66,6 +66,7 @@ use std::ffi::CString; use std::io::{Write, stderr, stdout}; use std::rc::Rc; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use std::sync::mpsc::{Sender, channel}; use string_cache::Atom; @@ -209,7 +210,11 @@ pub struct Window { /// The current state of the window object current_state: Cell<WindowState>, - current_viewport: Cell<Rect<Au>> + current_viewport: Cell<Rect<Au>>, + + /// A flag to prevent async events from attempting to interact with this window. + #[ignore_heap_size_of = "defined in std"] + ignore_further_async_events: Arc<AtomicBool>, } impl Window { @@ -219,6 +224,7 @@ impl Window { *self.js_runtime.borrow_for_script_deallocation() = None; self.browsing_context.set(None); self.current_state.set(WindowState::Zombie); + self.ignore_further_async_events.store(true, Ordering::Relaxed); } } @@ -781,6 +787,12 @@ impl<'a, T: Reflectable> ScriptHelpers for &'a T { } impl Window { + pub fn get_runnable_wrapper(&self) -> RunnableWrapper { + RunnableWrapper { + cancelled: self.ignore_further_async_events.clone() + } + } + pub fn clear_js_runtime(&self) { self.Document().upcast::<Node>().teardown(); @@ -798,6 +810,7 @@ impl Window { self.current_state.set(WindowState::Zombie); *self.js_runtime.borrow_mut() = None; self.browsing_context.set(None); + self.ignore_further_async_events.store(true, Ordering::Relaxed); } /// https://drafts.csswg.org/cssom-view/#dom-window-scroll @@ -1258,6 +1271,8 @@ impl Window { devtools_markers: DOMRefCell::new(HashSet::new()), devtools_wants_updates: Cell::new(false), webdriver_script_chan: DOMRefCell::new(None), + + ignore_further_async_events: Arc::new(AtomicBool::new(false)), }; WindowBinding::Wrap(runtime.cx(), win) diff --git a/components/script/script_task.rs b/components/script/script_task.rs index ac67e4a5996..dda6faaa85b 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -95,6 +95,7 @@ use std::option::Option; use std::ptr; use std::rc::Rc; use std::result::Result; +use std::sync::atomic::{Ordering, AtomicBool}; use std::sync::mpsc::{Receiver, Select, Sender, channel}; use std::sync::{Arc, Mutex}; use string_cache::Atom; @@ -158,7 +159,38 @@ impl InProgressLoad { } } +/// Encapsulated state required to create cancellable runnables from non-script threads. +pub struct RunnableWrapper { + pub cancelled: Arc<AtomicBool>, +} + +impl RunnableWrapper { + pub fn wrap_runnable<T: Runnable + Send + 'static>(&self, runnable: T) -> Box<Runnable + Send> { + box CancellableRunnable { + cancelled: self.cancelled.clone(), + inner: box runnable, + } + } +} + +/// A runnable that can be discarded by toggling a shared flag. +pub struct CancellableRunnable<T: Runnable + Send> { + cancelled: Arc<AtomicBool>, + inner: Box<T>, +} + +impl<T: Runnable + Send> Runnable for CancellableRunnable<T> { + fn is_cancelled(&self) -> bool { + self.cancelled.load(Ordering::Relaxed) + } + + fn handler(self: Box<CancellableRunnable<T>>) { + self.inner.handler() + } +} + pub trait Runnable { + fn is_cancelled(&self) -> bool { false } fn handler(self: Box<Self>); } @@ -990,10 +1022,13 @@ impl ScriptTask { runnable.handler(self), MainThreadScriptMsg::DocumentLoadsComplete(id) => self.handle_loads_complete(id), - MainThreadScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => + MainThreadScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => { // The category of the runnable is ignored by the pattern, however // it is still respected by profiling (see categorize_msg). - runnable.handler(), + if !runnable.is_cancelled() { + runnable.handler() + } + } MainThreadScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => LiveDOMReferences::cleanup(addr), MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => |