aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/atoms/static_atoms.txt2
-rw-r--r--components/canvas/Cargo.toml4
-rw-r--r--components/canvas/canvas_paint_thread.rs32
-rw-r--r--components/canvas/webgl_paint_thread.rs8
-rw-r--r--components/canvas_traits/Cargo.toml2
-rw-r--r--components/canvas_traits/lib.rs9
-rw-r--r--components/compositing/Cargo.toml2
-rw-r--r--components/compositing/compositor.rs34
-rw-r--r--components/compositing/compositor_thread.rs3
-rw-r--r--components/compositing/lib.rs2
-rw-r--r--components/compositing/touch.rs14
-rw-r--r--components/compositing/windowing.rs5
-rw-r--r--components/config/Cargo.toml2
-rw-r--r--components/config/opts.rs2
-rw-r--r--components/constellation/Cargo.toml4
-rw-r--r--components/constellation/browsingcontext.rs2
-rw-r--r--components/constellation/constellation.rs3
-rw-r--r--components/constellation/pipeline.rs3
-rw-r--r--components/geometry/Cargo.toml2
-rw-r--r--components/geometry/lib.rs4
-rw-r--r--components/gfx/Cargo.toml2
-rw-r--r--components/gfx/display_list/mod.rs42
-rw-r--r--components/gfx/platform/windows/font.rs2
-rw-r--r--components/gfx/text/glyph.rs2
-rw-r--r--components/layout/Cargo.toml4
-rw-r--r--components/layout/block.rs20
-rw-r--r--components/layout/display_list_builder.rs35
-rw-r--r--components/layout/flex.rs4
-rw-r--r--components/layout/floats.rs2
-rw-r--r--components/layout/flow.rs14
-rw-r--r--components/layout/fragment.rs64
-rw-r--r--components/layout/inline.rs12
-rw-r--r--components/layout/list_item.rs2
-rw-r--r--components/layout/model.rs14
-rw-r--r--components/layout/multicol.rs5
-rw-r--r--components/layout/query.rs10
-rw-r--r--components/layout/sequential.rs10
-rw-r--r--components/layout/table.rs2
-rw-r--r--components/layout/table_row.rs2
-rw-r--r--components/layout/webrender_helpers.rs14
-rw-r--r--components/layout/wrapper.rs11
-rw-r--r--components/layout_thread/Cargo.toml4
-rw-r--r--components/layout_thread/lib.rs20
-rw-r--r--components/net_traits/image/base.rs12
-rw-r--r--components/net_traits/image_cache.rs4
-rw-r--r--components/script/Cargo.toml4
-rw-r--r--components/script/dom/bindings/trace.rs17
-rw-r--r--components/script/dom/canvaspattern.rs2
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs23
-rw-r--r--components/script/dom/css.rs3
-rw-r--r--components/script/dom/cssmediarule.rs4
-rw-r--r--components/script/dom/cssstyledeclaration.rs5
-rw-r--r--components/script/dom/csssupportsrule.rs4
-rw-r--r--components/script/dom/document.rs23
-rw-r--r--components/script/dom/dommatrix.rs6
-rw-r--r--components/script/dom/dommatrixreadonly.rs73
-rw-r--r--components/script/dom/element.rs37
-rw-r--r--components/script/dom/eventsource.rs2
-rw-r--r--components/script/dom/htmlareaelement.rs2
-rw-r--r--components/script/dom/htmlcanvaselement.rs2
-rw-r--r--components/script/dom/htmlcollection.rs7
-rw-r--r--components/script/dom/htmlimageelement.rs3
-rw-r--r--components/script/dom/htmllinkelement.rs3
-rw-r--r--components/script/dom/htmlmetaelement.rs3
-rw-r--r--components/script/dom/htmlstyleelement.rs3
-rw-r--r--components/script/dom/imagedata.rs2
-rw-r--r--components/script/dom/medialist.rs4
-rw-r--r--components/script/dom/node.rs6
-rw-r--r--components/script/dom/webglrenderingcontext.rs2
-rw-r--r--components/script/dom/window.rs21
-rw-r--r--components/script/dom/xmlhttprequest.rs2
-rw-r--r--components/script/layout_wrapper.rs30
-rw-r--r--components/script/script_runtime.rs10
-rw-r--r--components/script/script_thread.rs18
-rw-r--r--components/script/stylesheet_loader.rs2
-rw-r--r--components/script/task_source/dom_manipulation.rs13
-rw-r--r--components/script/task_source/user_interaction.rs13
-rw-r--r--components/script/timers.rs2
-rw-r--r--components/script/webdriver_handlers.rs4
-rw-r--r--components/script_layout_interface/Cargo.toml2
-rw-r--r--components/script_layout_interface/message.rs3
-rw-r--r--components/script_layout_interface/rpc.rs3
-rw-r--r--components/script_traits/Cargo.toml4
-rw-r--r--components/script_traits/lib.rs15
-rw-r--r--components/script_traits/script_msg.rs3
-rw-r--r--components/script_traits/webdriver_msg.rs2
-rw-r--r--components/selectors/attr.rs4
-rw-r--r--components/selectors/bloom.rs7
-rw-r--r--components/selectors/context.rs158
-rw-r--r--components/selectors/lib.rs1
-rw-r--r--components/selectors/matching.rs279
-rw-r--r--components/selectors/parser.rs79
-rw-r--r--components/selectors/tree.rs12
-rw-r--r--components/servo/Cargo.toml2
-rw-r--r--components/style/Cargo.toml10
-rw-r--r--components/style/animation.rs36
-rw-r--r--components/style/applicable_declarations.rs137
-rw-r--r--components/style/bezier.rs2
-rw-r--r--components/style/context.rs3
-rw-r--r--components/style/data.rs354
-rw-r--r--components/style/dom.rs11
-rw-r--r--components/style/encoding_support.rs2
-rw-r--r--components/style/gecko/conversions.rs11
-rw-r--r--components/style/gecko/generated/bindings.rs20
-rw-r--r--components/style/gecko/generated/structs_debug.rs118
-rw-r--r--components/style/gecko/generated/structs_release.rs118
-rw-r--r--components/style/gecko/global_style_data.rs6
-rw-r--r--components/style/gecko/snapshot.rs24
-rw-r--r--components/style/gecko/snapshot_helpers.rs8
-rw-r--r--components/style/gecko/values.rs22
-rw-r--r--components/style/gecko/wrapper.rs127
-rw-r--r--components/style/gecko_bindings/sugar/ns_css_value.rs5
-rw-r--r--components/style/gecko_bindings/sugar/ns_timing_function.rs2
-rw-r--r--components/style/gecko_string_cache/mod.rs80
-rw-r--r--components/style/invalidation/element/element_wrapper.rs341
-rw-r--r--components/style/invalidation/element/invalidation_map.rs402
-rw-r--r--components/style/invalidation/element/invalidator.rs702
-rw-r--r--components/style/invalidation/element/mod.rs10
-rw-r--r--components/style/invalidation/element/restyle_hints.rs243
-rw-r--r--components/style/invalidation/mod.rs1
-rw-r--r--components/style/invalidation/stylesheets.rs49
-rw-r--r--components/style/lib.rs20
-rw-r--r--components/style/logical_geometry.rs3
-rw-r--r--components/style/matching.rs109
-rw-r--r--components/style/parallel.rs86
-rw-r--r--components/style/parser.rs32
-rw-r--r--components/style/properties/data.py22
-rw-r--r--components/style/properties/declaration_block.rs67
-rw-r--r--components/style/properties/gecko.mako.rs46
-rw-r--r--components/style/properties/helpers.mako.rs5
-rw-r--r--components/style/properties/helpers/animated_properties.mako.rs384
-rw-r--r--components/style/properties/longhand/box.mako.rs29
-rw-r--r--components/style/properties/longhand/font.mako.rs79
-rw-r--r--components/style/properties/longhand/list.mako.rs2
-rw-r--r--components/style/properties/properties.mako.rs108
-rw-r--r--components/style/properties/shorthand/font.mako.rs125
-rw-r--r--components/style/properties/shorthand/list.mako.rs2
-rw-r--r--components/style/restyle_hints.rs1271
-rw-r--r--components/style/rule_tree/mod.rs83
-rw-r--r--components/style/selector_map.rs154
-rw-r--r--components/style/servo/selector_parser.rs34
-rw-r--r--components/style/sharing/mod.rs58
-rw-r--r--components/style/style_adjuster.rs31
-rw-r--r--components/style/stylesheets/keyframes_rule.rs24
-rw-r--r--components/style/stylesheets/mod.rs6
-rw-r--r--components/style/stylesheets/rule_parser.rs47
-rw-r--r--components/style/stylesheets/rules_iterator.rs38
-rw-r--r--components/style/stylesheets/stylesheet.rs16
-rw-r--r--components/style/stylesheets/viewport_rule.rs15
-rw-r--r--components/style/stylist.rs262
-rw-r--r--components/style/traversal.rs187
-rw-r--r--components/style/values/computed/length.rs142
-rw-r--r--components/style/values/computed/mod.rs2
-rw-r--r--components/style/values/computed/position.rs6
-rw-r--r--components/style/values/computed/transform.rs7
-rw-r--r--components/style/values/generics/basic_shape.rs13
-rw-r--r--components/style/values/generics/gecko.rs25
-rw-r--r--components/style/values/generics/grid.rs12
-rw-r--r--components/style/values/generics/image.rs30
-rw-r--r--components/style/values/generics/mod.rs30
-rw-r--r--components/style/values/specified/calc.rs11
-rw-r--r--components/style/values/specified/length.rs47
-rw-r--r--components/style/values/specified/mod.rs27
-rw-r--r--components/style/values/specified/position.rs10
-rw-r--r--components/style/values/specified/transform.rs6
-rw-r--r--components/style_derive/lib.rs2
-rw-r--r--components/style_derive/to_css.rs65
-rw-r--r--components/style_traits/Cargo.toml3
-rw-r--r--components/style_traits/lib.rs30
-rw-r--r--components/style_traits/values.rs11
-rw-r--r--components/style_traits/viewport.rs13
-rw-r--r--components/webdriver_server/Cargo.toml2
172 files changed, 4623 insertions, 3441 deletions
diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt
index a41aae29289..56d949eff5d 100644
--- a/components/atoms/static_atoms.txt
+++ b/components/atoms/static_atoms.txt
@@ -68,3 +68,5 @@ fullscreenchange
fullscreenerror
gattserverdisconnected
onchange
+
+reftest-wait
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml
index 439064f31a1..2e5d1f662f6 100644
--- a/components/canvas/Cargo.toml
+++ b/components/canvas/Cargo.toml
@@ -13,11 +13,11 @@ path = "lib.rs"
azure = {git = "https://github.com/servo/rust-azure"}
canvas_traits = {path = "../canvas_traits"}
cssparser = "0.14.0"
-euclid = "0.13"
+euclid = "0.14.4"
gleam = "0.4"
ipc-channel = "0.7"
log = "0.3.5"
num-traits = "0.1.32"
-offscreen_gl_context = "0.8"
+offscreen_gl_context = { version = "0.9", features = ["serde"] }
servo_config = {path = "../config"}
webrender_traits = {git = "https://github.com/servo/webrender", features = ["ipc"]}
diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs
index 39c633e127b..94a235d78ce 100644
--- a/components/canvas/canvas_paint_thread.rs
+++ b/components/canvas/canvas_paint_thread.rs
@@ -10,10 +10,7 @@ use azure::azure_hl::{ExtendMode, GradientStop, LinearGradientPattern, RadialGra
use azure::azure_hl::SurfacePattern;
use canvas_traits::*;
use cssparser::RGBA;
-use euclid::matrix2d::Matrix2D;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D};
use ipc_channel::ipc::{self, IpcSender};
use num_traits::ToPrimitive;
use std::borrow::ToOwned;
@@ -71,7 +68,7 @@ struct CanvasPaintState<'a> {
stroke_style: Pattern,
stroke_opts: StrokeOptions<'a>,
/// The current 2D transform matrix.
- transform: Matrix2D<f32>,
+ transform: Transform2D<f32>,
shadow_offset_x: f64,
shadow_offset_y: f64,
shadow_blur: f64,
@@ -91,7 +88,7 @@ impl<'a> CanvasPaintState<'a> {
fill_style: Pattern::Color(ColorPattern::new(Color::black())),
stroke_style: Pattern::Color(ColorPattern::new(Color::black())),
stroke_opts: StrokeOptions::new(1.0, JoinStyle::MiterOrBevel, CapStyle::Butt, 10.0, &[]),
- transform: Matrix2D::identity(),
+ transform: Transform2D::identity(),
shadow_offset_x: 0.0,
shadow_offset_y: 0.0,
shadow_blur: 0.0,
@@ -528,7 +525,7 @@ impl<'a> CanvasPaintThread<'a> {
self.state.stroke_opts.miter_limit = limit;
}
- fn set_transform(&mut self, transform: &Matrix2D<f32>) {
+ fn set_transform(&mut self, transform: &Transform2D<f32>) {
self.state.transform = transform.clone();
self.drawtarget.set_transform(transform)
}
@@ -547,6 +544,10 @@ impl<'a> CanvasPaintThread<'a> {
fn recreate(&mut self, size: Size2D<i32>) {
self.drawtarget = CanvasPaintThread::create(size);
+ // Webrender doesn't let images change size, so we clear the webrender image key.
+ if let Some(image_key) = self.image_key.take() {
+ self.webrender_api.delete_image(image_key);
+ }
}
fn send_pixels(&mut self, chan: IpcSender<Option<Vec<u8>>>) {
@@ -602,7 +603,7 @@ impl<'a> CanvasPaintThread<'a> {
// https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata
fn put_image_data(&mut self, imagedata: Vec<u8>,
- offset: Point2D<f64>,
+ offset: Vector2D<f64>,
image_data_size: Size2D<f64>,
mut dirty_rect: Rect<f64>) {
if image_data_size.width <= 0.0 || image_data_size.height <= 0.0 {
@@ -716,9 +717,8 @@ impl<'a> CanvasPaintThread<'a> {
let draw_target = self.drawtarget.create_similar_draw_target(&Size2D::new(source_rect.size.width as i32,
source_rect.size.height as i32),
self.drawtarget.get_format());
- let matrix = Matrix2D::identity()
- .pre_translated(-source_rect.origin.x as AzFloat,
- -source_rect.origin.y as AzFloat)
+ let matrix = Transform2D::identity()
+ .pre_translate(-source_rect.origin.to_vector().cast().unwrap())
.pre_mul(&self.state.transform);
draw_target.set_transform(&matrix);
draw_target
@@ -734,8 +734,8 @@ impl<'a> CanvasPaintThread<'a> {
&Point2D::new(shadow_src_rect.origin.x as AzFloat,
shadow_src_rect.origin.y as AzFloat),
&self.state.shadow_color,
- &Point2D::new(self.state.shadow_offset_x as AzFloat,
- self.state.shadow_offset_y as AzFloat),
+ &Vector2D::new(self.state.shadow_offset_x as AzFloat,
+ self.state.shadow_offset_y as AzFloat),
(self.state.shadow_blur / 2.0f64) as AzFloat,
self.state.draw_options.composition);
}
@@ -997,7 +997,7 @@ impl ToAzurePattern for FillOrStrokeStyle {
&Point2D::new(linear_gradient_style.x0 as AzFloat, linear_gradient_style.y0 as AzFloat),
&Point2D::new(linear_gradient_style.x1 as AzFloat, linear_gradient_style.y1 as AzFloat),
drawtarget.create_gradient_stops(&gradient_stops, ExtendMode::Clamp),
- &Matrix2D::identity())))
+ &Transform2D::identity())))
},
FillOrStrokeStyle::RadialGradient(ref radial_gradient_style) => {
let gradient_stops: Vec<GradientStop> = radial_gradient_style.stops.iter().map(|s| {
@@ -1012,7 +1012,7 @@ impl ToAzurePattern for FillOrStrokeStyle {
&Point2D::new(radial_gradient_style.x1 as AzFloat, radial_gradient_style.y1 as AzFloat),
radial_gradient_style.r0 as AzFloat, radial_gradient_style.r1 as AzFloat,
drawtarget.create_gradient_stops(&gradient_stops, ExtendMode::Clamp),
- &Matrix2D::identity())))
+ &Transform2D::identity())))
},
FillOrStrokeStyle::Surface(ref surface_style) => {
drawtarget.create_source_surface_from_data(&surface_style.surface_data,
@@ -1024,7 +1024,7 @@ impl ToAzurePattern for FillOrStrokeStyle {
source_surface.azure_source_surface,
surface_style.repeat_x,
surface_style.repeat_y,
- &Matrix2D::identity()))
+ &Transform2D::identity()))
})
}
}
diff --git a/components/canvas/webgl_paint_thread.rs b/components/canvas/webgl_paint_thread.rs
index 9f87d3a4f51..db509945100 100644
--- a/components/canvas/webgl_paint_thread.rs
+++ b/components/canvas/webgl_paint_thread.rs
@@ -4,7 +4,7 @@
use canvas_traits::{CanvasCommonMsg, CanvasData, CanvasMsg, CanvasImageData};
use canvas_traits::{FromLayoutMsg, FromScriptMsg, byte_swap};
-use euclid::size::Size2D;
+use euclid::Size2D;
use gleam::gl;
use ipc_channel::ipc::{self, IpcSender};
use offscreen_gl_context::{ColorAttachmentType, GLContext, GLLimits};
@@ -291,7 +291,7 @@ impl WebGLPaintThread {
#[allow(unsafe_code)]
fn recreate(&mut self, size: Size2D<i32>) -> Result<(), &'static str> {
match self.data {
- WebGLPaintTaskData::Readback(ref mut context, _, _) => {
+ WebGLPaintTaskData::Readback(ref mut context, ref webrender_api, ref mut image_key) => {
if size.width > self.size.width ||
size.height > self.size.height {
self.size = try!(context.resize(size));
@@ -299,6 +299,10 @@ impl WebGLPaintThread {
self.size = size;
context.gl().scissor(0, 0, size.width, size.height);
}
+ // Webrender doesn't let images change size, so we clear the webrender image key.
+ if let Some(image_key) = image_key.take() {
+ webrender_api.delete_image(image_key);
+ }
}
WebGLPaintTaskData::WebRender(ref api, id) => {
let device_size = webrender_traits::DeviceIntSize::from_untyped(&size);
diff --git a/components/canvas_traits/Cargo.toml b/components/canvas_traits/Cargo.toml
index 3b1c3a0ff02..81f463ae2aa 100644
--- a/components/canvas_traits/Cargo.toml
+++ b/components/canvas_traits/Cargo.toml
@@ -11,7 +11,7 @@ path = "lib.rs"
[dependencies]
cssparser = "0.14.0"
-euclid = "0.13"
+euclid = "0.14.4"
heapsize = "0.4"
heapsize_derive = "0.1"
ipc-channel = "0.7"
diff --git a/components/canvas_traits/lib.rs b/components/canvas_traits/lib.rs
index 3d74b84f475..3a143001d10 100644
--- a/components/canvas_traits/lib.rs
+++ b/components/canvas_traits/lib.rs
@@ -16,10 +16,7 @@ extern crate ipc_channel;
extern crate webrender_traits;
use cssparser::RGBA;
-use euclid::matrix2d::Matrix2D;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D};
use ipc_channel::ipc::IpcSender;
use std::default::Default;
use std::str::FromStr;
@@ -87,7 +84,7 @@ pub enum Canvas2dMsg {
IsPointInPath(f64, f64, FillRule, IpcSender<bool>),
LineTo(Point2D<f32>),
MoveTo(Point2D<f32>),
- PutImageData(Vec<u8>, Point2D<f64>, Size2D<f64>, Rect<f64>),
+ PutImageData(Vec<u8>, Vector2D<f64>, Size2D<f64>, Rect<f64>),
QuadraticCurveTo(Point2D<f32>, Point2D<f32>),
Rect(Rect<f32>),
RestoreContext,
@@ -102,7 +99,7 @@ pub enum Canvas2dMsg {
SetMiterLimit(f32),
SetGlobalAlpha(f32),
SetGlobalComposition(CompositionOrBlending),
- SetTransform(Matrix2D<f32>),
+ SetTransform(Transform2D<f32>),
SetShadowOffsetX(f64),
SetShadowOffsetY(f64),
SetShadowBlur(f64),
diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml
index aa8ad1bb6bf..3f2c1b87621 100644
--- a/components/compositing/Cargo.toml
+++ b/components/compositing/Cargo.toml
@@ -10,7 +10,7 @@ name = "compositing"
path = "lib.rs"
[dependencies]
-euclid = "0.13"
+euclid = "0.14"
gfx_traits = {path = "../gfx_traits"}
gleam = "0.4"
image = "0.12"
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 1baa297f191..5cb72fce030 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -7,11 +7,7 @@ use SendableFrameTree;
use compositor_thread::{CompositorProxy, CompositorReceiver};
use compositor_thread::{InitialCompositorState, Msg, RenderListener};
use delayed_composition::DelayedCompositionTimerProxy;
-use euclid::Point2D;
-use euclid::point::TypedPoint2D;
-use euclid::rect::TypedRect;
-use euclid::scale_factor::ScaleFactor;
-use euclid::size::TypedSize2D;
+use euclid::{Point2D, TypedPoint2D, TypedVector2D, TypedRect, ScaleFactor, TypedSize2D};
use gfx_traits::Epoch;
use gleam::gl;
use image::{DynamicImage, ImageFormat, RgbImage};
@@ -39,7 +35,7 @@ use style_traits::viewport::ViewportConstraints;
use time::{precise_time_ns, precise_time_s};
use touch::{TouchHandler, TouchAction};
use webrender;
-use webrender_traits::{self, ClipId, LayoutPoint, ScrollEventPhase, ScrollLocation, ScrollClamping};
+use webrender_traits::{self, ClipId, LayoutPoint, LayoutVector2D, ScrollEventPhase, ScrollLocation, ScrollClamping};
use windowing::{self, MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg};
#[derive(Debug, PartialEq)]
@@ -1009,10 +1005,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
match self.touch_handler.on_touch_move(identifier, point) {
TouchAction::Scroll(delta) => {
match point.cast() {
- Some(point) => self.on_scroll_window_event(ScrollLocation::Delta(
- webrender_traits::LayerPoint::from_untyped(
- &delta.to_untyped())),
- point),
+ Some(point) => self.on_scroll_window_event(
+ ScrollLocation::Delta(
+ LayoutVector2D::from_untyped(&delta.to_untyped())
+ ),
+ point
+ ),
None => error!("Point cast failed."),
}
}
@@ -1020,7 +1018,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let cursor = TypedPoint2D::new(-1, -1); // Make sure this hits the base layer.
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: magnification,
- scroll_location: ScrollLocation::Delta(webrender_traits::LayerPoint::from_untyped(
+ scroll_location: ScrollLocation::Delta(webrender_traits::LayoutVector2D::from_untyped(
&scroll_delta.to_untyped())),
cursor: cursor,
phase: ScrollEventPhase::Move(true),
@@ -1155,9 +1153,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
break;
}
};
- let delta = (TypedPoint2D::from_untyped(&combined_delta.to_untyped()) / self.scale)
- .to_untyped();
- let delta = webrender_traits::LayerPoint::from_untyped(&delta);
+ // TODO: units don't match!
+ let delta = combined_delta / self.scale.get();
+
let cursor =
(combined_event.cursor.to_f32() / self.scale).to_untyped();
let location = webrender_traits::ScrollLocation::Delta(delta);
@@ -1171,7 +1169,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
(last_combined_event @ &mut None, _) => {
*last_combined_event = Some(ScrollZoomEvent {
magnification: scroll_event.magnification,
- scroll_location: ScrollLocation::Delta(webrender_traits::LayerPoint::from_untyped(
+ scroll_location: ScrollLocation::Delta(webrender_traits::LayoutVector2D::from_untyped(
&this_delta.to_untyped())),
cursor: this_cursor,
phase: scroll_event.phase,
@@ -1208,9 +1206,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if let Some(combined_event) = last_combined_event {
let scroll_location = match combined_event.scroll_location {
ScrollLocation::Delta(delta) => {
- let scaled_delta = (TypedPoint2D::from_untyped(&delta.to_untyped()) / self.scale)
+ let scaled_delta = (TypedVector2D::from_untyped(&delta.to_untyped()) / self.scale)
.to_untyped();
- let calculated_delta = webrender_traits::LayoutPoint::from_untyped(&scaled_delta);
+ let calculated_delta = webrender_traits::LayoutVector2D::from_untyped(&scaled_delta);
ScrollLocation::Delta(calculated_delta)
},
// Leave ScrollLocation unchanged if it is Start or End location.
@@ -1318,7 +1316,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn on_pinch_zoom_window_event(&mut self, magnification: f32) {
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: magnification,
- scroll_location: ScrollLocation::Delta(TypedPoint2D::zero()), // TODO: Scroll to keep the center in view?
+ scroll_location: ScrollLocation::Delta(TypedVector2D::zero()), // TODO: Scroll to keep the center in view?
cursor: TypedPoint2D::new(-1, -1), // Make sure this hits the base layer.
phase: ScrollEventPhase::Move(true),
event_count: 1,
diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs
index 53706ae4483..4ff18471ea8 100644
--- a/components/compositing/compositor_thread.rs
+++ b/components/compositing/compositor_thread.rs
@@ -6,8 +6,7 @@
use SendableFrameTree;
use compositor::CompositingReason;
-use euclid::point::Point2D;
-use euclid::size::Size2D;
+use euclid::{Point2D, Size2D};
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::{Key, KeyModifiers, KeyState, PipelineId};
use net_traits::image::base::Image;
diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs
index e4307ff04cc..a11c444609b 100644
--- a/components/compositing/lib.rs
+++ b/components/compositing/lib.rs
@@ -26,7 +26,7 @@ extern crate webrender_traits;
pub use compositor_thread::CompositorProxy;
pub use compositor::IOCompositor;
-use euclid::size::TypedSize2D;
+use euclid::TypedSize2D;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::TopLevelBrowsingContextId;
diff --git a/components/compositing/touch.rs b/components/compositing/touch.rs
index 12bc365c77f..1edafa017c8 100644
--- a/components/compositing/touch.rs
+++ b/components/compositing/touch.rs
@@ -2,8 +2,8 @@
* 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 euclid::point::TypedPoint2D;
-use euclid::scale_factor::ScaleFactor;
+use euclid::{TypedPoint2D, TypedVector2D};
+use euclid::ScaleFactor;
use script_traits::{DevicePixel, EventResult, TouchId};
use self::TouchState::*;
@@ -56,9 +56,9 @@ pub enum TouchAction {
/// Simulate a mouse click.
Click,
/// Scroll by the provided offset.
- Scroll(TypedPoint2D<f32, DevicePixel>),
+ Scroll(TypedVector2D<f32, DevicePixel>),
/// Zoom by a magnification factor and scroll by the provided offset.
- Zoom(f32, TypedPoint2D<f32, DevicePixel>),
+ Zoom(f32, TypedVector2D<f32, DevicePixel>),
/// Send a JavaScript event to content.
DispatchEvent,
/// Don't do anything.
@@ -221,10 +221,8 @@ impl TouchHandler {
debug_assert!(self.touch_count() == 2);
let p0 = self.active_touch_points[0].point;
let p1 = self.active_touch_points[1].point;
- let center = (p0 + p1) / ScaleFactor::new(2.0);
-
- let d = p0 - p1;
- let distance = f32::sqrt(d.x * d.x + d.y * d.y);
+ let center = p0.lerp(p1, 0.5);
+ let distance = (p0 - p1).length();
(distance, center)
}
diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs
index e4c75762379..436f084fecf 100644
--- a/components/compositing/windowing.rs
+++ b/components/compositing/windowing.rs
@@ -6,10 +6,7 @@
use compositor_thread::EventLoopWaker;
use euclid::{Point2D, Size2D};
-use euclid::point::TypedPoint2D;
-use euclid::rect::TypedRect;
-use euclid::scale_factor::ScaleFactor;
-use euclid::size::TypedSize2D;
+use euclid::{TypedPoint2D, TypedRect, ScaleFactor, TypedSize2D};
use gleam::gl;
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use net_traits::net_error_list::NetError;
diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml
index c3b7e403256..804633d949e 100644
--- a/components/config/Cargo.toml
+++ b/components/config/Cargo.toml
@@ -10,7 +10,7 @@ name = "servo_config"
path = "lib.rs"
[dependencies]
-euclid = "0.13"
+euclid = "0.14.4"
getopts = "0.2.11"
lazy_static = "0.2"
log = "0.3.5"
diff --git a/components/config/opts.rs b/components/config/opts.rs
index d01a5dae480..d257206092e 100644
--- a/components/config/opts.rs
+++ b/components/config/opts.rs
@@ -5,7 +5,7 @@
//! Configuration options for a single run of the servo application. Created
//! from command line arguments.
-use euclid::size::TypedSize2D;
+use euclid::TypedSize2D;
use getopts::Options;
use num_cpus;
use prefs::{self, PrefValue, PREFS};
diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml
index cd41655de00..ccb9fb37adf 100644
--- a/components/constellation/Cargo.toml
+++ b/components/constellation/Cargo.toml
@@ -18,7 +18,7 @@ canvas_traits = {path = "../canvas_traits"}
compositing = {path = "../compositing"}
debugger = {path = "../debugger"}
devtools_traits = {path = "../devtools_traits"}
-euclid = "0.13"
+euclid = "0.14.4"
gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"}
hyper = "0.10"
@@ -29,7 +29,7 @@ log = "0.3.5"
msg = {path = "../msg"}
net = {path = "../net"}
net_traits = {path = "../net_traits"}
-offscreen_gl_context = "0.8"
+offscreen_gl_context = { version = "0.9", features = ["serde"] }
profile_traits = {path = "../profile_traits"}
script_traits = {path = "../script_traits"}
serde = "0.9"
diff --git a/components/constellation/browsingcontext.rs b/components/constellation/browsingcontext.rs
index b4569c61b10..2d124b3b605 100644
--- a/components/constellation/browsingcontext.rs
+++ b/components/constellation/browsingcontext.rs
@@ -2,7 +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 euclid::size::TypedSize2D;
+use euclid::TypedSize2D;
use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, PipelineId};
use pipeline::Pipeline;
use script_traits::LoadData;
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 4a1598b0e9c..f4f07c5ea84 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -78,8 +78,7 @@ use compositing::compositor_thread::CompositorProxy;
use compositing::compositor_thread::Msg as ToCompositorMsg;
use debugger;
use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg};
-use euclid::scale_factor::ScaleFactor;
-use euclid::size::{Size2D, TypedSize2D};
+use euclid::{Size2D, TypedSize2D, ScaleFactor};
use event_loop::EventLoop;
use gfx::font_cache_thread::FontCacheThread;
use gfx_traits::Epoch;
diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs
index 1b53e2d06ff..6779cd2060f 100644
--- a/components/constellation/pipeline.rs
+++ b/components/constellation/pipeline.rs
@@ -7,8 +7,7 @@ use compositing::CompositionPipeline;
use compositing::CompositorProxy;
use compositing::compositor_thread::Msg as CompositorMsg;
use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg};
-use euclid::scale_factor::ScaleFactor;
-use euclid::size::TypedSize2D;
+use euclid::{TypedSize2D, ScaleFactor};
use event_loop::EventLoop;
use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::Error;
diff --git a/components/geometry/Cargo.toml b/components/geometry/Cargo.toml
index a6e6b690f85..5c5a327160d 100644
--- a/components/geometry/Cargo.toml
+++ b/components/geometry/Cargo.toml
@@ -15,5 +15,5 @@ servo = ["euclid/unstable"]
[dependencies]
app_units = "0.4.1"
-euclid = "0.13"
+euclid = "0.14"
heapsize = "0.4"
diff --git a/components/geometry/lib.rs b/components/geometry/lib.rs
index 8b98301dde7..c71b687e814 100644
--- a/components/geometry/lib.rs
+++ b/components/geometry/lib.rs
@@ -7,9 +7,7 @@ extern crate euclid;
#[macro_use] extern crate heapsize;
use app_units::{Au, MAX_AU, MIN_AU};
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Point2D, Rect, Size2D};
// Units for use with euclid::length and euclid::scale_factor.
diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml
index 06003773dd5..673bff6fee5 100644
--- a/components/gfx/Cargo.toml
+++ b/components/gfx/Cargo.toml
@@ -13,7 +13,7 @@ path = "lib.rs"
[dependencies]
app_units = "0.4.1"
bitflags = "0.7"
-euclid = "0.13"
+euclid = "0.14.4"
fnv = "1.0"
fontsan = {git = "https://github.com/servo/fontsan"}
gfx_traits = {path = "../gfx_traits"}
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs
index 8ca587bfca4..af17eb4d659 100644
--- a/components/gfx/display_list/mod.rs
+++ b/components/gfx/display_list/mod.rs
@@ -15,10 +15,8 @@
//! low-level drawing primitives.
use app_units::Au;
-use euclid::{Matrix4D, Point2D, Rect, Size2D};
+use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D, TypedRect, SideOffsets2D};
use euclid::num::{One, Zero};
-use euclid::rect::TypedRect;
-use euclid::side_offsets::SideOffsets2D;
use gfx_traits::StackingContextId;
use gfx_traits::print_tree::PrintTree;
use ipc_channel::ipc::IpcSharedMemory;
@@ -66,7 +64,7 @@ impl<'a> ScrollOffsetLookup<'a> {
fn new_for_reference_frame(&mut self,
clip_id: ClipId,
- transform: &Matrix4D<f32>,
+ transform: &Transform3D<f32>,
point: &mut Point2D<Au>)
-> Option<ScrollOffsetLookup> {
// If a transform function causes the current transformation matrix of an object
@@ -79,8 +77,8 @@ impl<'a> ScrollOffsetLookup<'a> {
let scroll_offset = self.full_offset_for_scroll_root(&clip_id);
*point = Point2D::new(point.x - Au::from_f32_px(scroll_offset.x),
point.y - Au::from_f32_px(scroll_offset.y));
- let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(),
- point.y.to_f32_px()));
+ let frac_point = inv_transform.transform_point2d(&Point2D::new(point.x.to_f32_px(),
+ point.y.to_f32_px()));
*point = Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y));
let mut sublookup = ScrollOffsetLookup {
@@ -88,7 +86,7 @@ impl<'a> ScrollOffsetLookup<'a> {
calculated_total_offsets: HashMap::new(),
raw_offsets: self.raw_offsets,
};
- sublookup.calculated_total_offsets.insert(clip_id, Point2D::zero());
+ sublookup.calculated_total_offsets.insert(clip_id, Vector2D::zero());
Some(sublookup)
}
@@ -96,7 +94,7 @@ impl<'a> ScrollOffsetLookup<'a> {
self.parents.insert(scroll_root.id, scroll_root.parent_id);
}
- fn full_offset_for_scroll_root(&mut self, id: &ClipId) -> Point2D<f32> {
+ fn full_offset_for_scroll_root(&mut self, id: &ClipId) -> Vector2D<f32> {
if let Some(offset) = self.calculated_total_offsets.get(id) {
return *offset;
}
@@ -105,11 +103,11 @@ impl<'a> ScrollOffsetLookup<'a> {
let parent_id = *self.parents.get(id).unwrap();
self.full_offset_for_scroll_root(&parent_id)
} else {
- Point2D::zero()
+ Vector2D::zero()
};
let offset = parent_offset +
- self.raw_offsets.get(id).cloned().unwrap_or_else(Point2D::zero);
+ self.raw_offsets.get(id).cloned().unwrap_or_else(Vector2D::zero);
self.calculated_total_offsets.insert(*id, offset);
offset
}
@@ -184,10 +182,10 @@ impl DisplayList {
point: &Point2D<Au>,
offset_lookup: &mut ScrollOffsetLookup,
result: &mut Vec<usize>) {
- let mut point = *point - stacking_context.bounds.origin;
+ let mut point = *point - stacking_context.bounds.origin.to_vector();
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
let old_offset = offset_lookup.calculated_total_offsets.get(&clip_id).cloned();
- offset_lookup.calculated_total_offsets.insert(clip_id, Point2D::zero());
+ offset_lookup.calculated_total_offsets.insert(clip_id, Vector2D::zero());
self.text_index_contents(node, traversal, &point, offset_lookup, result);
@@ -257,10 +255,10 @@ impl DisplayList {
result: &mut Vec<DisplayItemMetadata>) {
debug_assert!(stacking_context.context_type == StackingContextType::Real);
- let mut point = *point - stacking_context.bounds.origin;
+ let mut point = *point - stacking_context.bounds.origin.to_vector();
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
let old_offset = offset_lookup.calculated_total_offsets.get(&clip_id).cloned();
- offset_lookup.calculated_total_offsets.insert(clip_id, Point2D::zero());
+ offset_lookup.calculated_total_offsets.insert(clip_id, Vector2D::zero());
self.hit_test_contents(traversal, &point, offset_lookup, result);
@@ -427,13 +425,13 @@ pub struct StackingContext {
pub mix_blend_mode: MixBlendMode,
/// A transform to be applied to this stacking context.
- pub transform: Option<Matrix4D<f32>>,
+ pub transform: Option<Transform3D<f32>>,
/// The transform style of this stacking context.
pub transform_style: TransformStyle,
/// The perspective matrix to be applied to children.
- pub perspective: Option<Matrix4D<f32>>,
+ pub perspective: Option<Transform3D<f32>>,
/// The scroll policy of this layer.
pub scroll_policy: ScrollPolicy,
@@ -452,9 +450,9 @@ impl StackingContext {
z_index: i32,
filters: filter::T,
mix_blend_mode: MixBlendMode,
- transform: Option<Matrix4D<f32>>,
+ transform: Option<Transform3D<f32>>,
transform_style: TransformStyle,
- perspective: Option<Matrix4D<f32>>,
+ perspective: Option<Transform3D<f32>>,
scroll_policy: ScrollPolicy,
parent_scroll_id: ClipId)
-> StackingContext {
@@ -802,7 +800,7 @@ impl ClippingRegion {
/// Translates this clipping region by the given vector.
#[inline]
- pub fn translate(&self, delta: &Point2D<Au>) -> ClippingRegion {
+ pub fn translate(&self, delta: &Vector2D<Au>) -> ClippingRegion {
ClippingRegion {
main: self.main.translate(delta),
complex: self.complex.iter().map(|complex| {
@@ -1158,7 +1156,7 @@ pub struct BoxShadowDisplayItem {
pub box_bounds: Rect<Au>,
/// The offset of this shadow from the box.
- pub offset: Point2D<Au>,
+ pub offset: Vector2D<Au>,
/// The color of this shadow.
pub color: ColorF,
@@ -1383,14 +1381,14 @@ impl WebRenderImageInfo {
}
/// The type of the scroll offset list. This is only populated if WebRender is in use.
-pub type ScrollOffsetMap = HashMap<ClipId, Point2D<f32>>;
+pub type ScrollOffsetMap = HashMap<ClipId, Vector2D<f32>>;
pub trait SimpleMatrixDetection {
fn is_identity_or_simple_translation(&self) -> bool;
}
-impl SimpleMatrixDetection for Matrix4D<f32> {
+impl SimpleMatrixDetection for Transform3D<f32> {
#[inline]
fn is_identity_or_simple_translation(&self) -> bool {
let (_0, _1) = (Zero::zero(), One::one());
diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs
index 6046a70770f..5746739a107 100644
--- a/components/gfx/platform/windows/font.rs
+++ b/components/gfx/platform/windows/font.rs
@@ -372,7 +372,7 @@ impl FontHandleMethods for FontHandle {
descent: au_from_du_s(dm.descent as i32),
max_advance: au_from_pt(0.0), // FIXME
average_advance: au_from_pt(0.0), // FIXME
- line_gap: au_from_du((dm.ascent + dm.descent + dm.lineGap as u16) as i32),
+ line_gap: au_from_du_s((dm.ascent + dm.descent + dm.lineGap as u16) as i32),
};
debug!("Font metrics (@{} pt): {:?}", self.em_size * 12., metrics);
metrics
diff --git a/components/gfx/text/glyph.rs b/components/gfx/text/glyph.rs
index 21692cd8d5f..2229bbbecd8 100644
--- a/components/gfx/text/glyph.rs
+++ b/components/gfx/text/glyph.rs
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
-use euclid::point::Point2D;
+use euclid::Point2D;
use range::{self, EachIndex, Range, RangeIndex};
#[cfg(any(target_feature = "sse2", target_feature = "neon"))]
use simd::u32x4;
diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml
index e9679887da6..effcbe0d4f0 100644
--- a/components/layout/Cargo.toml
+++ b/components/layout/Cargo.toml
@@ -14,7 +14,7 @@ app_units = "0.4.1"
atomic_refcell = "0.1"
bitflags = "0.7"
canvas_traits = {path = "../canvas_traits"}
-euclid = "0.13"
+euclid = "0.14.4"
fnv = "1.0"
gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"}
@@ -29,7 +29,7 @@ ordered-float = "0.4"
parking_lot = "0.3.3"
profile_traits = {path = "../profile_traits"}
range = {path = "../range"}
-rayon = "0.7.1"
+rayon = "0.8"
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
selectors = { path = "../selectors" }
diff --git a/components/layout/block.rs b/components/layout/block.rs
index 9cd6978097e..6a3db5a6141 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -322,7 +322,7 @@ impl CandidateBSizeIterator {
let block_size = match (fragment.style.content_block_size(), block_container_block_size) {
(LengthOrPercentageOrAuto::Percentage(percent), Some(block_container_block_size)) => {
- MaybeAuto::Specified(block_container_block_size.scale_by(percent))
+ MaybeAuto::Specified(block_container_block_size.scale_by(percent.0))
}
(LengthOrPercentageOrAuto::Calc(calc), _) => {
MaybeAuto::from_option(calc.to_used_value(block_container_block_size))
@@ -333,7 +333,7 @@ impl CandidateBSizeIterator {
};
let max_block_size = match (fragment.style.max_block_size(), block_container_block_size) {
(LengthOrPercentageOrNone::Percentage(percent), Some(block_container_block_size)) => {
- Some(block_container_block_size.scale_by(percent))
+ Some(block_container_block_size.scale_by(percent.0))
}
(LengthOrPercentageOrNone::Calc(calc), _) => {
calc.to_used_value(block_container_block_size)
@@ -344,7 +344,7 @@ impl CandidateBSizeIterator {
};
let min_block_size = match (fragment.style.min_block_size(), block_container_block_size) {
(LengthOrPercentage::Percentage(percent), Some(block_container_block_size)) => {
- block_container_block_size.scale_by(percent)
+ block_container_block_size.scale_by(percent.0)
}
(LengthOrPercentage::Calc(calc), _) => {
calc.to_used_value(block_container_block_size).unwrap_or(Au(0))
@@ -1178,7 +1178,7 @@ impl BlockFlow {
}
(LengthOrPercentageOrAuto::Length(length), _) => Some(length),
(LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => {
- Some(container_size.scale_by(percent))
+ Some(container_size.scale_by(percent.0))
}
(LengthOrPercentageOrAuto::Percentage(_), None) |
(LengthOrPercentageOrAuto::Auto, None) => {
@@ -1967,7 +1967,7 @@ impl Flow for BlockFlow {
// flow w.r.t. the containing block.
self.base
.late_absolute_position_info
- .stacking_relative_position_of_absolute_containing_block + position_start
+ .stacking_relative_position_of_absolute_containing_block + position_start.to_vector()
};
if !self.base.writing_mode.is_vertical() {
@@ -2000,9 +2000,9 @@ impl Flow for BlockFlow {
self.base
.late_absolute_position_info
.stacking_relative_position_of_absolute_containing_block =
- self.base.stacking_relative_position +
+ self.base.stacking_relative_position.to_point() +
(border_box_origin + relative_offset).to_physical(self.base.writing_mode,
- container_size)
+ container_size).to_vector()
}
// Compute absolute position info for children.
@@ -2036,7 +2036,7 @@ impl Flow for BlockFlow {
self.base.position.size.to_physical(self.base.writing_mode);
// Compute the origin and clipping rectangle for children.
- let relative_offset = relative_offset.to_physical(self.base.writing_mode);
+ let relative_offset = relative_offset.to_physical(self.base.writing_mode).to_vector();
let is_stacking_context = self.fragment.establishes_stacking_context();
let origin_for_children = if is_stacking_context {
// We establish a stacking context, so the position of our children is vertically
@@ -2048,7 +2048,7 @@ impl Flow for BlockFlow {
let margin = self.fragment.margin.to_physical(self.base.writing_mode);
Point2D::new(-margin.left, Au(0))
} else {
- self.base.stacking_relative_position + relative_offset
+ self.base.stacking_relative_position.to_point() + relative_offset
};
// Process children.
@@ -2171,7 +2171,7 @@ impl Flow for BlockFlow {
.early_absolute_position_info
.relative_containing_block_mode,
CoordinateSystem::Own)
- .translate(stacking_context_position));
+ .translate(&stacking_context_position.to_vector()));
}
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index b97aeb4c1fa..1adce80ade2 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -14,7 +14,7 @@ use app_units::{AU_PER_PX, Au};
use block::{BlockFlow, BlockStackingContextType};
use canvas_traits::{CanvasData, CanvasMsg, FromLayoutMsg};
use context::LayoutContext;
-use euclid::{Matrix4D, Point2D, Rect, SideOffsets2D, Size2D, TypedSize2D};
+use euclid::{Transform3D, Point2D, Vector2D, Rect, SideOffsets2D, Size2D, TypedSize2D};
use flex::FlexFlow;
use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
use flow_ref::FlowRef;
@@ -65,6 +65,7 @@ use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndi
use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
use style::values::generics::image::{Image, ShapeExtent};
use style::values::generics::image::PaintWorklet;
+use style::values::specified::length::Percentage;
use style::values::specified::position::{X, Y};
use style_traits::CSSPixel;
use style_traits::cursor::Cursor;
@@ -501,7 +502,7 @@ pub trait FragmentDisplayListBuilding {
/// * `clip`: The region to clip the display items to.
fn build_display_list(&mut self,
state: &mut DisplayListBuildState,
- stacking_relative_flow_origin: &Point2D<Au>,
+ stacking_relative_flow_origin: &Vector2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
relative_containing_block_mode: WritingMode,
border_painting_mode: BorderPaintingMode,
@@ -653,14 +654,14 @@ fn convert_gradient_stops(gradient_items: &[GradientItem],
{
let first = stop_items.first_mut().unwrap();
if first.position.is_none() {
- first.position = Some(LengthOrPercentage::Percentage(0.0));
+ first.position = Some(LengthOrPercentage::Percentage(Percentage(0.0)));
}
}
// If the last color stop does not have a position, set its position to 100%.
{
let last = stop_items.last_mut().unwrap();
if last.position.is_none() {
- last.position = Some(LengthOrPercentage::Percentage(1.0));
+ last.position = Some(LengthOrPercentage::Percentage(Percentage(1.0)));
}
}
@@ -1223,8 +1224,8 @@ impl FragmentDisplayListBuilding for Fragment {
// This is the vector between the center and the ending point; i.e. half
// of the distance between the starting point and the ending point.
- let delta = Point2D::new(Au::from_f32_px(dir.x * inv_dir_length * line_length / 2.0),
- Au::from_f32_px(dir.y * inv_dir_length * line_length / 2.0));
+ let delta = Vector2D::new(Au::from_f32_px(dir.x * inv_dir_length * line_length / 2.0),
+ Au::from_f32_px(dir.y * inv_dir_length * line_length / 2.0));
// This is the length of the gradient line.
let length = Au::from_f32_px(
@@ -1347,8 +1348,8 @@ impl FragmentDisplayListBuilding for Fragment {
// NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back).
for box_shadow in style.get_effects().box_shadow.0.iter().rev() {
let bounds =
- shadow_bounds(&absolute_bounds.translate(&Point2D::new(box_shadow.offset_x,
- box_shadow.offset_y)),
+ shadow_bounds(&absolute_bounds.translate(&Vector2D::new(box_shadow.offset_x,
+ box_shadow.offset_y)),
box_shadow.blur_radius,
box_shadow.spread_radius);
@@ -1362,7 +1363,7 @@ impl FragmentDisplayListBuilding for Fragment {
base: base,
box_bounds: *absolute_bounds,
color: style.resolve_color(box_shadow.color).to_gfx_color(),
- offset: Point2D::new(box_shadow.offset_x, box_shadow.offset_y),
+ offset: Vector2D::new(box_shadow.offset_x, box_shadow.offset_y),
blur_radius: box_shadow.blur_radius,
spread_radius: box_shadow.spread_radius,
border_radius: model::specified_border_radius(style.get_border()
@@ -1707,7 +1708,7 @@ impl FragmentDisplayListBuilding for Fragment {
fn build_display_list(&mut self,
state: &mut DisplayListBuildState,
- stacking_relative_flow_origin: &Point2D<Au>,
+ stacking_relative_flow_origin: &Vector2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
relative_containing_block_mode: WritingMode,
border_painting_mode: BorderPaintingMode,
@@ -2001,7 +2002,7 @@ impl FragmentDisplayListBuilding for Fragment {
let border_box_offset =
border_box.translate(&-base_flow.stacking_relative_position).origin;
// Then, using that, compute our overflow region relative to our border box.
- let overflow = base_flow.overflow.paint.translate(&-border_box_offset);
+ let overflow = base_flow.overflow.paint.translate(&-border_box_offset.to_vector());
// Create the filter pipeline.
let effects = self.style().get_effects();
@@ -2046,7 +2047,7 @@ impl FragmentDisplayListBuilding for Fragment {
} else {
self.style().get_color().color
};
- let offset = text_shadow.map(|s| Point2D::new(s.offset_x, s.offset_y)).unwrap_or_else(Point2D::zero);
+ let offset = text_shadow.map(|s| Vector2D::new(s.offset_x, s.offset_y)).unwrap_or_else(Vector2D::zero);
let shadow_blur_radius = text_shadow.map(|s| s.blur_radius).unwrap_or(Au(0));
// Determine the orientation and cursor to use.
@@ -2068,7 +2069,7 @@ impl FragmentDisplayListBuilding for Fragment {
LogicalPoint::new(self.style.writing_mode,
Au(0),
metrics.ascent).to_physical(self.style.writing_mode,
- container_size);
+ container_size).to_vector();
// Create the text display item.
let base = state.create_base_display_item(&stacking_relative_content_box,
@@ -2158,7 +2159,7 @@ impl FragmentDisplayListBuilding for Fragment {
base: base,
box_bounds: stacking_relative_box,
color: color.to_gfx_color(),
- offset: Point2D::zero(),
+ offset: Vector2D::zero(),
blur_radius: blur_radius,
spread_radius: Au(0),
border_radius: Au(0),
@@ -2300,7 +2301,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
};
let perspective = self.fragment.perspective_matrix(&border_box)
- .unwrap_or_else(Matrix4D::identity);
+ .unwrap_or_else(Transform3D::identity);
let transform = transform.pre_mul(&perspective).inverse();
let origin = &border_box.origin;
@@ -2818,7 +2819,7 @@ impl BaseFlowDisplayListBuilding for BaseFlow {
let thread_id = self.thread_id;
let stacking_context_relative_bounds =
- Rect::new(self.stacking_relative_position,
+ Rect::new(self.stacking_relative_position.to_point(),
self.position.size.to_physical(self.writing_mode));
let mut color = THREAD_TINT_COLORS[thread_id as usize % THREAD_TINT_COLORS.len()];
@@ -2871,7 +2872,7 @@ struct StopRun {
fn position_to_offset(position: LengthOrPercentage, total_length: Au) -> f32 {
match position {
LengthOrPercentage::Length(Au(length)) => length as f32 / total_length.0 as f32,
- LengthOrPercentage::Percentage(percentage) => percentage as f32,
+ LengthOrPercentage::Percentage(percentage) => percentage.0 as f32,
LengthOrPercentage::Calc(calc) => {
calc.to_used_value(Some(total_length)).unwrap().0 as f32 / total_length.0 as f32
},
diff --git a/components/layout/flex.rs b/components/layout/flex.rs
index 0abd32b6dc0..e2bd1c0838d 100644
--- a/components/layout/flex.rs
+++ b/components/layout/flex.rs
@@ -46,7 +46,7 @@ impl AxisSize {
LengthOrPercentageOrAuto::Length(length) => AxisSize::Definite(length),
LengthOrPercentageOrAuto::Percentage(percent) => {
match content_size {
- Some(size) => AxisSize::Definite(size.scale_by(percent)),
+ Some(size) => AxisSize::Definite(size.scale_by(percent.0)),
None => AxisSize::Infinite
}
}
@@ -74,7 +74,7 @@ fn from_flex_basis(flex_basis: LengthOrPercentageOrAutoOrContent,
(LengthOrPercentageOrAutoOrContent::Length(length), _) =>
MaybeAuto::Specified(length),
(LengthOrPercentageOrAutoOrContent::Percentage(percent), Some(size)) =>
- MaybeAuto::Specified(size.scale_by(percent)),
+ MaybeAuto::Specified(size.scale_by(percent.0)),
(LengthOrPercentageOrAutoOrContent::Percentage(_), None) =>
MaybeAuto::Auto,
(LengthOrPercentageOrAutoOrContent::Calc(calc), _) =>
diff --git a/components/layout/floats.rs b/components/layout/floats.rs
index aa3c02d1d3f..43d05e4bad4 100644
--- a/components/layout/floats.rs
+++ b/components/layout/floats.rs
@@ -505,7 +505,7 @@ impl SpeculatedFloatPlacement {
// might flow around this float.
if let LengthOrPercentageOrAuto::Percentage(percentage) =
flow.as_block().fragment.style.content_inline_size() {
- if percentage > 0.0 {
+ if percentage.0 > 0.0 {
float_inline_size = Au::from_px(1)
}
}
diff --git a/components/layout/flow.rs b/components/layout/flow.rs
index 1944f0decbd..5930df5dd81 100644
--- a/components/layout/flow.rs
+++ b/components/layout/flow.rs
@@ -29,7 +29,7 @@ use app_units::Au;
use block::{BlockFlow, FormattingContextType};
use context::LayoutContext;
use display_list_builder::DisplayListBuildState;
-use euclid::{Matrix4D, Point2D, Rect, Size2D};
+use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D};
use flex::FlexFlow;
use floats::{Floats, SpeculatedFloatPlacement};
use flow_list::{FlowList, MutFlowListIterator};
@@ -260,7 +260,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
match self.class() {
FlowClass::Block | FlowClass::TableCaption | FlowClass::TableCell => {}
_ => {
- overflow.translate(&position.origin);
+ overflow.translate(&position.origin.to_vector());
return overflow;
}
}
@@ -285,7 +285,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
if !self.as_block().fragment.establishes_stacking_context() ||
self.as_block().fragment.style.get_box().transform.0.is_none() {
- overflow.translate(&position.origin);
+ overflow.translate(&position.origin.to_vector());
return overflow;
}
@@ -294,7 +294,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
let transform_2d = self.as_block()
.fragment
.transform_matrix(&position)
- .unwrap_or(Matrix4D::identity())
+ .unwrap_or(Transform3D::identity())
.to_2d();
let transformed_overflow = Overflow {
paint: f32_rect_to_au_rect(transform_2d.transform_rect(
@@ -308,7 +308,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
// unnecessary once we are taking into account 3D transformations above.
overflow.union(&transformed_overflow);
- overflow.translate(&position.origin);
+ overflow.translate(&position.origin.to_vector());
overflow
}
@@ -917,7 +917,7 @@ pub struct BaseFlow {
/// The position of this flow relative to the start of the nearest ancestor stacking context.
/// This is computed during the top-down pass of display list construction.
- pub stacking_relative_position: Point2D<Au>,
+ pub stacking_relative_position: Vector2D<Au>,
/// Details about descendants with position 'absolute' or 'fixed' for which we are the
/// containing block. This is in tree order. This includes any direct children.
@@ -1098,7 +1098,7 @@ impl BaseFlow {
parallel: FlowParallelInfo::new(),
floats: Floats::new(writing_mode),
collapsible_margins: CollapsibleMargins::new(),
- stacking_relative_position: Point2D::zero(),
+ stacking_relative_position: Vector2D::zero(),
abs_descendants: AbsoluteDescendants::new(),
speculated_float_placement_in: SpeculatedFloatPlacement::zero(),
speculated_float_placement_out: SpeculatedFloatPlacement::zero(),
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index c0d766dc544..3eb403b98a9 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -10,7 +10,7 @@ use StyleArc;
use app_units::Au;
use canvas_traits::CanvasMsg;
use context::{LayoutContext, with_thread_local_font_context};
-use euclid::{Matrix4D, Point2D, Radians, Rect, Size2D};
+use euclid::{Transform3D, Point2D, Vector2D, Radians, Rect, Size2D};
use floats::ClearType;
use flow::{self, ImmutableFlowUtils};
use flow_ref::FlowRef;
@@ -2234,7 +2234,7 @@ impl Fragment {
}
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage(
percentage)) => {
- offset -= minimum_line_metrics.space_needed().scale_by(percentage)
+ offset -= minimum_line_metrics.space_needed().scale_by(percentage.0)
}
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(formula)) => {
offset -= formula.to_used_value(Some(minimum_line_metrics.space_needed())).unwrap()
@@ -2422,7 +2422,7 @@ impl Fragment {
/// This is the method you should use for display list construction as well as
/// `getBoundingClientRect()` and so forth.
pub fn stacking_relative_border_box(&self,
- stacking_relative_flow_origin: &Point2D<Au>,
+ stacking_relative_flow_origin: &Vector2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
relative_containing_block_mode: WritingMode,
coordinate_system: CoordinateSystem)
@@ -2440,7 +2440,7 @@ impl Fragment {
// this.
let relative_position = self.relative_position(relative_containing_block_size);
border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode))
- .translate(stacking_relative_flow_origin)
+ .translate(&stacking_relative_flow_origin)
}
/// Given the stacking-context-relative border box, returns the stacking-context-relative
@@ -2551,7 +2551,7 @@ impl Fragment {
// Box shadows cause us to draw outside our border box.
for box_shadow in &self.style().get_effects().box_shadow.0 {
- let offset = Point2D::new(box_shadow.offset_x, box_shadow.offset_y);
+ let offset = Vector2D::new(box_shadow.offset_x, box_shadow.offset_y);
let inflation = box_shadow.spread_radius + box_shadow.blur_radius *
BLUR_INFLATION_FACTOR;
overflow.paint = overflow.paint.union(&border_box.translate(&offset)
@@ -2842,13 +2842,13 @@ impl Fragment {
}
/// Returns the 4D matrix representing this fragment's transform.
- pub fn transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Matrix4D<f32>> {
+ pub fn transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> {
let operations = match self.style.get_box().transform.0 {
None => return None,
Some(ref operations) => operations,
};
- let mut transform = Matrix4D::identity();
+ let mut transform = Transform3D::identity();
let transform_origin = &self.style.get_box().transform_origin;
let transform_origin_x =
transform_origin.horizontal
@@ -2860,30 +2860,30 @@ impl Fragment {
.to_f32_px();
let transform_origin_z = transform_origin.depth.to_f32_px();
- let pre_transform = Matrix4D::create_translation(transform_origin_x,
- transform_origin_y,
- transform_origin_z);
- let post_transform = Matrix4D::create_translation(-transform_origin_x,
- -transform_origin_y,
- -transform_origin_z);
+ let pre_transform = Transform3D::create_translation(transform_origin_x,
+ transform_origin_y,
+ transform_origin_z);
+ let post_transform = Transform3D::create_translation(-transform_origin_x,
+ -transform_origin_y,
+ -transform_origin_z);
for operation in operations {
let matrix = match *operation {
transform::ComputedOperation::Rotate(ax, ay, az, theta) => {
let theta = 2.0f32 * f32::consts::PI - theta.radians();
- Matrix4D::create_rotation(ax, ay, az, Radians::new(theta))
+ Transform3D::create_rotation(ax, ay, az, Radians::new(theta))
}
transform::ComputedOperation::Perspective(d) => {
create_perspective_matrix(d)
}
transform::ComputedOperation::Scale(sx, sy, sz) => {
- Matrix4D::create_scale(sx, sy, sz)
+ Transform3D::create_scale(sx, sy, sz)
}
transform::ComputedOperation::Translate(tx, ty, tz) => {
let tx = tx.to_used_value(stacking_relative_border_box.size.width).to_f32_px();
let ty = ty.to_used_value(stacking_relative_border_box.size.height).to_f32_px();
let tz = tz.to_f32_px();
- Matrix4D::create_translation(tx, ty, tz)
+ Transform3D::create_translation(tx, ty, tz)
}
transform::ComputedOperation::Matrix(m) => {
m.to_gfx_matrix()
@@ -2893,14 +2893,14 @@ impl Fragment {
unreachable!()
}
transform::ComputedOperation::Skew(theta_x, theta_y) => {
- Matrix4D::create_skew(Radians::new(theta_x.radians()),
+ Transform3D::create_skew(Radians::new(theta_x.radians()),
Radians::new(theta_y.radians()))
}
transform::ComputedOperation::InterpolateMatrix { .. } |
transform::ComputedOperation::AccumulateMatrix { .. } => {
- // TODO: Convert InterpolateMatrix/AccmulateMatrix into a valid Matrix4D by
+ // TODO: Convert InterpolateMatrix/AccmulateMatrix into a valid Transform3D by
// the reference box.
- Matrix4D::identity()
+ Transform3D::identity()
}
};
@@ -2911,7 +2911,7 @@ impl Fragment {
}
/// Returns the 4D matrix representing this fragment's perspective.
- pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Matrix4D<f32>> {
+ pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> {
match self.style().get_box().perspective {
Either::First(length) => {
let perspective_origin = self.style().get_box().perspective_origin;
@@ -2924,12 +2924,12 @@ impl Fragment {
.to_used_value(stacking_relative_border_box.size.height)
.to_f32_px());
- let pre_transform = Matrix4D::create_translation(perspective_origin.x,
- perspective_origin.y,
- 0.0);
- let post_transform = Matrix4D::create_translation(-perspective_origin.x,
- -perspective_origin.y,
- 0.0);
+ let pre_transform = Transform3D::create_translation(perspective_origin.x,
+ perspective_origin.y,
+ 0.0);
+ let post_transform = Transform3D::create_translation(-perspective_origin.x,
+ -perspective_origin.y,
+ 0.0);
let perspective_matrix = create_perspective_matrix(length);
@@ -3099,9 +3099,9 @@ impl Overflow {
self.paint = self.paint.union(&other.paint);
}
- pub fn translate(&mut self, point: &Point2D<Au>) {
- self.scroll = self.scroll.translate(point);
- self.paint = self.paint.translate(point);
+ pub fn translate(&mut self, by: &Vector2D<Au>) {
+ self.scroll = self.scroll.translate(by);
+ self.paint = self.paint.translate(by);
}
}
@@ -3184,11 +3184,11 @@ impl Serialize for DebugId {
// and behaves as it does in other browsers.
// See https://lists.w3.org/Archives/Public/www-style/2016Jan/0020.html for more details.
#[inline]
-fn create_perspective_matrix(d: Au) -> Matrix4D<f32> {
+fn create_perspective_matrix(d: Au) -> Transform3D<f32> {
let d = d.to_f32_px();
if d <= 0.0 {
- Matrix4D::identity()
+ Transform3D::identity()
} else {
- Matrix4D::create_perspective(d)
+ Transform3D::create_perspective(d)
}
}
diff --git a/components/layout/inline.rs b/components/layout/inline.rs
index 8a54eac9e64..6e76cac5ddd 100644
--- a/components/layout/inline.rs
+++ b/components/layout/inline.rs
@@ -1600,11 +1600,11 @@ impl Flow for InlineFlow {
block_flow.base
.late_absolute_position_info
.stacking_relative_position_of_absolute_containing_block =
- stacking_relative_position + *padding_box_origin;
+ *padding_box_origin + stacking_relative_position;
}
block_flow.base.stacking_relative_position =
- stacking_relative_content_box.origin;
+ stacking_relative_content_box.origin.to_vector();
// Write the clip in our coordinate system into the child flow. (The kid will
// fix it up to be in its own coordinate system if necessary.)
@@ -1617,7 +1617,7 @@ impl Flow for InlineFlow {
self.base.late_absolute_position_info;
block_flow.base.stacking_relative_position =
- stacking_relative_border_box.origin;
+ stacking_relative_border_box.origin.to_vector();
// As above, this is in our coordinate system for now.
block_flow.base.clip = self.base.clip.clone()
@@ -1633,10 +1633,10 @@ impl Flow for InlineFlow {
block_flow.base
.late_absolute_position_info
.stacking_relative_position_of_absolute_containing_block =
- stacking_relative_position + *padding_box_origin;
+ *padding_box_origin + stacking_relative_position;
block_flow.base.stacking_relative_position =
- stacking_relative_border_box.origin;
+ stacking_relative_border_box.origin.to_vector();
// As above, this is in our coordinate system for now.
block_flow.base.clip = self.base.clip.clone()
@@ -1694,7 +1694,7 @@ impl Flow for InlineFlow {
relative_containing_block_size,
relative_containing_block_mode,
CoordinateSystem::Own)
- .translate(stacking_context_position))
+ .translate(&stacking_context_position.to_vector()))
}
}
diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs
index 8a79aaa40b7..cb8cd27df70 100644
--- a/components/layout/list_item.rs
+++ b/components/layout/list_item.rs
@@ -197,7 +197,7 @@ impl Flow for ListItemFlow {
.early_absolute_position_info
.relative_containing_block_mode,
CoordinateSystem::Own)
- .translate(stacking_context_position));
+ .translate(&stacking_context_position.to_vector()));
}
}
}
diff --git a/components/layout/model.rs b/components/layout/model.rs
index 97fb5cbc43e..7f564a599ab 100644
--- a/components/layout/model.rs
+++ b/components/layout/model.rs
@@ -7,7 +7,7 @@
#![deny(unsafe_code)]
use app_units::Au;
-use euclid::{Matrix4D, SideOffsets2D, Size2D};
+use euclid::{Transform3D, SideOffsets2D, Size2D};
use fragment::Fragment;
use std::cmp::{max, min};
use std::fmt;
@@ -144,7 +144,7 @@ impl MarginCollapseInfo {
LengthOrPercentageOrAuto::Auto => true,
LengthOrPercentageOrAuto::Length(Au(v)) => v == 0,
LengthOrPercentageOrAuto::Percentage(v) => {
- v == 0. || containing_block_size.is_none()
+ v.0 == 0. || containing_block_size.is_none()
}
LengthOrPercentageOrAuto::Calc(_) => false,
};
@@ -154,7 +154,7 @@ impl MarginCollapseInfo {
LengthOrPercentage::Length(Au(0)) => {
FinalMarginState::MarginsCollapseThrough
},
- LengthOrPercentage::Percentage(v) if v == 0. => {
+ LengthOrPercentage::Percentage(v) if v.0 == 0. => {
FinalMarginState::MarginsCollapseThrough
},
_ => {
@@ -408,7 +408,7 @@ impl MaybeAuto {
match length {
LengthOrPercentageOrAuto::Auto => MaybeAuto::Auto,
LengthOrPercentageOrAuto::Percentage(percent) => {
- MaybeAuto::Specified(containing_length.scale_by(percent))
+ MaybeAuto::Specified(containing_length.scale_by(percent.0))
}
LengthOrPercentageOrAuto::Calc(calc) => {
MaybeAuto::from_option(calc.to_used_value(Some(containing_length)))
@@ -509,12 +509,12 @@ pub fn specified_margin_from_style(style: &ServoComputedValues,
}
pub trait ToGfxMatrix {
- fn to_gfx_matrix(&self) -> Matrix4D<f32>;
+ fn to_gfx_matrix(&self) -> Transform3D<f32>;
}
impl ToGfxMatrix for ComputedMatrix {
- fn to_gfx_matrix(&self) -> Matrix4D<f32> {
- Matrix4D::row_major(
+ fn to_gfx_matrix(&self) -> Transform3D<f32> {
+ Transform3D::row_major(
self.m11 as f32, self.m12 as f32, self.m13 as f32, self.m14 as f32,
self.m21 as f32, self.m22 as f32, self.m23 as f32, self.m24 as f32,
self.m31 as f32, self.m32 as f32, self.m33 as f32, self.m34 as f32,
diff --git a/components/layout/multicol.rs b/components/layout/multicol.rs
index b43a6eb17ff..afe45c2aea7 100644
--- a/components/layout/multicol.rs
+++ b/components/layout/multicol.rs
@@ -11,8 +11,7 @@ use app_units::Au;
use block::BlockFlow;
use context::LayoutContext;
use display_list_builder::DisplayListBuildState;
-use euclid::Point2D;
-use euclid::Size2D;
+use euclid::{Point2D, Vector2D};
use floats::FloatKind;
use flow::{Flow, FlowClass, OpaqueFlow, mut_base, FragmentationContext};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
@@ -173,7 +172,7 @@ impl Flow for MulticolFlow {
let pitch = pitch.to_physical(self.block_flow.base.writing_mode);
for (i, child) in self.block_flow.base.children.iter_mut().enumerate() {
let point = &mut mut_base(child).stacking_relative_position;
- *point = *point + Size2D::new(pitch.width * i as i32, pitch.height * i as i32);
+ *point = *point + Vector2D::new(pitch.width * i as i32, pitch.height * i as i32);
}
}
diff --git a/components/layout/query.rs b/components/layout/query.rs
index da10696ec29..ae7a506124b 100644
--- a/components/layout/query.rs
+++ b/components/layout/query.rs
@@ -7,9 +7,7 @@
use app_units::Au;
use construct::ConstructionResult;
use context::LayoutContext;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Point2D, Vector2D, Rect, Size2D};
use flow::{self, Flow};
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::{DisplayItemMetadata, DisplayList, OpaqueNode, ScrollOffsetMap};
@@ -613,7 +611,7 @@ impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
Some(ParentBorderBoxInfo {
node_address: fragment.node,
- origin: border_box.origin + Point2D::new(border_width.left, border_width.top),
+ origin: border_box.origin + Vector2D::new(border_width.left, border_width.top),
})
} else {
None
@@ -774,7 +772,7 @@ fn process_resolved_style_request_internal<'a, N>(requested_node: N,
let position = maybe_data.map_or(Point2D::zero(), |data| {
match (*data).flow_construction_result {
ConstructionResult::Flow(ref flow_ref, _) =>
- flow::base(flow_ref.deref()).stacking_relative_position,
+ flow::base(flow_ref.deref()).stacking_relative_position.to_point(),
// TODO(dzbarsky) search parents until we find node with a flow ref.
// https://github.com/servo/servo/issues/8307
_ => Point2D::zero()
@@ -852,7 +850,7 @@ pub fn process_offset_parent_query<N: LayoutNode>(requested_node: N, layout_root
let parent_info = iterator.parent_nodes.into_iter().rev().filter_map(|info| info).next();
match (node_offset_box, parent_info) {
(Some(node_offset_box), Some(parent_info)) => {
- let origin = node_offset_box.offset - parent_info.origin;
+ let origin = node_offset_box.offset - parent_info.origin.to_vector();
let size = node_offset_box.rectangle.size;
OffsetParentResponse {
node_address: Some(parent_info.node_address.to_untrusted_node_address()),
diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs
index 404d29a44d1..f6f8ce429e4 100644
--- a/components/layout/sequential.rs
+++ b/components/layout/sequential.rs
@@ -7,7 +7,7 @@
use app_units::Au;
use context::LayoutContext;
use display_list_builder::DisplayListBuildState;
-use euclid::point::Point2D;
+use euclid::{Point2D, Vector2D};
use floats::SpeculatedFloatPlacement;
use flow::{self, Flow, ImmutableFlowUtils, InorderFlowTraversal, MutableFlowUtils};
use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
@@ -109,16 +109,16 @@ pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut Flow, iterator
if kid.is_block_flow() && kid.as_block().fragment.establishes_stacking_context() {
stacking_context_position = Point2D::new(kid.as_block().fragment.margin.inline_start, Au(0)) +
flow::base(kid).stacking_relative_position +
- stacking_context_position;
+ stacking_context_position.to_vector();
let relative_position = kid.as_block()
.stacking_relative_position(CoordinateSystem::Own);
if let Some(matrix) = kid.as_block()
.fragment
.transform_matrix(&relative_position) {
- let transform_matrix = matrix.transform_point(&Point2D::zero());
+ let transform_matrix = matrix.transform_point2d(&Point2D::zero());
stacking_context_position = stacking_context_position +
- Point2D::new(Au::from_f32_px(transform_matrix.x),
- Au::from_f32_px(transform_matrix.y))
+ Vector2D::new(Au::from_f32_px(transform_matrix.x),
+ Au::from_f32_px(transform_matrix.y))
}
}
doit(kid, level + 1, iterator, &stacking_context_position);
diff --git a/components/layout/table.rs b/components/layout/table.rs
index c3ba52a305b..d199b059a69 100644
--- a/components/layout/table.rs
+++ b/components/layout/table.rs
@@ -253,7 +253,7 @@ impl Flow for TableFlow {
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Calc(_) |
LengthOrPercentageOrAuto::Length(_) => 0.0,
- LengthOrPercentageOrAuto::Percentage(percentage) => percentage,
+ LengthOrPercentageOrAuto::Percentage(percentage) => percentage.0,
},
preferred: Au(0),
constrained: false,
diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs
index 2fb61a336de..45b47ee8de5 100644
--- a/components/layout/table_row.rs
+++ b/components/layout/table_row.rs
@@ -310,7 +310,7 @@ impl Flow for TableRowFlow {
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Calc(_) |
LengthOrPercentageOrAuto::Length(_) => 0.0,
- LengthOrPercentageOrAuto::Percentage(percentage) => percentage,
+ LengthOrPercentageOrAuto::Percentage(percentage) => percentage.0,
},
preferred: child_base.intrinsic_inline_sizes.preferred_inline_size,
constrained: match child_specified_inline_size {
diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs
index 537d5f04267..dc8bf7bc9a5 100644
--- a/components/layout/webrender_helpers.rs
+++ b/components/layout/webrender_helpers.rs
@@ -8,7 +8,7 @@
// completely converting layout to directly generate WebRender display lists, for example.
use app_units::Au;
-use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
+use euclid::{Point2D, Vector2D, Rect, SideOffsets2D, Size2D};
use gfx::display_list::{BorderDetails, BorderRadii, BoxShadowClipMode, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal, StackingContextType};
use msg::constellation_msg::PipelineId;
@@ -86,12 +86,22 @@ trait ToPointF {
fn to_pointf(&self) -> webrender_traits::LayoutPoint;
}
+trait ToVectorF {
+ fn to_vectorf(&self) -> webrender_traits::LayoutVector2D;
+}
+
impl ToPointF for Point2D<Au> {
fn to_pointf(&self) -> webrender_traits::LayoutPoint {
webrender_traits::LayoutPoint::new(self.x.to_f32_px(), self.y.to_f32_px())
}
}
+impl ToVectorF for Vector2D<Au> {
+ fn to_vectorf(&self) -> webrender_traits::LayoutVector2D {
+ webrender_traits::LayoutVector2D::new(self.x.to_f32_px(), self.y.to_f32_px())
+ }
+}
+
impl ToSizeF for Size2D<Au> {
fn to_sizef(&self) -> webrender_traits::LayoutSize {
webrender_traits::LayoutSize::new(self.width.to_f32_px(), self.height.to_f32_px())
@@ -450,7 +460,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
builder.push_box_shadow(rect,
clip,
box_bounds,
- item.offset.to_pointf(),
+ item.offset.to_vectorf(),
item.color,
item.blur_radius.to_f32_px(),
item.spread_radius.to_f32_px(),
diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs
index 4ae7c403e37..87fe65bb3b9 100644
--- a/components/layout/wrapper.rs
+++ b/components/layout/wrapper.rs
@@ -171,19 +171,14 @@ impl<T: ThreadSafeLayoutNode> ThreadSafeLayoutNodeHelpers for T {
let damage = {
let data = node.get_raw_data().unwrap();
- if let Some(r) = data.style_data.element_data.borrow().get_restyle() {
- // We're reflowing a node that just got a restyle, and so the
- // damage has been computed and stored in the RestyleData.
- r.damage
- } else if !data.layout_data.borrow().flags.contains(::data::HAS_BEEN_TRAVERSED) {
+
+ if !data.layout_data.borrow().flags.contains(::data::HAS_BEEN_TRAVERSED) {
// We're reflowing a node that was styled for the first time and
// has never been visited by layout. Return rebuild_and_reflow,
// because that's what the code expects.
RestyleDamage::rebuild_and_reflow()
} else {
- // We're reflowing a node whose style data didn't change, but whose
- // layout may change due to changes in ancestors or descendants.
- RestyleDamage::empty()
+ data.style_data.element_data.borrow().restyle.damage
}
};
diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml
index b1c86761eac..0e35b35abd9 100644
--- a/components/layout_thread/Cargo.toml
+++ b/components/layout_thread/Cargo.toml
@@ -11,7 +11,7 @@ path = "lib.rs"
[dependencies]
app_units = "0.4.1"
-euclid = "0.13"
+euclid = "0.14.4"
fnv = "1.0"
gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"}
@@ -25,7 +25,7 @@ msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
parking_lot = {version = "0.3.3", features = ["nightly"]}
profile_traits = {path = "../profile_traits"}
-rayon = "0.7.1"
+rayon = "0.8"
script = {path = "../script"}
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index 364ce2745ac..d53fa05a37f 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -40,10 +40,7 @@ extern crate style;
extern crate webrender_traits;
use app_units::Au;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::scale_factor::ScaleFactor;
-use euclid::size::Size2D;
+use euclid::{Point2D, Rect, Size2D, ScaleFactor};
use fnv::FnvHashMap;
use gfx::display_list::{OpaqueNode, WebRenderImageInfo};
use gfx::font;
@@ -111,9 +108,9 @@ use std::thread;
use style::animation::Animation;
use style::context::{QuirksMode, ReflowGoal, SharedStyleContext};
use style::context::{StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
-use style::data::StoredRestyleHint;
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::error_reporting::{NullReporter, RustLogReporter};
+use style::invalidation::element::restyle_hints::RestyleHint;
use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaList, MediaType};
use style::selector_parser::SnapshotMap;
@@ -910,7 +907,7 @@ impl LayoutThread {
|| {
flow::mut_base(layout_root).stacking_relative_position =
LogicalPoint::zero(writing_mode).to_physical(writing_mode,
- self.viewport_size);
+ self.viewport_size).to_vector();
flow::mut_base(layout_root).clip = data.page_clip_rect;
@@ -1119,7 +1116,7 @@ impl LayoutThread {
let el = node.as_element().unwrap();
if let Some(mut d) = element.mutate_data() {
if d.has_styles() {
- d.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
+ d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
if let Some(p) = el.parent_element() {
@@ -1155,7 +1152,7 @@ impl LayoutThread {
if needs_dirtying {
if let Some(mut d) = element.mutate_data() {
if d.has_styles() {
- d.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
+ d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
}
@@ -1200,12 +1197,11 @@ impl LayoutThread {
}
let mut style_data = style_data.borrow_mut();
- let mut restyle_data = style_data.ensure_restyle();
// Stash the data on the element for processing by the style system.
- restyle_data.hint.insert(restyle.hint.into());
- restyle_data.damage = restyle.damage;
- debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
+ style_data.restyle.hint.insert(restyle.hint.into());
+ style_data.restyle.damage = restyle.damage;
+ debug!("Noting restyle for {:?}: {:?}", el, style_data.restyle);
}
// Create a layout context for use throughout the following passes.
diff --git a/components/net_traits/image/base.rs b/components/net_traits/image/base.rs
index a52ed0e717b..ccdb1130613 100644
--- a/components/net_traits/image/base.rs
+++ b/components/net_traits/image/base.rs
@@ -4,9 +4,10 @@
use ipc_channel::ipc::IpcSharedMemory;
use piston_image::{self, DynamicImage, ImageFormat};
+use std::fmt;
use webrender_traits;
-#[derive(Clone, Copy, Deserialize, Eq, PartialEq, Serialize, HeapSizeOf)]
+#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, HeapSizeOf)]
pub enum PixelFormat {
/// Luminance channel only
K8,
@@ -29,7 +30,14 @@ pub struct Image {
pub id: Option<webrender_traits::ImageKey>,
}
-#[derive(Clone, Deserialize, Eq, PartialEq, Serialize, HeapSizeOf)]
+impl fmt::Debug for Image {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}",
+ self.width, self.height, self.format, self.id)
+ }
+}
+
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, HeapSizeOf)]
pub struct ImageMetadata {
pub width: u32,
pub height: u32,
diff --git a/components/net_traits/image_cache.rs b/components/net_traits/image_cache.rs
index cfc41ea2142..47ebc5c3eeb 100644
--- a/components/net_traits/image_cache.rs
+++ b/components/net_traits/image_cache.rs
@@ -60,7 +60,7 @@ impl ImageResponder {
}
/// The returned image.
-#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
+#[derive(Clone, Debug, Deserialize, Serialize, HeapSizeOf)]
pub enum ImageResponse {
/// The requested image was loaded.
Loaded(Arc<Image>, ServoUrl),
@@ -84,7 +84,7 @@ pub enum ImageState {
#[derive(Copy, Clone, PartialEq, Eq, Deserialize, Serialize, HeapSizeOf, Hash, Debug)]
pub struct PendingImageId(pub u64);
-#[derive(Deserialize, Serialize)]
+#[derive(Debug, Deserialize, Serialize)]
pub struct PendingImageResponse {
pub response: ImageResponse,
pub id: PendingImageId,
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index 44940fc270e..77ec98958cb 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -41,7 +41,7 @@ devtools_traits = {path = "../devtools_traits"}
dom_struct = {path = "../dom_struct"}
domobject_derive = {path = "../domobject_derive"}
encoding = "0.2"
-euclid = "0.13"
+euclid = "0.14.4"
fnv = "1.0"
gleam = "0.4"
gfx_traits = {path = "../gfx_traits"}
@@ -63,7 +63,7 @@ mime_guess = "1.8.0"
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
num-traits = "0.1.32"
-offscreen_gl_context = "0.8"
+offscreen_gl_context = { version = "0.9", features = ["serde"] }
open = "1.1.1"
parking_lot = "0.3"
phf = "0.7.18"
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index db3cd3b5c42..67e65c6a4b0 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -43,10 +43,8 @@ use dom::bindings::str::{DOMString, USVString};
use dom::bindings::utils::WindowProxyHandler;
use dom::document::PendingRestyle;
use encoding::types::EncodingRef;
-use euclid::{Matrix2D, Matrix4D, Point2D};
-use euclid::length::Length as EuclidLength;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Transform2D, Transform3D, Point2D, Vector2D, Rect, Size2D};
+use euclid::Length as EuclidLength;
use html5ever::{Prefix, LocalName, Namespace, QualName};
use html5ever::buffer_queue::BufferQueue;
use html5ever::tendril::IncompleteUtf8;
@@ -457,14 +455,14 @@ unsafe impl<T: Send> JSTraceable for Sender<T> {
}
}
-unsafe impl JSTraceable for Matrix2D<f32> {
+unsafe impl JSTraceable for Transform2D<f32> {
#[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
}
}
-unsafe impl JSTraceable for Matrix4D<f64> {
+unsafe impl JSTraceable for Transform3D<f64> {
#[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
@@ -478,6 +476,13 @@ unsafe impl JSTraceable for Point2D<f32> {
}
}
+unsafe impl JSTraceable for Vector2D<f32> {
+ #[inline]
+ unsafe fn trace(&self, _trc: *mut JSTracer) {
+ // Do nothing
+ }
+}
+
unsafe impl<T> JSTraceable for EuclidLength<u64, T> {
#[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) {
diff --git a/components/script/dom/canvaspattern.rs b/components/script/dom/canvaspattern.rs
index 6a6d6789918..5d9dd5cb767 100644
--- a/components/script/dom/canvaspattern.rs
+++ b/components/script/dom/canvaspattern.rs
@@ -9,7 +9,7 @@ use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::canvasgradient::ToFillOrStrokeStyle;
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
-use euclid::size::Size2D;
+use euclid::Size2D;
// https://html.spec.whatwg.org/multipage/#canvaspattern
#[dom_struct]
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index 8fb17c90fea..7f08ba1c13a 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -34,10 +34,7 @@ use dom::htmlimageelement::HTMLImageElement;
use dom::imagedata::ImageData;
use dom::node::{document_from_node, Node, NodeDamage, window_from_node};
use dom_struct::dom_struct;
-use euclid::matrix2d::Matrix2D;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D, vec2};
use ipc_channel::ipc::{self, IpcSender};
use net_traits::image::base::PixelFormat;
use net_traits::image_cache::ImageResponse;
@@ -82,7 +79,7 @@ struct CanvasContextState {
line_cap: LineCapStyle,
line_join: LineJoinStyle,
miter_limit: f64,
- transform: Matrix2D<f32>,
+ transform: Transform2D<f32>,
shadow_offset_x: f64,
shadow_offset_y: f64,
shadow_blur: f64,
@@ -102,7 +99,7 @@ impl CanvasContextState {
line_cap: LineCapStyle::Butt,
line_join: LineJoinStyle::Miter,
miter_limit: 10.0,
- transform: Matrix2D::identity(),
+ transform: Transform2D::identity(),
shadow_offset_x: 0.0,
shadow_offset_y: 0.0,
shadow_blur: 0.0,
@@ -559,7 +556,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
}
let transform = self.state.borrow().transform;
- self.state.borrow_mut().transform = transform.pre_scaled(x as f32, y as f32);
+ self.state.borrow_mut().transform = transform.pre_scale(x as f32, y as f32);
self.update_transform()
}
@@ -572,7 +569,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
let (sin, cos) = (angle.sin(), angle.cos());
let transform = self.state.borrow().transform;
self.state.borrow_mut().transform = transform.pre_mul(
- &Matrix2D::row_major(cos as f32, sin as f32,
+ &Transform2D::row_major(cos as f32, sin as f32,
-sin as f32, cos as f32,
0.0, 0.0));
self.update_transform()
@@ -585,7 +582,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
}
let transform = self.state.borrow().transform;
- self.state.borrow_mut().transform = transform.pre_translated(x as f32, y as f32);
+ self.state.borrow_mut().transform = transform.pre_translate(vec2(x as f32, y as f32));
self.update_transform()
}
@@ -598,7 +595,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
let transform = self.state.borrow().transform;
self.state.borrow_mut().transform = transform.pre_mul(
- &Matrix2D::row_major(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32));
+ &Transform2D::row_major(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32));
self.update_transform()
}
@@ -610,13 +607,13 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
}
self.state.borrow_mut().transform =
- Matrix2D::row_major(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32);
+ Transform2D::row_major(a as f32, b as f32, c as f32, d as f32, e as f32, f as f32);
self.update_transform()
}
// https://html.spec.whatwg.org/multipage/#dom-context-2d-resettransform
fn ResetTransform(&self) {
- self.state.borrow_mut().transform = Matrix2D::identity();
+ self.state.borrow_mut().transform = Transform2D::identity();
self.update_transform()
}
@@ -1079,7 +1076,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D {
dirty_width: Finite<f64>,
dirty_height: Finite<f64>) {
let data = imagedata.get_data_array();
- let offset = Point2D::new(*dx, *dy);
+ let offset = Vector2D::new(*dx, *dy);
let image_data_size = Size2D::new(imagedata.Width() as f64, imagedata.Height() as f64);
let dirty_rect = Rect::new(Point2D::new(*dirty_x, *dirty_y),
diff --git a/components/script/dom/css.rs b/components/script/dom/css.rs
index 66f522b2371..31ca219093e 100644
--- a/components/script/dom/css.rs
+++ b/components/script/dom/css.rs
@@ -10,9 +10,10 @@ use dom::bindings::str::DOMString;
use dom::window::Window;
use dom_struct::dom_struct;
use style::context::QuirksMode;
-use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
+use style::parser::ParserContext;
use style::stylesheets::CssRuleType;
use style::stylesheets::supports_rule::{Declaration, parse_condition_or_declaration};
+use style_traits::PARSING_MODE_DEFAULT;
#[dom_struct]
pub struct CSS {
diff --git a/components/script/dom/cssmediarule.rs b/components/script/dom/cssmediarule.rs
index 1f5535f1b3e..3c27127ecf4 100644
--- a/components/script/dom/cssmediarule.rs
+++ b/components/script/dom/cssmediarule.rs
@@ -16,11 +16,11 @@ use dom::medialist::MediaList;
use dom::window::Window;
use dom_struct::dom_struct;
use style::media_queries::parse_media_query_list;
-use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
+use style::parser::ParserContext;
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylearc::Arc;
use style::stylesheets::{CssRuleType, MediaRule};
-use style_traits::ToCss;
+use style_traits::{PARSING_MODE_DEFAULT, ToCss};
#[dom_struct]
pub struct CSSMediaRule {
diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs
index ecf4b35e61c..2ef448720d6 100644
--- a/components/script/dom/cssstyledeclaration.rs
+++ b/components/script/dom/cssstyledeclaration.rs
@@ -17,13 +17,12 @@ use dom_struct::dom_struct;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use style::attr::AttrValue;
-use style::parser::PARSING_MODE_DEFAULT;
use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{parse_one_declaration_into, parse_style_attribute, SourcePropertyDeclaration};
use style::selector_parser::PseudoElement;
use style::shared_lock::Locked;
use style::stylearc::Arc;
-use style_traits::ToCss;
+use style_traits::{PARSING_MODE_DEFAULT, ToCss};
// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
@@ -147,7 +146,7 @@ impl CSSStyleOwner {
match *self {
CSSStyleOwner::Element(ref el) => window_from_node(&**el).Document().base_url(),
CSSStyleOwner::CSSRule(ref rule, _) => {
- rule.parent_stylesheet().style_stylesheet().url_data.clone()
+ (*rule.parent_stylesheet().style_stylesheet().url_data.read()).clone()
}
}
}
diff --git a/components/script/dom/csssupportsrule.rs b/components/script/dom/csssupportsrule.rs
index 2897b47b35f..73503d0dfa6 100644
--- a/components/script/dom/csssupportsrule.rs
+++ b/components/script/dom/csssupportsrule.rs
@@ -13,12 +13,12 @@ use dom::cssrule::SpecificCSSRule;
use dom::cssstylesheet::CSSStyleSheet;
use dom::window::Window;
use dom_struct::dom_struct;
-use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
+use style::parser::ParserContext;
use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylearc::Arc;
use style::stylesheets::{CssRuleType, SupportsRule};
use style::stylesheets::supports_rule::SupportsCondition;
-use style_traits::ToCss;
+use style_traits::{PARSING_MODE_DEFAULT, ToCss};
#[dom_struct]
pub struct CSSSupportsRule {
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 8211830e9eb..5db39cf4feb 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -92,7 +92,7 @@ use dom::windowproxy::WindowProxy;
use dom_struct::dom_struct;
use encoding::EncodingRef;
use encoding::all::UTF_8;
-use euclid::point::Point2D;
+use euclid::{Point2D, Vector2D};
use html5ever::{LocalName, QualName};
use hyper::header::{Header, SetCookie};
use hyper_serde::Serde;
@@ -131,7 +131,7 @@ use std::rc::Rc;
use std::time::{Duration, Instant};
use style::attr::AttrValue;
use style::context::{QuirksMode, ReflowGoal};
-use style::restyle_hints::{RestyleHint, RESTYLE_STYLE_ATTRIBUTE};
+use style::invalidation::element::restyle_hints::{RestyleHint, RESTYLE_SELF, RESTYLE_STYLE_ATTRIBUTE};
use style::selector_parser::{RestyleDamage, Snapshot};
use style::shared_lock::SharedRwLock as StyleSharedRwLock;
use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
@@ -865,7 +865,7 @@ impl Document {
if let Some(iframe) = el.downcast::<HTMLIFrameElement>() {
if let Some(pipeline_id) = iframe.pipeline_id() {
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
- let child_origin = Point2D::new(rect.X() as f32, rect.Y() as f32);
+ let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
let child_point = client_point - child_origin;
let event = CompositorEvent::MouseButtonEvent(mouse_event_type, button, child_point);
@@ -1020,7 +1020,7 @@ impl Document {
if let Some(iframe) = el.downcast::<HTMLIFrameElement>() {
if let Some(pipeline_id) = iframe.pipeline_id() {
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
- let child_origin = Point2D::new(rect.X() as f32, rect.Y() as f32);
+ let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
let child_point = client_point - child_origin;
let event = CompositorEvent::TouchpadPressureEvent(child_point,
@@ -1124,7 +1124,7 @@ impl Document {
if let Some(iframe) = new_target.downcast::<HTMLIFrameElement>() {
if let Some(pipeline_id) = iframe.pipeline_id() {
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
- let child_origin = Point2D::new(rect.X() as f32, rect.Y() as f32);
+ let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
let child_point = client_point - child_origin;
let event = CompositorEvent::MouseMoveEvent(Some(child_point));
@@ -1231,7 +1231,7 @@ impl Document {
if let Some(iframe) = el.downcast::<HTMLIFrameElement>() {
if let Some(pipeline_id) = iframe.pipeline_id() {
let rect = iframe.upcast::<Element>().GetBoundingClientRect();
- let child_origin = Point2D::new(rect.X() as f32, rect.Y() as f32);
+ let child_origin = Vector2D::new(rect.X() as f32, rect.Y() as f32);
let child_point = point - child_origin;
let event = CompositorEvent::TouchEvent(event_type, touch_id, child_point);
@@ -2376,17 +2376,24 @@ impl Document {
entry.snapshot = Some(Snapshot::new(el.html_element_in_html_document()));
}
if attr.local_name() == &local_name!("style") {
- entry.hint.insert(RestyleHint::for_replacements(RESTYLE_STYLE_ATTRIBUTE));
+ entry.hint.insert(RESTYLE_STYLE_ATTRIBUTE);
}
// FIXME(emilio): This should become something like
// element.is_attribute_mapped(attr.local_name()).
if attr.local_name() == &local_name!("width") ||
attr.local_name() == &local_name!("height") {
- entry.hint.insert(RestyleHint::for_self());
+ entry.hint.insert(RESTYLE_SELF);
}
let mut snapshot = entry.snapshot.as_mut().unwrap();
+ if attr.local_name() == &local_name!("id") {
+ snapshot.id_changed = true;
+ } else if attr.local_name() == &local_name!("class") {
+ snapshot.class_changed = true;
+ } else {
+ snapshot.other_attributes_changed = true;
+ }
if snapshot.attrs.is_none() {
let attrs = el.attrs()
.iter()
diff --git a/components/script/dom/dommatrix.rs b/components/script/dom/dommatrix.rs
index eb33d42efcf..3b262650bac 100644
--- a/components/script/dom/dommatrix.rs
+++ b/components/script/dom/dommatrix.rs
@@ -11,7 +11,7 @@ use dom::bindings::reflector::reflect_dom_object;
use dom::dommatrixreadonly::{dommatrixinit_to_matrix, DOMMatrixReadOnly, entries_to_matrix};
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
-use euclid::Matrix4D;
+use euclid::Transform3D;
#[dom_struct]
@@ -21,12 +21,12 @@ pub struct DOMMatrix {
impl DOMMatrix {
#[allow(unrooted_must_root)]
- pub fn new(global: &GlobalScope, is2D: bool, matrix: Matrix4D<f64>) -> Root<Self> {
+ pub fn new(global: &GlobalScope, is2D: bool, matrix: Transform3D<f64>) -> Root<Self> {
let dommatrix = Self::new_inherited(is2D, matrix);
reflect_dom_object(box dommatrix, global, Wrap)
}
- pub fn new_inherited(is2D: bool, matrix: Matrix4D<f64>) -> Self {
+ pub fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
DOMMatrix {
parent: DOMMatrixReadOnly::new_inherited(is2D, matrix)
}
diff --git a/components/script/dom/dommatrixreadonly.rs b/components/script/dom/dommatrixreadonly.rs
index 875e8e6f42c..b31ce329ce2 100644
--- a/components/script/dom/dommatrixreadonly.rs
+++ b/components/script/dom/dommatrixreadonly.rs
@@ -14,25 +14,25 @@ use dom::dommatrix::DOMMatrix;
use dom::dompoint::DOMPoint;
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
-use euclid::{Matrix4D, Point4D, Radians};
+use euclid::{Transform3D, Radians};
use std::cell::{Cell, Ref};
use std::f64;
#[dom_struct]
pub struct DOMMatrixReadOnly {
reflector_: Reflector,
- matrix: DOMRefCell<Matrix4D<f64>>,
+ matrix: DOMRefCell<Transform3D<f64>>,
is2D: Cell<bool>,
}
impl DOMMatrixReadOnly {
#[allow(unrooted_must_root)]
- pub fn new(global: &GlobalScope, is2D: bool, matrix: Matrix4D<f64>) -> Root<Self> {
+ pub fn new(global: &GlobalScope, is2D: bool, matrix: Transform3D<f64>) -> Root<Self> {
let dommatrix = Self::new_inherited(is2D, matrix);
reflect_dom_object(box dommatrix, global, Wrap)
}
- pub fn new_inherited(is2D: bool, matrix: Matrix4D<f64>) -> Self {
+ pub fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
DOMMatrixReadOnly {
reflector_: Reflector::new(),
matrix: DOMRefCell::new(matrix),
@@ -42,7 +42,7 @@ impl DOMMatrixReadOnly {
// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly
pub fn Constructor(global: &GlobalScope) -> Fallible<Root<Self>> {
- Ok(Self::new(global, true, Matrix4D::identity()))
+ Ok(Self::new(global, true, Transform3D::identity()))
}
// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly-numbersequence
@@ -61,7 +61,7 @@ impl DOMMatrixReadOnly {
})
}
- pub fn matrix(&self) -> Ref<Matrix4D<f64>> {
+ pub fn matrix(&self) -> Ref<Transform3D<f64>> {
self.matrix.borrow()
}
@@ -183,7 +183,7 @@ impl DOMMatrixReadOnly {
// https://drafts.fxtf.org/geometry-1/#dom-dommatrix-translateself
pub fn translate_self(&self, tx: f64, ty: f64, tz: f64) {
// Step 1.
- let translation = Matrix4D::create_translation(tx, ty, tz);
+ let translation = Transform3D::create_translation(tx, ty, tz);
let mut matrix = self.matrix.borrow_mut();
*matrix = translation.post_mul(&matrix);
// Step 2.
@@ -202,7 +202,7 @@ impl DOMMatrixReadOnly {
let scaleY = scaleY.unwrap_or(scaleX);
// Step 3.
{
- let scale3D = Matrix4D::create_scale(scaleX, scaleY, scaleZ);
+ let scale3D = Transform3D::create_scale(scaleX, scaleY, scaleZ);
let mut matrix = self.matrix.borrow_mut();
*matrix = scale3D.post_mul(&matrix);
}
@@ -225,7 +225,7 @@ impl DOMMatrixReadOnly {
self.translate_self(originX, originY, originZ);
// Step 2.
{
- let scale3D = Matrix4D::create_scale(scale, scale, scale);
+ let scale3D = Transform3D::create_scale(scale, scale, scale);
let mut matrix = self.matrix.borrow_mut();
*matrix = scale3D.post_mul(&matrix);
}
@@ -256,19 +256,19 @@ impl DOMMatrixReadOnly {
}
if rotZ != 0.0 {
// Step 5.
- let rotation = Matrix4D::create_rotation(0.0, 0.0, 1.0, Radians::new(rotZ.to_radians()));
+ let rotation = Transform3D::create_rotation(0.0, 0.0, 1.0, Radians::new(rotZ.to_radians()));
let mut matrix = self.matrix.borrow_mut();
*matrix = rotation.post_mul(&matrix);
}
if rotY != 0.0 {
// Step 6.
- let rotation = Matrix4D::create_rotation(0.0, 1.0, 0.0, Radians::new(rotY.to_radians()));
+ let rotation = Transform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(rotY.to_radians()));
let mut matrix = self.matrix.borrow_mut();
*matrix = rotation.post_mul(&matrix);
}
if rotX != 0.0 {
// Step 7.
- let rotation = Matrix4D::create_rotation(1.0, 0.0, 0.0, Radians::new(rotX.to_radians()));
+ let rotation = Transform3D::create_rotation(1.0, 0.0, 0.0, Radians::new(rotX.to_radians()));
let mut matrix = self.matrix.borrow_mut();
*matrix = rotation.post_mul(&matrix);
}
@@ -281,7 +281,7 @@ impl DOMMatrixReadOnly {
if y != 0.0 || x < 0.0 {
// Step 1.
let rotZ = Radians::new(f64::atan2(y, x));
- let rotation = Matrix4D::create_rotation(0.0, 0.0, 1.0, rotZ);
+ let rotation = Transform3D::create_rotation(0.0, 0.0, 1.0, rotZ);
let mut matrix = self.matrix.borrow_mut();
*matrix = rotation.post_mul(&matrix);
}
@@ -292,7 +292,7 @@ impl DOMMatrixReadOnly {
pub fn rotate_axis_angle_self(&self, x: f64, y: f64, z: f64, angle: f64) {
// Step 1.
let (norm_x, norm_y, norm_z) = normalize_point(x, y, z);
- let rotation = Matrix4D::create_rotation(norm_x, norm_y, norm_z, Radians::new(angle.to_radians()));
+ let rotation = Transform3D::create_rotation(norm_x, norm_y, norm_z, Radians::new(angle.to_radians()));
let mut matrix = self.matrix.borrow_mut();
*matrix = rotation.post_mul(&matrix);
// Step 2.
@@ -305,7 +305,7 @@ impl DOMMatrixReadOnly {
// https://drafts.fxtf.org/geometry-1/#dom-dommatrix-skewxself
pub fn skew_x_self(&self, sx: f64) {
// Step 1.
- let skew = Matrix4D::create_skew(Radians::new(sx.to_radians()), Radians::new(0.0));
+ let skew = Transform3D::create_skew(Radians::new(sx.to_radians()), Radians::new(0.0));
let mut matrix = self.matrix.borrow_mut();
*matrix = skew.post_mul(&matrix);
// Step 2 in DOMMatrix.SkewXSelf
@@ -314,7 +314,7 @@ impl DOMMatrixReadOnly {
// https://drafts.fxtf.org/geometry-1/#dom-dommatrix-skewyself
pub fn skew_y_self(&self, sy: f64) {
// Step 1.
- let skew = Matrix4D::create_skew(Radians::new(0.0), Radians::new(sy.to_radians()));
+ let skew = Transform3D::create_skew(Radians::new(0.0), Radians::new(sy.to_radians()));
let mut matrix = self.matrix.borrow_mut();
*matrix = skew.post_mul(&matrix);
// Step 2 in DOMMatrix.SkewYSelf
@@ -327,7 +327,7 @@ impl DOMMatrixReadOnly {
*matrix = matrix.inverse().unwrap_or_else(|| {
// Step 2.
self.is2D.set(false);
- Matrix4D::row_major(f64::NAN, f64::NAN, f64::NAN, f64::NAN,
+ Transform3D::row_major(f64::NAN, f64::NAN, f64::NAN, f64::NAN,
f64::NAN, f64::NAN, f64::NAN, f64::NAN,
f64::NAN, f64::NAN, f64::NAN, f64::NAN,
f64::NAN, f64::NAN, f64::NAN, f64::NAN)
@@ -513,7 +513,7 @@ impl DOMMatrixReadOnlyMethods for DOMMatrixReadOnly {
// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-flipx
fn FlipX(&self) -> Root<DOMMatrix> {
let is2D = self.is2D.get();
- let flip = Matrix4D::row_major(-1.0, 0.0, 0.0, 0.0,
+ let flip = Transform3D::row_major(-1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
@@ -524,7 +524,7 @@ impl DOMMatrixReadOnlyMethods for DOMMatrixReadOnly {
// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-flipy
fn FlipY(&self) -> Root<DOMMatrix> {
let is2D = self.is2D.get();
- let flip = Matrix4D::row_major(1.0, 0.0, 0.0, 0.0,
+ let flip = Transform3D::row_major(1.0, 0.0, 0.0, 0.0,
0.0, -1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
@@ -539,21 +539,26 @@ impl DOMMatrixReadOnlyMethods for DOMMatrixReadOnly {
// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-transformpoint
fn TransformPoint(&self, point: &DOMPointInit) -> Root<DOMPoint> {
- let matrix = self.matrix.borrow();
- let result = matrix.transform_point4d(&Point4D::new(point.x, point.y, point.z, point.w));
- DOMPoint::new(
- &self.global(),
- result.x as f64,
- result.y as f64,
- result.z as f64,
- result.w as f64)
+ // Euclid always normalizes the homogeneous coordinate which is usually the right
+ // thing but may (?) not be compliant with the CSS matrix spec (or at least is
+ // probably not the behavior web authors will expect even if it is mathematically
+ // correct in the context of geometry computations).
+ // Since this is the only place where this is needed, better implement it here
+ // than in euclid (which does not have a notion of 4d points).
+ let mat = self.matrix.borrow();
+ let x = point.x * mat.m11 + point.y * mat.m21 + point.z * mat.m31 + point.w * mat.m41;
+ let y = point.x * mat.m12 + point.y * mat.m22 + point.z * mat.m32 + point.w * mat.m42;
+ let z = point.x * mat.m13 + point.y * mat.m23 + point.z * mat.m33 + point.w * mat.m43;
+ let w = point.x * mat.m14 + point.y * mat.m24 + point.z * mat.m34 + point.w * mat.m44;
+
+ DOMPoint::new(&self.global(), x, y, z, w)
}
}
// https://drafts.fxtf.org/geometry-1/#create-a-2d-matrix
-fn create_2d_matrix(entries: &[f64]) -> Matrix4D<f64> {
- Matrix4D::row_major(entries[0], entries[1], 0.0, 0.0,
+fn create_2d_matrix(entries: &[f64]) -> Transform3D<f64> {
+ Transform3D::row_major(entries[0], entries[1], 0.0, 0.0,
entries[2], entries[3], 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
entries[4], entries[5], 0.0, 1.0)
@@ -561,15 +566,15 @@ fn create_2d_matrix(entries: &[f64]) -> Matrix4D<f64> {
// https://drafts.fxtf.org/geometry-1/#create-a-3d-matrix
-fn create_3d_matrix(entries: &[f64]) -> Matrix4D<f64> {
- Matrix4D::row_major(entries[0], entries[1], entries[2], entries[3],
+fn create_3d_matrix(entries: &[f64]) -> Transform3D<f64> {
+ Transform3D::row_major(entries[0], entries[1], entries[2], entries[3],
entries[4], entries[5], entries[6], entries[7],
entries[8], entries[9], entries[10], entries[11],
entries[12], entries[13], entries[14], entries[15])
}
// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly-numbersequence
-pub fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Matrix4D<f64>)> {
+pub fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Transform3D<f64>)> {
if entries.len() == 6 {
Ok((true, create_2d_matrix(&entries)))
} else if entries.len() == 16 {
@@ -582,7 +587,7 @@ pub fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Matrix4D<f64>)> {
// https://drafts.fxtf.org/geometry-1/#validate-and-fixup
-pub fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Matrix4D<f64>)> {
+pub fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
// Step 1.
if dict.a.is_some() && dict.m11.is_some() && dict.a.unwrap() != dict.m11.unwrap() ||
dict.b.is_some() && dict.m12.is_some() && dict.b.unwrap() != dict.m12.unwrap() ||
@@ -621,7 +626,7 @@ pub fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Matrix4D
if is2D.is_none() {
is2D = Some(true);
}
- let matrix = Matrix4D::row_major(m11, m12, dict.m13, dict.m14,
+ let matrix = Transform3D::row_major(m11, m12, dict.m13, dict.m14,
m21, m22, dict.m23, dict.m24,
dict.m31, dict.m32, dict.m33, dict.m34,
m41, m42, dict.m43, dict.m44);
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 3d7b0676fb8..7e01bc66764 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -85,7 +85,7 @@ use net_traits::request::CorsSettings;
use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType;
use script_thread::Runnable;
-use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
+use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode};
use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
use selectors::matching::{RelevantLinkStatus, matches_selector_list};
@@ -97,19 +97,20 @@ use std::convert::TryFrom;
use std::default::Default;
use std::fmt;
use std::rc::Rc;
+use style::CaseSensitivityExt;
+use style::applicable_declarations::ApplicableDeclarationBlock;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
use style::context::{QuirksMode, ReflowGoal};
use style::element_state::*;
+use style::invalidation::element::restyle_hints::RESTYLE_SELF;
use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x};
-use style::restyle_hints::RestyleHint;
use style::rule_tree::CascadeLevel;
use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
use style::selector_parser::extended_filtering;
use style::shared_lock::{SharedRwLock, Locked};
use style::sink::Push;
use style::stylearc::Arc;
-use style::stylist::ApplicableDeclarationBlock;
use style::thread_state;
use style::values::{CSSFloat, Either};
use style::values::specified;
@@ -252,7 +253,7 @@ impl Element {
// FIXME(bholley): I think we should probably only do this for
// NodeStyleDamaged, but I'm preserving existing behavior.
- restyle.hint.insert(RestyleHint::for_self());
+ restyle.hint.insert(RESTYLE_SELF);
if damage == NodeDamage::OtherNodeDamage {
restyle.damage = RestyleDamage::rebuild_and_reflow();
@@ -345,7 +346,7 @@ impl RawLayoutElementHelpers for Element {
pub trait LayoutElementHelpers {
#[allow(unsafe_code)]
- unsafe fn has_class_for_layout(&self, name: &Atom) -> bool;
+ unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool;
#[allow(unsafe_code)]
unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>;
@@ -373,9 +374,9 @@ pub trait LayoutElementHelpers {
impl LayoutElementHelpers for LayoutJS<Element> {
#[allow(unsafe_code)]
#[inline]
- unsafe fn has_class_for_layout(&self, name: &Atom) -> bool {
+ unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
get_attr_for_layout(&*self.unsafe_get(), &ns!(), &local_name!("class")).map_or(false, |attr| {
- attr.value_tokens_forever().unwrap().iter().any(|atom| atom == name)
+ attr.value_tokens_forever().unwrap().iter().any(|atom| case_sensitivity.eq_atom(atom, name))
})
}
@@ -1158,16 +1159,10 @@ impl Element {
})
}
- pub fn has_class(&self, name: &Atom) -> bool {
- let quirks_mode = document_from_node(self).quirks_mode();
- let is_equal = |lhs: &Atom, rhs: &Atom| {
- match quirks_mode {
- QuirksMode::NoQuirks | QuirksMode::LimitedQuirks => lhs == rhs,
- QuirksMode::Quirks => lhs.eq_ignore_ascii_case(&rhs),
- }
- };
- self.get_attribute(&ns!(), &local_name!("class"))
- .map_or(false, |attr| attr.value().as_tokens().iter().any(|atom| is_equal(name, atom)))
+ pub fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
+ self.get_attribute(&ns!(), &local_name!("class")).map_or(false, |attr| {
+ attr.value().as_tokens().iter().any(|atom| case_sensitivity.eq_atom(name, atom))
+ })
}
pub fn set_atomic_attribute(&self, local_name: &LocalName, value: DOMString) {
@@ -2503,12 +2498,12 @@ impl<'a> ::selectors::Element for Root<Element> {
}
}
- fn get_id(&self) -> Option<Atom> {
- self.id_attribute.borrow().clone()
+ fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
+ self.id_attribute.borrow().as_ref().map_or(false, |atom| case_sensitivity.eq_atom(id, atom))
}
- fn has_class(&self, name: &Atom) -> bool {
- Element::has_class(&**self, name)
+ fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
+ Element::has_class(&**self, name, case_sensitivity)
}
fn is_html_element_in_html_document(&self) -> bool {
diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs
index 81a2bc87584..4526384ebb5 100644
--- a/components/script/dom/eventsource.rs
+++ b/components/script/dom/eventsource.rs
@@ -16,7 +16,7 @@ use dom::eventtarget::EventTarget;
use dom::globalscope::GlobalScope;
use dom::messageevent::MessageEvent;
use dom_struct::dom_struct;
-use euclid::length::Length;
+use euclid::Length;
use hyper::header::{Accept, qitem};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
diff --git a/components/script/dom/htmlareaelement.rs b/components/script/dom/htmlareaelement.rs
index 548efbd43de..6826a188399 100644
--- a/components/script/dom/htmlareaelement.rs
+++ b/components/script/dom/htmlareaelement.rs
@@ -19,7 +19,7 @@ use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
-use euclid::point::Point2D;
+use euclid::Point2D;
use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use std::default::Default;
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
index c8956c1620b..2454b634040 100644
--- a/components/script/dom/htmlcanvaselement.rs
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -26,7 +26,7 @@ use dom::node::{Node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::webglrenderingcontext::{LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext};
use dom_struct::dom_struct;
-use euclid::size::Size2D;
+use euclid::Size2D;
use html5ever::{LocalName, Prefix};
use image::ColorType;
use image::png::PNGEncoder;
diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs
index aff4125a29f..814ea766f53 100644
--- a/components/script/dom/htmlcollection.rs
+++ b/components/script/dom/htmlcollection.rs
@@ -11,7 +11,7 @@ use dom::bindings::str::DOMString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::xmlname::namespace_from_domstring;
use dom::element::Element;
-use dom::node::Node;
+use dom::node::{Node, document_from_node};
use dom::window::Window;
use dom_struct::dom_struct;
use html5ever::{LocalName, QualName};
@@ -199,7 +199,10 @@ impl HTMLCollection {
}
impl CollectionFilter for ClassNameFilter {
fn filter(&self, elem: &Element, _root: &Node) -> bool {
- self.classes.iter().all(|class| elem.has_class(class))
+ let case_sensitivity = document_from_node(elem)
+ .quirks_mode()
+ .classes_and_ids_case_sensitivity();
+ self.classes.iter().all(|class| elem.has_class(class, case_sensitivity))
}
}
let filter = ClassNameFilter {
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index e1209021d35..6a4b67a6a92 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -35,7 +35,7 @@ use dom::values::UNSIGNED_LONG_MAX;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use dom_struct::dom_struct;
-use euclid::point::Point2D;
+use euclid::Point2D;
use html5ever::{LocalName, Prefix};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
@@ -493,6 +493,7 @@ impl HTMLImageElement {
request.image = None;
request.metadata = None;
let document = document_from_node(self);
+ LoadBlocker::terminate(&mut request.blocker);
request.blocker = Some(LoadBlocker::new(&*document, LoadType::Image(url.clone())));
}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index da004317179..ff004261db5 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -32,10 +32,11 @@ use std::cell::Cell;
use std::default::Default;
use style::attr::AttrValue;
use style::media_queries::parse_media_query_list;
-use style::parser::{PARSING_MODE_DEFAULT, ParserContext as CssParserContext};
+use style::parser::ParserContext as CssParserContext;
use style::str::HTML_SPACE_CHARACTERS;
use style::stylearc::Arc;
use style::stylesheets::{CssRuleType, Stylesheet};
+use style_traits::PARSING_MODE_DEFAULT;
use stylesheet_loader::{StylesheetLoader, StylesheetContextSource, StylesheetOwner};
unsafe_no_jsmanaged_fields!(Stylesheet);
diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs
index 888855dc7b6..cc11a9d9da4 100644
--- a/components/script/dom/htmlmetaelement.rs
+++ b/components/script/dom/htmlmetaelement.rs
@@ -19,6 +19,7 @@ use dom::node::{Node, UnbindContext, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
+use parking_lot::RwLock;
use servo_config::prefs::PREFS;
use std::ascii::AsciiExt;
use std::sync::atomic::AtomicBool;
@@ -105,7 +106,7 @@ impl HTMLMetaElement {
rules: CssRules::new(vec![rule], shared_lock),
origin: Origin::Author,
shared_lock: shared_lock.clone(),
- url_data: window_from_node(self).get_url(),
+ url_data: RwLock::new(window_from_node(self).get_url()),
namespaces: Default::default(),
media: Arc::new(shared_lock.wrap(MediaList::empty())),
// Viewport constraints are always recomputed on resize; they don't need to
diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs
index 4067cb7439d..bf4266e1875 100644
--- a/components/script/dom/htmlstyleelement.rs
+++ b/components/script/dom/htmlstyleelement.rs
@@ -23,9 +23,10 @@ use net_traits::ReferrerPolicy;
use script_layout_interface::message::Msg;
use std::cell::Cell;
use style::media_queries::parse_media_query_list;
-use style::parser::{PARSING_MODE_DEFAULT, ParserContext as CssParserContext};
+use style::parser::ParserContext as CssParserContext;
use style::stylearc::Arc;
use style::stylesheets::{CssRuleType, Stylesheet, Origin};
+use style_traits::PARSING_MODE_DEFAULT;
use stylesheet_loader::{StylesheetLoader, StylesheetOwner};
#[dom_struct]
diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs
index 220ead847e8..a8a5e1ed4e7 100644
--- a/components/script/dom/imagedata.rs
+++ b/components/script/dom/imagedata.rs
@@ -10,7 +10,7 @@ use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
-use euclid::size::Size2D;
+use euclid::Size2D;
use js::jsapi::{Heap, JSContext, JSObject};
use js::rust::Runtime;
use js::typedarray::{Uint8ClampedArray, CreateWith};
diff --git a/components/script/dom/medialist.rs b/components/script/dom/medialist.rs
index d77af9dc1b9..55ca57cb4fd 100644
--- a/components/script/dom/medialist.rs
+++ b/components/script/dom/medialist.rs
@@ -14,11 +14,11 @@ use dom::window::Window;
use dom_struct::dom_struct;
use style::media_queries::{MediaQuery, parse_media_query_list};
use style::media_queries::MediaList as StyleMediaList;
-use style::parser::{PARSING_MODE_DEFAULT, ParserContext};
+use style::parser::ParserContext;
use style::shared_lock::{SharedRwLock, Locked};
use style::stylearc::Arc;
use style::stylesheets::CssRuleType;
-use style_traits::ToCss;
+use style_traits::{PARSING_MODE_DEFAULT, ToCss};
#[dom_struct]
pub struct MediaList {
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 32cb8054ee7..88ad070bba7 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -56,9 +56,7 @@ use dom::text::Text;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use dom::window::Window;
use dom_struct::dom_struct;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Point2D, Vector2D, Rect, Size2D};
use heapsize::{HeapSizeOf, heap_size_of};
use html5ever::{Prefix, Namespace, QualName};
use js::jsapi::{JSContext, JSObject, JSRuntime};
@@ -612,7 +610,7 @@ impl Node {
}
}
- pub fn scroll_offset(&self) -> Point2D<f32> {
+ pub fn scroll_offset(&self) -> Vector2D<f32> {
let document = self.owner_doc();
let window = document.window();
window.scroll_offset_query(self)
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index ce9b16a0598..3b8ee165bce 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -40,7 +40,7 @@ use dom::webgltexture::{TexParameterValue, WebGLTexture};
use dom::webgluniformlocation::WebGLUniformLocation;
use dom::window::Window;
use dom_struct::dom_struct;
-use euclid::size::Size2D;
+use euclid::Size2D;
use half::f16;
use ipc_channel::ipc::{self, IpcSender};
use js::conversions::ConversionBehavior;
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index bcc5a749d33..1f6a7c26eea 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -53,7 +53,7 @@ use dom::windowproxy::WindowProxy;
use dom::worklet::Worklet;
use dom::workletglobalscope::WorkletGlobalScopeType;
use dom_struct::dom_struct;
-use euclid::{Point2D, Rect, Size2D};
+use euclid::{Point2D, Vector2D, Rect, Size2D};
use fetch;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
@@ -84,7 +84,7 @@ use script_traits::{ConstellationControlMsg, DocumentState, LoadData, MozBrowser
use script_traits::{ScriptMsg as ConstellationMsg, ScrollState, TimerEvent, TimerEventId};
use script_traits::{TimerSchedulerMsg, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
-use servo_atoms::Atom;
+use selectors::attr::CaseSensitivity;
use servo_config::opts;
use servo_config::prefs::PREFS;
use servo_geometry::{f32_rect_to_au_rect, max_rect};
@@ -107,12 +107,13 @@ use std::sync::mpsc::TryRecvError::{Disconnected, Empty};
use style::context::ReflowGoal;
use style::error_reporting::ParseErrorReporter;
use style::media_queries;
-use style::parser::{PARSING_MODE_DEFAULT, ParserContext as CssParserContext};
+use style::parser::ParserContext as CssParserContext;
use style::properties::PropertyId;
use style::properties::longhands::overflow_x;
use style::selector_parser::PseudoElement;
use style::str::HTML_SPACE_CHARACTERS;
use style::stylesheets::CssRuleType;
+use style_traits::PARSING_MODE_DEFAULT;
use task_source::dom_manipulation::DOMManipulationTaskSource;
use task_source::file_reading::FileReadingTaskSource;
use task_source::history_traversal::HistoryTraversalTaskSource;
@@ -254,7 +255,7 @@ pub struct Window {
error_reporter: CSSErrorReporter,
/// A list of scroll offsets for each scrollable element.
- scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Point2D<f32>>>,
+ scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Vector2D<f32>>>,
/// All the MediaQueryLists we need to update
media_query_lists: WeakMediaQueryListVec,
@@ -364,7 +365,7 @@ impl Window {
/// Sets a new list of scroll offsets.
///
/// This is called when layout gives us new ones and WebRender is in use.
- pub fn set_scroll_offsets(&self, offsets: HashMap<UntrustedNodeAddress, Point2D<f32>>) {
+ pub fn set_scroll_offsets(&self, offsets: HashMap<UntrustedNodeAddress, Vector2D<f32>>) {
*self.scroll_offsets.borrow_mut() = offsets
}
@@ -1154,7 +1155,7 @@ impl Window {
self.layout_chan.send(Msg::UpdateScrollStateFromScript(ScrollState {
scroll_root_id: scroll_root_id,
- scroll_offset: Point2D::new(-x, -y),
+ scroll_offset: Vector2D::new(-x, -y),
})).unwrap();
// TODO (farodin91): Raise an event to stop the current_viewport
@@ -1365,7 +1366,7 @@ impl Window {
// See http://testthewebforward.org/docs/reftests.html
let html_element = document.GetDocumentElement();
let reftest_wait = html_element.map_or(false, |elem| {
- elem.has_class(&Atom::from("reftest-wait"))
+ elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive)
});
let ready_state = document.ReadyState();
@@ -1448,7 +1449,7 @@ impl Window {
self.layout_rpc.node_overflow().0.unwrap()
}
- pub fn scroll_offset_query(&self, node: &Node) -> Point2D<f32> {
+ pub fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32> {
let mut node = Root::from_ref(node);
loop {
if let Some(scroll_offset) = self.scroll_offsets
@@ -1461,8 +1462,8 @@ impl Window {
None => break,
}
}
- let offset = self.current_viewport.get().origin;
- Point2D::new(offset.x.to_f32_px(), offset.y.to_f32_px())
+ let vp_origin = self.current_viewport.get().origin;
+ Vector2D::new(vp_origin.x.to_f32_px(), vp_origin.y.to_f32_px())
}
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index 362dad8a925..bd4af97d347 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -40,7 +40,7 @@ use dom_struct::dom_struct;
use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label;
use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef};
-use euclid::length::Length;
+use euclid::Length;
use html5ever::serialize;
use html5ever::serialize::SerializeOpts;
use hyper::header::{ContentLength, ContentType, ContentEncoding};
diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs
index 15589619e17..1d28242e926 100644
--- a/components/script/layout_wrapper.rs
+++ b/components/script/layout_wrapper.rs
@@ -49,7 +49,7 @@ use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, Truste
use script_layout_interface::{OpaqueStyleAndLayoutData, StyleData};
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
-use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
+use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus};
use selectors::matching::VisitedHandlingMode;
use servo_atoms::Atom;
@@ -61,6 +61,8 @@ use std::marker::PhantomData;
use std::mem::transmute;
use std::sync::atomic::Ordering;
use style;
+use style::CaseSensitivityExt;
+use style::applicable_declarations::ApplicableDeclarationBlock;
use style::attr::AttrValue;
use style::computed_values::display;
use style::context::{QuirksMode, SharedStyleContext};
@@ -76,7 +78,6 @@ use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocke
use style::sink::Push;
use style::str::is_whitespace;
use style::stylearc::Arc;
-use style::stylist::ApplicableDeclarationBlock;
#[derive(Copy, Clone)]
pub struct ServoLayoutNode<'a> {
@@ -414,6 +415,13 @@ impl<'le> TElement for ServoLayoutElement<'le> {
self.get_attr(namespace, attr).is_some()
}
+ #[inline]
+ fn get_id(&self) -> Option<Atom> {
+ unsafe {
+ (*self.element.id_attribute()).clone()
+ }
+ }
+
#[inline(always)]
fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
unsafe {
@@ -779,16 +787,18 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
}
#[inline]
- fn get_id(&self) -> Option<Atom> {
+ fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
unsafe {
- (*self.element.id_attribute()).clone()
+ (*self.element.id_attribute())
+ .as_ref()
+ .map_or(false, |atom| case_sensitivity.eq_atom(atom, id))
}
}
#[inline]
- fn has_class(&self, name: &Atom) -> bool {
+ fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
unsafe {
- self.element.has_class_for_layout(name)
+ self.element.has_class_for_layout(name, case_sensitivity)
}
}
@@ -1249,12 +1259,12 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false
}
- fn get_id(&self) -> Option<Atom> {
- debug!("ServoThreadSafeLayoutElement::get_id called");
- None
+ fn has_id(&self, _id: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
+ debug!("ServoThreadSafeLayoutElement::has_id called");
+ false
}
- fn has_class(&self, _name: &Atom) -> bool {
+ fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
debug!("ServoThreadSafeLayoutElement::has_class called");
false
}
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index 8c243b18695..efbdb6bdc40 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -27,6 +27,7 @@ use script_thread::{Runnable, STACK_ROOTS, trace_thread};
use servo_config::opts;
use servo_config::prefs::PREFS;
use std::cell::Cell;
+use std::fmt;
use std::io::{Write, stdout};
use std::marker::PhantomData;
use std::os;
@@ -45,6 +46,15 @@ pub enum CommonScriptMsg {
RunnableMsg(ScriptThreadEventCategory, Box<Runnable + Send>),
}
+impl fmt::Debug for CommonScriptMsg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ CommonScriptMsg::CollectReports(_) => write!(f, "CollectReports(...)"),
+ CommonScriptMsg::RunnableMsg(category, _) => write!(f, "RunnableMsg({:?}, ...)", category),
+ }
+ }
+}
+
/// A cloneable interface for communicating with an event loop.
pub trait ScriptChan: JSTraceable {
/// Send a message to the associated event loop.
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 8dabb29c81b..c329767954b 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -58,8 +58,7 @@ use dom::windowproxy::WindowProxy;
use dom::worker::TrustedWorkerAddress;
use dom::worklet::WorkletThreadPool;
use dom::workletglobalscope::WorkletGlobalScopeInit;
-use euclid::Rect;
-use euclid::point::Point2D;
+use euclid::{Point2D, Vector2D, Rect};
use hyper::header::{ContentType, HttpDate, Headers, LastModified};
use hyper::header::ReferrerPolicy as ReferrerPolicyHeader;
use hyper::mime::{Mime, SubLevel, TopLevel};
@@ -232,6 +231,7 @@ pub trait Runnable {
fn main_thread_handler(self: Box<Self>, _script_thread: &ScriptThread) { self.handler(); }
}
+#[derive(Debug)]
enum MixedMessage {
FromConstellation(ConstellationControlMsg),
FromScript(MainThreadScriptMsg),
@@ -241,6 +241,7 @@ enum MixedMessage {
}
/// Messages used to control the script event loop
+#[derive(Debug)]
pub enum MainThreadScriptMsg {
/// Common variants associated with the script messages
Common(CommonScriptMsg),
@@ -983,6 +984,7 @@ impl ScriptThread {
// Process the gathered events.
for msg in sequential {
+ debug!("Processing event {:?}.", msg);
let category = self.categorize_msg(&msg);
let result = self.profile_event(category, move || {
@@ -1336,7 +1338,7 @@ impl ScriptThread {
fn handle_set_scroll_state(&self,
id: PipelineId,
- scroll_states: &[(UntrustedNodeAddress, Point2D<f32>)]) {
+ scroll_states: &[(UntrustedNodeAddress, Vector2D<f32>)]) {
let window = match { self.documents.borrow().find_window(id) } {
Some(window) => window,
None => return warn!("Set scroll state message sent to nonexistent pipeline: {:?}", id),
@@ -1347,8 +1349,7 @@ impl ScriptThread {
if node_address == UntrustedNodeAddress(ptr::null()) {
window.update_viewport_for_scroll(-scroll_offset.x, -scroll_offset.y);
} else {
- scroll_offsets.insert(node_address,
- Point2D::new(-scroll_offset.x, -scroll_offset.y));
+ scroll_offsets.insert(node_address, -*scroll_offset);
}
}
window.set_scroll_offsets(scroll_offsets)
@@ -1649,6 +1650,13 @@ impl ScriptThread {
let load = self.incomplete_loads.borrow_mut().remove(idx);
load.layout_chan.clone()
} else if let Some(document) = self.documents.borrow_mut().remove(id) {
+ // We don't want to dispatch `mouseout` event pointing to non-existing element
+ if let Some(target) = self.topmost_mouse_over_target.get() {
+ if target.upcast::<Node>().owner_doc() == document {
+ self.topmost_mouse_over_target.set(None);
+ }
+ }
+
let window = document.window();
if discard_bc == DiscardBrowsingContext::Yes {
window.window_proxy().discard_browsing_context();
diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs
index 3143d8b34ad..4d2518a7cca 100644
--- a/components/script/stylesheet_loader.rs
+++ b/components/script/stylesheet_loader.rs
@@ -160,7 +160,7 @@ impl FetchResponseListener for StylesheetContext {
&data,
protocol_encoding_label,
Some(environment_encoding),
- &final_url,
+ final_url,
Some(&loader),
win.css_error_reporter());
}
diff --git a/components/script/task_source/dom_manipulation.rs b/components/script/task_source/dom_manipulation.rs
index 75fd4021168..12deff1880e 100644
--- a/components/script/task_source/dom_manipulation.rs
+++ b/components/script/task_source/dom_manipulation.rs
@@ -9,6 +9,7 @@ use dom::eventtarget::EventTarget;
use dom::window::Window;
use script_thread::{MainThreadScriptMsg, Runnable, RunnableWrapper, ScriptThread};
use servo_atoms::Atom;
+use std::fmt;
use std::result::Result;
use std::sync::mpsc::Sender;
use task_source::TaskSource;
@@ -16,6 +17,12 @@ use task_source::TaskSource;
#[derive(JSTraceable, Clone)]
pub struct DOMManipulationTaskSource(pub Sender<MainThreadScriptMsg>);
+impl fmt::Debug for DOMManipulationTaskSource {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "DOMManipulationTaskSource(...)")
+ }
+}
+
impl TaskSource for DOMManipulationTaskSource {
fn queue_with_wrapper<T>(&self,
msg: Box<T>,
@@ -56,6 +63,12 @@ impl DOMManipulationTaskSource {
pub struct DOMManipulationTask(pub Box<Runnable + Send>);
+impl fmt::Debug for DOMManipulationTask {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "DOMManipulationTask(...)")
+ }
+}
+
impl DOMManipulationTask {
pub fn handle_task(self, script_thread: &ScriptThread) {
if !self.0.is_cancelled() {
diff --git a/components/script/task_source/user_interaction.rs b/components/script/task_source/user_interaction.rs
index c97e1e1895a..2878a347eb4 100644
--- a/components/script/task_source/user_interaction.rs
+++ b/components/script/task_source/user_interaction.rs
@@ -9,6 +9,7 @@ use dom::eventtarget::EventTarget;
use dom::window::Window;
use script_thread::{MainThreadScriptMsg, Runnable, RunnableWrapper, ScriptThread};
use servo_atoms::Atom;
+use std::fmt;
use std::result::Result;
use std::sync::mpsc::Sender;
use task_source::TaskSource;
@@ -16,6 +17,12 @@ use task_source::TaskSource;
#[derive(JSTraceable, Clone)]
pub struct UserInteractionTaskSource(pub Sender<MainThreadScriptMsg>);
+impl fmt::Debug for UserInteractionTaskSource {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "UserInteractionTaskSource(...)")
+ }
+}
+
impl TaskSource for UserInteractionTaskSource {
fn queue_with_wrapper<T>(&self,
msg: Box<T>,
@@ -47,6 +54,12 @@ impl UserInteractionTaskSource {
pub struct UserInteractionTask(pub Box<Runnable + Send>);
+impl fmt::Debug for UserInteractionTask {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "UserInteractionTask(...)")
+ }
+}
+
impl UserInteractionTask {
pub fn handle_task(self, script_thread: &ScriptThread) {
if !self.0.is_cancelled() {
diff --git a/components/script/timers.rs b/components/script/timers.rs
index cccefd5eb77..54b2d3f2519 100644
--- a/components/script/timers.rs
+++ b/components/script/timers.rs
@@ -12,7 +12,7 @@ use dom::eventsource::EventSourceTimeoutCallback;
use dom::globalscope::GlobalScope;
use dom::testbinding::TestBindingCallback;
use dom::xmlhttprequest::XHRTimeoutCallback;
-use euclid::length::Length;
+use euclid::Length;
use heapsize::HeapSizeOf;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{HandleValue, Heap};
diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs
index 1aa2ad05dab..bae4480d14c 100644
--- a/components/script/webdriver_handlers.rs
+++ b/components/script/webdriver_handlers.rs
@@ -22,9 +22,7 @@ use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlinputelement::HTMLInputElement;
use dom::htmloptionelement::HTMLOptionElement;
use dom::node::{Node, window_from_node};
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
+use euclid::{Point2D, Rect, Size2D};
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::{HandleValue, JSContext};
diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml
index c2e7ae3ab6f..6fa9b81ad71 100644
--- a/components/script_layout_interface/Cargo.toml
+++ b/components/script_layout_interface/Cargo.toml
@@ -14,7 +14,7 @@ app_units = "0.4.1"
atomic_refcell = "0.1"
canvas_traits = {path = "../canvas_traits"}
cssparser = "0.14.0"
-euclid = "0.13"
+euclid = "0.14.4"
gfx_traits = {path = "../gfx_traits"}
heapsize = "0.4"
heapsize_derive = "0.1"
diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs
index 104de7da3d9..84b93c527aa 100644
--- a/components/script_layout_interface/message.rs
+++ b/components/script_layout_interface/message.rs
@@ -4,8 +4,7 @@
use {OpaqueStyleAndLayoutData, TrustedNodeAddress, PendingImage};
use app_units::Au;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
+use euclid::{Point2D, Rect};
use gfx_traits::Epoch;
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use msg::constellation_msg::PipelineId;
diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs
index a39e51d3633..00bad9705b2 100644
--- a/components/script_layout_interface/rpc.rs
+++ b/components/script_layout_interface/rpc.rs
@@ -3,8 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
+use euclid::{Point2D, Rect};
use script_traits::UntrustedNodeAddress;
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x};
use webrender_traits::ClipId;
diff --git a/components/script_traits/Cargo.toml b/components/script_traits/Cargo.toml
index ac85cd782ec..ddcf1a3765f 100644
--- a/components/script_traits/Cargo.toml
+++ b/components/script_traits/Cargo.toml
@@ -15,7 +15,7 @@ bluetooth_traits = {path = "../bluetooth_traits"}
canvas_traits = {path = "../canvas_traits"}
cookie = "0.6"
devtools_traits = {path = "../devtools_traits"}
-euclid = "0.13"
+euclid = "0.14.4"
gfx_traits = {path = "../gfx_traits"}
heapsize = "0.4"
heapsize_derive = "0.1"
@@ -25,7 +25,7 @@ ipc-channel = "0.7"
libc = "0.2"
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
-offscreen_gl_context = "0.8"
+offscreen_gl_context = { version = "0.9", features = ["serde"] }
profile_traits = {path = "../profile_traits"}
rustc-serialize = "0.3.4"
serde = "0.9"
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index c036eee28d9..752d4e48b0a 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -44,12 +44,7 @@ pub mod webdriver_msg;
use app_units::Au;
use bluetooth_traits::BluetoothRequest;
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
-use euclid::Size2D;
-use euclid::length::Length;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::scale_factor::ScaleFactor;
-use euclid::size::TypedSize2D;
+use euclid::{Size2D, Length, Point2D, Vector2D, Rect, ScaleFactor, TypedSize2D};
use gfx_traits::Epoch;
use heapsize::HeapSizeOf;
use hyper::header::Headers;
@@ -254,7 +249,7 @@ pub enum ConstellationControlMsg {
/// Notifies script of the viewport.
Viewport(PipelineId, Rect<f32>),
/// Notifies script of a new set of scroll offsets.
- SetScrollState(PipelineId, Vec<(UntrustedNodeAddress, Point2D<f32>)>),
+ SetScrollState(PipelineId, Vec<(UntrustedNodeAddress, Vector2D<f32>)>),
/// Requests that the script thread immediately send the constellation the title of a pipeline.
GetTitle(PipelineId),
/// Notifies script thread of a change to one of its document's activity
@@ -451,11 +446,11 @@ pub enum TimerSchedulerMsg {
/// Notifies the script thread to fire due timers.
/// `TimerSource` must be `FromWindow` when dispatched to `ScriptThread` and
/// must be `FromWorker` when dispatched to a `DedicatedGlobalWorkerScope`
-#[derive(Deserialize, Serialize)]
+#[derive(Debug, Deserialize, Serialize)]
pub struct TimerEvent(pub TimerSource, pub TimerEventId);
/// Describes the thread that requested the TimerEvent.
-#[derive(Copy, Clone, HeapSizeOf, Deserialize, Serialize)]
+#[derive(Copy, Clone, Debug, HeapSizeOf, Deserialize, Serialize)]
pub enum TimerSource {
/// The event was requested from a window (ScriptThread).
FromWindow(PipelineId),
@@ -690,7 +685,7 @@ pub struct ScrollState {
/// The ID of the scroll root.
pub scroll_root_id: ClipId,
/// The scrolling offset of this stacking context.
- pub scroll_offset: Point2D<f32>,
+ pub scroll_offset: Vector2D<f32>,
}
/// One hardware pixel.
diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs
index d7449f70f29..b9e5b48b4b9 100644
--- a/components/script_traits/script_msg.rs
+++ b/components/script_traits/script_msg.rs
@@ -14,8 +14,7 @@ use WorkerGlobalScopeInit;
use WorkerScriptLoadOrigin;
use canvas_traits::CanvasMsg;
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
-use euclid::point::Point2D;
-use euclid::size::{Size2D, TypedSize2D};
+use euclid::{Point2D, Size2D, TypedSize2D};
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, FrameType, PipelineId, TraversalDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
diff --git a/components/script_traits/webdriver_msg.rs b/components/script_traits/webdriver_msg.rs
index ed7f2ea8f18..e53e6e77ae3 100644
--- a/components/script_traits/webdriver_msg.rs
+++ b/components/script_traits/webdriver_msg.rs
@@ -5,7 +5,7 @@
#![allow(missing_docs)]
use cookie_rs::Cookie;
-use euclid::rect::Rect;
+use euclid::Rect;
use hyper_serde::Serde;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::BrowsingContextId;
diff --git a/components/selectors/attr.rs b/components/selectors/attr.rs
index 274081ae9f7..da208c94b1f 100644
--- a/components/selectors/attr.rs
+++ b/components/selectors/attr.rs
@@ -127,7 +127,7 @@ pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub enum ParsedCaseSensitivity {
- CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
+ CaseSensitive,
AsciiCaseInsensitive,
AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
}
@@ -150,7 +150,7 @@ impl ParsedCaseSensitivity {
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub enum CaseSensitivity {
- CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
+ CaseSensitive,
AsciiCaseInsensitive,
}
diff --git a/components/selectors/bloom.rs b/components/selectors/bloom.rs
index b60fc1be0b8..0f6fabbaa10 100644
--- a/components/selectors/bloom.rs
+++ b/components/selectors/bloom.rs
@@ -7,10 +7,13 @@
use fnv::FnvHasher;
use std::hash::{Hash, Hasher};
+// The top 12 bits of the 32-bit hash value are not used by the bloom filter.
+// Consumers may rely on this to pack hashes more efficiently.
+pub const BLOOM_HASH_MASK: u32 = 0x00ffffff;
const KEY_SIZE: usize = 12;
+
const ARRAY_SIZE: usize = 1 << KEY_SIZE;
const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;
-const KEY_SHIFT: usize = 16;
/// A counting Bloom filter with 8-bit counters. For now we assume
/// that having two hash functions is enough, but we may revisit that
@@ -183,7 +186,7 @@ fn hash1(hash: u32) -> u32 {
#[inline]
fn hash2(hash: u32) -> u32 {
- (hash >> KEY_SHIFT) & KEY_MASK
+ (hash >> KEY_SIZE) & KEY_MASK
}
#[test]
diff --git a/components/selectors/context.rs b/components/selectors/context.rs
new file mode 100644
index 00000000000..12ebd977837
--- /dev/null
+++ b/components/selectors/context.rs
@@ -0,0 +1,158 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 attr::CaseSensitivity;
+use bloom::BloomFilter;
+
+bitflags! {
+ /// Set of flags that determine the different kind of elements affected by
+ /// the selector matching process.
+ ///
+ /// This is used to implement efficient sharing.
+ #[derive(Default)]
+ pub flags StyleRelations: usize {
+ /// Whether this element is affected by presentational hints. This is
+ /// computed externally (that is, in Servo).
+ const AFFECTED_BY_PRESENTATIONAL_HINTS = 1 << 0,
+ /// Whether this element has pseudo-element styles. Computed externally.
+ const AFFECTED_BY_PSEUDO_ELEMENTS = 1 << 1,
+ }
+}
+
+/// What kind of selector matching mode we should use.
+///
+/// There are two modes of selector matching. The difference is only noticeable
+/// in presence of pseudo-elements.
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub enum MatchingMode {
+ /// Don't ignore any pseudo-element selectors.
+ Normal,
+
+ /// Ignores any stateless pseudo-element selectors in the rightmost sequence
+ /// of simple selectors.
+ ///
+ /// This is useful, for example, to match against ::before when you aren't a
+ /// pseudo-element yourself.
+ ///
+ /// For example, in presence of `::before:hover`, it would never match, but
+ /// `::before` would be ignored as in "matching".
+ ///
+ /// It's required for all the selectors you match using this mode to have a
+ /// pseudo-element.
+ ForStatelessPseudoElement,
+}
+
+/// The mode to use when matching unvisited and visited links.
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+pub enum VisitedHandlingMode {
+ /// All links are matched as if they are unvisted.
+ AllLinksUnvisited,
+ /// All links are matched as if they are visited and unvisited (both :link
+ /// and :visited match).
+ ///
+ /// This is intended to be used from invalidation code, to be conservative
+ /// about whether we need to restyle a link.
+ AllLinksVisitedAndUnvisited,
+ /// A element's "relevant link" is the element being matched if it is a link
+ /// or the nearest ancestor link. The relevant link is matched as though it
+ /// is visited, and all other links are matched as if they are unvisited.
+ RelevantLinkVisited,
+}
+
+/// Which quirks mode is this document in.
+///
+/// See: https://quirks.spec.whatwg.org/
+#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
+pub enum QuirksMode {
+ /// Quirks mode.
+ Quirks,
+ /// Limited quirks mode.
+ LimitedQuirks,
+ /// No quirks mode.
+ NoQuirks,
+}
+
+impl QuirksMode {
+ #[inline]
+ pub fn classes_and_ids_case_sensitivity(self) -> CaseSensitivity {
+ match self {
+ QuirksMode::NoQuirks |
+ QuirksMode::LimitedQuirks => CaseSensitivity::CaseSensitive,
+ QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive,
+ }
+ }
+}
+
+/// Data associated with the matching process for a element. This context is
+/// used across many selectors for an element, so it's not appropriate for
+/// transient data that applies to only a single selector.
+#[derive(Clone)]
+pub struct MatchingContext<'a> {
+ /// Output that records certains relations between elements noticed during
+ /// matching (and also extended after matching).
+ pub relations: StyleRelations,
+ /// Input with the matching mode we should use when matching selectors.
+ pub matching_mode: MatchingMode,
+ /// Input with the bloom filter used to fast-reject selectors.
+ pub bloom_filter: Option<&'a BloomFilter>,
+ /// Input that controls how matching for links is handled.
+ pub visited_handling: VisitedHandlingMode,
+ /// Output that records whether we encountered a "relevant link" while
+ /// matching _any_ selector for this element. (This differs from
+ /// `RelevantLinkStatus` which tracks the status for the _current_ selector
+ /// only.)
+ pub relevant_link_found: bool,
+
+ quirks_mode: QuirksMode,
+ classes_and_ids_case_sensitivity: CaseSensitivity,
+}
+
+impl<'a> MatchingContext<'a> {
+ /// Constructs a new `MatchingContext`.
+ pub fn new(matching_mode: MatchingMode,
+ bloom_filter: Option<&'a BloomFilter>,
+ quirks_mode: QuirksMode)
+ -> Self
+ {
+ Self {
+ relations: StyleRelations::empty(),
+ matching_mode: matching_mode,
+ bloom_filter: bloom_filter,
+ visited_handling: VisitedHandlingMode::AllLinksUnvisited,
+ relevant_link_found: false,
+ quirks_mode: quirks_mode,
+ classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
+ }
+ }
+
+ /// Constructs a new `MatchingContext` for use in visited matching.
+ pub fn new_for_visited(matching_mode: MatchingMode,
+ bloom_filter: Option<&'a BloomFilter>,
+ visited_handling: VisitedHandlingMode,
+ quirks_mode: QuirksMode)
+ -> Self
+ {
+ Self {
+ relations: StyleRelations::empty(),
+ matching_mode: matching_mode,
+ bloom_filter: bloom_filter,
+ visited_handling: visited_handling,
+ relevant_link_found: false,
+ quirks_mode: quirks_mode,
+ classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
+ }
+ }
+
+ /// The quirks mode of the document.
+ #[inline]
+ pub fn quirks_mode(&self) -> QuirksMode {
+ self.quirks_mode
+ }
+
+ /// The case-sensitivity for class and ID selectors
+ #[inline]
+ pub fn classes_and_ids_case_sensitivity(&self) -> CaseSensitivity {
+ self.classes_and_ids_case_sensitivity
+ }
+}
diff --git a/components/selectors/lib.rs b/components/selectors/lib.rs
index 130475271fc..16a7949cfa9 100644
--- a/components/selectors/lib.rs
+++ b/components/selectors/lib.rs
@@ -15,6 +15,7 @@ extern crate smallvec;
pub mod attr;
pub mod bloom;
+pub mod context;
pub mod matching;
pub mod parser;
#[cfg(test)] mod size_of_tests;
diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs
index 9bb49cea13a..62cd7ee5448 100644
--- a/components/selectors/matching.rs
+++ b/components/selectors/matching.rs
@@ -3,33 +3,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use attr::{ParsedAttrSelectorOperation, AttrSelectorOperation, NamespaceConstraint};
-use bloom::BloomFilter;
+use bloom::{BLOOM_HASH_MASK, BloomFilter};
use parser::{AncestorHashes, Combinator, Component, LocalName};
use parser::{Selector, SelectorImpl, SelectorIter, SelectorList};
use std::borrow::Borrow;
use tree::Element;
+pub use context::*;
+
// The bloom filter for descendant CSS selectors will have a <1% false
// positive rate until it has this many selectors in it, then it will
// rapidly increase.
pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;
bitflags! {
- /// Set of flags that determine the different kind of elements affected by
- /// the selector matching process.
- ///
- /// This is used to implement efficient sharing.
- #[derive(Default)]
- pub flags StyleRelations: usize {
- /// Whether this element is affected by presentational hints. This is
- /// computed externally (that is, in Servo).
- const AFFECTED_BY_PRESENTATIONAL_HINTS = 1 << 0,
- /// Whether this element has pseudo-element styles. Computed externally.
- const AFFECTED_BY_PSEUDO_ELEMENTS = 1 << 1,
- }
-}
-
-bitflags! {
/// Set of flags that are set on either the element or its parent (depending
/// on the flag) if the element could potentially match a selector.
pub flags ElementSelectorFlags: usize {
@@ -66,111 +53,6 @@ impl ElementSelectorFlags {
}
}
-/// What kind of selector matching mode we should use.
-///
-/// There are two modes of selector matching. The difference is only noticeable
-/// in presence of pseudo-elements.
-#[derive(Debug, PartialEq, Copy, Clone)]
-pub enum MatchingMode {
- /// Don't ignore any pseudo-element selectors.
- Normal,
-
- /// Ignores any stateless pseudo-element selectors in the rightmost sequence
- /// of simple selectors.
- ///
- /// This is useful, for example, to match against ::before when you aren't a
- /// pseudo-element yourself.
- ///
- /// For example, in presence of `::before:hover`, it would never match, but
- /// `::before` would be ignored as in "matching".
- ///
- /// It's required for all the selectors you match using this mode to have a
- /// pseudo-element.
- ForStatelessPseudoElement,
-}
-
-/// The mode to use when matching unvisited and visited links.
-#[derive(PartialEq, Eq, Copy, Clone, Debug)]
-pub enum VisitedHandlingMode {
- /// All links are matched as if they are unvisted.
- AllLinksUnvisited,
- /// A element's "relevant link" is the element being matched if it is a link
- /// or the nearest ancestor link. The relevant link is matched as though it
- /// is visited, and all other links are matched as if they are unvisited.
- RelevantLinkVisited,
-}
-
-/// Which quirks mode is this document in.
-///
-/// See: https://quirks.spec.whatwg.org/
-#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
-pub enum QuirksMode {
- /// Quirks mode.
- Quirks,
- /// Limited quirks mode.
- LimitedQuirks,
- /// No quirks mode.
- NoQuirks,
-}
-
-/// Data associated with the matching process for a element. This context is
-/// used across many selectors for an element, so it's not appropriate for
-/// transient data that applies to only a single selector.
-#[derive(Clone)]
-pub struct MatchingContext<'a> {
- /// Output that records certains relations between elements noticed during
- /// matching (and also extended after matching).
- pub relations: StyleRelations,
- /// Input with the matching mode we should use when matching selectors.
- pub matching_mode: MatchingMode,
- /// Input with the bloom filter used to fast-reject selectors.
- pub bloom_filter: Option<&'a BloomFilter>,
- /// Input that controls how matching for links is handled.
- pub visited_handling: VisitedHandlingMode,
- /// Output that records whether we encountered a "relevant link" while
- /// matching _any_ selector for this element. (This differs from
- /// `RelevantLinkStatus` which tracks the status for the _current_ selector
- /// only.)
- pub relevant_link_found: bool,
- /// The quirks mode of the document.
- pub quirks_mode: QuirksMode,
-}
-
-impl<'a> MatchingContext<'a> {
- /// Constructs a new `MatchingContext`.
- pub fn new(matching_mode: MatchingMode,
- bloom_filter: Option<&'a BloomFilter>,
- quirks_mode: QuirksMode)
- -> Self
- {
- Self {
- relations: StyleRelations::empty(),
- matching_mode: matching_mode,
- bloom_filter: bloom_filter,
- visited_handling: VisitedHandlingMode::AllLinksUnvisited,
- relevant_link_found: false,
- quirks_mode: quirks_mode,
- }
- }
-
- /// Constructs a new `MatchingContext` for use in visited matching.
- pub fn new_for_visited(matching_mode: MatchingMode,
- bloom_filter: Option<&'a BloomFilter>,
- visited_handling: VisitedHandlingMode,
- quirks_mode: QuirksMode)
- -> Self
- {
- Self {
- relations: StyleRelations::empty(),
- matching_mode: matching_mode,
- bloom_filter: bloom_filter,
- visited_handling: visited_handling,
- relevant_link_found: false,
- quirks_mode: quirks_mode,
- }
- }
-}
-
/// Holds per-element data alongside a pointer to MatchingContext.
pub struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
/// Shared `MatchingContext`.
@@ -184,11 +66,12 @@ pub struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
/// been advanced partway through the current compound selector, and the callee may need
/// the whole thing.
offset: usize,
- /// Holds a bool flag to see if LocalMatchingContext is within a functional
- /// pseudo class argument. This is used for pseudo classes like
- /// `:-moz-any` or `:not`. If this flag is true, :active and :hover
- /// quirk shouldn't match.
- pub within_functional_pseudo_class_argument: bool,
+ /// Holds a bool flag to see whether :active and :hover quirk should try to
+ /// match or not. This flag can only be true in these two cases:
+ /// - LocalMatchingContext is currently within a functional pseudo class
+ /// like `:-moz-any` or `:not`.
+ /// - PseudoElements are encountered when matching mode is ForStatelessPseudoElement.
+ pub hover_active_quirk_disabled: bool,
}
impl<'a, 'b, Impl> LocalMatchingContext<'a, 'b, Impl>
@@ -201,14 +84,22 @@ impl<'a, 'b, Impl> LocalMatchingContext<'a, 'b, Impl>
shared: shared,
selector: selector,
offset: 0,
- within_functional_pseudo_class_argument: false,
+ // We flip this off once third sequence is reached.
+ hover_active_quirk_disabled: selector.has_pseudo_element(),
}
}
/// Updates offset of Selector to show new compound selector.
/// To be able to correctly re-synthesize main SelectorIter.
pub fn note_next_sequence(&mut self, selector_iter: &SelectorIter<Impl>) {
- if let QuirksMode::Quirks = self.shared.quirks_mode {
+ if let QuirksMode::Quirks = self.shared.quirks_mode() {
+ if self.selector.has_pseudo_element() && self.offset != 0 {
+ // This is the _second_ call to note_next_sequence,
+ // which means we've moved past the compound
+ // selector adjacent to the pseudo-element.
+ self.hover_active_quirk_disabled = false;
+ }
+
self.offset = self.selector.len() - selector_iter.selector_length();
}
}
@@ -216,8 +107,8 @@ impl<'a, 'b, Impl> LocalMatchingContext<'a, 'b, Impl>
/// Returns true if current compound selector matches :active and :hover quirk.
/// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk
pub fn active_hover_quirk_matches(&mut self) -> bool {
- if self.shared.quirks_mode != QuirksMode::Quirks ||
- self.within_functional_pseudo_class_argument {
+ if self.shared.quirks_mode() != QuirksMode::Quirks ||
+ self.hover_active_quirk_disabled {
return false;
}
@@ -279,19 +170,30 @@ fn may_match<E>(hashes: &AncestorHashes,
-> bool
where E: Element,
{
- // Check against the list of precomputed hashes.
- for hash in hashes.0.iter() {
- // If we hit the 0 sentinel hash, that means the rest are zero as well.
- if *hash == 0 {
- break;
- }
-
- if !bf.might_contain_hash(*hash) {
+ // Check the first three hashes. Note that we can check for zero before
+ // masking off the high bits, since if any of the first three hashes is
+ // zero the fourth will be as well. We also take care to avoid the
+ // special-case complexity of the fourth hash until we actually reach it,
+ // because we usually don't.
+ //
+ // To be clear: this is all extremely hot.
+ for i in 0..3 {
+ let packed = hashes.packed_hashes[i];
+ if packed == 0 {
+ // No more hashes left - unable to fast-reject.
+ return true;
+ }
+
+ if !bf.might_contain_hash(packed & BLOOM_HASH_MASK) {
+ // Hooray! We fast-rejected on this hash.
return false;
}
}
- true
+ // Now do the slighty-more-complex work of synthesizing the fourth hash,
+ // and check it against the filter if it exists.
+ let fourth = hashes.fourth_hash();
+ fourth == 0 || bf.might_contain_hash(fourth)
}
/// Tracks whether we are currently looking for relevant links for a given
@@ -360,6 +262,10 @@ impl RelevantLinkStatus {
return false
}
+ if context.visited_handling == VisitedHandlingMode::AllLinksVisitedAndUnvisited {
+ return true;
+ }
+
// Non-relevant links are always unvisited.
if *self != RelevantLinkStatus::Found {
return false
@@ -381,6 +287,10 @@ impl RelevantLinkStatus {
return false
}
+ if context.visited_handling == VisitedHandlingMode::AllLinksVisitedAndUnvisited {
+ return true;
+ }
+
// Non-relevant links are always unvisited.
if *self != RelevantLinkStatus::Found {
return true
@@ -442,9 +352,9 @@ enum SelectorMatchingResult {
/// Matches a selector, fast-rejecting against a bloom filter.
///
-/// We accept an offset to allow consumers to represent and match against partial
-/// selectors (indexed from the right). We use this API design, rather than
-/// having the callers pass a SelectorIter, because creating a SelectorIter
+/// We accept an offset to allow consumers to represent and match against
+/// partial selectors (indexed from the right). We use this API design, rather
+/// than having the callers pass a SelectorIter, because creating a SelectorIter
/// requires dereferencing the selector to get the length, which adds an
/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
/// (which the caller can store inline with the selector pointer).
@@ -467,12 +377,74 @@ pub fn matches_selector<E, F>(selector: &Selector<E::Impl>,
}
let mut local_context = LocalMatchingContext::new(context, selector);
- matches_complex_selector(&selector, offset, element, &mut local_context, flags_setter)
+ let iter = if offset == 0 {
+ selector.iter()
+ } else {
+ selector.iter_from(offset)
+ };
+ matches_complex_selector(iter, element, &mut local_context, flags_setter)
+}
+
+/// Whether a compound selector matched, and whether it was the rightmost
+/// selector inside the complex selector.
+pub enum CompoundSelectorMatchingResult {
+ /// The compound selector matched, and the next combinator offset is
+ /// `next_combinator_offset`.
+ ///
+ /// If the next combinator offset is zero, it means that it's the rightmost
+ /// selector.
+ Matched { next_combinator_offset: usize, },
+ /// The selector didn't match.
+ NotMatched,
+}
+
+/// Matches a compound selector belonging to `selector`, starting at offset
+/// `from_offset`, matching left to right.
+///
+/// Requires that `from_offset` points to a `Combinator`.
+///
+/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
+/// complex selector, but it happens to be the case we don't need it.
+pub fn matches_compound_selector<E>(
+ selector: &Selector<E::Impl>,
+ mut from_offset: usize,
+ context: &mut MatchingContext,
+ element: &E,
+) -> CompoundSelectorMatchingResult
+where
+ E: Element
+{
+ if cfg!(debug_assertions) {
+ selector.combinator_at(from_offset); // This asserts.
+ }
+
+ let mut local_context = LocalMatchingContext::new(context, selector);
+ for component in selector.iter_raw_rev_from(from_offset - 1) {
+ if matches!(*component, Component::Combinator(..)) {
+ return CompoundSelectorMatchingResult::Matched {
+ next_combinator_offset: from_offset - 1,
+ }
+ }
+
+ if !matches_simple_selector(
+ component,
+ element,
+ &mut local_context,
+ &RelevantLinkStatus::NotLooking,
+ &mut |_, _| {}) {
+ return CompoundSelectorMatchingResult::NotMatched;
+ }
+
+ from_offset -= 1;
+ }
+
+ return CompoundSelectorMatchingResult::Matched {
+ next_combinator_offset: 0,
+ }
}
/// Matches a complex selector.
-pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>,
- offset: usize,
+pub fn matches_complex_selector<E, F>(mut iter: SelectorIter<E::Impl>,
element: &E,
mut context: &mut LocalMatchingContext<E::Impl>,
flags_setter: &mut F)
@@ -480,12 +452,6 @@ pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>,
where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
- let mut iter = if offset == 0 {
- complex_selector.iter()
- } else {
- complex_selector.iter_from(offset)
- };
-
if cfg!(debug_assertions) {
if context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement {
assert!(iter.clone().any(|c| {
@@ -655,12 +621,11 @@ fn matches_simple_selector<E, F>(
let ns = ::parser::namespace_empty_string::<E::Impl>();
element.get_namespace() == ns.borrow()
}
- // TODO: case-sensitivity depends on the document type and quirks mode
Component::ID(ref id) => {
- element.get_id().map_or(false, |attr| attr == *id)
+ element.has_id(id, context.shared.classes_and_ids_case_sensitivity())
}
Component::Class(ref class) => {
- element.has_class(class)
+ element.has_class(class, context.shared.classes_and_ids_case_sensitivity())
}
Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } => {
let is_html = element.is_html_element_in_html_document();
@@ -761,13 +726,13 @@ fn matches_simple_selector<E, F>(
matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
}
Component::Negation(ref negated) => {
- let old_value = context.within_functional_pseudo_class_argument;
- context.within_functional_pseudo_class_argument = true;
+ let old_value = context.hover_active_quirk_disabled;
+ context.hover_active_quirk_disabled = true;
let result = !negated.iter().all(|ss| {
matches_simple_selector(ss, element, context,
relevant_link, flags_setter)
});
- context.within_functional_pseudo_class_argument = old_value;
+ context.hover_active_quirk_disabled = old_value;
result
}
}
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs
index 393070ffff2..0899858bd7d 100644
--- a/components/selectors/parser.rs
+++ b/components/selectors/parser.rs
@@ -4,6 +4,7 @@
use attr::{AttrSelectorWithNamespace, ParsedAttrSelectorOperation, AttrSelectorOperator};
use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint};
+use bloom::BLOOM_HASH_MASK;
use cssparser::{ParseError, BasicParseError};
use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
use precomputed_hash::PrecomputedHash;
@@ -203,16 +204,25 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
}
}
-/// Copied from Gecko, who copied it from WebKit. Note that increasing the
-/// number of hashes here will adversely affect the cache hit when fast-
-/// rejecting long lists of Rules with inline hashes.
-const NUM_ANCESTOR_HASHES: usize = 4;
-
/// Ancestor hashes for the bloom filter. We precompute these and store them
/// inline with selectors to optimize cache performance during matching.
/// This matters a lot.
+///
+/// We use 4 hashes, which is copied from Gecko, who copied it from WebKit.
+/// Note that increasing the number of hashes here will adversely affect the
+/// cache hit when fast-rejecting long lists of Rules with inline hashes.
+///
+/// Because the bloom filter only uses the bottom 24 bits of the hash, we pack
+/// the fourth hash into the upper bits of the first three hashes in order to
+/// shrink Rule (whose size matters a lot). This scheme minimizes the runtime
+/// overhead of the packing for the first three hashes (we just need to mask
+/// off the upper bits) at the expense of making the fourth somewhat more
+/// complicated to assemble, because we often bail out before checking all the
+/// hashes.
#[derive(Eq, PartialEq, Clone, Debug)]
-pub struct AncestorHashes(pub [u32; NUM_ANCESTOR_HASHES]);
+pub struct AncestorHashes {
+ pub packed_hashes: [u32; 3],
+}
impl AncestorHashes {
pub fn new<Impl: SelectorImpl>(s: &Selector<Impl>) -> Self {
@@ -220,20 +230,38 @@ impl AncestorHashes {
}
pub fn from_iter<Impl: SelectorImpl>(iter: SelectorIter<Impl>) -> Self {
- let mut hashes = [0; NUM_ANCESTOR_HASHES];
// Compute ancestor hashes for the bloom filter.
+ let mut hashes = [0u32; 4];
let mut hash_iter = AncestorIter::new(iter)
.map(|x| x.ancestor_hash())
.filter(|x| x.is_some())
.map(|x| x.unwrap());
- for i in 0..NUM_ANCESTOR_HASHES {
+ for i in 0..4 {
hashes[i] = match hash_iter.next() {
- Some(x) => x,
+ Some(x) => x & BLOOM_HASH_MASK,
None => break,
}
}
- AncestorHashes(hashes)
+ // Now, pack the fourth hash (if it exists) into the upper byte of each of
+ // the other three hashes.
+ let fourth = hashes[3];
+ if fourth != 0 {
+ hashes[0] |= (fourth & 0x000000ff) << 24;
+ hashes[1] |= (fourth & 0x0000ff00) << 16;
+ hashes[2] |= (fourth & 0x00ff0000) << 8;
+ }
+
+ AncestorHashes {
+ packed_hashes: [hashes[0], hashes[1], hashes[2]],
+ }
+ }
+
+ /// Returns the fourth hash, reassembled from parts.
+ pub fn fourth_hash(&self) -> u32 {
+ ((self.packed_hashes[0] & 0xff000000) >> 24) |
+ ((self.packed_hashes[1] & 0xff000000) >> 16) |
+ ((self.packed_hashes[2] & 0xff000000) >> 8)
}
}
@@ -421,18 +449,36 @@ impl<Impl: SelectorImpl> Selector<Impl> {
}
}
- /// Returns an iterator over the entire sequence of simple selectors and combinators,
- /// from right to left.
+ /// Returns the combinator at index `index`, or panics if the component is
+ /// not a combinator.
+ pub fn combinator_at(&self, index: usize) -> Combinator {
+ match self.0.slice[self.0.slice.len() - index] {
+ Component::Combinator(c) => c,
+ ref other => {
+ panic!("Not a combinator: {:?}, {:?}, index: {}",
+ other, self, index)
+ }
+ }
+ }
+
+ /// Returns an iterator over the entire sequence of simple selectors and
+ /// combinators, from right to left.
pub fn iter_raw(&self) -> Rev<slice::Iter<Component<Impl>>> {
self.iter_raw_rev().rev()
}
- /// Returns an iterator over the entire sequence of simple selectors and combinators,
- /// from left to right.
+ /// Returns an iterator over the entire sequence of simple selectors and
+ /// combinators, from left to right.
pub fn iter_raw_rev(&self) -> slice::Iter<Component<Impl>> {
self.0.slice.iter()
}
+ /// Returns an iterator over the sequence of simple selectors and
+ /// combinators after `offset`, from left to right.
+ pub fn iter_raw_rev_from(&self, offset: usize) -> slice::Iter<Component<Impl>> {
+ self.0.slice[(self.0.slice.len() - offset)..].iter()
+ }
+
/// Creates a Selector from a vec of Components. Used in tests.
pub fn from_vec(vec: Vec<Component<Impl>>, specificity_and_flags: u32) -> Self {
let header = HeaderWithLength::new(SpecificityAndFlags(specificity_and_flags), vec.len());
@@ -1357,7 +1403,10 @@ fn parse_attribute_flags<'i, 't, E>(input: &mut CssParser<'i, 't>)
-> Result<ParsedCaseSensitivity,
ParseError<'i, SelectorParseError<'i, E>>> {
match input.next() {
- Err(_) => Ok(ParsedCaseSensitivity::CaseSensitive),
+ Err(_) => {
+ // Selectors spec says language-defined, but HTML says sensitive.
+ Ok(ParsedCaseSensitivity::CaseSensitive)
+ }
Ok(Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => {
Ok(ParsedCaseSensitivity::AsciiCaseInsensitive)
}
diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs
index 7a47f2b4ab1..99c07c6bf3b 100644
--- a/components/selectors/tree.rs
+++ b/components/selectors/tree.rs
@@ -5,7 +5,7 @@
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency
//! between layout and style.
-use attr::{AttrSelectorOperation, NamespaceConstraint};
+use attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
use matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, RelevantLinkStatus};
use parser::SelectorImpl;
use std::fmt::Debug;
@@ -63,9 +63,15 @@ pub trait Element: Sized + Debug {
/// Whether this element is a `link`.
fn is_link(&self) -> bool;
- fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
+ fn has_id(&self,
+ id: &<Self::Impl as SelectorImpl>::Identifier,
+ case_sensitivity: CaseSensitivity)
+ -> bool;
- fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;
+ fn has_class(&self,
+ name: &<Self::Impl as SelectorImpl>::ClassName,
+ case_sensitivity: CaseSensitivity)
+ -> bool;
/// Returns whether this element matches `:empty`.
///
diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml
index 1b2fdc5a929..eafb5c92b5c 100644
--- a/components/servo/Cargo.toml
+++ b/components/servo/Cargo.toml
@@ -30,7 +30,7 @@ debugger = {path = "../debugger"}
devtools = {path = "../devtools"}
devtools_traits = {path = "../devtools_traits"}
env_logger = "0.4"
-euclid = "0.13"
+euclid = "0.14.4"
gfx = {path = "../gfx"}
gleam = "0.4"
ipc-channel = "0.7"
diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml
index 1365cd68b73..4592cd9ba7f 100644
--- a/components/style/Cargo.toml
+++ b/components/style/Cargo.toml
@@ -16,7 +16,7 @@ path = "lib.rs"
doctest = false
[features]
-gecko = ["nsstring_vendor", "rayon/unstable", "num_cpus", "style_traits/gecko"]
+gecko = ["nsstring_vendor", "num_cpus", "style_traits/gecko"]
use_bindgen = ["bindgen", "regex", "toml"]
servo = ["serde", "serde_derive", "heapsize", "heapsize_derive",
"style_traits/servo", "servo_atoms", "servo_config", "html5ever",
@@ -25,7 +25,7 @@ servo = ["serde", "serde_derive", "heapsize", "heapsize_derive",
# FIXME: Uncomment when https://github.com/servo/servo/pull/16953 has landed:
#"arrayvec/use_union"
- "rayon/unstable", "servo_url"]
+ "servo_url"]
testing = []
gecko_debug = ["nsstring_vendor/gecko_debug"]
@@ -40,7 +40,7 @@ byteorder = "1.0"
cfg-if = "0.1.0"
cssparser = "0.14.0"
encoding = {version = "0.2", optional = true}
-euclid = "0.13"
+euclid = "0.14.4"
fnv = "1.0"
heapsize = {version = "0.4", optional = true}
heapsize_derive = {version = "0.1", optional = true}
@@ -57,7 +57,7 @@ ordered-float = "0.4"
parking_lot = "0.3.3"
pdqsort = "0.1.0"
precomputed-hash = "0.1"
-rayon = "0.7.1"
+rayon = "0.8"
selectors = { path = "../selectors" }
serde = {version = "0.9", optional = true}
serde_derive = {version = "0.9", optional = true}
@@ -78,7 +78,7 @@ kernel32-sys = "0.2"
[build-dependencies]
lazy_static = "0.2"
log = "0.3"
-bindgen = { version = "0.25.3", optional = true }
+bindgen = { version = "0.25.5", optional = true }
regex = {version = "0.2", optional = true}
walkdir = "1.0"
toml = {version = "0.2.1", optional = true, default-features = false}
diff --git a/components/style/animation.rs b/components/style/animation.rs
index f77cc8bfab6..f8fde047bf1 100644
--- a/components/style/animation.rs
+++ b/components/style/animation.rs
@@ -9,10 +9,10 @@ use Atom;
use bezier::Bezier;
use context::SharedStyleContext;
use dom::OpaqueNode;
-use euclid::point::Point2D;
+use euclid::Point2D;
use font_metrics::FontMetricsProvider;
use properties::{self, CascadeFlags, ComputedValues, Importance};
-use properties::animated_properties::{AnimatedProperty, TransitionProperty};
+use properties::animated_properties::{AnimatableLonghand, AnimatedProperty, TransitionProperty};
use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
use properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
@@ -330,7 +330,27 @@ impl PropertyAnimation {
-> Option<PropertyAnimation> {
debug_assert!(!transition_property.is_shorthand() &&
transition_property != &TransitionProperty::All);
- let animated_property = AnimatedProperty::from_transition_property(transition_property,
+
+ // We're not expecting |transition_property| to be a shorthand (including 'all') and
+ // all other transitionable properties should be animatable longhands (since transitionable
+ // is a subset of animatable).
+ let animatable_longhand =
+ AnimatableLonghand::from_transition_property(transition_property).unwrap();
+
+ PropertyAnimation::from_animatable_longhand(&animatable_longhand,
+ timing_function,
+ duration,
+ old_style,
+ new_style)
+ }
+
+ fn from_animatable_longhand(animatable_longhand: &AnimatableLonghand,
+ timing_function: TimingFunction,
+ duration: Time,
+ old_style: &ComputedValues,
+ new_style: &ComputedValues)
+ -> Option<PropertyAnimation> {
+ let animated_property = AnimatedProperty::from_animatable_longhand(animatable_longhand,
old_style,
new_style);
@@ -743,22 +763,22 @@ pub fn update_style_for_animation(context: &SharedStyleContext,
let mut new_style = (*style).clone();
- for transition_property in &animation.properties_changed {
+ for property in &animation.properties_changed {
debug!("update_style_for_animation: scanning prop {:?} for animation \"{}\"",
- transition_property, name);
- match PropertyAnimation::from_transition_property(transition_property,
+ property, name);
+ match PropertyAnimation::from_animatable_longhand(property,
timing_function,
Time::from_seconds(relative_duration as f32),
&from_style,
&target_style) {
Some(property_animation) => {
- debug!("update_style_for_animation: got property animation for prop {:?}", transition_property);
+ debug!("update_style_for_animation: got property animation for prop {:?}", property);
debug!("update_style_for_animation: {:?}", property_animation);
property_animation.update(Arc::make_mut(&mut new_style), relative_progress);
}
None => {
debug!("update_style_for_animation: property animation {:?} not animating",
- transition_property);
+ property);
}
}
}
diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs
new file mode 100644
index 00000000000..17cf13f7f4d
--- /dev/null
+++ b/components/style/applicable_declarations.rs
@@ -0,0 +1,137 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! Applicable declarations management.
+
+use properties::PropertyDeclarationBlock;
+use rule_tree::{CascadeLevel, StyleSource};
+use shared_lock::Locked;
+use smallvec::SmallVec;
+use std::fmt::{Debug, self};
+use std::mem;
+use stylearc::Arc;
+
+/// List of applicable declarations. This is a transient structure that shuttles
+/// declarations between selector matching and inserting into the rule tree, and
+/// therefore we want to avoid heap-allocation where possible.
+///
+/// In measurements on wikipedia, we pretty much never have more than 8 applicable
+/// declarations, so we could consider making this 8 entries instead of 16.
+/// However, it may depend a lot on workload, and stack space is cheap.
+pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;
+
+/// Blink uses 18 bits to store source order, and does not check overflow [1].
+/// That's a limit that could be reached in realistic webpages, so we use
+/// 24 bits and enforce defined behavior in the overflow case.
+///
+/// Note that the value of 24 is also hard-coded into the level() accessor,
+/// which does a byte-aligned load of the 4th byte. If you change this value
+/// you'll need to change that as well.
+///
+/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/
+/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
+const SOURCE_ORDER_BITS: usize = 24;
+const SOURCE_ORDER_MASK: u32 = (1 << SOURCE_ORDER_BITS) - 1;
+const SOURCE_ORDER_MAX: u32 = SOURCE_ORDER_MASK;
+
+/// Stores the source order of a block and the cascade level it belongs to.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Copy, Clone, Eq, PartialEq)]
+struct SourceOrderAndCascadeLevel(u32);
+
+impl SourceOrderAndCascadeLevel {
+ fn new(source_order: u32, cascade_level: CascadeLevel) -> SourceOrderAndCascadeLevel {
+ let mut bits = ::std::cmp::min(source_order, SOURCE_ORDER_MAX);
+ bits |= (cascade_level as u8 as u32) << SOURCE_ORDER_BITS;
+ SourceOrderAndCascadeLevel(bits)
+ }
+
+ fn order(&self) -> u32 {
+ self.0 & SOURCE_ORDER_MASK
+ }
+
+ fn level(&self) -> CascadeLevel {
+ unsafe {
+ // Transmute rather than shifting so that we're sure the compiler
+ // emits a simple byte-aligned load.
+ let as_bytes: [u8; 4] = mem::transmute(self.0);
+ CascadeLevel::from_byte(as_bytes[3])
+ }
+ }
+}
+
+impl Debug for SourceOrderAndCascadeLevel {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("SourceOrderAndCascadeLevel")
+ .field("order", &self.order())
+ .field("level", &self.level())
+ .finish()
+ }
+}
+
+/// A property declaration together with its precedence among rules of equal
+/// specificity so that we can sort them.
+///
+/// This represents the declarations in a given declaration block for a given
+/// importance.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Debug, Clone, PartialEq)]
+pub struct ApplicableDeclarationBlock {
+ /// The style source, either a style rule, or a property declaration block.
+ #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
+ pub source: StyleSource,
+ /// The source order of the block, and the cascade level it belongs to.
+ order_and_level: SourceOrderAndCascadeLevel,
+ /// The specificity of the selector this block is represented by.
+ pub specificity: u32,
+}
+
+impl ApplicableDeclarationBlock {
+ /// Constructs an applicable declaration block from a given property
+ /// declaration block and importance.
+ #[inline]
+ pub fn from_declarations(declarations: Arc<Locked<PropertyDeclarationBlock>>,
+ level: CascadeLevel)
+ -> Self {
+ ApplicableDeclarationBlock {
+ source: StyleSource::Declarations(declarations),
+ order_and_level: SourceOrderAndCascadeLevel::new(0, level),
+ specificity: 0,
+ }
+ }
+
+ /// Constructs an applicable declaration block from the given components
+ #[inline]
+ pub fn new(source: StyleSource,
+ order: u32,
+ level: CascadeLevel,
+ specificity: u32) -> Self {
+ ApplicableDeclarationBlock {
+ source: source,
+ order_and_level: SourceOrderAndCascadeLevel::new(order, level),
+ specificity: specificity,
+ }
+
+ }
+
+ /// Returns the source order of the block.
+ #[inline]
+ pub fn source_order(&self) -> u32 {
+ self.order_and_level.order()
+ }
+
+ /// Returns the cascade level of the block.
+ #[inline]
+ pub fn level(&self) -> CascadeLevel {
+ self.order_and_level.level()
+ }
+
+ /// Convenience method to consume self and return the source alongside the
+ /// level.
+ #[inline]
+ pub fn order_and_level(self) -> (StyleSource, CascadeLevel) {
+ let level = self.level();
+ (self.source, level)
+ }
+}
diff --git a/components/style/bezier.rs b/components/style/bezier.rs
index 2bae08ac800..30f6f6735a2 100644
--- a/components/style/bezier.rs
+++ b/components/style/bezier.rs
@@ -8,7 +8,7 @@
#![deny(missing_docs)]
-use euclid::point::Point2D;
+use euclid::Point2D;
const NEWTON_METHOD_ITERATIONS: u8 = 8;
diff --git a/components/style/context.rs b/components/style/context.rs
index 4526ba82aa8..c63f20e92d5 100644
--- a/components/style/context.rs
+++ b/components/style/context.rs
@@ -250,7 +250,8 @@ impl TraversalStatistics {
self.traversal_time_ms = (time::precise_time_s() - start) * 1000.0;
self.selectors = traversal.shared_context().stylist.num_selectors() as u32;
self.revalidation_selectors = traversal.shared_context().stylist.num_revalidation_selectors() as u32;
- self.dependency_selectors = traversal.shared_context().stylist.num_dependencies() as u32;
+ self.dependency_selectors =
+ traversal.shared_context().stylist.invalidation_map().len() as u32;
self.declarations = traversal.shared_context().stylist.num_declarations() as u32;
self.stylist_rebuilds = traversal.shared_context().stylist.num_rebuilds() as u32;
}
diff --git a/components/style/data.rs b/components/style/data.rs
index c66417f4a59..0363d397d74 100644
--- a/components/style/data.rs
+++ b/components/style/data.rs
@@ -7,16 +7,15 @@
use arrayvec::ArrayVec;
use context::SharedStyleContext;
use dom::TElement;
+use invalidation::element::restyle_hints::RestyleHint;
use properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock};
use properties::longhands::display::computed_value as display;
-use restyle_hints::{CascadeHint, HintComputationContext, RestyleReplacements, RestyleHint};
use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
use selectors::matching::VisitedHandlingMode;
use shared_lock::{Locked, StylesheetGuards};
use std::fmt;
use stylearc::Arc;
-use traversal::TraversalFlags;
/// The structure that represents the result of style computation. This is
/// effectively a tuple of rules and computed values, that is, the rule node,
@@ -268,6 +267,10 @@ impl EagerPseudoStyles {
rules: StrongRuleNode)
-> bool {
match visited_handling {
+ VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
+ unreachable!("We should never try to selector match with \
+ AllLinksVisitedAndUnvisited");
+ },
VisitedHandlingMode::AllLinksUnvisited => {
self.add_unvisited_rules(&pseudo, rules)
},
@@ -286,6 +289,10 @@ impl EagerPseudoStyles {
visited_handling: VisitedHandlingMode)
-> bool {
match visited_handling {
+ VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
+ unreachable!("We should never try to selector match with \
+ AllLinksVisitedAndUnvisited");
+ },
VisitedHandlingMode::AllLinksUnvisited => {
self.remove_unvisited_rules(&pseudo)
},
@@ -341,174 +348,81 @@ impl ElementStyles {
}
}
-/// Restyle hint for storing on ElementData.
-///
-/// We wrap it in a newtype to force the encapsulation of the complexity of
-/// handling the correct invalidations in this file.
-#[derive(Clone, Debug)]
-pub struct StoredRestyleHint(RestyleHint);
-
-impl StoredRestyleHint {
- /// Propagates this restyle hint to a child element.
- pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
- use std::mem;
-
- // In the middle of an animation only restyle, we don't need to
- // propagate any restyle hints, and we need to remove ourselves.
- if traversal_flags.for_animation_only() {
- self.0.remove_animation_hints();
- return Self::empty();
- }
-
- debug_assert!(!self.0.has_animation_hint(),
- "There should not be any animation restyle hints \
- during normal traversal");
-
- // Else we should clear ourselves, and return the propagated hint.
- let new_hint = mem::replace(&mut self.0, RestyleHint::empty())
- .propagate_for_non_animation_restyle();
- StoredRestyleHint(new_hint)
- }
-
- /// Creates an empty `StoredRestyleHint`.
- pub fn empty() -> Self {
- StoredRestyleHint(RestyleHint::empty())
- }
-
- /// Creates a restyle hint that forces the whole subtree to be restyled,
- /// including the element.
- pub fn subtree() -> Self {
- StoredRestyleHint(RestyleHint::subtree())
- }
-
- /// Creates a restyle hint that forces the element and all its later
- /// siblings to have their whole subtrees restyled, including the elements
- /// themselves.
- pub fn subtree_and_later_siblings() -> Self {
- StoredRestyleHint(RestyleHint::subtree_and_later_siblings())
- }
-
- /// Creates a restyle hint that indicates the element must be recascaded.
- pub fn recascade_self() -> Self {
- StoredRestyleHint(RestyleHint::recascade_self())
- }
-
- /// Returns true if the hint indicates that our style may be invalidated.
- pub fn has_self_invalidations(&self) -> bool {
- self.0.affects_self()
- }
-
- /// Returns true if the hint indicates that our sibling's style may be
- /// invalidated.
- pub fn has_sibling_invalidations(&self) -> bool {
- self.0.affects_later_siblings()
- }
-
- /// Whether the restyle hint is empty (nothing requires to be restyled).
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- /// Insert another restyle hint, effectively resulting in the union of both.
- pub fn insert(&mut self, other: Self) {
- self.0.insert(other.0)
- }
-
- /// Contains whether the whole subtree is invalid.
- pub fn contains_subtree(&self) -> bool {
- self.0.contains(&RestyleHint::subtree())
- }
-
- /// Insert another restyle hint, effectively resulting in the union of both.
- pub fn insert_from(&mut self, other: &Self) {
- self.0.insert_from(&other.0)
- }
-
- /// Returns true if the hint has animation-only restyle.
- pub fn has_animation_hint(&self) -> bool {
- self.0.has_animation_hint()
- }
-
- /// Returns true if the hint indicates the current element must be
- /// recascaded.
- pub fn has_recascade_self(&self) -> bool {
- self.0.has_recascade_self()
- }
-
- /// Insert the specified `CascadeHint`.
- pub fn insert_cascade_hint(&mut self, cascade_hint: CascadeHint) {
- self.0.insert_cascade_hint(cascade_hint);
- }
-}
-
-impl Default for StoredRestyleHint {
- fn default() -> Self {
- StoredRestyleHint::empty()
- }
-}
-
-impl From<RestyleHint> for StoredRestyleHint {
- fn from(hint: RestyleHint) -> Self {
- StoredRestyleHint(hint)
+bitflags! {
+ flags RestyleFlags: u8 {
+ /// Whether the styles changed for this restyle.
+ const WAS_RESTYLED = 1 << 0,
+ /// Whether we reframed/reconstructed any ancestor or self.
+ const ANCESTOR_WAS_RECONSTRUCTED = 1 << 1,
}
}
/// Transient data used by the restyle algorithm. This structure is instantiated
/// either before or during restyle traversal, and is cleared at the end of node
/// processing.
-#[derive(Debug, Default)]
+#[derive(Debug)]
pub struct RestyleData {
/// The restyle hint, which indicates whether selectors need to be rematched
/// for this element, its children, and its descendants.
- pub hint: StoredRestyleHint,
+ pub hint: RestyleHint,
+
+ /// A few flags to have in mind.
+ flags: RestyleFlags,
/// The restyle damage, indicating what kind of layout changes are required
/// afte restyling.
pub damage: RestyleDamage,
-
- /// The restyle damage that has already been handled by our ancestors, and does
- /// not need to be applied again at this element. Only non-empty during the
- /// traversal, once ancestor damage has been calculated.
- ///
- /// Note that this optimization mostly makes sense in terms of Gecko's top-down
- /// frame constructor and change list processing model. We don't bother with it
- /// for Servo for now.
- #[cfg(feature = "gecko")]
- pub damage_handled: RestyleDamage,
}
impl RestyleData {
- /// Returns true if this RestyleData might invalidate the current style.
- pub fn has_invalidations(&self) -> bool {
- self.hint.has_self_invalidations()
+ fn new() -> Self {
+ Self {
+ hint: RestyleHint::empty(),
+ flags: RestyleFlags::empty(),
+ damage: RestyleDamage::empty(),
+ }
}
- /// Returns true if this RestyleData might invalidate sibling styles.
- pub fn has_sibling_invalidations(&self) -> bool {
- self.hint.has_sibling_invalidations()
+ /// Clear all the restyle state associated with this element.
+ fn clear(&mut self) {
+ *self = Self::new();
}
- /// Returns damage handled.
- #[cfg(feature = "gecko")]
- pub fn damage_handled(&self) -> RestyleDamage {
- self.damage_handled
+ /// Returns whether this element or any ancestor is going to be
+ /// reconstructed.
+ pub fn reconstructed_self_or_ancestor(&self) -> bool {
+ self.reconstructed_ancestor() ||
+ self.damage.contains(RestyleDamage::reconstruct())
}
- /// Returns damage handled (always empty for servo).
- #[cfg(feature = "servo")]
- pub fn damage_handled(&self) -> RestyleDamage {
- RestyleDamage::empty()
+ /// Returns whether any ancestor of this element was restyled.
+ fn reconstructed_ancestor(&self) -> bool {
+ self.flags.contains(ANCESTOR_WAS_RECONSTRUCTED)
}
- /// Sets damage handled.
- #[cfg(feature = "gecko")]
- pub fn set_damage_handled(&mut self, d: RestyleDamage) {
- self.damage_handled = d;
+ /// Sets the flag that tells us whether we've reconstructed an ancestor.
+ pub fn set_reconstructed_ancestor(&mut self) {
+ // If it weren't for animation-only traversals, we could assert
+ // `!self.reconstructed_ancestor()` here.
+ self.flags.insert(ANCESTOR_WAS_RECONSTRUCTED);
}
- /// Sets damage handled. No-op for Servo.
- #[cfg(feature = "servo")]
- pub fn set_damage_handled(&mut self, _: RestyleDamage) {}
+ /// Mark this element as restyled, which is useful to know whether we need
+ /// to do a post-traversal.
+ pub fn set_restyled(&mut self) {
+ self.flags.insert(WAS_RESTYLED);
+ }
+
+ /// Mark this element as restyled, which is useful to know whether we need
+ /// to do a post-traversal.
+ pub fn is_restyle(&self) -> bool {
+ self.flags.contains(WAS_RESTYLED)
+ }
+
+ /// Returns whether this element has been part of a restyle.
+ pub fn contains_restyle_data(&self) -> bool {
+ self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
+ }
}
/// Style system data associated with an Element.
@@ -521,78 +435,61 @@ pub struct ElementData {
/// The computed styles for the element and its pseudo-elements.
styles: Option<ElementStyles>,
- /// Restyle tracking. We separate this into a separate allocation so that
- /// we can drop it when no restyles are pending on the elemnt.
- restyle: Option<Box<RestyleData>>,
+ /// Restyle state.
+ pub restyle: RestyleData,
}
/// The kind of restyle that a single element should do.
+#[derive(Debug)]
pub enum RestyleKind {
/// We need to run selector matching plus re-cascade, that is, a full
/// restyle.
MatchAndCascade,
/// We need to recascade with some replacement rule, such as the style
/// attribute, or animation rules.
- CascadeWithReplacements(RestyleReplacements),
+ CascadeWithReplacements(RestyleHint),
/// We only need to recascade, for example, because only inherited
/// properties in the parent changed.
CascadeOnly,
}
impl ElementData {
- /// Computes the final restyle hint for this element, potentially allocating
- /// a `RestyleData` if we need to.
- ///
- /// This expands the snapshot (if any) into a restyle hint, and handles
- /// explicit sibling restyle hints from the stored restyle hint.
- ///
- /// Returns true if later siblings must be restyled.
- pub fn compute_final_hint<'a, E: TElement>(
+ /// Borrows both styles and restyle mutably at the same time.
+ pub fn styles_and_restyle_mut(
+ &mut self
+ ) -> (&mut ElementStyles, &mut RestyleData) {
+ (self.styles.as_mut().unwrap(),
+ &mut self.restyle)
+ }
+
+ /// Invalidates style for this element, its descendants, and later siblings,
+ /// based on the snapshot of the element that we took when attributes or
+ /// state changed.
+ pub fn invalidate_style_if_needed<'a, E: TElement>(
&mut self,
element: E,
- shared_context: &SharedStyleContext,
- hint_context: HintComputationContext<'a, E>)
- -> bool
+ shared_context: &SharedStyleContext)
{
- debug!("compute_final_hint: {:?}, {:?}",
- element,
- shared_context.traversal_flags);
-
- let mut hint = match self.get_restyle() {
- Some(r) => r.hint.0.clone(),
- None => RestyleHint::empty(),
- };
+ use invalidation::element::invalidator::TreeStyleInvalidator;
- debug!("compute_final_hint: {:?}, has_snapshot: {}, handled_snapshot: {}, \
- pseudo: {:?}",
+ debug!("invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \
+ handled_snapshot: {}, pseudo: {:?}",
element,
+ shared_context.traversal_flags,
element.has_snapshot(),
element.handled_snapshot(),
element.implemented_pseudo_element());
if element.has_snapshot() && !element.handled_snapshot() {
- let snapshot_hint =
- shared_context.stylist.compute_restyle_hint(&element,
- shared_context,
- hint_context);
- hint.insert(snapshot_hint);
+ let invalidator = TreeStyleInvalidator::new(
+ element,
+ Some(self),
+ shared_context,
+ );
+ invalidator.invalidate();
unsafe { element.set_handled_snapshot() }
debug_assert!(element.handled_snapshot());
}
-
- let empty_hint = hint.is_empty();
-
- // If the hint includes a directive for later siblings, strip it out and
- // notify the caller to modify the base hint for future siblings.
- let later_siblings = hint.remove_later_siblings_hint();
-
- // Insert the hint, overriding the previous hint. This effectively takes
- // care of removing the later siblings restyle hint.
- if !empty_hint {
- self.ensure_restyle().hint = hint.into();
- }
-
- later_siblings
}
@@ -600,7 +497,7 @@ impl ElementData {
pub fn new(existing: Option<ElementStyles>) -> Self {
ElementData {
styles: existing,
- restyle: None,
+ restyle: RestyleData::new(),
}
}
@@ -611,31 +508,45 @@ impl ElementData {
/// Returns whether we have any outstanding style invalidation.
pub fn has_invalidations(&self) -> bool {
- self.restyle.as_ref().map_or(false, |r| r.has_invalidations())
+ self.restyle.hint.has_self_invalidations()
}
/// Returns the kind of restyling that we're going to need to do on this
/// element, based of the stored restyle hint.
- pub fn restyle_kind(&self) -> RestyleKind {
+ pub fn restyle_kind(&self,
+ shared_context: &SharedStyleContext)
+ -> RestyleKind {
debug_assert!(!self.has_styles() || self.has_invalidations(),
"Should've stopped earlier");
if !self.has_styles() {
+ debug_assert!(!shared_context.traversal_flags.for_animation_only(),
+ "Unstyled element shouldn't be traversed during \
+ animation-only traversal");
return RestyleKind::MatchAndCascade;
}
- debug_assert!(self.restyle.is_some());
- let restyle_data = self.restyle.as_ref().unwrap();
+ let hint = self.restyle.hint;
+ if shared_context.traversal_flags.for_animation_only() {
+ // return either CascadeWithReplacements or CascadeOnly in case of
+ // animation-only restyle.
+ if hint.has_animation_hint() {
+ return RestyleKind::CascadeWithReplacements(hint & RestyleHint::for_animations());
+ }
+ return RestyleKind::CascadeOnly;
+ }
- let hint = &restyle_data.hint.0;
if hint.match_self() {
return RestyleKind::MatchAndCascade;
}
if hint.has_replacements() {
- return RestyleKind::CascadeWithReplacements(hint.replacements);
+ debug_assert!(!hint.has_animation_hint(),
+ "Animation only restyle hint should have already processed");
+ return RestyleKind::CascadeWithReplacements(hint & RestyleHint::replacements());
}
- debug_assert!(hint.has_recascade_self(), "We definitely need to do something!");
+ debug_assert!(hint.has_recascade_self(),
+ "We definitely need to do something!");
return RestyleKind::CascadeOnly;
}
@@ -660,13 +571,6 @@ impl ElementData {
self.styles.as_mut().expect("Calling styles_mut() on unstyled ElementData")
}
- /// Borrows both styles and restyle mutably at the same time.
- pub fn styles_and_restyle_mut(&mut self) -> (&mut ElementStyles,
- Option<&mut RestyleData>) {
- (self.styles.as_mut().unwrap(),
- self.restyle.as_mut().map(|r| &mut **r))
- }
-
/// Sets the computed element styles.
pub fn set_styles(&mut self, styles: ElementStyles) {
self.styles = Some(styles);
@@ -705,47 +609,9 @@ impl ElementData {
important_rules != other_important_rules
}
- /// Returns true if the Element has a RestyleData.
- pub fn has_restyle(&self) -> bool {
- self.restyle.is_some()
- }
-
- /// Drops any RestyleData.
- pub fn clear_restyle(&mut self) {
- self.restyle = None;
- }
-
- /// Creates a RestyleData if one doesn't exist.
- ///
- /// Asserts that the Element has been styled.
- pub fn ensure_restyle(&mut self) -> &mut RestyleData {
- debug_assert!(self.styles.is_some(), "restyling unstyled element");
- if self.restyle.is_none() {
- self.restyle = Some(Box::new(RestyleData::default()));
- }
- self.restyle.as_mut().unwrap()
- }
-
- /// Gets a reference to the restyle data, if any.
- pub fn get_restyle(&self) -> Option<&RestyleData> {
- self.restyle.as_ref().map(|r| &**r)
- }
-
- /// Gets a reference to the restyle data. Panic if the element does not
- /// have restyle data.
- pub fn restyle(&self) -> &RestyleData {
- self.get_restyle().expect("Calling restyle without RestyleData")
- }
-
- /// Gets a mutable reference to the restyle data, if any.
- pub fn get_restyle_mut(&mut self) -> Option<&mut RestyleData> {
- self.restyle.as_mut().map(|r| &mut **r)
- }
-
- /// Gets a mutable reference to the restyle data. Panic if the element does
- /// not have restyle data.
- pub fn restyle_mut(&mut self) -> &mut RestyleData {
- self.get_restyle_mut().expect("Calling restyle_mut without RestyleData")
+ /// Drops any restyle state from the element.
+ pub fn clear_restyle_state(&mut self) {
+ self.restyle.clear();
}
/// Returns SMIL overriden value if exists.
diff --git a/components/style/dom.rs b/components/style/dom.rs
index aef401dc43e..769cedfda28 100644
--- a/components/style/dom.rs
+++ b/components/style/dom.rs
@@ -8,6 +8,7 @@
#![deny(missing_docs)]
use {Atom, Namespace, LocalName};
+use applicable_declarations::ApplicableDeclarationBlock;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
#[cfg(feature = "gecko")] use context::UpdateAnimationsTasks;
use data::ElementData;
@@ -29,7 +30,6 @@ use std::fmt::Debug;
use std::hash::Hash;
use std::ops::Deref;
use stylearc::Arc;
-use stylist::ApplicableDeclarationBlock;
use thread_state;
pub use style_traits::UnsafeNode;
@@ -376,6 +376,9 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
/// Whether this element has an attribute with a given namespace.
fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool;
+ /// The ID for this element.
+ fn get_id(&self) -> Option<Atom>;
+
/// Internal iterator for the classes of this element.
fn each_class<F>(&self, callback: F) where F: FnMut(&Atom);
@@ -564,13 +567,13 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
Some(d) => d,
None => return false,
};
- return data.get_restyle()
- .map_or(false, |r| r.hint.has_animation_hint());
+ return data.restyle.hint.has_animation_hint()
}
/// Gets declarations from XBL bindings from the element. Only gecko element could have this.
fn get_declarations_from_xbl_bindings<V>(&self,
- _: &mut V)
+ _pseudo_element: Option<&PseudoElement>,
+ _applicable_declarations: &mut V)
-> bool
where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
false
diff --git a/components/style/encoding_support.rs b/components/style/encoding_support.rs
index af37e20889a..4f6365ffeae 100644
--- a/components/style/encoding_support.rs
+++ b/components/style/encoding_support.rs
@@ -79,7 +79,7 @@ impl Stylesheet {
bytes: &[u8],
protocol_encoding_label: Option<&str>,
environment_encoding: Option<EncodingRef>,
- url_data: &UrlExtraData,
+ url_data: UrlExtraData,
stylesheet_loader: Option<&StylesheetLoader>,
error_reporter: &ParseErrorReporter) {
let (string, _) = decode_stylesheet_bytes(
diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs
index 4266c61826a..ac87b180953 100644
--- a/components/style/gecko/conversions.rs
+++ b/components/style/gecko/conversions.rs
@@ -19,13 +19,14 @@ use stylesheets::{Origin, RulesMutateError};
use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
+use values::specified::length::Percentage;
impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
let has_percentage = other.percentage.is_some();
nsStyleCoord_CalcValue {
mLength: other.unclamped_length().0,
- mPercent: other.percentage.unwrap_or(0.0),
+ mPercent: other.percentage.map_or(0., |p| p.0),
mHasPercent: has_percentage,
}
}
@@ -34,7 +35,7 @@ impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
impl From<nsStyleCoord_CalcValue> for CalcLengthOrPercentage {
fn from(other: nsStyleCoord_CalcValue) -> CalcLengthOrPercentage {
let percentage = if other.mHasPercent {
- Some(other.mPercent)
+ Some(Percentage(other.mPercent))
} else {
None
};
@@ -55,7 +56,7 @@ impl From<LengthOrPercentage> for nsStyleCoord_CalcValue {
LengthOrPercentage::Percentage(pc) => {
nsStyleCoord_CalcValue {
mLength: 0,
- mPercent: pc,
+ mPercent: pc.0,
mHasPercent: true,
}
},
@@ -78,7 +79,7 @@ impl LengthOrPercentageOrAuto {
LengthOrPercentageOrAuto::Percentage(pc) => {
Some(nsStyleCoord_CalcValue {
mLength: 0,
- mPercent: pc,
+ mPercent: pc.0,
mHasPercent: true,
})
},
@@ -92,7 +93,7 @@ impl From<nsStyleCoord_CalcValue> for LengthOrPercentage {
fn from(other: nsStyleCoord_CalcValue) -> LengthOrPercentage {
match (other.mHasPercent, other.mLength) {
(false, _) => LengthOrPercentage::Length(Au(other.mLength)),
- (true, 0) => LengthOrPercentage::Percentage(other.mPercent),
+ (true, 0) => LengthOrPercentage::Percentage(Percentage(other.mPercent)),
_ => LengthOrPercentage::Calc(other.into()),
}
}
diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs
index 12fdef2ebad..8ccd98dd2cc 100644
--- a/components/style/gecko/generated/bindings.rs
+++ b/components/style/gecko/generated/bindings.rs
@@ -906,7 +906,9 @@ extern "C" {
}
extern "C" {
pub fn Gecko_SetCounterStyleToName(ptr: *mut CounterStylePtr,
- name: *mut nsIAtom);
+ name: *mut nsIAtom,
+ pres_context:
+ RawGeckoPresContextBorrowed);
}
extern "C" {
pub fn Gecko_SetCounterStyleToSymbols(ptr: *mut CounterStylePtr,
@@ -2113,9 +2115,9 @@ extern "C" {
}
extern "C" {
pub fn Servo_StyleRule_GetSelectorTextAtIndex(rule:
- RawServoStyleRuleBorrowed,
- index: u32,
- result: *mut nsAString);
+ RawServoStyleRuleBorrowed,
+ index: u32,
+ result: *mut nsAString);
}
extern "C" {
pub fn Servo_StyleRule_GetSpecificityAtIndex(rule:
@@ -2237,13 +2239,6 @@ extern "C" {
RawGeckoComputedKeyframeValuesListBorrowedMut);
}
extern "C" {
- pub fn Servo_AnimationValueMap_Push(arg1:
- RawServoAnimationValueMapBorrowedMut,
- property: nsCSSPropertyID,
- value:
- RawServoAnimationValueBorrowed);
-}
-extern "C" {
pub fn Servo_ComputedValues_ExtractAnimationValue(computed_values:
ServoComputedValuesBorrowed,
property:
@@ -2254,6 +2249,9 @@ extern "C" {
pub fn Servo_Property_IsAnimatable(property: nsCSSPropertyID) -> bool;
}
extern "C" {
+ pub fn Servo_Property_IsTransitionable(property: nsCSSPropertyID) -> bool;
+}
+extern "C" {
pub fn Servo_Property_IsDiscreteAnimatable(property: nsCSSPropertyID)
-> bool;
}
diff --git a/components/style/gecko/generated/structs_debug.rs b/components/style/gecko/generated/structs_debug.rs
index a0a630f02c3..28161278e66 100644
--- a/components/style/gecko/generated/structs_debug.rs
+++ b/components/style/gecko/generated/structs_debug.rs
@@ -6644,9 +6644,10 @@ pub mod root {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TraversalRestyleBehavior {
Normal = 0,
- ForReconstruct = 1,
- ForAnimationOnly = 2,
- ForCSSRuleChanges = 3,
+ ForNewlyBoundElement = 1,
+ ForReconstruct = 2,
+ ForAnimationOnly = 3,
+ ForCSSRuleChanges = 4,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -9234,31 +9235,106 @@ pub mod root {
unsafe { ::std::mem::transmute(unit_field_val) };
}
#[inline]
+ pub fn mClassAttributeChanged(&self) -> bool {
+ let mask = 32usize as u8;
+ let unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ let val = (unit_field_val & mask) >> 5usize;
+ unsafe { ::std::mem::transmute(val as u8) }
+ }
+ #[inline]
+ pub fn set_mClassAttributeChanged(&mut self, val: bool) {
+ let mask = 32usize as u8;
+ let val = val as u8 as u8;
+ let mut unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ unit_field_val &= !mask;
+ unit_field_val |= (val << 5usize) & mask;
+ self._bitfield_1 =
+ unsafe { ::std::mem::transmute(unit_field_val) };
+ }
+ #[inline]
+ pub fn mIdAttributeChanged(&self) -> bool {
+ let mask = 64usize as u8;
+ let unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ let val = (unit_field_val & mask) >> 6usize;
+ unsafe { ::std::mem::transmute(val as u8) }
+ }
+ #[inline]
+ pub fn set_mIdAttributeChanged(&mut self, val: bool) {
+ let mask = 64usize as u8;
+ let val = val as u8 as u8;
+ let mut unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ unit_field_val &= !mask;
+ unit_field_val |= (val << 6usize) & mask;
+ self._bitfield_1 =
+ unsafe { ::std::mem::transmute(unit_field_val) };
+ }
+ #[inline]
+ pub fn mOtherAttributeChanged(&self) -> bool {
+ let mask = 128usize as u8;
+ let unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ let val = (unit_field_val & mask) >> 7usize;
+ unsafe { ::std::mem::transmute(val as u8) }
+ }
+ #[inline]
+ pub fn set_mOtherAttributeChanged(&mut self, val: bool) {
+ let mask = 128usize as u8;
+ let val = val as u8 as u8;
+ let mut unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ unit_field_val &= !mask;
+ unit_field_val |= (val << 7usize) & mask;
+ self._bitfield_1 =
+ unsafe { ::std::mem::transmute(unit_field_val) };
+ }
+ #[inline]
pub fn new_bitfield_1(mIsHTMLElementInHTMLDocument: bool,
mIsInChromeDocument: bool,
mSupportsLangAttr: bool,
mIsTableBorderNonzero: bool,
- mIsMozBrowserFrame: bool) -> u8 {
+ mIsMozBrowserFrame: bool,
+ mClassAttributeChanged: bool,
+ mIdAttributeChanged: bool,
+ mOtherAttributeChanged: bool) -> u8 {
({
({
({
({
- ({ 0 } |
- ((mIsHTMLElementInHTMLDocument as u8
- as u8) << 0usize) &
- (1usize as u8))
+ ({
+ ({
+ ({
+ ({ 0 } |
+ ((mIsHTMLElementInHTMLDocument
+ as u8 as u8) <<
+ 0usize) &
+ (1usize as u8))
+ } |
+ ((mIsInChromeDocument as u8
+ as u8) << 1usize) &
+ (2usize as u8))
+ } |
+ ((mSupportsLangAttr as u8 as u8)
+ << 2usize) &
+ (4usize as u8))
+ } |
+ ((mIsTableBorderNonzero as u8 as u8)
+ << 3usize) & (8usize as u8))
} |
- ((mIsInChromeDocument as u8 as u8) <<
- 1usize) & (2usize as u8))
+ ((mIsMozBrowserFrame as u8 as u8) <<
+ 4usize) & (16usize as u8))
} |
- ((mSupportsLangAttr as u8 as u8) << 2usize) &
- (4usize as u8))
+ ((mClassAttributeChanged as u8 as u8) <<
+ 5usize) & (32usize as u8))
} |
- ((mIsTableBorderNonzero as u8 as u8) << 3usize) &
- (8usize as u8))
+ ((mIdAttributeChanged as u8 as u8) << 6usize) &
+ (64usize as u8))
} |
- ((mIsMozBrowserFrame as u8 as u8) << 4usize) &
- (16usize as u8))
+ ((mOtherAttributeChanged as u8 as u8) << 7usize) &
+ (128usize as u8))
}
}
#[repr(C)]
@@ -10436,6 +10512,12 @@ pub mod root {
eFloatID_SpellCheckerUnderlineRelativeSize = 1,
eFloatID_CaretAspectRatio = 2,
}
+ pub const LookAndFeel_FontID_FontID_MINIMUM:
+ root::mozilla::LookAndFeel_FontID =
+ LookAndFeel_FontID::eFont_Caption;
+ pub const LookAndFeel_FontID_FontID_MAXIMUM:
+ root::mozilla::LookAndFeel_FontID =
+ LookAndFeel_FontID::eFont_Widget;
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum LookAndFeel_FontID {
@@ -34397,7 +34479,7 @@ pub mod root {
root::nsCharTraits ) ));
}
#[test]
- fn __bindgen_test_layout__bindgen_ty_id_215166_instantiation_103() {
+ fn __bindgen_test_layout__bindgen_ty_id_215184_instantiation_103() {
assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! (
"Size of template specialization: " , stringify ! ( u8 )
));
@@ -34406,7 +34488,7 @@ pub mod root {
) ));
}
#[test]
- fn __bindgen_test_layout__bindgen_ty_id_215202_instantiation_104() {
+ fn __bindgen_test_layout__bindgen_ty_id_215220_instantiation_104() {
assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! (
"Size of template specialization: " , stringify ! ( u8 )
));
diff --git a/components/style/gecko/generated/structs_release.rs b/components/style/gecko/generated/structs_release.rs
index a8b493e82b3..f39368f0dff 100644
--- a/components/style/gecko/generated/structs_release.rs
+++ b/components/style/gecko/generated/structs_release.rs
@@ -6500,9 +6500,10 @@ pub mod root {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TraversalRestyleBehavior {
Normal = 0,
- ForReconstruct = 1,
- ForAnimationOnly = 2,
- ForCSSRuleChanges = 3,
+ ForNewlyBoundElement = 1,
+ ForReconstruct = 2,
+ ForAnimationOnly = 3,
+ ForCSSRuleChanges = 4,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -8975,31 +8976,106 @@ pub mod root {
unsafe { ::std::mem::transmute(unit_field_val) };
}
#[inline]
+ pub fn mClassAttributeChanged(&self) -> bool {
+ let mask = 32usize as u8;
+ let unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ let val = (unit_field_val & mask) >> 5usize;
+ unsafe { ::std::mem::transmute(val as u8) }
+ }
+ #[inline]
+ pub fn set_mClassAttributeChanged(&mut self, val: bool) {
+ let mask = 32usize as u8;
+ let val = val as u8 as u8;
+ let mut unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ unit_field_val &= !mask;
+ unit_field_val |= (val << 5usize) & mask;
+ self._bitfield_1 =
+ unsafe { ::std::mem::transmute(unit_field_val) };
+ }
+ #[inline]
+ pub fn mIdAttributeChanged(&self) -> bool {
+ let mask = 64usize as u8;
+ let unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ let val = (unit_field_val & mask) >> 6usize;
+ unsafe { ::std::mem::transmute(val as u8) }
+ }
+ #[inline]
+ pub fn set_mIdAttributeChanged(&mut self, val: bool) {
+ let mask = 64usize as u8;
+ let val = val as u8 as u8;
+ let mut unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ unit_field_val &= !mask;
+ unit_field_val |= (val << 6usize) & mask;
+ self._bitfield_1 =
+ unsafe { ::std::mem::transmute(unit_field_val) };
+ }
+ #[inline]
+ pub fn mOtherAttributeChanged(&self) -> bool {
+ let mask = 128usize as u8;
+ let unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ let val = (unit_field_val & mask) >> 7usize;
+ unsafe { ::std::mem::transmute(val as u8) }
+ }
+ #[inline]
+ pub fn set_mOtherAttributeChanged(&mut self, val: bool) {
+ let mask = 128usize as u8;
+ let val = val as u8 as u8;
+ let mut unit_field_val: u8 =
+ unsafe { ::std::mem::transmute(self._bitfield_1) };
+ unit_field_val &= !mask;
+ unit_field_val |= (val << 7usize) & mask;
+ self._bitfield_1 =
+ unsafe { ::std::mem::transmute(unit_field_val) };
+ }
+ #[inline]
pub fn new_bitfield_1(mIsHTMLElementInHTMLDocument: bool,
mIsInChromeDocument: bool,
mSupportsLangAttr: bool,
mIsTableBorderNonzero: bool,
- mIsMozBrowserFrame: bool) -> u8 {
+ mIsMozBrowserFrame: bool,
+ mClassAttributeChanged: bool,
+ mIdAttributeChanged: bool,
+ mOtherAttributeChanged: bool) -> u8 {
({
({
({
({
- ({ 0 } |
- ((mIsHTMLElementInHTMLDocument as u8
- as u8) << 0usize) &
- (1usize as u8))
+ ({
+ ({
+ ({
+ ({ 0 } |
+ ((mIsHTMLElementInHTMLDocument
+ as u8 as u8) <<
+ 0usize) &
+ (1usize as u8))
+ } |
+ ((mIsInChromeDocument as u8
+ as u8) << 1usize) &
+ (2usize as u8))
+ } |
+ ((mSupportsLangAttr as u8 as u8)
+ << 2usize) &
+ (4usize as u8))
+ } |
+ ((mIsTableBorderNonzero as u8 as u8)
+ << 3usize) & (8usize as u8))
} |
- ((mIsInChromeDocument as u8 as u8) <<
- 1usize) & (2usize as u8))
+ ((mIsMozBrowserFrame as u8 as u8) <<
+ 4usize) & (16usize as u8))
} |
- ((mSupportsLangAttr as u8 as u8) << 2usize) &
- (4usize as u8))
+ ((mClassAttributeChanged as u8 as u8) <<
+ 5usize) & (32usize as u8))
} |
- ((mIsTableBorderNonzero as u8 as u8) << 3usize) &
- (8usize as u8))
+ ((mIdAttributeChanged as u8 as u8) << 6usize) &
+ (64usize as u8))
} |
- ((mIsMozBrowserFrame as u8 as u8) << 4usize) &
- (16usize as u8))
+ ((mOtherAttributeChanged as u8 as u8) << 7usize) &
+ (128usize as u8))
}
}
#[repr(C)]
@@ -10169,6 +10245,12 @@ pub mod root {
eFloatID_SpellCheckerUnderlineRelativeSize = 1,
eFloatID_CaretAspectRatio = 2,
}
+ pub const LookAndFeel_FontID_FontID_MINIMUM:
+ root::mozilla::LookAndFeel_FontID =
+ LookAndFeel_FontID::eFont_Caption;
+ pub const LookAndFeel_FontID_FontID_MAXIMUM:
+ root::mozilla::LookAndFeel_FontID =
+ LookAndFeel_FontID::eFont_Widget;
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum LookAndFeel_FontID {
@@ -33852,7 +33934,7 @@ pub mod root {
root::nsCharTraits ) ));
}
#[test]
- fn __bindgen_test_layout__bindgen_ty_id_211327_instantiation_98() {
+ fn __bindgen_test_layout__bindgen_ty_id_211345_instantiation_98() {
assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! (
"Size of template specialization: " , stringify ! ( u8 )
));
@@ -33861,7 +33943,7 @@ pub mod root {
) ));
}
#[test]
- fn __bindgen_test_layout__bindgen_ty_id_211363_instantiation_99() {
+ fn __bindgen_test_layout__bindgen_ty_id_211381_instantiation_99() {
assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! (
"Size of template specialization: " , stringify ! ( u8 )
));
diff --git a/components/style/gecko/global_style_data.rs b/components/style/gecko/global_style_data.rs
index 2ba8749e24f..0f80a18816e 100644
--- a/components/style/gecko/global_style_data.rs
+++ b/components/style/gecko/global_style_data.rs
@@ -80,6 +80,12 @@ lazy_static! {
} else {
let configuration = rayon::Configuration::new()
.num_threads(num_threads)
+ // Enable a breadth-first rayon traversal. This causes the work
+ // queue to be always FIFO, rather than FIFO for stealers and
+ // FILO for the owner (which is what rayon does by default). This
+ // ensures that we process all the elements at a given depth before
+ // proceeding to the next depth, which is important for style sharing.
+ .breadth_first()
.thread_name(thread_name)
.start_handler(thread_startup)
.exit_handler(thread_shutdown);
diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs
index b2f7907a64a..f8368d2ae43 100644
--- a/components/style/gecko/snapshot.rs
+++ b/components/style/gecko/snapshot.rs
@@ -13,7 +13,7 @@ use gecko_bindings::bindings;
use gecko_bindings::structs::ServoElementSnapshot;
use gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
use gecko_bindings::structs::ServoElementSnapshotTable;
-use restyle_hints::ElementSnapshot;
+use invalidation::element::element_wrapper::ElementSnapshot;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
use string_cache::{Atom, Namespace};
@@ -62,6 +62,25 @@ impl GeckoElementSnapshot {
self.has_any(Flags::OtherPseudoClassState)
}
+ /// Returns true if the snapshot recorded an id change.
+ #[inline]
+ pub fn id_changed(&self) -> bool {
+ self.mIdAttributeChanged()
+ }
+
+ /// Returns true if the snapshot recorded a class attribute change.
+ #[inline]
+ pub fn class_changed(&self) -> bool {
+ self.mClassAttributeChanged()
+ }
+
+ /// Returns true if the snapshot recorded an attribute change which isn't a
+ /// class or id change.
+ #[inline]
+ pub fn other_attr_changed(&self) -> bool {
+ self.mOtherAttributeChanged()
+ }
+
/// selectors::Element::attr_matches
pub fn attr_matches(&self,
ns: &NamespaceConstraint<&Namespace>,
@@ -164,13 +183,14 @@ impl ElementSnapshot for GeckoElementSnapshot {
}
#[inline]
- fn has_class(&self, name: &Atom) -> bool {
+ fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
if !self.has_any(Flags::MaybeClass) {
return false;
}
snapshot_helpers::has_class(self.as_ptr(),
name,
+ case_sensitivity,
bindings::Gecko_SnapshotClassOrClassList)
}
diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs
index f7a0efa48c0..70af6f7082a 100644
--- a/components/style/gecko/snapshot_helpers.rs
+++ b/components/style/gecko/snapshot_helpers.rs
@@ -4,7 +4,10 @@
//! Element an snapshot common logic.
+use CaseSensitivityExt;
use gecko_bindings::structs::nsIAtom;
+use gecko_string_cache::WeakAtom;
+use selectors::attr::CaseSensitivity;
use std::{ptr, slice};
use string_cache::Atom;
@@ -16,6 +19,7 @@ pub type ClassOrClassList<T> = unsafe extern fn (T, *mut *mut nsIAtom, *mut *mut
/// element has the class that `name` represents.
pub fn has_class<T>(item: T,
name: &Atom,
+ case_sensitivity: CaseSensitivity,
getter: ClassOrClassList<T>) -> bool
{
unsafe {
@@ -24,10 +28,10 @@ pub fn has_class<T>(item: T,
let length = getter(item, &mut class, &mut list);
match length {
0 => false,
- 1 => name.as_ptr() == class,
+ 1 => case_sensitivity.eq_atom(name, WeakAtom::new(class)),
n => {
let classes = slice::from_raw_parts(list, n as usize);
- classes.iter().any(|ptr| name.as_ptr() == *ptr)
+ classes.iter().any(|ptr| case_sensitivity.eq_atom(name, WeakAtom::new(*ptr)))
}
}
}
diff --git a/components/style/gecko/values.rs b/components/style/gecko/values.rs
index f2a92e1ec89..cd774901897 100644
--- a/components/style/gecko/values.rs
+++ b/components/style/gecko/values.rs
@@ -12,6 +12,7 @@ use cssparser::RGBA;
use gecko_bindings::structs::{CounterStylePtr, nsStyleCoord};
use gecko_bindings::structs::{StyleGridTrackBreadth, StyleShapeRadius};
use gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
+use media_queries::Device;
use nsstring::{nsACString, nsCString};
use std::cmp::max;
use values::{Auto, Either, ExtremumLength, None_, Normal};
@@ -104,7 +105,7 @@ impl GeckoStyleCoordConvertible for LengthOrPercentage {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
let value = match *self {
LengthOrPercentage::Length(au) => CoordDataValue::Coord(au.0),
- LengthOrPercentage::Percentage(p) => CoordDataValue::Percent(p),
+ LengthOrPercentage::Percentage(p) => CoordDataValue::Percent(p.0),
LengthOrPercentage::Calc(calc) => CoordDataValue::Calc(calc.into()),
};
coord.set_value(value);
@@ -113,7 +114,7 @@ impl GeckoStyleCoordConvertible for LengthOrPercentage {
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
match coord.as_value() {
CoordDataValue::Coord(coord) => Some(LengthOrPercentage::Length(Au(coord))),
- CoordDataValue::Percent(p) => Some(LengthOrPercentage::Percentage(p)),
+ CoordDataValue::Percent(p) => Some(LengthOrPercentage::Percentage(Percentage(p))),
CoordDataValue::Calc(calc) => Some(LengthOrPercentage::Calc(calc.into())),
_ => None,
}
@@ -137,7 +138,7 @@ impl GeckoStyleCoordConvertible for LengthOrPercentageOrAuto {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
let value = match *self {
LengthOrPercentageOrAuto::Length(au) => CoordDataValue::Coord(au.0),
- LengthOrPercentageOrAuto::Percentage(p) => CoordDataValue::Percent(p),
+ LengthOrPercentageOrAuto::Percentage(p) => CoordDataValue::Percent(p.0),
LengthOrPercentageOrAuto::Auto => CoordDataValue::Auto,
LengthOrPercentageOrAuto::Calc(calc) => CoordDataValue::Calc(calc.into()),
};
@@ -147,7 +148,7 @@ impl GeckoStyleCoordConvertible for LengthOrPercentageOrAuto {
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
match coord.as_value() {
CoordDataValue::Coord(coord) => Some(LengthOrPercentageOrAuto::Length(Au(coord))),
- CoordDataValue::Percent(p) => Some(LengthOrPercentageOrAuto::Percentage(p)),
+ CoordDataValue::Percent(p) => Some(LengthOrPercentageOrAuto::Percentage(Percentage(p))),
CoordDataValue::Auto => Some(LengthOrPercentageOrAuto::Auto),
CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrAuto::Calc(calc.into())),
_ => None,
@@ -159,7 +160,7 @@ impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone {
fn to_gecko_style_coord<T: CoordDataMut>(&self, coord: &mut T) {
let value = match *self {
LengthOrPercentageOrNone::Length(au) => CoordDataValue::Coord(au.0),
- LengthOrPercentageOrNone::Percentage(p) => CoordDataValue::Percent(p),
+ LengthOrPercentageOrNone::Percentage(p) => CoordDataValue::Percent(p.0),
LengthOrPercentageOrNone::None => CoordDataValue::None,
LengthOrPercentageOrNone::Calc(calc) => CoordDataValue::Calc(calc.into()),
};
@@ -169,7 +170,7 @@ impl GeckoStyleCoordConvertible for LengthOrPercentageOrNone {
fn from_gecko_style_coord<T: CoordData>(coord: &T) -> Option<Self> {
match coord.as_value() {
CoordDataValue::Coord(coord) => Some(LengthOrPercentageOrNone::Length(Au(coord))),
- CoordDataValue::Percent(p) => Some(LengthOrPercentageOrNone::Percentage(p)),
+ CoordDataValue::Percent(p) => Some(LengthOrPercentageOrNone::Percentage(Percentage(p))),
CoordDataValue::None => Some(LengthOrPercentageOrNone::None),
CoordDataValue::Calc(calc) => Some(LengthOrPercentageOrNone::Calc(calc.into())),
_ => None,
@@ -397,15 +398,16 @@ pub fn round_border_to_device_pixels(width: Au, au_per_device_px: Au) -> Au {
impl CounterStyleOrNone {
/// Convert this counter style to a Gecko CounterStylePtr.
- pub fn to_gecko_value(self, gecko_value: &mut CounterStylePtr) {
+ pub fn to_gecko_value(self, gecko_value: &mut CounterStylePtr, device: &Device) {
use gecko_bindings::bindings::Gecko_SetCounterStyleToName as set_name;
use gecko_bindings::bindings::Gecko_SetCounterStyleToSymbols as set_symbols;
+ let pres_context = unsafe { &*device.pres_context };
match self {
- CounterStyleOrNone::None_ => unsafe {
- set_name(gecko_value, atom!("none").into_addrefed());
+ CounterStyleOrNone::None => unsafe {
+ set_name(gecko_value, atom!("none").into_addrefed(), pres_context);
},
CounterStyleOrNone::Name(name) => unsafe {
- set_name(gecko_value, name.0.into_addrefed());
+ set_name(gecko_value, name.0.into_addrefed(), pres_context);
},
CounterStyleOrNone::Symbols(symbols_type, symbols) => {
let symbols: Vec<_> = symbols.0.iter().map(|symbol| match *symbol {
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index 0ceb2c82a1b..b74fc684825 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -14,7 +14,9 @@
//! style system it's kind of pointless in the Stylo case, and only Servo forces
//! the separation between the style system implementation and everything else.
+use CaseSensitivityExt;
use app_units::Au;
+use applicable_declarations::ApplicableDeclarationBlock;
use atomic_refcell::AtomicRefCell;
use context::{QuirksMode, SharedStyleContext, UpdateAnimationsTasks};
use data::ElementData;
@@ -66,7 +68,8 @@ use logical_geometry::WritingMode;
use media_queries::Device;
use properties::{ComputedValues, parse_style_attribute};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
-use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty};
+use properties::animated_properties::{AnimatableLonghand, AnimationValue, AnimationValueMap};
+use properties::animated_properties::TransitionProperty;
use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::{AttrValue, ElementExt, PseudoClassStringArg};
@@ -87,7 +90,6 @@ use std::ptr;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use stylearc::Arc;
use stylesheets::UrlExtraData;
-use stylist::ApplicableDeclarationBlock;
/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
///
@@ -381,17 +383,19 @@ impl<'lb> GeckoXBLBinding<'lb> {
// Implements Gecko's nsXBLBinding::WalkRules().
fn get_declarations_for<E, V>(&self,
element: &E,
+ pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V)
where E: TElement,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
if let Some(base_binding) = self.base_binding() {
- base_binding.get_declarations_for(element, applicable_declarations);
+ base_binding.get_declarations_for(element, pseudo_element, applicable_declarations);
}
let raw_data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyleSet(self.0) };
if let Some(raw_data) = raw_data {
let data = PerDocumentStyleData::from_ffi(&*raw_data).borrow();
data.stylist.push_applicable_declarations_as_xbl_only_stylist(element,
+ pseudo_element,
applicable_declarations);
}
}
@@ -407,6 +411,24 @@ impl<'le> fmt::Debug for GeckoElement<'le> {
if let Some(id) = self.get_id() {
try!(write!(f, " id={}", id));
}
+
+ let mut first = true;
+ let mut any = false;
+ self.each_class(|c| {
+ if first {
+ first = false;
+ any = true;
+ let _ = f.write_str(" class=\"");
+ } else {
+ let _ = f.write_str(" ");
+ }
+ let _ = write!(f, "{}", c);
+ });
+
+ if any {
+ f.write_str("\"")?;
+ }
+
write!(f, "> ({:#x})", self.as_node().opaque().0)
}
}
@@ -744,6 +766,23 @@ impl<'le> TElement for GeckoElement<'le> {
}
}
+ fn get_id(&self) -> Option<Atom> {
+ if !self.has_id() {
+ return None
+ }
+
+ let ptr = unsafe {
+ bindings::Gecko_AtomAttrValue(self.0,
+ atom!("id").as_ptr())
+ };
+
+ if ptr.is_null() {
+ None
+ } else {
+ Some(Atom::from(ptr))
+ }
+ }
+
fn each_class<F>(&self, callback: F)
where F: FnMut(&Atom)
{
@@ -906,6 +945,7 @@ impl<'le> TElement for GeckoElement<'le> {
// Implements Gecko's nsBindingManager::WalkRules(). Returns whether to cut off the
// inheritance.
fn get_declarations_from_xbl_bindings<V>(&self,
+ pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V)
-> bool
where V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> {
@@ -915,7 +955,9 @@ impl<'le> TElement for GeckoElement<'le> {
while let Some(element) = current {
if let Some(binding) = element.get_xbl_binding() {
- binding.get_declarations_for(self, applicable_declarations);
+ binding.get_declarations_for(self,
+ pseudo_element,
+ applicable_declarations);
// If we're not looking at our original element, allow the binding to cut off
// style inheritance.
@@ -998,7 +1040,6 @@ impl<'le> TElement for GeckoElement<'le> {
after_change_style: &ComputedValues)
-> bool {
use gecko_bindings::structs::nsCSSPropertyID;
- use properties::{PropertyId, animated_properties};
use std::collections::HashSet;
debug_assert!(self.might_need_transitions_update(Some(before_change_style),
@@ -1033,6 +1074,8 @@ impl<'le> TElement for GeckoElement<'le> {
continue;
}
+ let transition_property: TransitionProperty = property.into();
+
let mut property_check_helper = |property: &TransitionProperty| -> bool {
if self.needs_transitions_update_per_property(property,
combined_duration,
@@ -1049,26 +1092,25 @@ impl<'le> TElement for GeckoElement<'le> {
}
false
};
- if property == nsCSSPropertyID::eCSSPropertyExtra_all_properties {
- if TransitionProperty::any(property_check_helper) {
- return true;
- }
- } else {
- let is_shorthand = PropertyId::from_nscsspropertyid(property).ok().map_or(false, |p| {
- p.as_shorthand().is_ok()
- });
- if is_shorthand {
- let shorthand: TransitionProperty = property.into();
+
+ match transition_property {
+ TransitionProperty::All => {
+ if TransitionProperty::any(property_check_helper) {
+ return true;
+ }
+ },
+ TransitionProperty::Unsupported(_) => { },
+ ref shorthand if shorthand.is_shorthand() => {
if shorthand.longhands().iter().any(|p| property_check_helper(p)) {
return true;
}
- } else {
- if animated_properties::nscsspropertyid_is_animatable(property) &&
- property_check_helper(&property.into()) {
+ },
+ ref longhand => {
+ if property_check_helper(longhand) {
return true;
}
- }
- }
+ },
+ };
}
// Check if we have to cancel the running transition because this is not a matching
@@ -1088,22 +1130,21 @@ impl<'le> TElement for GeckoElement<'le> {
-> bool {
use properties::animated_properties::AnimatedProperty;
- // We don't allow transitions on properties that are not interpolable.
- if property.is_discrete() {
- return false;
- }
+ // |property| should be an animatable longhand
+ let animatable_longhand = AnimatableLonghand::from_transition_property(property).unwrap();
if existing_transitions.contains_key(property) {
// If there is an existing transition, update only if the end value differs.
// If the end value has not changed, we should leave the currently running
// transition as-is since we don't want to interrupt its timing function.
let after_value =
- Arc::new(AnimationValue::from_computed_values(property, after_change_style));
+ Arc::new(AnimationValue::from_computed_values(&animatable_longhand,
+ after_change_style));
return existing_transitions.get(property).unwrap() != &after_value;
}
combined_duration > 0.0f32 &&
- AnimatedProperty::from_transition_property(property,
+ AnimatedProperty::from_animatable_longhand(&animatable_longhand,
before_change_style,
after_change_style).does_animate()
}
@@ -1222,6 +1263,10 @@ impl<'le> PresentationalHintsSynthesizer for GeckoElement<'le> {
// Unvisited vs. visited styles are computed up-front based on the
// visited mode (not the element's actual state).
let declarations = match visited_handling {
+ VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
+ unreachable!("We should never try to selector match with \
+ AllLinksVisitedAndUnvisited");
+ },
VisitedHandlingMode::AllLinksUnvisited => unsafe {
Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0)
},
@@ -1526,12 +1571,12 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
}
NonTSPseudoClass::MozPlaceholder => false,
NonTSPseudoClass::MozAny(ref sels) => {
- let old_value = context.within_functional_pseudo_class_argument;
- context.within_functional_pseudo_class_argument = true;
+ let old_value = context.hover_active_quirk_disabled;
+ context.hover_active_quirk_disabled = true;
let result = sels.iter().any(|s| {
- matches_complex_selector(s, 0, self, context, flags_setter)
+ matches_complex_selector(s.iter(), self, context, flags_setter)
});
- context.within_functional_pseudo_class_argument = old_value;
+ context.hover_active_quirk_disabled = old_value;
result
}
NonTSPseudoClass::Lang(ref lang_arg) => {
@@ -1574,30 +1619,30 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
self.get_state().intersects(NonTSPseudoClass::AnyLink.state_flag())
}
- fn get_id(&self) -> Option<Atom> {
+ fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
if !self.has_id() {
- return None;
+ return false
}
- let ptr = unsafe {
- bindings::Gecko_AtomAttrValue(self.0,
- atom!("id").as_ptr())
- };
+ unsafe {
+ let ptr = bindings::Gecko_AtomAttrValue(self.0, atom!("id").as_ptr());
- if ptr.is_null() {
- None
- } else {
- Some(Atom::from(ptr))
+ if ptr.is_null() {
+ false
+ } else {
+ case_sensitivity.eq_atom(WeakAtom::new(ptr), id)
+ }
}
}
- fn has_class(&self, name: &Atom) -> bool {
+ fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
if !self.may_have_class() {
return false;
}
snapshot_helpers::has_class(self.0,
name,
+ case_sensitivity,
Gecko_ClassOrClassList)
}
diff --git a/components/style/gecko_bindings/sugar/ns_css_value.rs b/components/style/gecko_bindings/sugar/ns_css_value.rs
index 2c70c6ba020..5a4d143125d 100644
--- a/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -15,6 +15,7 @@ use std::mem;
use std::ops::{Index, IndexMut};
use std::slice;
use values::computed::{Angle, LengthOrPercentage};
+use values::specified::length::Percentage;
use values::specified::url::SpecifiedUrl;
impl nsCSSValue {
@@ -79,7 +80,7 @@ impl nsCSSValue {
bindings::Gecko_CSSValue_SetAbsoluteLength(self, au.0)
}
LengthOrPercentage::Percentage(pc) => {
- bindings::Gecko_CSSValue_SetPercentage(self, pc)
+ bindings::Gecko_CSSValue_SetPercentage(self, pc.0)
}
LengthOrPercentage::Calc(calc) => {
bindings::Gecko_CSSValue_SetCalc(self, calc.into())
@@ -94,7 +95,7 @@ impl nsCSSValue {
LengthOrPercentage::Length(Au(bindings::Gecko_CSSValue_GetAbsoluteLength(self)))
},
nsCSSUnit::eCSSUnit_Percent => {
- LengthOrPercentage::Percentage(bindings::Gecko_CSSValue_GetPercentage(self))
+ LengthOrPercentage::Percentage(Percentage(bindings::Gecko_CSSValue_GetPercentage(self)))
},
nsCSSUnit::eCSSUnit_Calc => {
LengthOrPercentage::Calc(bindings::Gecko_CSSValue_GetCalc(self).into())
diff --git a/components/style/gecko_bindings/sugar/ns_timing_function.rs b/components/style/gecko_bindings/sugar/ns_timing_function.rs
index f9a9948bf50..fbcde7ecd1e 100644
--- a/components/style/gecko_bindings/sugar/ns_timing_function.rs
+++ b/components/style/gecko_bindings/sugar/ns_timing_function.rs
@@ -2,7 +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 euclid::point::{Point2D, TypedPoint2D};
+use euclid::{Point2D, TypedPoint2D};
use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type};
use std::mem;
use values::computed::ToComputedValue;
diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs
index 79c4ef02138..6e1abc57059 100644
--- a/components/style/gecko_string_cache/mod.rs
+++ b/components/style/gecko_string_cache/mod.rs
@@ -11,7 +11,7 @@ use gecko_bindings::bindings::Gecko_Atomize;
use gecko_bindings::bindings::Gecko_Atomize16;
use gecko_bindings::bindings::Gecko_ReleaseAtom;
use gecko_bindings::structs::nsIAtom;
-use nsstring::nsAString;
+use nsstring::{nsAString, nsString};
use precomputed_hash::PrecomputedHash;
use std::ascii::AsciiExt;
use std::borrow::{Cow, Borrow};
@@ -176,6 +176,54 @@ impl WeakAtom {
let const_ptr: *const nsIAtom = &self.0;
const_ptr as *mut nsIAtom
}
+
+ /// Convert this atom to ASCII lower-case
+ pub fn to_ascii_lowercase(&self) -> Atom {
+ let slice = self.as_slice();
+ match slice.iter().position(|&char16| (b'A' as u16) <= char16 && char16 <= (b'Z' as u16)) {
+ None => self.clone(),
+ Some(i) => {
+ let mut buffer: [u16; 64] = unsafe { mem::uninitialized() };
+ let mut vec;
+ let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
+ buffer_prefix.copy_from_slice(slice);
+ buffer_prefix
+ } else {
+ vec = slice.to_vec();
+ &mut vec
+ };
+ for char16 in &mut mutable_slice[i..] {
+ if *char16 <= 0x7F {
+ *char16 = (*char16 as u8).to_ascii_lowercase() as u16
+ }
+ }
+ Atom::from(&*mutable_slice)
+ }
+ }
+ }
+
+ /// Return whether two atoms are ASCII-case-insensitive matches
+ pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
+ if self == other {
+ return true;
+ }
+
+ let a = self.as_slice();
+ let b = other.as_slice();
+ a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
+ if a16 <= 0x7F && b16 <= 0x7F {
+ (a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
+ } else {
+ a16 == b16
+ }
+ })
+ }
+
+ /// Return whether this atom is an ASCII-case-insensitive match for the given string
+ pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
+ self.chars().map(|r| r.map(|c: char| c.to_ascii_lowercase()))
+ .eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
+ }
}
impl fmt::Debug for WeakAtom {
@@ -233,29 +281,6 @@ impl Atom {
mem::forget(self);
ptr
}
-
- /// Return whether two atoms are ASCII-case-insensitive matches
- pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
- if self == other {
- return true;
- }
-
- let a = self.as_slice();
- let b = other.as_slice();
- a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
- if a16 <= 0x7F && b16 <= 0x7F {
- (a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
- } else {
- a16 == b16
- }
- })
- }
-
- /// Return whether this atom is an ASCII-case-insensitive match for the given string
- pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
- self.chars().map(|r| r.map(|c: char| c.to_ascii_lowercase()))
- .eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
- }
}
impl Hash for Atom {
@@ -321,6 +346,13 @@ impl<'a> From<&'a str> for Atom {
}
}
+impl<'a> From<&'a [u16]> for Atom {
+ #[inline]
+ fn from(slice: &[u16]) -> Atom {
+ Atom::from(&*nsString::from(slice))
+ }
+}
+
impl<'a> From<&'a nsAString> for Atom {
#[inline]
fn from(string: &nsAString) -> Atom {
diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs
new file mode 100644
index 00000000000..2bb2c3857dc
--- /dev/null
+++ b/components/style/invalidation/element/element_wrapper.rs
@@ -0,0 +1,341 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! A wrapper over an element and a snapshot, that allows us to selector-match
+//! against a past state of the element.
+
+use {Atom, CaseSensitivityExt, LocalName, Namespace};
+use dom::TElement;
+use element_state::ElementState;
+use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
+use selectors::Element;
+use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
+use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext};
+use selectors::matching::RelevantLinkStatus;
+use std::cell::Cell;
+use std::fmt;
+
+/// In order to compute restyle hints, we perform a selector match against a
+/// list of partial selectors whose rightmost simple selector may be sensitive
+/// to the thing being changed. We do this matching twice, once for the element
+/// as it exists now and once for the element as it existed at the time of the
+/// last restyle. If the results of the selector match differ, that means that
+/// the given partial selector is sensitive to the change, and we compute a
+/// restyle hint based on its combinator.
+///
+/// In order to run selector matching against the old element state, we generate
+/// a wrapper for the element which claims to have the old state. This is the
+/// ElementWrapper logic below.
+///
+/// Gecko does this differently for element states, and passes a mask called
+/// mStateMask, which indicates the states that need to be ignored during
+/// selector matching. This saves an ElementWrapper allocation and an additional
+/// selector match call at the expense of additional complexity inside the
+/// selector matching logic. This only works for boolean states though, so we
+/// still need to take the ElementWrapper approach for attribute-dependent
+/// style. So we do it the same both ways for now to reduce complexity, but it's
+/// worth measuring the performance impact (if any) of the mStateMask approach.
+pub trait ElementSnapshot : Sized {
+ /// The state of the snapshot, if any.
+ fn state(&self) -> Option<ElementState>;
+
+ /// If this snapshot contains attribute information.
+ fn has_attrs(&self) -> bool;
+
+ /// The ID attribute per this snapshot. Should only be called if
+ /// `has_attrs()` returns true.
+ fn id_attr(&self) -> Option<Atom>;
+
+ /// Whether this snapshot contains the class `name`. Should only be called
+ /// if `has_attrs()` returns true.
+ fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool;
+
+ /// A callback that should be called for each class of the snapshot. Should
+ /// only be called if `has_attrs()` returns true.
+ fn each_class<F>(&self, F)
+ where F: FnMut(&Atom);
+
+ /// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
+ fn lang_attr(&self) -> Option<AttrValue>;
+}
+
+/// A simple wrapper over an element and a snapshot, that allows us to
+/// selector-match against a past state of the element.
+#[derive(Clone)]
+pub struct ElementWrapper<'a, E>
+ where E: TElement,
+{
+ element: E,
+ cached_snapshot: Cell<Option<&'a Snapshot>>,
+ snapshot_map: &'a SnapshotMap,
+}
+
+impl<'a, E> ElementWrapper<'a, E>
+ where E: TElement,
+{
+ /// Trivially constructs an `ElementWrapper`.
+ pub fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {
+ ElementWrapper {
+ element: el,
+ cached_snapshot: Cell::new(None),
+ snapshot_map: snapshot_map,
+ }
+ }
+
+ /// Gets the snapshot associated with this element, if any.
+ pub fn snapshot(&self) -> Option<&'a Snapshot> {
+ if !self.element.has_snapshot() {
+ return None;
+ }
+
+ if let Some(s) = self.cached_snapshot.get() {
+ return Some(s);
+ }
+
+ let snapshot = self.snapshot_map.get(&self.element);
+ debug_assert!(snapshot.is_some(), "has_snapshot lied!");
+
+ self.cached_snapshot.set(snapshot);
+
+ snapshot
+ }
+
+ /// Returns the states that have changed since the element was snapshotted.
+ pub fn state_changes(&self) -> ElementState {
+ let snapshot = match self.snapshot() {
+ Some(s) => s,
+ None => return ElementState::empty(),
+ };
+
+ match snapshot.state() {
+ Some(state) => state ^ self.element.get_state(),
+ None => ElementState::empty(),
+ }
+ }
+
+ /// Returns the value of the `xml:lang=""` (or, if appropriate, `lang=""`)
+ /// attribute from this element's snapshot or the closest ancestor
+ /// element snapshot with the attribute specified.
+ fn get_lang(&self) -> Option<AttrValue> {
+ let mut current = self.clone();
+ loop {
+ let lang = match self.snapshot() {
+ Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(),
+ _ => current.element.lang_attr(),
+ };
+ if lang.is_some() {
+ return lang;
+ }
+ match current.parent_element() {
+ Some(parent) => current = parent,
+ None => return None,
+ }
+ }
+ }
+}
+
+impl<'a, E> fmt::Debug for ElementWrapper<'a, E>
+ where E: TElement,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // Ignore other fields for now, can change later if needed.
+ self.element.fmt(f)
+ }
+}
+
+impl<'a, E> Element for ElementWrapper<'a, E>
+ where E: TElement,
+{
+ type Impl = SelectorImpl;
+
+ fn match_non_ts_pseudo_class<F>(&self,
+ pseudo_class: &NonTSPseudoClass,
+ context: &mut LocalMatchingContext<Self::Impl>,
+ relevant_link: &RelevantLinkStatus,
+ _setter: &mut F)
+ -> bool
+ where F: FnMut(&Self, ElementSelectorFlags),
+ {
+ // Some pseudo-classes need special handling to evaluate them against
+ // the snapshot.
+ match *pseudo_class {
+ #[cfg(feature = "gecko")]
+ NonTSPseudoClass::MozAny(ref selectors) => {
+ use selectors::matching::matches_complex_selector;
+ return selectors.iter().any(|s| {
+ matches_complex_selector(s.iter(), self, context, _setter)
+ })
+ }
+
+ // :dir is implemented in terms of state flags, but which state flag
+ // it maps to depends on the argument to :dir. That means we can't
+ // just add its state flags to the NonTSPseudoClass, because if we
+ // added all of them there, and tested via intersects() here, we'd
+ // get incorrect behavior for :not(:dir()) cases.
+ //
+ // FIXME(bz): How can I set this up so once Servo adds :dir()
+ // support we don't forget to update this code?
+ #[cfg(feature = "gecko")]
+ NonTSPseudoClass::Dir(ref s) => {
+ use invalidation::element::invalidation_map::dir_selector_to_state;
+ let selector_flag = dir_selector_to_state(s);
+ if selector_flag.is_empty() {
+ // :dir() with some random argument; does not match.
+ return false;
+ }
+ let state = match self.snapshot().and_then(|s| s.state()) {
+ Some(snapshot_state) => snapshot_state,
+ None => self.element.get_state(),
+ };
+ return state.contains(selector_flag);
+ }
+
+ // For :link and :visited, we don't actually want to test the element
+ // state directly. Instead, we use the `relevant_link` to determine if
+ // they match.
+ NonTSPseudoClass::Link => {
+ return relevant_link.is_unvisited(self, context.shared);
+ }
+ NonTSPseudoClass::Visited => {
+ return relevant_link.is_visited(self, context.shared);
+ }
+
+ #[cfg(feature = "gecko")]
+ NonTSPseudoClass::MozTableBorderNonzero => {
+ if let Some(snapshot) = self.snapshot() {
+ if snapshot.has_other_pseudo_class_state() {
+ return snapshot.mIsTableBorderNonzero();
+ }
+ }
+ }
+
+ #[cfg(feature = "gecko")]
+ NonTSPseudoClass::MozBrowserFrame => {
+ if let Some(snapshot) = self.snapshot() {
+ if snapshot.has_other_pseudo_class_state() {
+ return snapshot.mIsMozBrowserFrame();
+ }
+ }
+ }
+
+ // :lang() needs to match using the closest ancestor xml:lang="" or
+ // lang="" attribtue from snapshots.
+ NonTSPseudoClass::Lang(ref lang_arg) => {
+ return self.element.match_element_lang(Some(self.get_lang()), lang_arg);
+ }
+
+ _ => {}
+ }
+
+ let flag = pseudo_class.state_flag();
+ if flag.is_empty() {
+ return self.element.match_non_ts_pseudo_class(pseudo_class,
+ context,
+ relevant_link,
+ &mut |_, _| {})
+ }
+ match self.snapshot().and_then(|s| s.state()) {
+ Some(snapshot_state) => snapshot_state.intersects(flag),
+ None => {
+ self.element.match_non_ts_pseudo_class(pseudo_class,
+ context,
+ relevant_link,
+ &mut |_, _| {})
+ }
+ }
+ }
+
+ fn match_pseudo_element(&self,
+ pseudo_element: &PseudoElement,
+ context: &mut MatchingContext)
+ -> bool
+ {
+ self.element.match_pseudo_element(pseudo_element, context)
+ }
+
+ fn is_link(&self) -> bool {
+ self.element.is_link()
+ }
+
+ fn parent_element(&self) -> Option<Self> {
+ self.element.parent_element()
+ .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ }
+
+ fn first_child_element(&self) -> Option<Self> {
+ self.element.first_child_element()
+ .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ }
+
+ fn last_child_element(&self) -> Option<Self> {
+ self.element.last_child_element()
+ .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ }
+
+ fn prev_sibling_element(&self) -> Option<Self> {
+ self.element.prev_sibling_element()
+ .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ }
+
+ fn next_sibling_element(&self) -> Option<Self> {
+ self.element.next_sibling_element()
+ .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ }
+
+ fn is_html_element_in_html_document(&self) -> bool {
+ self.element.is_html_element_in_html_document()
+ }
+
+ fn get_local_name(&self) -> &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName {
+ self.element.get_local_name()
+ }
+
+ fn get_namespace(&self) -> &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl {
+ self.element.get_namespace()
+ }
+
+ fn attr_matches(&self,
+ ns: &NamespaceConstraint<&Namespace>,
+ local_name: &LocalName,
+ operation: &AttrSelectorOperation<&AttrValue>)
+ -> bool {
+ match self.snapshot() {
+ Some(snapshot) if snapshot.has_attrs() => {
+ snapshot.attr_matches(ns, local_name, operation)
+ }
+ _ => self.element.attr_matches(ns, local_name, operation)
+ }
+ }
+
+ fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
+ match self.snapshot() {
+ Some(snapshot) if snapshot.has_attrs() => {
+ snapshot.id_attr().map_or(false, |atom| case_sensitivity.eq_atom(&atom, id))
+ }
+ _ => self.element.has_id(id, case_sensitivity)
+ }
+ }
+
+ fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
+ match self.snapshot() {
+ Some(snapshot) if snapshot.has_attrs() => {
+ snapshot.has_class(name, case_sensitivity)
+ }
+ _ => self.element.has_class(name, case_sensitivity)
+ }
+ }
+
+ fn is_empty(&self) -> bool {
+ self.element.is_empty()
+ }
+
+ fn is_root(&self) -> bool {
+ self.element.is_root()
+ }
+
+ fn pseudo_element_originating_element(&self) -> Option<Self> {
+ self.element.closest_non_native_anonymous_ancestor()
+ .map(|e| ElementWrapper::new(e, self.snapshot_map))
+ }
+}
diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs
new file mode 100644
index 00000000000..8e4dfebec99
--- /dev/null
+++ b/components/style/invalidation/element/invalidation_map.rs
@@ -0,0 +1,402 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! Code for invalidations due to state or attribute changes.
+
+use {Atom, LocalName, Namespace};
+use context::QuirksMode;
+use element_state::ElementState;
+use selector_map::{MaybeCaseInsensitiveHashMap, SelectorMap, SelectorMapEntry};
+use selector_parser::SelectorImpl;
+use selectors::attr::NamespaceConstraint;
+use selectors::parser::{AncestorHashes, Combinator, Component};
+use selectors::parser::{Selector, SelectorAndHashes, SelectorIter, SelectorMethods};
+use selectors::visitor::SelectorVisitor;
+use smallvec::SmallVec;
+
+#[cfg(feature = "gecko")]
+/// Gets the element state relevant to the given `:dir` pseudo-class selector.
+pub fn dir_selector_to_state(s: &[u16]) -> ElementState {
+ use element_state::{IN_LTR_STATE, IN_RTL_STATE};
+
+ // Jump through some hoops to deal with our Box<[u16]> thing.
+ const LTR: [u16; 4] = [b'l' as u16, b't' as u16, b'r' as u16, 0];
+ const RTL: [u16; 4] = [b'r' as u16, b't' as u16, b'l' as u16, 0];
+
+ if LTR == *s {
+ IN_LTR_STATE
+ } else if RTL == *s {
+ IN_RTL_STATE
+ } else {
+ // :dir(something-random) is a valid selector, but shouldn't
+ // match anything.
+ ElementState::empty()
+ }
+}
+
+/// Mapping between (partial) CompoundSelectors (and the combinator to their
+/// right) and the states and attributes they depend on.
+///
+/// In general, for all selectors in all applicable stylesheets of the form:
+///
+/// |a _ b _ c _ d _ e|
+///
+/// Where:
+/// * |b| and |d| are simple selectors that depend on state (like :hover) or
+/// attributes (like [attr...], .foo, or #foo).
+/// * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on
+/// state or attributes.
+///
+/// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|,
+/// even though those selectors may not appear on their own in any stylesheet.
+/// This allows us to quickly scan through the dependency sites of all style
+/// rules and determine the maximum effect that a given state or attribute
+/// change may have on the style of elements in the document.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct Dependency {
+ /// The dependency selector.
+ #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
+ pub selector: Selector<SelectorImpl>,
+ /// The ancestor hashes associated with the above selector at the given
+ /// offset.
+ #[cfg_attr(feature = "servo", ignore_heap_size_of = "No heap data")]
+ pub hashes: AncestorHashes,
+ /// The offset into the selector that we should match on.
+ pub selector_offset: usize,
+}
+
+impl Dependency {
+ /// Returns the combinator to the right of the partial selector this
+ /// dependency represents.
+ ///
+ /// TODO(emilio): Consider storing inline if it helps cache locality?
+ pub fn combinator(&self) -> Option<Combinator> {
+ if self.selector_offset == 0 {
+ return None;
+ }
+
+ Some(self.selector.combinator_at(self.selector_offset))
+ }
+
+ /// Whether this dependency affects the style of the element.
+ ///
+ /// NOTE(emilio): pseudo-elements need to be here to account for eager
+ /// pseudos, since they just grab the style from the originating element.
+ ///
+ /// TODO(emilio): We could look at the selector itself to see if it's an
+ /// eager pseudo, and return false here if not.
+ pub fn affects_self(&self) -> bool {
+ matches!(self.combinator(), None | Some(Combinator::PseudoElement))
+ }
+
+ /// Whether this dependency may affect style of any of our descendants.
+ pub fn affects_descendants(&self) -> bool {
+ matches!(self.combinator(), Some(Combinator::PseudoElement) |
+ Some(Combinator::Child) |
+ Some(Combinator::Descendant))
+ }
+
+ /// Whether this dependency may affect style of any of our later siblings.
+ pub fn affects_later_siblings(&self) -> bool {
+ matches!(self.combinator(), Some(Combinator::NextSibling) |
+ Some(Combinator::LaterSibling))
+ }
+}
+
+impl SelectorMapEntry for Dependency {
+ fn selector(&self) -> SelectorIter<SelectorImpl> {
+ self.selector.iter_from(self.selector_offset)
+ }
+
+ fn hashes(&self) -> &AncestorHashes {
+ &self.hashes
+ }
+}
+
+/// The same, but for state selectors, which can track more exactly what state
+/// do they track.
+#[derive(Clone)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct StateDependency {
+ /// The other dependency fields.
+ pub dep: Dependency,
+ /// The state this dependency is affected by.
+ pub state: ElementState,
+}
+
+impl SelectorMapEntry for StateDependency {
+ fn selector(&self) -> SelectorIter<SelectorImpl> {
+ self.dep.selector.iter_from(self.dep.selector_offset)
+ }
+
+ fn hashes(&self) -> &AncestorHashes {
+ &self.dep.hashes
+ }
+}
+
+/// A map where we store invalidations.
+///
+/// This is slightly different to a SelectorMap, in the sense of that the same
+/// selector may appear multiple times.
+///
+/// In particular, we want to lookup as few things as possible to get the fewer
+/// selectors the better, so this looks up by id, class, or looks at the list of
+/// state/other attribute affecting selectors.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct InvalidationMap {
+ /// A map from a given class name to all the selectors with that class
+ /// selector.
+ pub class_to_selector: MaybeCaseInsensitiveHashMap<Atom, SelectorMap<Dependency>>,
+ /// A map from a given id to all the selectors with that ID in the
+ /// stylesheets currently applying to the document.
+ pub id_to_selector: MaybeCaseInsensitiveHashMap<Atom, SelectorMap<Dependency>>,
+ /// A map of all the state dependencies.
+ pub state_affecting_selectors: SelectorMap<StateDependency>,
+ /// A map of other attribute affecting selectors.
+ pub other_attribute_affecting_selectors: SelectorMap<Dependency>,
+ /// Whether there are attribute rules of the form `[class~="foo"]` that may
+ /// match. In that case, we need to look at
+ /// `other_attribute_affecting_selectors` too even if only the `class` has
+ /// changed.
+ pub has_class_attribute_selectors: bool,
+ /// Whether there are attribute rules of the form `[id|="foo"]` that may
+ /// match. In that case, we need to look at
+ /// `other_attribute_affecting_selectors` too even if only the `id` has
+ /// changed.
+ pub has_id_attribute_selectors: bool,
+}
+
+impl InvalidationMap {
+ /// Creates an empty `InvalidationMap`.
+ pub fn new() -> Self {
+ Self {
+ class_to_selector: MaybeCaseInsensitiveHashMap::new(),
+ id_to_selector: MaybeCaseInsensitiveHashMap::new(),
+ state_affecting_selectors: SelectorMap::new(),
+ other_attribute_affecting_selectors: SelectorMap::new(),
+ has_class_attribute_selectors: false,
+ has_id_attribute_selectors: false,
+ }
+ }
+
+ /// Returns the number of dependencies stored in the invalidation map.
+ pub fn len(&self) -> usize {
+ self.state_affecting_selectors.len() +
+ self.other_attribute_affecting_selectors.len() +
+ self.id_to_selector.iter().fold(0, |accum, (_, ref v)| {
+ accum + v.len()
+ }) +
+ self.class_to_selector.iter().fold(0, |accum, (_, ref v)| {
+ accum + v.len()
+ })
+ }
+
+ /// Adds a selector to this `InvalidationMap`.
+ pub fn note_selector(
+ &mut self,
+ selector_and_hashes: &SelectorAndHashes<SelectorImpl>,
+ quirks_mode: QuirksMode)
+ {
+ self.collect_invalidations_for(selector_and_hashes, quirks_mode)
+ }
+
+ /// Clears this map, leaving it empty.
+ pub fn clear(&mut self) {
+ self.class_to_selector.clear();
+ self.id_to_selector.clear();
+ self.state_affecting_selectors = SelectorMap::new();
+ self.other_attribute_affecting_selectors = SelectorMap::new();
+ self.has_id_attribute_selectors = false;
+ self.has_class_attribute_selectors = false;
+ }
+
+ fn collect_invalidations_for(
+ &mut self,
+ selector_and_hashes: &SelectorAndHashes<SelectorImpl>,
+ quirks_mode: QuirksMode)
+ {
+ debug!("InvalidationMap::collect_invalidations_for({:?})",
+ selector_and_hashes.selector);
+
+ let mut iter = selector_and_hashes.selector.iter();
+ let mut combinator;
+ let mut index = 0;
+
+ loop {
+ let sequence_start = index;
+
+ let mut compound_visitor = CompoundSelectorDependencyCollector {
+ classes: SmallVec::new(),
+ ids: SmallVec::new(),
+ state: ElementState::empty(),
+ other_attributes: false,
+ has_id_attribute_selectors: false,
+ has_class_attribute_selectors: false,
+ };
+
+ // Visit all the simple selectors in this sequence.
+ //
+ // Note that this works because we can't have combinators nested
+ // inside simple selectors (i.e. in :not() or :-moz-any()).
+ //
+ // If we ever support that we'll need to visit nested complex
+ // selectors as well, in order to mark them as affecting descendants
+ // at least.
+ for ss in &mut iter {
+ ss.visit(&mut compound_visitor);
+ index += 1; // Account for the simple selector.
+ }
+
+ // Reuse the bloom hashes if this is the base selector. Otherwise,
+ // rebuild them.
+ let mut hashes = None;
+
+ let mut get_hashes = || -> AncestorHashes {
+ if hashes.is_none() {
+ hashes = Some(if sequence_start == 0 {
+ selector_and_hashes.hashes.clone()
+ } else {
+ let seq_iter = selector_and_hashes.selector.iter_from(sequence_start);
+ AncestorHashes::from_iter(seq_iter)
+ });
+ }
+ hashes.clone().unwrap()
+ };
+
+ self.has_id_attribute_selectors |= compound_visitor.has_id_attribute_selectors;
+ self.has_class_attribute_selectors |= compound_visitor.has_class_attribute_selectors;
+
+ for class in compound_visitor.classes {
+ self.class_to_selector
+ .entry(class, quirks_mode)
+ .or_insert_with(SelectorMap::new)
+ .insert(Dependency {
+ selector: selector_and_hashes.selector.clone(),
+ selector_offset: sequence_start,
+ hashes: get_hashes(),
+ }, quirks_mode);
+ }
+
+ for id in compound_visitor.ids {
+ self.id_to_selector
+ .entry(id, quirks_mode)
+ .or_insert_with(SelectorMap::new)
+ .insert(Dependency {
+ selector: selector_and_hashes.selector.clone(),
+ selector_offset: sequence_start,
+ hashes: get_hashes(),
+ }, quirks_mode);
+ }
+
+ if !compound_visitor.state.is_empty() {
+ self.state_affecting_selectors
+ .insert(StateDependency {
+ dep: Dependency {
+ selector: selector_and_hashes.selector.clone(),
+ selector_offset: sequence_start,
+ hashes: get_hashes(),
+ },
+ state: compound_visitor.state,
+ }, quirks_mode);
+ }
+
+ if compound_visitor.other_attributes {
+ self.other_attribute_affecting_selectors
+ .insert(Dependency {
+ selector: selector_and_hashes.selector.clone(),
+ selector_offset: sequence_start,
+ hashes: get_hashes(),
+ }, quirks_mode);
+ }
+
+ combinator = iter.next_sequence();
+ if combinator.is_none() {
+ break;
+ }
+
+ index += 1; // Account for the combinator.
+ }
+ }
+}
+
+/// A struct that collects invalidations for a given compound selector.
+struct CompoundSelectorDependencyCollector {
+ /// The state this compound selector is affected by.
+ state: ElementState,
+
+ /// The classes this compound selector is affected by.
+ ///
+ /// NB: This will be often a single class, but could be multiple in
+ /// presence of :not, :-moz-any, .foo.bar.baz, etc.
+ classes: SmallVec<[Atom; 5]>,
+
+ /// The IDs this compound selector is affected by.
+ ///
+ /// NB: This will be almost always a single id, but could be multiple in
+ /// presence of :not, :-moz-any, #foo#bar, etc.
+ ids: SmallVec<[Atom; 5]>,
+
+ /// Whether it affects other attribute-dependent selectors that aren't ID or
+ /// class selectors (NB: We still set this to true in presence of [class] or
+ /// [id] attribute selectors).
+ other_attributes: bool,
+
+ /// Whether there were attribute selectors with the id attribute.
+ has_id_attribute_selectors: bool,
+
+ /// Whether there were attribute selectors with the class attribute.
+ has_class_attribute_selectors: bool,
+}
+
+impl SelectorVisitor for CompoundSelectorDependencyCollector {
+ type Impl = SelectorImpl;
+
+ fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
+ #[cfg(feature = "gecko")]
+ use selector_parser::NonTSPseudoClass;
+
+ match *s {
+ Component::ID(ref id) => {
+ self.ids.push(id.clone());
+ }
+ Component::Class(ref class) => {
+ self.classes.push(class.clone());
+ }
+ Component::NonTSPseudoClass(ref pc) => {
+ self.other_attributes |= pc.is_attr_based();
+ self.state |= match *pc {
+ #[cfg(feature = "gecko")]
+ NonTSPseudoClass::Dir(ref s) => {
+ dir_selector_to_state(s)
+ }
+ _ => pc.state_flag(),
+ };
+ }
+ _ => {}
+ }
+
+ true
+ }
+
+ fn visit_attribute_selector(
+ &mut self,
+ constraint: &NamespaceConstraint<&Namespace>,
+ _local_name: &LocalName,
+ local_name_lower: &LocalName,
+ ) -> bool {
+ self.other_attributes = true;
+ let may_match_in_no_namespace = match *constraint {
+ NamespaceConstraint::Any => true,
+ NamespaceConstraint::Specific(ref ns) => ns.is_empty(),
+ };
+
+ if may_match_in_no_namespace {
+ self.has_id_attribute_selectors |= *local_name_lower == local_name!("id");
+ self.has_class_attribute_selectors |= *local_name_lower == local_name!("class");
+ }
+
+ true
+ }
+}
diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs
new file mode 100644
index 00000000000..ed759076e23
--- /dev/null
+++ b/components/style/invalidation/element/invalidator.rs
@@ -0,0 +1,702 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! The struct that takes care of encapsulating all the logic on where and how
+//! element styles need to be invalidated.
+
+use Atom;
+use context::SharedStyleContext;
+use data::ElementData;
+use dom::{TElement, TNode};
+use element_state::{ElementState, IN_VISITED_OR_UNVISITED_STATE};
+use invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
+use invalidation::element::invalidation_map::*;
+use invalidation::element::restyle_hints::*;
+use selector_map::SelectorMap;
+use selector_parser::SelectorImpl;
+use selectors::attr::CaseSensitivity;
+use selectors::matching::{MatchingContext, MatchingMode, VisitedHandlingMode};
+use selectors::matching::{matches_selector, matches_compound_selector};
+use selectors::matching::CompoundSelectorMatchingResult;
+use selectors::parser::{Combinator, Component, Selector};
+use smallvec::SmallVec;
+use std::fmt;
+
+/// The struct that takes care of encapsulating all the logic on where and how
+/// element styles need to be invalidated.
+pub struct TreeStyleInvalidator<'a, 'b: 'a, E>
+ where E: TElement,
+{
+ element: E,
+ data: Option<&'a mut ElementData>,
+ shared_context: &'a SharedStyleContext<'b>,
+}
+
+type InvalidationVector = SmallVec<[Invalidation; 10]>;
+
+/// An `Invalidation` is a complex selector that describes which elements,
+/// relative to a current element we are processing, must be restyled.
+///
+/// When `offset` points to the right-most compound selector in `selector`,
+/// then the Invalidation `represents` the fact that the current element
+/// must be restyled if the compound selector matches. Otherwise, if
+/// describes which descendants (or later siblings) must be restyled.
+#[derive(Clone)]
+struct Invalidation {
+ selector: Selector<SelectorImpl>,
+ offset: usize,
+}
+
+impl fmt::Debug for Invalidation {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use cssparser::ToCss;
+
+ f.write_str("Invalidation(")?;
+ for component in self.selector.iter_raw_rev_from(self.offset - 1) {
+ if matches!(*component, Component::Combinator(..)) {
+ break;
+ }
+ component.to_css(f)?;
+ }
+ f.write_str(")")
+ }
+}
+
+/// The result of processing a single invalidation for a given element.
+struct InvalidationResult {
+ /// Whether the element itself was invalidated.
+ invalidated_self: bool,
+ /// Whether the invalidation we've processed is effective for the next
+ /// sibling or descendant after us.
+ effective_for_next: bool,
+}
+
+impl<'a, 'b: 'a, E> TreeStyleInvalidator<'a, 'b, E>
+ where E: TElement,
+{
+ /// Trivially constructs a new `TreeStyleInvalidator`.
+ pub fn new(
+ element: E,
+ data: Option<&'a mut ElementData>,
+ shared_context: &'a SharedStyleContext<'b>,
+ ) -> Self {
+ Self {
+ element: element,
+ data: data,
+ shared_context: shared_context,
+ }
+ }
+
+ /// Perform the invalidation pass.
+ pub fn invalidate(mut self) {
+ debug!("StyleTreeInvalidator::invalidate({:?})", self.element);
+ debug_assert!(self.element.has_snapshot(), "Why bothering?");
+ debug_assert!(self.data.is_some(), "How exactly?");
+
+ let shared_context = self.shared_context;
+
+ let wrapper =
+ ElementWrapper::new(self.element, shared_context.snapshot_map);
+ let state_changes = wrapper.state_changes();
+ let snapshot = wrapper.snapshot().expect("has_snapshot lied");
+
+ if !snapshot.has_attrs() && state_changes.is_empty() {
+ return;
+ }
+
+ // If we are sensitive to visitedness and the visited state changed, we
+ // force a restyle here. Matching doesn't depend on the actual visited
+ // state at all, so we can't look at matching results to decide what to
+ // do for this case.
+ if state_changes.intersects(IN_VISITED_OR_UNVISITED_STATE) {
+ trace!(" > visitedness change, force subtree restyle");
+ // We can't just return here because there may also be attribute
+ // changes as well that imply additional hints.
+ let mut data = self.data.as_mut().unwrap();
+ data.restyle.hint.insert(RestyleHint::restyle_subtree());
+ }
+
+ let mut classes_removed = SmallVec::<[Atom; 8]>::new();
+ let mut classes_added = SmallVec::<[Atom; 8]>::new();
+ if snapshot.class_changed() {
+ // TODO(emilio): Do this more efficiently!
+ snapshot.each_class(|c| {
+ if !self.element.has_class(c, CaseSensitivity::CaseSensitive) {
+ classes_removed.push(c.clone())
+ }
+ });
+
+ self.element.each_class(|c| {
+ if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
+ classes_added.push(c.clone())
+ }
+ })
+ }
+
+ let mut id_removed = None;
+ let mut id_added = None;
+ if snapshot.id_changed() {
+ let old_id = snapshot.id_attr();
+ let current_id = self.element.get_id();
+
+ if old_id != current_id {
+ id_removed = old_id;
+ id_added = current_id;
+ }
+ }
+
+ let lookup_element =
+ if self.element.implemented_pseudo_element().is_some() {
+ self.element.pseudo_element_originating_element().unwrap()
+ } else {
+ self.element
+ };
+
+ let mut descendant_invalidations = InvalidationVector::new();
+ let mut sibling_invalidations = InvalidationVector::new();
+ let invalidated_self = {
+ let mut collector = InvalidationCollector {
+ wrapper: wrapper,
+ element: self.element,
+ shared_context: self.shared_context,
+ lookup_element: lookup_element,
+ removed_id: id_removed.as_ref(),
+ classes_removed: &classes_removed,
+ descendant_invalidations: &mut descendant_invalidations,
+ sibling_invalidations: &mut sibling_invalidations,
+ invalidates_self: false,
+ };
+
+ let map = shared_context.stylist.invalidation_map();
+
+ if let Some(ref id) = id_removed {
+ if let Some(deps) = map.id_to_selector.get(id, shared_context.quirks_mode) {
+ collector.collect_dependencies_in_map(deps)
+ }
+ }
+
+ if let Some(ref id) = id_added {
+ if let Some(deps) = map.id_to_selector.get(id, shared_context.quirks_mode) {
+ collector.collect_dependencies_in_map(deps)
+ }
+ }
+
+ for class in classes_added.iter().chain(classes_removed.iter()) {
+ if let Some(deps) = map.class_to_selector.get(class, shared_context.quirks_mode) {
+ collector.collect_dependencies_in_map(deps)
+ }
+ }
+
+ let should_examine_attribute_selector_map =
+ snapshot.other_attr_changed() ||
+ (snapshot.class_changed() && map.has_class_attribute_selectors) ||
+ (snapshot.id_changed() && map.has_id_attribute_selectors);
+
+ if should_examine_attribute_selector_map {
+ collector.collect_dependencies_in_map(
+ &map.other_attribute_affecting_selectors
+ )
+ }
+
+ if !state_changes.is_empty() {
+ collector.collect_state_dependencies(
+ &map.state_affecting_selectors,
+ state_changes,
+ )
+ }
+
+ collector.invalidates_self
+ };
+
+ if invalidated_self {
+ if let Some(ref mut data) = self.data {
+ data.restyle.hint.insert(RESTYLE_SELF);
+ }
+ }
+
+ debug!("Collected invalidations (self: {}): ", invalidated_self);
+ debug!(" > descendants: {:?}", descendant_invalidations);
+ debug!(" > siblings: {:?}", sibling_invalidations);
+ self.invalidate_descendants(&descendant_invalidations);
+ self.invalidate_siblings(&mut sibling_invalidations);
+ }
+
+ /// Go through later DOM siblings, invalidating style as needed using the
+ /// `sibling_invalidations` list.
+ ///
+ /// Returns whether any sibling's style or any sibling descendant's style
+ /// was invalidated.
+ fn invalidate_siblings(
+ &mut self,
+ sibling_invalidations: &mut InvalidationVector,
+ ) -> bool {
+ if sibling_invalidations.is_empty() {
+ return false;
+ }
+
+ let mut current = self.element.next_sibling_element();
+ let mut any_invalidated = false;
+
+ while let Some(sibling) = current {
+ let mut sibling_data = sibling.get_data().map(|d| d.borrow_mut());
+ let sibling_data = sibling_data.as_mut().map(|d| &mut **d);
+
+ let mut sibling_invalidator = TreeStyleInvalidator::new(
+ sibling,
+ sibling_data,
+ self.shared_context
+ );
+
+ let mut invalidations_for_descendants = InvalidationVector::new();
+ any_invalidated |=
+ sibling_invalidator.process_sibling_invalidations(
+ &mut invalidations_for_descendants,
+ sibling_invalidations,
+ );
+
+ any_invalidated |=
+ sibling_invalidator.invalidate_descendants(
+ &invalidations_for_descendants
+ );
+
+ if sibling_invalidations.is_empty() {
+ break;
+ }
+
+ current = sibling.next_sibling_element();
+ }
+
+ any_invalidated
+ }
+
+ /// Given a descendant invalidation list, go through the current element's
+ /// descendants, and invalidate style on them.
+ fn invalidate_descendants(
+ &mut self,
+ invalidations: &InvalidationVector,
+ ) -> bool {
+ if invalidations.is_empty() {
+ return false;
+ }
+
+ debug!("StyleTreeInvalidator::invalidate_descendants({:?})",
+ self.element);
+ debug!(" > {:?}", invalidations);
+
+ match self.data {
+ None => return false,
+ Some(ref data) => {
+ if data.restyle.hint.contains_subtree() {
+ return false;
+ }
+ }
+ }
+
+ let mut sibling_invalidations = InvalidationVector::new();
+
+ let mut any_children = false;
+ for child in self.element.as_node().traversal_children() {
+ let child = match child.as_element() {
+ Some(e) => e,
+ None => continue,
+ };
+
+ let mut child_data = child.get_data().map(|d| d.borrow_mut());
+ let child_data = child_data.as_mut().map(|d| &mut **d);
+
+ let mut child_invalidator = TreeStyleInvalidator::new(
+ child,
+ child_data,
+ self.shared_context
+ );
+
+ let mut invalidations_for_descendants = InvalidationVector::new();
+ any_children |= child_invalidator.process_sibling_invalidations(
+ &mut invalidations_for_descendants,
+ &mut sibling_invalidations,
+ );
+
+ any_children |= child_invalidator.process_descendant_invalidations(
+ invalidations,
+ &mut invalidations_for_descendants,
+ &mut sibling_invalidations,
+ );
+
+ any_children |= child_invalidator.invalidate_descendants(
+ &invalidations_for_descendants
+ );
+ }
+
+ if any_children {
+ unsafe { self.element.set_dirty_descendants() };
+ }
+
+ any_children
+ }
+
+ /// Process the given sibling invalidations coming from our previous
+ /// sibling.
+ ///
+ /// The sibling invalidations are somewhat special because they can be
+ /// modified on the fly. New invalidations may be added and removed.
+ ///
+ /// In particular, all descendants get the same set of invalidations from
+ /// the parent, but the invalidations from a given sibling depend on the
+ /// ones we got from the previous one.
+ ///
+ /// Returns whether invalidated the current element's style.
+ fn process_sibling_invalidations(
+ &mut self,
+ descendant_invalidations: &mut InvalidationVector,
+ sibling_invalidations: &mut InvalidationVector,
+ ) -> bool {
+ let mut i = 0;
+ let mut new_sibling_invalidations = InvalidationVector::new();
+ let mut invalidated_self = false;
+
+ while i < sibling_invalidations.len() {
+ let result = self.process_invalidation(
+ &sibling_invalidations[i],
+ descendant_invalidations,
+ &mut new_sibling_invalidations
+ );
+
+ invalidated_self |= result.invalidated_self;
+ if !result.effective_for_next {
+ sibling_invalidations.remove(i);
+ } else {
+ i += 1;
+ }
+ }
+
+ sibling_invalidations.extend(new_sibling_invalidations.into_iter());
+ invalidated_self
+ }
+
+ /// Process a given invalidation list coming from our parent,
+ /// adding to `descendant_invalidations` and `sibling_invalidations` as
+ /// needed.
+ ///
+ /// Returns whether our style was invalidated as a result.
+ fn process_descendant_invalidations(
+ &mut self,
+ invalidations: &InvalidationVector,
+ descendant_invalidations: &mut InvalidationVector,
+ sibling_invalidations: &mut InvalidationVector,
+ ) -> bool {
+ let mut invalidated = false;
+
+ for invalidation in invalidations {
+ let result = self.process_invalidation(
+ invalidation,
+ descendant_invalidations,
+ sibling_invalidations,
+ );
+
+ invalidated |= result.invalidated_self;
+ if result.effective_for_next {
+ descendant_invalidations.push(invalidation.clone());
+ }
+ }
+
+ invalidated
+ }
+
+ /// Processes a given invalidation, potentially invalidating the style of
+ /// the current element.
+ ///
+ /// Returns whether invalidated the style of the element, and whether the
+ /// invalidation should be effective to subsequent siblings or descendants
+ /// down in the tree.
+ fn process_invalidation(
+ &mut self,
+ invalidation: &Invalidation,
+ descendant_invalidations: &mut InvalidationVector,
+ sibling_invalidations: &mut InvalidationVector
+ ) -> InvalidationResult {
+ debug!("TreeStyleInvalidator::process_invalidation({:?}, {:?})",
+ self.element, invalidation);
+
+ let mut context =
+ MatchingContext::new_for_visited(
+ MatchingMode::Normal,
+ None,
+ VisitedHandlingMode::AllLinksVisitedAndUnvisited,
+ self.shared_context.quirks_mode,
+ );
+
+ let matching_result = matches_compound_selector(
+ &invalidation.selector,
+ invalidation.offset,
+ &mut context,
+ &self.element
+ );
+
+ let mut invalidated_self = false;
+ match matching_result {
+ CompoundSelectorMatchingResult::Matched { next_combinator_offset: 0 } => {
+ debug!(" > Invalidation matched completely");
+ invalidated_self = true;
+ }
+ CompoundSelectorMatchingResult::Matched { next_combinator_offset } => {
+ let next_combinator =
+ invalidation.selector.combinator_at(next_combinator_offset);
+
+ if matches!(next_combinator, Combinator::PseudoElement) {
+ let pseudo_selector =
+ invalidation.selector
+ .iter_raw_rev_from(next_combinator_offset - 1)
+ .next()
+ .unwrap();
+ let pseudo = match *pseudo_selector {
+ Component::PseudoElement(ref pseudo) => pseudo,
+ _ => unreachable!("Someone seriously messed up selector parsing"),
+ };
+
+ // FIXME(emilio): This is not ideal, and could not be
+ // accurate if we ever have stateful element-backed eager
+ // pseudos.
+ //
+ // Ideally, we'd just remove element-backed eager pseudos
+ // altogether, given they work fine without it. Only gotcha
+ // is that we wouldn't style them in parallel, which may or
+ // may not be an issue.
+ //
+ // Also, this could be more fine grained now (perhaps a
+ // RESTYLE_PSEUDOS hint?).
+ //
+ // Note that we'll also restyle the pseudo-element because
+ // it would match this invalidation.
+ if pseudo.is_eager() {
+ invalidated_self = true;
+ }
+ }
+
+
+ let next_invalidation = Invalidation {
+ selector: invalidation.selector.clone(),
+ offset: next_combinator_offset,
+ };
+
+ debug!(" > Invalidation matched, next: {:?}, ({:?})",
+ next_invalidation, next_combinator);
+ if next_combinator.is_ancestor() {
+ descendant_invalidations.push(next_invalidation);
+ } else {
+ sibling_invalidations.push(next_invalidation);
+ }
+ }
+ CompoundSelectorMatchingResult::NotMatched => {}
+ }
+
+ if invalidated_self {
+ if let Some(ref mut data) = self.data {
+ data.restyle.hint.insert(RESTYLE_SELF);
+ }
+ }
+
+ // TODO(emilio): For pseudo-elements this should be mostly false, except
+ // for the weird pseudos in <input type="number">.
+ //
+ // We should be able to do better here!
+ let effective_for_next =
+ match invalidation.selector.combinator_at(invalidation.offset) {
+ Combinator::NextSibling |
+ Combinator::Child => false,
+ _ => true,
+ };
+
+ InvalidationResult {
+ invalidated_self: invalidated_self,
+ effective_for_next: effective_for_next,
+ }
+ }
+}
+
+struct InvalidationCollector<'a, 'b: 'a, E>
+ where E: TElement,
+{
+ element: E,
+ wrapper: ElementWrapper<'b, E>,
+ shared_context: &'a SharedStyleContext<'b>,
+ lookup_element: E,
+ removed_id: Option<&'a Atom>,
+ classes_removed: &'a SmallVec<[Atom; 8]>,
+ descendant_invalidations: &'a mut InvalidationVector,
+ sibling_invalidations: &'a mut InvalidationVector,
+ invalidates_self: bool,
+}
+
+impl<'a, 'b: 'a, E> InvalidationCollector<'a, 'b, E>
+ where E: TElement,
+{
+ fn collect_dependencies_in_map(
+ &mut self,
+ map: &SelectorMap<Dependency>,
+ ) {
+ map.lookup_with_additional(
+ self.lookup_element,
+ self.shared_context.quirks_mode,
+ self.removed_id,
+ self.classes_removed,
+ &mut |dependency| {
+ self.scan_dependency(
+ dependency,
+ /* is_visited_dependent = */ false
+ );
+ true
+ },
+ );
+ }
+ fn collect_state_dependencies(
+ &mut self,
+ map: &SelectorMap<StateDependency>,
+ state_changes: ElementState,
+ ) {
+ map.lookup_with_additional(
+ self.lookup_element,
+ self.shared_context.quirks_mode,
+ self.removed_id,
+ self.classes_removed,
+ &mut |dependency| {
+ if !dependency.state.intersects(state_changes) {
+ return true;
+ }
+ self.scan_dependency(
+ &dependency.dep,
+ dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE)
+ );
+ true
+ },
+ );
+ }
+
+ fn scan_dependency(
+ &mut self,
+ dependency: &Dependency,
+ is_visited_dependent: bool
+ ) {
+ debug!("TreeStyleInvalidator::scan_dependency({:?}, {:?}, {})",
+ self.element,
+ dependency,
+ is_visited_dependent);
+
+ if !self.dependency_may_be_relevant(dependency) {
+ return;
+ }
+
+ // TODO(emilio): Add a bloom filter here?
+ //
+ // If we decide to do so, we can't use the bloom filter for snapshots,
+ // given that arbitrary elements in the parent chain may have mutated
+ // their id's/classes, which means that they won't be in the filter, and
+ // as such we may fast-reject selectors incorrectly.
+ //
+ // We may be able to improve this if we record as we go down the tree
+ // whether any parent had a snapshot, and whether those snapshots were
+ // taken due to an element class/id change, but it's not clear it'd be
+ // worth it.
+ let mut now_context =
+ MatchingContext::new_for_visited(MatchingMode::Normal, None,
+ VisitedHandlingMode::AllLinksUnvisited,
+ self.shared_context.quirks_mode);
+ let mut then_context =
+ MatchingContext::new_for_visited(MatchingMode::Normal, None,
+ VisitedHandlingMode::AllLinksUnvisited,
+ self.shared_context.quirks_mode);
+
+ let matched_then =
+ matches_selector(&dependency.selector,
+ dependency.selector_offset,
+ &dependency.hashes,
+ &self.wrapper,
+ &mut then_context,
+ &mut |_, _| {});
+ let matches_now =
+ matches_selector(&dependency.selector,
+ dependency.selector_offset,
+ &dependency.hashes,
+ &self.element,
+ &mut now_context,
+ &mut |_, _| {});
+
+ // Check for mismatches in both the match result and also the status
+ // of whether a relevant link was found.
+ if matched_then != matches_now ||
+ then_context.relevant_link_found != now_context.relevant_link_found {
+ return self.note_dependency(dependency);
+ }
+
+ // If there is a relevant link, then we also matched in visited
+ // mode.
+ //
+ // Match again in this mode to ensure this also matches.
+ //
+ // Note that we never actually match directly against the element's true
+ // visited state at all, since that would expose us to timing attacks.
+ //
+ // The matching process only considers the relevant link state and
+ // visited handling mode when deciding if visited matches. Instead, we
+ // are rematching here in case there is some :visited selector whose
+ // matching result changed for some other state or attribute change of
+ // this element (for example, for things like [foo]:visited).
+ //
+ // NOTE: This thing is actually untested because testing it is flaky,
+ // see the tests that were added and then backed out in bug 1328509.
+ if is_visited_dependent && now_context.relevant_link_found {
+ then_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
+ let matched_then =
+ matches_selector(&dependency.selector,
+ dependency.selector_offset,
+ &dependency.hashes,
+ &self.wrapper,
+ &mut then_context,
+ &mut |_, _| {});
+
+ now_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
+ let matches_now =
+ matches_selector(&dependency.selector,
+ dependency.selector_offset,
+ &dependency.hashes,
+ &self.element,
+ &mut now_context,
+ &mut |_, _| {});
+ if matched_then != matches_now {
+ return self.note_dependency(dependency);
+ }
+ }
+ }
+
+ fn note_dependency(&mut self, dependency: &Dependency) {
+ if dependency.affects_self() {
+ self.invalidates_self = true;
+ }
+
+ if dependency.affects_descendants() {
+ debug_assert_ne!(dependency.selector_offset, 0);
+ debug_assert!(!dependency.affects_later_siblings());
+ self.descendant_invalidations.push(Invalidation {
+ selector: dependency.selector.clone(),
+ offset: dependency.selector_offset,
+ });
+ } else if dependency.affects_later_siblings() {
+ debug_assert_ne!(dependency.selector_offset, 0);
+ self.sibling_invalidations.push(Invalidation {
+ selector: dependency.selector.clone(),
+ offset: dependency.selector_offset,
+ });
+ }
+ }
+
+ /// Returns whether `dependency` may cause us to invalidate the style of
+ /// more elements than what we've already invalidated.
+ fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
+ if dependency.affects_descendants() || dependency.affects_later_siblings() {
+ return true;
+ }
+
+ debug_assert!(dependency.affects_self());
+ !self.invalidates_self
+ }
+}
diff --git a/components/style/invalidation/element/mod.rs b/components/style/invalidation/element/mod.rs
new file mode 100644
index 00000000000..61aae2ffcd2
--- /dev/null
+++ b/components/style/invalidation/element/mod.rs
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! Invalidation of element styles due to attribute or style changes.
+
+pub mod element_wrapper;
+pub mod invalidation_map;
+pub mod invalidator;
+pub mod restyle_hints;
diff --git a/components/style/invalidation/element/restyle_hints.rs b/components/style/invalidation/element/restyle_hints.rs
new file mode 100644
index 00000000000..4b968da031f
--- /dev/null
+++ b/components/style/invalidation/element/restyle_hints.rs
@@ -0,0 +1,243 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
+
+#[cfg(feature = "gecko")]
+use gecko_bindings::structs::nsRestyleHint;
+use traversal::TraversalFlags;
+
+bitflags! {
+ /// The kind of restyle we need to do for a given element.
+ pub flags RestyleHint: u8 {
+ /// Do a selector match of the element.
+ const RESTYLE_SELF = 1 << 0,
+
+ /// Do a selector match of the element's descendants.
+ const RESTYLE_DESCENDANTS = 1 << 1,
+
+ /// Recascade the current element.
+ const RECASCADE_SELF = 1 << 2,
+
+ /// Recascade all descendant elements.
+ const RECASCADE_DESCENDANTS = 1 << 3,
+
+ /// Replace the style data coming from CSS transitions without updating
+ /// any other style data. This hint is only processed in animation-only
+ /// traversal which is prior to normal traversal.
+ const RESTYLE_CSS_TRANSITIONS = 1 << 4,
+
+ /// Replace the style data coming from CSS animations without updating
+ /// any other style data. This hint is only processed in animation-only
+ /// traversal which is prior to normal traversal.
+ const RESTYLE_CSS_ANIMATIONS = 1 << 5,
+
+ /// Don't re-run selector-matching on the element, only the style
+ /// attribute has changed, and this change didn't have any other
+ /// dependencies.
+ const RESTYLE_STYLE_ATTRIBUTE = 1 << 6,
+
+ /// Replace the style data coming from SMIL animations without updating
+ /// any other style data. This hint is only processed in animation-only
+ /// traversal which is prior to normal traversal.
+ const RESTYLE_SMIL = 1 << 7,
+ }
+}
+
+impl RestyleHint {
+ /// Creates a new `RestyleHint` indicating that the current element and all
+ /// its descendants must be fully restyled.
+ pub fn restyle_subtree() -> Self {
+ RESTYLE_SELF | RESTYLE_DESCENDANTS
+ }
+
+ /// Creates a new `RestyleHint` indicating that the current element and all
+ /// its descendants must be recascaded.
+ pub fn recascade_subtree() -> Self {
+ RECASCADE_SELF | RECASCADE_DESCENDANTS
+ }
+
+ /// Returns whether this hint invalidates the element and all its
+ /// descendants.
+ pub fn contains_subtree(&self) -> bool {
+ self.contains(RESTYLE_SELF | RESTYLE_DESCENDANTS)
+ }
+
+ /// Returns whether we need to restyle this element.
+ pub fn has_self_invalidations(&self) -> bool {
+ self.intersects(RESTYLE_SELF | RECASCADE_SELF | Self::replacements())
+ }
+
+ /// Propagates this restyle hint to a child element.
+ pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
+ use std::mem;
+
+ // In the middle of an animation only restyle, we don't need to
+ // propagate any restyle hints, and we need to remove ourselves.
+ if traversal_flags.for_animation_only() {
+ self.remove_animation_hints();
+ return Self::empty();
+ }
+
+ debug_assert!(!self.has_animation_hint(),
+ "There should not be any animation restyle hints \
+ during normal traversal");
+
+ // Else we should clear ourselves, and return the propagated hint.
+ mem::replace(self, Self::empty())
+ .propagate_for_non_animation_restyle()
+ }
+
+ /// Returns a new `CascadeHint` appropriate for children of the current
+ /// element.
+ fn propagate_for_non_animation_restyle(&self) -> Self {
+ if self.contains(RESTYLE_DESCENDANTS) {
+ return Self::restyle_subtree()
+ }
+ if self.contains(RECASCADE_DESCENDANTS) {
+ return Self::recascade_subtree()
+ }
+ Self::empty()
+ }
+
+ /// Creates a new `RestyleHint` that indicates the element must be
+ /// recascaded.
+ pub fn recascade_self() -> Self {
+ RECASCADE_SELF
+ }
+
+ /// Returns a hint that contains all the replacement hints.
+ pub fn replacements() -> Self {
+ RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
+ }
+
+ /// The replacements for the animation cascade levels.
+ #[inline]
+ pub fn for_animations() -> Self {
+ RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
+ }
+
+ /// Returns whether the hint specifies that the currently element must be
+ /// recascaded.
+ pub fn has_recascade_self(&self) -> bool {
+ self.contains(RECASCADE_SELF)
+ }
+
+ /// Returns whether the hint specifies that an animation cascade level must
+ /// be replaced.
+ #[inline]
+ pub fn has_animation_hint(&self) -> bool {
+ self.intersects(Self::for_animations())
+ }
+
+ /// Returns whether the hint specifies some restyle work other than an
+ /// animation cascade level replacement.
+ #[inline]
+ pub fn has_non_animation_hint(&self) -> bool {
+ !(*self & !Self::for_animations()).is_empty()
+ }
+
+ /// Returns whether the hint specifies that selector matching must be re-run
+ /// for the element.
+ #[inline]
+ pub fn match_self(&self) -> bool {
+ self.intersects(RESTYLE_SELF)
+ }
+
+ /// Returns whether the hint specifies that some cascade levels must be
+ /// replaced.
+ #[inline]
+ pub fn has_replacements(&self) -> bool {
+ self.intersects(Self::replacements())
+ }
+
+ /// Removes all of the animation-related hints.
+ #[inline]
+ pub fn remove_animation_hints(&mut self) {
+ self.remove(Self::for_animations());
+
+ // While RECASCADE_SELF is not animation-specific, we only ever add and
+ // process it during traversal. If we are here, removing animation
+ // hints, then we are in an animation-only traversal, and we know that
+ // any RECASCADE_SELF flag must have been set due to changes in
+ // inherited values after restyling for animations, and thus we want to
+ // remove it so that we don't later try to restyle the element during a
+ // normal restyle. (We could have separate RECASCADE_SELF_NORMAL and
+ // RECASCADE_SELF_ANIMATIONS flags to make it clear, but this isn't
+ // currently necessary.)
+ self.remove(RECASCADE_SELF);
+ }
+}
+
+impl Default for RestyleHint {
+ fn default() -> Self {
+ Self::empty()
+ }
+}
+
+#[cfg(feature = "gecko")]
+impl From<nsRestyleHint> for RestyleHint {
+ fn from(raw: nsRestyleHint) -> Self {
+ use gecko_bindings::structs::nsRestyleHint_eRestyle_ForceDescendants as eRestyle_ForceDescendants;
+ use gecko_bindings::structs::nsRestyleHint_eRestyle_LaterSiblings as eRestyle_LaterSiblings;
+ use gecko_bindings::structs::nsRestyleHint_eRestyle_Self as eRestyle_Self;
+ use gecko_bindings::structs::nsRestyleHint_eRestyle_SomeDescendants as eRestyle_SomeDescendants;
+ use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree as eRestyle_Subtree;
+
+ let mut hint = RestyleHint::empty();
+
+ debug_assert!(raw.0 & eRestyle_LaterSiblings.0 == 0,
+ "Handle later siblings manually if necessary plz.");
+
+ if (raw.0 & (eRestyle_Self.0 | eRestyle_Subtree.0)) != 0 {
+ hint.insert(RESTYLE_SELF);
+ }
+
+ if (raw.0 & (eRestyle_Subtree.0 | eRestyle_SomeDescendants.0)) != 0 {
+ hint.insert(RESTYLE_DESCENDANTS);
+ }
+
+ if (raw.0 & eRestyle_ForceDescendants.0) != 0 {
+ hint.insert(RECASCADE_DESCENDANTS);
+ }
+
+ hint.insert(RestyleHint::from_bits_truncate(raw.0 as u8));
+
+ hint
+ }
+}
+
+#[cfg(feature = "servo")]
+impl ::heapsize::HeapSizeOf for RestyleHint {
+ fn heap_size_of_children(&self) -> usize { 0 }
+}
+
+/// Asserts that all replacement hints have a matching nsRestyleHint value.
+#[cfg(feature = "gecko")]
+#[inline]
+pub fn assert_restyle_hints_match() {
+ use gecko_bindings::structs;
+
+ macro_rules! check_restyle_hints {
+ ( $( $a:ident => $b:ident ),*, ) => {
+ if cfg!(debug_assertions) {
+ let mut replacements = RestyleHint::replacements();
+ $(
+ assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b));
+ replacements.remove($b);
+ )*
+ assert_eq!(replacements, RestyleHint::empty(),
+ "all RestyleHint replacement bits should have an \
+ assertion");
+ }
+ }
+ }
+
+ check_restyle_hints! {
+ nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
+ nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
+ nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
+ nsRestyleHint_eRestyle_StyleAttribute_Animations => RESTYLE_SMIL,
+ }
+}
diff --git a/components/style/invalidation/mod.rs b/components/style/invalidation/mod.rs
index 4b7f9c3cd65..211c39ad2b4 100644
--- a/components/style/invalidation/mod.rs
+++ b/components/style/invalidation/mod.rs
@@ -4,5 +4,6 @@
//! Different bits of code related to invalidating style.
+pub mod element;
pub mod media_queries;
pub mod stylesheets;
diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs
index 2991867cb3b..b28ae6fed87 100644
--- a/components/style/invalidation/stylesheets.rs
+++ b/components/style/invalidation/stylesheets.rs
@@ -8,10 +8,11 @@
#![deny(unsafe_code)]
use Atom;
-use data::StoredRestyleHint;
use dom::{TElement, TNode};
use fnv::FnvHashSet;
+use invalidation::element::restyle_hints::RestyleHint;
use selector_parser::SelectorImpl;
+use selectors::attr::CaseSensitivity;
use selectors::parser::{Component, Selector};
use shared_lock::SharedRwLockReadGuard;
use stylesheets::{CssRule, Stylesheet};
@@ -37,7 +38,7 @@ impl InvalidationScope {
{
match *self {
InvalidationScope::Class(ref class) => {
- element.has_class(class)
+ element.has_class(class, CaseSensitivity::CaseSensitive)
}
InvalidationScope::ID(ref id) => {
match element.get_id() {
@@ -115,12 +116,37 @@ impl StylesheetInvalidationSet {
where E: TElement,
{
if let Some(e) = document_element {
- self.process_invalidations_in_subtree(e);
+ self.process_invalidations(e);
}
self.invalid_scopes.clear();
self.fully_invalid = false;
}
+ fn process_invalidations<E>(&self, element: E) -> bool
+ where E: TElement,
+ {
+ {
+ let mut data = match element.mutate_data() {
+ Some(data) => data,
+ None => return false,
+ };
+
+ if self.fully_invalid {
+ debug!("process_invalidations: fully_invalid({:?})",
+ element);
+ data.restyle.hint.insert(RestyleHint::restyle_subtree());
+ return true;
+ }
+ }
+
+ if self.invalid_scopes.is_empty() {
+ debug!("process_invalidations: empty invalidation set");
+ return false;
+ }
+
+ self.process_invalidations_in_subtree(element)
+ }
+
/// Process style invalidations in a given subtree, that is, look for all
/// the relevant scopes in the subtree, and mark as dirty only the relevant
/// ones.
@@ -139,26 +165,17 @@ impl StylesheetInvalidationSet {
return false;
}
- if let Some(ref r) = data.get_restyle() {
- if r.hint.contains_subtree() {
- debug!("process_invalidations_in_subtree: {:?} was already invalid",
- element);
- return false;
- }
- }
-
- if self.fully_invalid {
- debug!("process_invalidations_in_subtree: fully_invalid({:?})",
+ if data.restyle.hint.contains_subtree() {
+ debug!("process_invalidations_in_subtree: {:?} was already invalid",
element);
- data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
- return true;
+ return false;
}
for scope in &self.invalid_scopes {
if scope.matches(element) {
debug!("process_invalidations_in_subtree: {:?} matched {:?}",
element, scope);
- data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
+ data.restyle.hint.insert(RestyleHint::restyle_subtree());
return true;
}
}
diff --git a/components/style/lib.rs b/components/style/lib.rs
index 0cde26b37f0..192ee3f242f 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -91,6 +91,7 @@ extern crate unicode_segmentation;
mod macros;
pub mod animation;
+pub mod applicable_declarations;
#[allow(missing_docs)] // TODO.
#[cfg(feature = "servo")] pub mod attr;
pub mod bezier;
@@ -116,7 +117,6 @@ pub mod matching;
pub mod media_queries;
pub mod parallel;
pub mod parser;
-pub mod restyle_hints;
pub mod rule_tree;
pub mod scoped_tls;
pub mod selector_map;
@@ -216,3 +216,21 @@ pub fn serialize_comma_separated_list<W, T>(dest: &mut W,
Ok(())
}
+
+#[cfg(feature = "gecko")] use gecko_string_cache::WeakAtom;
+#[cfg(feature = "servo")] use servo_atoms::Atom as WeakAtom;
+
+/// Extension methods for selectors::attr::CaseSensitivity
+pub trait CaseSensitivityExt {
+ /// Return whether two atoms compare equal according to this case sensitivity.
+ fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool;
+}
+
+impl CaseSensitivityExt for selectors::attr::CaseSensitivity {
+ fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool {
+ match self {
+ selectors::attr::CaseSensitivity::CaseSensitive => a == b,
+ selectors::attr::CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),
+ }
+ }
+}
diff --git a/components/style/logical_geometry.rs b/components/style/logical_geometry.rs
index bb7eb053b8e..999de01ea28 100644
--- a/components/style/logical_geometry.rs
+++ b/components/style/logical_geometry.rs
@@ -4,9 +4,8 @@
//! Geometry in flow-relative space.
-use euclid::{Point2D, Rect, Size2D};
+use euclid::{Point2D, Rect, Size2D, SideOffsets2D};
use euclid::num::Zero;
-use euclid::side_offsets::SideOffsets2D;
use std::cmp::{max, min};
use std::fmt::{self, Debug, Error, Formatter};
use std::ops::{Add, Sub};
diff --git a/components/style/matching.rs b/components/style/matching.rs
index 00eaba9b63e..a1cde2f85d5 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -7,35 +7,45 @@
#![allow(unsafe_code)]
#![deny(missing_docs)]
+use applicable_declarations::ApplicableDeclarationList;
use cascade_info::CascadeInfo;
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, RestyleData};
use dom::{TElement, TNode};
use font_metrics::FontMetricsProvider;
+use invalidation::element::restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS};
+use invalidation::element::restyle_hints::{RESTYLE_SMIL, RESTYLE_STYLE_ATTRIBUTE};
+use invalidation::element::restyle_hints::RestyleHint;
use log::LogLevel::Trace;
-use properties::{ALLOW_SET_ROOT_FONT_SIZE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
+use properties::{ALLOW_SET_ROOT_FONT_SIZE, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
use properties::{AnimationRules, CascadeFlags, ComputedValues};
use properties::{VISITED_DEPENDENT_ONLY, cascade};
use properties::longhands::display::computed_value as display;
-use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleReplacements};
-use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
use rule_tree::{CascadeLevel, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, StyleRelations};
use selectors::matching::{VisitedHandlingMode, AFFECTED_BY_PSEUDO_ELEMENTS};
use sharing::StyleSharingBehavior;
use stylearc::Arc;
-use stylist::{ApplicableDeclarationList, RuleInclusion};
-
-/// The way a style should be inherited.
-enum InheritMode {
+use stylist::RuleInclusion;
+
+/// Whether we are cascading for an eager pseudo-element or something else.
+///
+/// Controls where we inherit styles from, and whether display:contents is
+/// prohibited.
+#[derive(PartialEq, Copy, Clone)]
+enum CascadeTarget {
/// Inherit from the parent element, as normal CSS dictates, _or_ from the
/// closest non-Native Anonymous element in case this is Native Anonymous
- /// Content.
+ /// Content. display:contents is allowed.
Normal,
/// Inherit from the primary style, this is used while computing eager
/// pseudos, like ::before and ::after when we're traversing the parent.
- FromPrimaryStyle,
+ /// Also prohibits display:contents from having an effect.
+ ///
+ /// TODO(emilio) display:contents really should apply to ::before/::after.
+ /// https://github.com/w3c/csswg-drafts/issues/1345
+ EagerPseudo,
}
/// Represents the result of comparing an element's old and new style.
@@ -249,7 +259,7 @@ trait PrivateMatchMethods: TElement {
font_metrics_provider: &FontMetricsProvider,
rule_node: &StrongRuleNode,
primary_style: &ComputedStyle,
- inherit_mode: InheritMode,
+ cascade_target: CascadeTarget,
cascade_visited: CascadeVisitedMode,
visited_values_to_insert: Option<Arc<ComputedValues>>)
-> Arc<ComputedValues> {
@@ -261,15 +271,17 @@ trait PrivateMatchMethods: TElement {
if cascade_visited.visited_dependent_only() {
cascade_flags.insert(VISITED_DEPENDENT_ONLY);
}
- if !self.is_native_anonymous() {
+ if self.is_native_anonymous() || cascade_target == CascadeTarget::EagerPseudo {
+ cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
+ } else {
cascade_flags.insert(ALLOW_SET_ROOT_FONT_SIZE);
}
// Grab the inherited values.
let parent_el;
let parent_data;
- let style_to_inherit_from = match inherit_mode {
- InheritMode::Normal => {
+ let style_to_inherit_from = match cascade_target {
+ CascadeTarget::Normal => {
parent_el = self.inheritance_parent();
parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
let parent_style = parent_data.as_ref().map(|d| {
@@ -285,7 +297,7 @@ trait PrivateMatchMethods: TElement {
});
parent_style.map(|s| cascade_visited.values(s))
}
- InheritMode::FromPrimaryStyle => {
+ CascadeTarget::EagerPseudo => {
parent_el = Some(self.clone());
Some(cascade_visited.values(primary_style))
}
@@ -388,17 +400,17 @@ trait PrivateMatchMethods: TElement {
// Grab the rule node.
let style = eager_pseudo_style.unwrap_or(primary_style);
let rule_node = cascade_visited.rules(style);
- let inherit_mode = if eager_pseudo_style.is_some() {
- InheritMode::FromPrimaryStyle
+ let cascade_target = if eager_pseudo_style.is_some() {
+ CascadeTarget::EagerPseudo
} else {
- InheritMode::Normal
+ CascadeTarget::Normal
};
self.cascade_with_rules(context.shared,
&context.thread_local.font_metrics_provider,
rule_node,
primary_style,
- inherit_mode,
+ cascade_target,
cascade_visited,
visited_values_to_insert)
}
@@ -534,7 +546,7 @@ trait PrivateMatchMethods: TElement {
&context.thread_local.font_metrics_provider,
&without_transition_rules,
primary_style,
- InheritMode::Normal,
+ CascadeTarget::Normal,
CascadeVisitedMode::Unvisited,
None))
}
@@ -695,8 +707,7 @@ trait PrivateMatchMethods: TElement {
// for followup work to make the optimization here more optimal by considering
// each bit individually.
let skip_applying_damage =
- restyle.damage_handled.contains(RestyleDamage::reconstruct()) ||
- restyle.damage.contains(RestyleDamage::reconstruct());
+ restyle.reconstructed_self_or_ancestor();
let difference = self.compute_style_difference(&old_values,
&new_values,
@@ -989,6 +1000,10 @@ pub trait MatchMethods : TElement {
&context.shared.guards);
let rules_changed = match visited_handling {
+ VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
+ unreachable!("We should never try to selector match with \
+ AllLinksVisitedAndUnvisited");
+ },
VisitedHandlingMode::AllLinksUnvisited => {
data.set_primary_rules(rules)
},
@@ -1068,6 +1083,10 @@ pub trait MatchMethods : TElement {
);
let rules_changed = match visited_handling {
+ VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
+ unreachable!("We should never try to selector match with \
+ AllLinksVisitedAndUnvisited");
+ },
VisitedHandlingMode::AllLinksUnvisited => {
data.set_primary_rules(primary_rule_node)
},
@@ -1167,12 +1186,10 @@ pub trait MatchMethods : TElement {
}
});
- if matches_different_pseudos {
- if let Some(r) = data.get_restyle_mut() {
- // Any changes to the matched pseudo-elements trigger
- // reconstruction.
- r.damage |= RestyleDamage::reconstruct();
- }
+ if matches_different_pseudos && data.restyle.is_restyle() {
+ // Any changes to the matched pseudo-elements trigger
+ // reconstruction.
+ data.restyle.damage |= RestyleDamage::reconstruct();
}
}
@@ -1237,16 +1254,11 @@ pub trait MatchMethods : TElement {
/// Computes and applies restyle damage.
fn accumulate_damage(&self,
shared_context: &SharedStyleContext,
- restyle: Option<&mut RestyleData>,
+ restyle: &mut RestyleData,
old_values: Option<&ComputedValues>,
new_values: &Arc<ComputedValues>,
pseudo: Option<&PseudoElement>)
-> ChildCascadeRequirement {
- let restyle = match restyle {
- Some(r) => r,
- None => return ChildCascadeRequirement::MustCascadeChildren,
- };
-
let old_values = match old_values {
Some(v) => v,
None => return ChildCascadeRequirement::MustCascadeChildren,
@@ -1275,11 +1287,12 @@ pub trait MatchMethods : TElement {
/// the rule tree.
///
/// Returns true if an !important rule was replaced.
- fn replace_rules(&self,
- replacements: RestyleReplacements,
- context: &StyleContext<Self>,
- data: &mut ElementData)
- -> bool {
+ fn replace_rules(
+ &self,
+ replacements: RestyleHint,
+ context: &StyleContext<Self>,
+ data: &mut ElementData
+ ) -> bool {
let mut result = false;
result |= self.replace_rules_internal(replacements, context, data,
CascadeVisitedMode::Unvisited);
@@ -1294,15 +1307,19 @@ pub trait MatchMethods : TElement {
/// the rule tree, for a specific visited mode.
///
/// Returns true if an !important rule was replaced.
- fn replace_rules_internal(&self,
- replacements: RestyleReplacements,
- context: &StyleContext<Self>,
- data: &mut ElementData,
- cascade_visited: CascadeVisitedMode)
- -> bool {
+ fn replace_rules_internal(
+ &self,
+ replacements: RestyleHint,
+ context: &StyleContext<Self>,
+ data: &mut ElementData,
+ cascade_visited: CascadeVisitedMode
+ ) -> bool {
use properties::PropertyDeclarationBlock;
use shared_lock::Locked;
+ debug_assert!(replacements.intersects(RestyleHint::replacements()) &&
+ (replacements & !RestyleHint::replacements()).is_empty());
+
let element_styles = &mut data.styles_mut();
let primary_rules = match cascade_visited.get_rules_mut(&mut element_styles.primary) {
Some(r) => r,
@@ -1343,7 +1360,7 @@ pub trait MatchMethods : TElement {
//
// Non-animation restyle hints will be processed in a subsequent
// normal traversal.
- if replacements.intersects(RestyleReplacements::for_animations()) {
+ if replacements.intersects(RestyleHint::for_animations()) {
debug_assert!(context.shared.traversal_flags.for_animation_only());
if replacements.contains(RESTYLE_SMIL) {
@@ -1465,7 +1482,7 @@ pub trait MatchMethods : TElement {
font_metrics_provider,
&without_animation_rules,
primary_style,
- InheritMode::Normal,
+ CascadeTarget::Normal,
CascadeVisitedMode::Unvisited,
None)
}
diff --git a/components/style/parallel.rs b/components/style/parallel.rs
index ea39a0c15d7..79126f03463 100644
--- a/components/style/parallel.rs
+++ b/components/style/parallel.rs
@@ -22,6 +22,7 @@
#![deny(missing_docs)]
+use arrayvec::ArrayVec;
use context::TraversalStatistics;
use dom::{OpaqueNode, SendNode, TElement, TNode};
use rayon;
@@ -39,24 +40,25 @@ use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
/// been measured and could potentially be tuned.
pub const WORK_UNIT_MAX: usize = 16;
-/// A list of node pointers.
-///
-/// Note that the inline storage doesn't need to be sized to WORK_UNIT_MAX, but
-/// it generally seems sensible to do so.
-type NodeList<N> = SmallVec<[SendNode<N>; WORK_UNIT_MAX]>;
+/// A set of nodes, sized to the work unit. This gets copied when sent to other
+/// threads, so we keep it compact.
+type WorkUnit<N> = ArrayVec<[SendNode<N>; WORK_UNIT_MAX]>;
/// Entry point for the parallel traversal.
#[allow(unsafe_code)]
pub fn traverse_dom<E, D>(traversal: &D,
root: E,
token: PreTraverseToken,
- queue: &rayon::ThreadPool)
+ pool: &rayon::ThreadPool)
where E: TElement,
D: DomTraversal<E>,
{
let dump_stats = traversal.shared_context().options.dump_style_statistics;
let start_time = if dump_stats { Some(time::precise_time_s()) } else { None };
- let mut nodes = NodeList::<E::ConcreteNode>::new();
+
+ // Set up the SmallVec. We need to move this, and in most cases this is just
+ // one node, so keep it small.
+ let mut nodes = SmallVec::<[SendNode<E::ConcreteNode>; 8]>::new();
debug_assert!(traversal.is_parallel());
// Handle Gecko's eager initial styling. We don't currently support it
@@ -82,16 +84,18 @@ pub fn traverse_dom<E, D>(traversal: &D,
let traversal_data = PerLevelTraversalData {
current_dom_depth: depth,
};
- let tls = ScopedTLS::<D::ThreadLocalContext>::new(queue);
+ let tls = ScopedTLS::<D::ThreadLocalContext>::new(pool);
let root = root.as_node().opaque();
- queue.install(|| {
+ pool.install(|| {
rayon::scope(|scope| {
- traverse_nodes(nodes,
+ let nodes = nodes;
+ traverse_nodes(&*nodes,
DispatchMode::TailCall,
root,
traversal_data,
scope,
+ pool,
traversal,
&tls);
});
@@ -144,13 +148,19 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
root: OpaqueNode,
mut traversal_data: PerLevelTraversalData,
scope: &'a rayon::Scope<'scope>,
+ pool: &'scope rayon::ThreadPool,
traversal: &'scope D,
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
where E: TElement + 'scope,
D: DomTraversal<E>,
{
debug_assert!(nodes.len() <= WORK_UNIT_MAX);
- let mut discovered_child_nodes = NodeList::<E::ConcreteNode>::new();
+
+ // Collect all the children of the elements in our work unit. This will
+ // contain the combined children of up to WORK_UNIT_MAX nodes, which may
+ // be numerous. As such, we store it in a large SmallVec to minimize heap-
+ // spilling, and never move it.
+ let mut discovered_child_nodes = SmallVec::<[SendNode<E::ConcreteNode>; 128]>::new();
{
// Scope the borrow of the TLS so that the borrow is dropped before
// a potential recursive call when we pass TailCall.
@@ -174,11 +184,12 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
let children = mem::replace(&mut discovered_child_nodes, Default::default());
let mut traversal_data_copy = traversal_data.clone();
traversal_data_copy.current_dom_depth += 1;
- traverse_nodes(children,
+ traverse_nodes(&*children,
DispatchMode::NotTailCall,
root,
traversal_data_copy,
scope,
+ pool,
traversal,
tls);
}
@@ -203,11 +214,12 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
// on this thread by passing TailCall.
if !discovered_child_nodes.is_empty() {
traversal_data.current_dom_depth += 1;
- traverse_nodes(discovered_child_nodes,
+ traverse_nodes(&discovered_child_nodes,
DispatchMode::TailCall,
root,
traversal_data,
scope,
+ pool,
traversal,
tls);
}
@@ -226,11 +238,12 @@ impl DispatchMode {
}
#[inline]
-fn traverse_nodes<'a, 'scope, E, D>(nodes: NodeList<E::ConcreteNode>,
+fn traverse_nodes<'a, 'scope, E, D>(nodes: &[SendNode<E::ConcreteNode>],
mode: DispatchMode,
root: OpaqueNode,
traversal_data: PerLevelTraversalData,
scope: &'a rayon::Scope<'scope>,
+ pool: &'scope rayon::ThreadPool,
traversal: &'scope D,
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>)
where E: TElement + 'scope,
@@ -238,42 +251,33 @@ fn traverse_nodes<'a, 'scope, E, D>(nodes: NodeList<E::ConcreteNode>,
{
debug_assert!(!nodes.is_empty());
+ // This is a tail call from the perspective of the caller. However, we only
+ // want to actually dispatch the job as a tail call if there's nothing left
+ // in our local queue. Otherwise we need to return to it to maintain proper
+ // breadth-first ordering.
+ let may_dispatch_tail = mode.is_tail_call() &&
+ !pool.current_thread_has_pending_tasks().unwrap();
+
// In the common case, our children fit within a single work unit, in which
// case we can pass the SmallVec directly and avoid extra allocation.
if nodes.len() <= WORK_UNIT_MAX {
- if mode.is_tail_call() {
- // If this is a tail call, bypass rayon and invoke top_down_dom directly.
- top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
+ let work = nodes.iter().cloned().collect::<WorkUnit<E::ConcreteNode>>();
+ if may_dispatch_tail {
+ top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
} else {
- // The caller isn't done yet. Append to the queue and return synchronously.
scope.spawn(move |scope| {
- let nodes = nodes;
- top_down_dom(&nodes, root, traversal_data, scope, traversal, tls);
+ let work = work;
+ top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
});
}
} else {
- // FIXME(bholley): This should be an ArrayVec.
- let mut first_chunk: Option<NodeList<E::ConcreteNode>> = None;
for chunk in nodes.chunks(WORK_UNIT_MAX) {
- if mode.is_tail_call() && first_chunk.is_none() {
- first_chunk = Some(chunk.iter().cloned().collect::<NodeList<E::ConcreteNode>>());
- } else {
- let boxed = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
- let traversal_data_copy = traversal_data.clone();
- scope.spawn(move |scope| {
- let b = boxed;
- top_down_dom(&*b, root, traversal_data_copy, scope, traversal, tls)
- });
-
- }
- }
-
- // If this is a tail call, bypass rayon for the first chunk and invoke top_down_dom
- // directly.
- debug_assert_eq!(first_chunk.is_some(), mode.is_tail_call());
- if let Some(c) = first_chunk {
- debug_assert_eq!(c.len(), WORK_UNIT_MAX);
- top_down_dom(&*c, root, traversal_data, scope, traversal, tls);
+ let nodes = chunk.iter().cloned().collect::<WorkUnit<E::ConcreteNode>>();
+ let traversal_data_copy = traversal_data.clone();
+ scope.spawn(move |scope| {
+ let n = nodes;
+ top_down_dom(&*n, root, traversal_data_copy, scope, pool, traversal, tls)
+ });
}
}
}
diff --git a/components/style/parser.rs b/components/style/parser.rs
index 420572042da..0145c128eca 100644
--- a/components/style/parser.rs
+++ b/components/style/parser.rs
@@ -7,37 +7,11 @@
use context::QuirksMode;
use cssparser::{Parser, SourcePosition, UnicodeRange};
use error_reporting::{ParseErrorReporter, ContextualParseError};
-use style_traits::{OneOrMoreCommaSeparated, ParseError};
+use style_traits::{OneOrMoreCommaSeparated, ParseError, ParsingMode};
+#[cfg(feature = "gecko")]
+use style_traits::{PARSING_MODE_DEFAULT, PARSING_MODE_ALLOW_UNITLESS_LENGTH, PARSING_MODE_ALLOW_ALL_NUMERIC_VALUES};
use stylesheets::{CssRuleType, Origin, UrlExtraData, Namespaces};
-bitflags! {
- /// The mode to use when parsing values.
- pub flags ParsingMode: u8 {
- /// In CSS, lengths must have units, except for zero values, where the unit can be omitted.
- /// https://www.w3.org/TR/css3-values/#lengths
- const PARSING_MODE_DEFAULT = 0x00,
- /// In SVG, a coordinate or length value without a unit identifier (e.g., "25") is assumed
- /// to be in user units (px).
- /// https://www.w3.org/TR/SVG/coords.html#Units
- const PARSING_MODE_ALLOW_UNITLESS_LENGTH = 0x01,
- /// In SVG, out-of-range values are not treated as an error in parsing.
- /// https://www.w3.org/TR/SVG/implnote.html#RangeClamping
- const PARSING_MODE_ALLOW_ALL_NUMERIC_VALUES = 0x02,
- }
-}
-
-impl ParsingMode {
- /// Whether the parsing mode allows unitless lengths for non-zero values to be intpreted as px.
- pub fn allows_unitless_lengths(&self) -> bool {
- self.intersects(PARSING_MODE_ALLOW_UNITLESS_LENGTH)
- }
-
- /// Whether the parsing mode allows all numeric values.
- pub fn allows_all_numeric_values(&self) -> bool {
- self.intersects(PARSING_MODE_ALLOW_ALL_NUMERIC_VALUES)
- }
-}
-
/// Asserts that all ParsingMode flags have a matching ParsingMode value in gecko.
#[cfg(feature = "gecko")]
#[inline]
diff --git a/components/style/properties/data.py b/components/style/properties/data.py
index 15d9f2fc89a..c0510d0c13f 100644
--- a/components/style/properties/data.py
+++ b/components/style/properties/data.py
@@ -194,12 +194,15 @@ class Longhand(object):
self.animation_value_type = animation_value_type
self.animatable = animation_value_type != "none"
+ self.transitionable = animation_value_type != "none" \
+ and animation_value_type != "discrete"
self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \
or animation_value_type == "discrete"
if self.logical:
# Logical properties will be animatable (i.e. the animation type is
# discrete). For now, it is still non-animatable.
self.animatable = False
+ self.transitionable = False
self.animation_type = None
# NB: Animatable implies clone because a property animation requires a
# copy of the computed value.
@@ -234,6 +237,25 @@ class Shorthand(object):
self.allowed_in_keyframe_block = allowed_in_keyframe_block \
and allowed_in_keyframe_block != "False"
+ def get_animatable(self):
+ animatable = False
+ for sub in self.sub_properties:
+ if sub.animatable:
+ animatable = True
+ break
+ return animatable
+
+ def get_transitionable(self):
+ transitionable = False
+ for sub in self.sub_properties:
+ if sub.transitionable:
+ transitionable = True
+ break
+ return transitionable
+
+ animatable = property(get_animatable)
+ transitionable = property(get_transitionable)
+
class Method(object):
def __init__(self, name, return_type=None, arg_types=None, is_mut=False):
diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs
index 3c36521ba61..bcbb26771a9 100644
--- a/components/style/properties/declaration_block.rs
+++ b/components/style/properties/declaration_block.rs
@@ -10,13 +10,14 @@ use context::QuirksMode;
use cssparser::{DeclarationListParser, parse_important, ParserInput};
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter};
use error_reporting::{ParseErrorReporter, ContextualParseError};
-use parser::{PARSING_MODE_DEFAULT, ParsingMode, ParserContext, log_css_error};
+use parser::{ParserContext, log_css_error};
use properties::animated_properties::AnimationValue;
use selectors::parser::SelectorParseError;
use shared_lock::Locked;
+use smallvec::SmallVec;
use std::fmt;
use std::slice::Iter;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, ParsingMode, StyleParseError};
use stylesheets::{CssRuleType, Origin, UrlExtraData};
use stylesheets::{MallocSizeOf, MallocSizeOfFn};
use super::*;
@@ -125,7 +126,7 @@ impl<'a, 'cx, 'cx_a:'cx> AnimationValueIterator<'a, 'cx, 'cx_a> {
}
impl<'a, 'cx, 'cx_a:'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {
- type Item = (TransitionProperty, AnimationValue);
+ type Item = (AnimatableLonghand, AnimationValue);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
use properties::Importance;
@@ -135,11 +136,11 @@ impl<'a, 'cx, 'cx_a:'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {
match next {
Some(&(ref decl, importance)) => {
if importance == Importance::Normal {
- let property = TransitionProperty::from_declaration(decl);
+ let property = AnimatableLonghand::from_declaration(decl);
let animation = AnimationValue::from_declaration(decl, &mut self.context,
self.default_values);
debug_assert!(property.is_none() == animation.is_none(),
- "The failure condition of TransitionProperty::from_declaration \
+ "The failure condition of AnimatableLonghand::from_declaration \
and AnimationValue::from_declaration should be the same");
// Skip the property if either ::from_declaration fails.
match (property, animation) {
@@ -200,7 +201,7 @@ impl PropertyDeclarationBlock {
}
}
- /// Return an iterator of (TransitionProperty, AnimationValue).
+ /// Return an iterator of (AnimatableLonghand, AnimationValue).
pub fn to_animation_value_iter<'a, 'cx, 'cx_a:'cx>(&'a self,
context: &'cx mut Context<'cx_a>,
default_values: &'a Arc<ComputedValues>)
@@ -553,7 +554,7 @@ impl PropertyDeclarationBlock {
let mut longhands = LonghandIdSet::new();
for (property, animation_value) in animation_value_map.iter() {
- longhands.set_transition_property_bit(property);
+ longhands.set_animatable_longhand_bit(property);
declarations.push((animation_value.uncompute(), Importance::Normal));
}
@@ -590,7 +591,7 @@ impl ToCss for PropertyDeclarationBlock {
// Step 1 -> dest = result list
// Step 2
- let mut already_serialized = Vec::new();
+ let mut already_serialized = PropertyDeclarationIdSet::new();
// Step 3
for &(ref declaration, importance) in &*self.declarations {
@@ -598,29 +599,38 @@ impl ToCss for PropertyDeclarationBlock {
let property = declaration.id();
// Step 3.2
- if already_serialized.contains(&property) {
+ if already_serialized.contains(property) {
continue;
}
// Step 3.3
let shorthands = declaration.shorthands();
if !shorthands.is_empty() {
- // Step 3.3.1
- let mut longhands = self.declarations.iter()
- .filter(|d| !already_serialized.contains(&d.0.id()))
- .collect::<Vec<_>>();
+ // Step 3.3.1 is done by checking already_serialized while
+ // iterating below.
// Step 3.3.2
for &shorthand in shorthands {
let properties = shorthand.longhands();
// Substep 2 & 3
- let mut current_longhands = Vec::new();
+ let mut current_longhands = SmallVec::<[_; 10]>::new();
let mut important_count = 0;
let mut found_system = None;
- if shorthand == ShorthandId::Font && longhands.iter().any(|&&(ref l, _)| l.get_system().is_some()) {
- for &&(ref longhand, longhand_importance) in longhands.iter() {
+ let is_system_font =
+ shorthand == ShorthandId::Font &&
+ self.declarations.iter().any(|&(ref l, _)| {
+ !already_serialized.contains(l.id()) &&
+ l.get_system().is_some()
+ });
+
+ if is_system_font {
+ for &(ref longhand, longhand_importance) in &self.declarations {
+ if already_serialized.contains(longhand.id()) {
+ continue;
+ }
+
if longhand.get_system().is_some() || longhand.is_default_line_height() {
current_longhands.push(longhand);
if found_system.is_none() {
@@ -632,7 +642,11 @@ impl ToCss for PropertyDeclarationBlock {
}
}
} else {
- for &&(ref longhand, longhand_importance) in longhands.iter() {
+ for &(ref longhand, longhand_importance) in &self.declarations {
+ if already_serialized.contains(longhand.id()) {
+ continue;
+ }
+
if longhand.id().is_longhand_of(shorthand) {
current_longhands.push(longhand);
if longhand_importance.important() {
@@ -642,10 +656,10 @@ impl ToCss for PropertyDeclarationBlock {
}
// Substep 1:
//
- // Assuming that the PropertyDeclarationBlock contains no
- // duplicate entries, if the current_longhands length is
- // equal to the properties length, it means that the
- // properties that map to shorthand are present in longhands
+ // Assuming that the PropertyDeclarationBlock contains no
+ // duplicate entries, if the current_longhands length is
+ // equal to the properties length, it means that the
+ // properties that map to shorthand are present in longhands
if current_longhands.len() != properties.len() {
continue;
}
@@ -725,18 +739,13 @@ impl ToCss for PropertyDeclarationBlock {
for current_longhand in &current_longhands {
// Substep 9
- already_serialized.push(current_longhand.id());
- let index_to_remove = longhands.iter().position(|l| l.0 == **current_longhand);
- if let Some(index) = index_to_remove {
- // Substep 10
- longhands.remove(index);
- }
+ already_serialized.insert(current_longhand.id());
}
}
}
// Step 3.3.4
- if already_serialized.contains(&property) {
+ if already_serialized.contains(property) {
continue;
}
@@ -756,7 +765,7 @@ impl ToCss for PropertyDeclarationBlock {
&mut is_first_serialization)?;
// Step 3.3.8
- already_serialized.push(property);
+ already_serialized.insert(property);
}
// Step 4
diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs
index 847b189bf25..b0953f9a1d9 100644
--- a/components/style/properties/gecko.mako.rs
+++ b/components/style/properties/gecko.mako.rs
@@ -63,8 +63,9 @@ use std::mem::{forget, transmute, zeroed};
use std::ptr;
use stylearc::Arc;
use std::cmp;
+use values::{Auto, CustomIdent, Either, KeyframesName};
use values::computed::{Shadow, ToComputedValue};
-use values::{Either, Auto, KeyframesName};
+use values::specified::length::Percentage;
use computed_values::border_style;
pub mod style_structs {
@@ -1190,8 +1191,8 @@ fn static_assert() {
pub fn set_${value.name}(&mut self, v: longhands::${value.name}::computed_value::T) {
use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine};
- let ident = v.ident.unwrap_or(String::new());
- self.gecko.${value.gecko}.mLineName.assign_utf8(&ident);
+ let ident = v.ident.as_ref().map_or(&[] as &[_], |ident| ident.0.as_slice());
+ self.gecko.${value.gecko}.mLineName.assign(ident);
self.gecko.${value.gecko}.mHasSpan = v.is_span;
self.gecko.${value.gecko}.mInteger = v.line_num.map(|i| {
// clamping the integer between a range
@@ -2430,8 +2431,8 @@ fn static_assert() {
self.gecko.mTransitionPropertyCount = v.len() as u32;
for (servo, gecko) in v.zip(self.gecko.mTransitions.iter_mut()) {
match servo {
- TransitionProperty::Unsupported(ref atom) => unsafe {
- Gecko_StyleTransition_SetUnsupportedProperty(gecko, atom.as_ptr())
+ TransitionProperty::Unsupported(ref ident) => unsafe {
+ Gecko_StyleTransition_SetUnsupportedProperty(gecko, ident.0.as_ptr())
},
_ => gecko.mProperty = (&servo).into(),
}
@@ -2466,11 +2467,11 @@ fn static_assert() {
if property == eCSSProperty_UNKNOWN || property == eCSSPropertyExtra_variable {
let atom = self.gecko.mTransitions[index].mUnknownProperty.raw::<nsIAtom>();
debug_assert!(!atom.is_null());
- TransitionProperty::Unsupported(atom.into())
+ TransitionProperty::Unsupported(CustomIdent(atom.into()))
} else if property == eCSSPropertyExtra_no_properties {
// Actually, we don't expect TransitionProperty::Unsupported also represents "none",
// but if the caller wants to convert it, it is fine. Please use it carefully.
- TransitionProperty::Unsupported(atom!("none"))
+ TransitionProperty::Unsupported(CustomIdent(atom!("none")))
} else {
property.into()
}
@@ -2683,19 +2684,19 @@ fn static_assert() {
}
for feature in features.iter() {
- if feature == &atom!("scroll-position") {
+ if feature.0 == atom!("scroll-position") {
self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL as u8;
- } else if feature == &atom!("opacity") {
+ } else if feature.0 == atom!("opacity") {
self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY as u8;
- } else if feature == &atom!("transform") {
+ } else if feature.0 == atom!("transform") {
self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM as u8;
}
unsafe {
- Gecko_AppendWillChange(&mut self.gecko, feature.as_ptr());
+ Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr());
}
- if let Ok(prop_id) = PropertyId::parse(feature.to_string().into()) {
+ if let Ok(prop_id) = PropertyId::parse(feature.0.to_string().into()) {
match prop_id.as_shorthand() {
Ok(shorthand) => {
for longhand in shorthand.longhands() {
@@ -3202,12 +3203,12 @@ fn static_assert() {
unsafe { Gecko_CopyListStyleImageFrom(&mut self.gecko, &other.gecko); }
}
- pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
+ pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T, device: &Device) {
use gecko_bindings::bindings::Gecko_SetCounterStyleToString;
use nsstring::{nsACString, nsCString};
use self::longhands::list_style_type::computed_value::T;
match v {
- T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle),
+ T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle, device),
T::String(s) => unsafe {
Gecko_SetCounterStyleToString(&mut self.gecko.mCounterStyle,
&nsCString::from(s) as &nsACString)
@@ -4045,7 +4046,7 @@ clip-path
CoordDataValue::Coord(coord) =>
vec.push(Either::Second(LengthOrPercentage::Length(Au(coord)))),
CoordDataValue::Percent(p) =>
- vec.push(Either::Second(LengthOrPercentage::Percentage(p))),
+ vec.push(Either::Second(LengthOrPercentage::Percentage(Percentage(p)))),
CoordDataValue::Calc(calc) =>
vec.push(Either::Second(LengthOrPercentage::Calc(calc.into()))),
_ => unreachable!(),
@@ -4068,7 +4069,7 @@ clip-path
match self.gecko.mStrokeDashoffset.as_value() {
CoordDataValue::Factor(number) => Either::First(number),
CoordDataValue::Coord(coord) => Either::Second(LengthOrPercentage::Length(Au(coord))),
- CoordDataValue::Percent(p) => Either::Second(LengthOrPercentage::Percentage(p)),
+ CoordDataValue::Percent(p) => Either::Second(LengthOrPercentage::Percentage(Percentage(p))),
CoordDataValue::Calc(calc) => Either::Second(LengthOrPercentage::Calc(calc.into())),
_ => unreachable!(),
}
@@ -4088,7 +4089,7 @@ clip-path
match self.gecko.mStrokeWidth.as_value() {
CoordDataValue::Factor(number) => Either::First(number),
CoordDataValue::Coord(coord) => Either::Second(LengthOrPercentage::Length(Au(coord))),
- CoordDataValue::Percent(p) => Either::Second(LengthOrPercentage::Percentage(p)),
+ CoordDataValue::Percent(p) => Either::Second(LengthOrPercentage::Percentage(Percentage(p))),
CoordDataValue::Calc(calc) => Either::Second(LengthOrPercentage::Calc(calc.into())),
_ => unreachable!(),
}
@@ -4254,7 +4255,7 @@ clip-path
self.gecko.mContents.is_empty()
}
- pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
+ pub fn set_content(&mut self, v: longhands::content::computed_value::T, device: &Device) {
use properties::longhands::content::computed_value::T;
use properties::longhands::content::computed_value::ContentItem;
use values::generics::CounterStyleOrNone;
@@ -4275,7 +4276,8 @@ clip-path
fn set_counter_function(data: &mut nsStyleContentData,
content_type: nsStyleContentType,
- name: &str, sep: &str, style: CounterStyleOrNone) {
+ name: &str, sep: &str,
+ style: CounterStyleOrNone, device: &Device) {
debug_assert!(content_type == eStyleContentType_Counter ||
content_type == eStyleContentType_Counters);
let counter_func = unsafe {
@@ -4285,7 +4287,7 @@ clip-path
if content_type == eStyleContentType_Counters {
counter_func.mSeparator.assign_utf8(sep);
}
- style.to_gecko_value(&mut counter_func.mCounterStyle);
+ style.to_gecko_value(&mut counter_func.mCounterStyle, device);
}
match v {
@@ -4349,11 +4351,11 @@ clip-path
=> self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote,
ContentItem::Counter(name, style) => {
set_counter_function(&mut self.gecko.mContents[i],
- eStyleContentType_Counter, &name, "", style);
+ eStyleContentType_Counter, &name, "", style, device);
}
ContentItem::Counters(name, sep, style) => {
set_counter_function(&mut self.gecko.mContents[i],
- eStyleContentType_Counters, &name, &sep, style);
+ eStyleContentType_Counters, &name, &sep, style, device);
}
ContentItem::Url(ref url) => {
unsafe {
diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs
index c8502551a0e..f2c0e818b37 100644
--- a/components/style/properties/helpers.mako.rs
+++ b/components/style/properties/helpers.mako.rs
@@ -344,6 +344,8 @@
<%
maybe_wm = ", wm" if property.logical else ""
maybe_cacheable = ", cacheable" if property.has_uncacheable_values == "True" else ""
+ props_need_device = "content list_style_type".split() if product == "gecko" else []
+ maybe_device = ", context.device" if property.ident in props_need_device else ""
%>
match *value {
DeclaredValue::Value(ref specified_value) => {
@@ -375,7 +377,8 @@
inherited_style.get_font());
% else:
context.mutate_style().mutate_${data.current_style_struct.name_lower}()
- .set_${property.ident}(computed ${maybe_cacheable} ${maybe_wm});
+ .set_${property.ident}(computed ${maybe_device}
+ ${maybe_cacheable} ${maybe_wm});
% endif
% endif
}
diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs
index f89bd2ef2db..a14b5f78cdd 100644
--- a/components/style/properties/helpers/animated_properties.mako.rs
+++ b/components/style/properties/helpers/animated_properties.mako.rs
@@ -7,7 +7,7 @@
<% from data import SYSTEM_FONT_LONGHANDS %>
use app_units::Au;
-use cssparser::{Parser, RGBA, serialize_identifier};
+use cssparser::{Parser, RGBA};
use euclid::{Point2D, Size2D};
#[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
#[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
@@ -28,15 +28,12 @@ use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
use properties::longhands::visibility::computed_value::T as Visibility;
#[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId};
use selectors::parser::SelectorParseError;
-#[cfg(feature = "servo")] use servo_atoms::Atom;
use smallvec::SmallVec;
use std::cmp;
#[cfg(feature = "gecko")] use std::collections::HashMap;
-use std::fmt;
-use style_traits::{ToCss, ParseError};
+use style_traits::ParseError;
use super::ComputedValues;
-use values::CSSFloat;
-use values::{Auto, Either};
+use values::{Auto, CSSFloat, CustomIdent, Either};
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
use values::computed::{BorderCornerRadius, ClipRect};
use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
@@ -44,92 +41,75 @@ use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToCompu
use values::generics::{SVGPaint, SVGPaintKind};
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
use values::generics::position as generic_position;
+use values::specified::length::Percentage;
-/// A given transition property, that is either `All`, or an animatable
-/// property.
-// NB: This needs to be here because it needs all the longhands generated
-// beforehand.
+/// A longhand property whose animation type is not "none".
+///
+/// NOTE: This includes the 'display' property since it is animatable from SMIL even though it is
+/// not animatable from CSS animations or Web Animations. CSS transitions also does not allow
+/// animating 'display', but for CSS transitions we have the separate TransitionProperty type.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub enum TransitionProperty {
- /// All, any animatable property changing should generate a transition.
- All,
+pub enum AnimatableLonghand {
% for prop in data.longhands:
% if prop.animatable:
/// ${prop.name}
${prop.camel_case},
% endif
% endfor
- // Shorthand properties may or may not contain any animatable property. Either should still be
- // parsed properly.
- % for prop in data.shorthands_except_all():
- /// ${prop.name}
- ${prop.camel_case},
- % endfor
- /// Unrecognized property which could be any non-animatable, custom property, or
- /// unknown property.
- Unsupported(Atom)
}
-no_viewport_percentage!(TransitionProperty);
-
-impl ComputedValueAsSpecified for TransitionProperty {}
-
-impl TransitionProperty {
- /// Iterates over each longhand property.
- pub fn each<F: FnMut(&TransitionProperty) -> ()>(mut cb: F) {
- % for prop in data.longhands:
- % if prop.animatable:
- cb(&TransitionProperty::${prop.camel_case});
- % endif
- % endfor
- }
-
- /// Iterates over every property that is not TransitionProperty::All, stopping and returning
- /// true when the provided callback returns true for the first time.
- pub fn any<F: FnMut(&TransitionProperty) -> bool>(mut cb: F) -> bool {
- % for prop in data.longhands:
- % if prop.animatable:
- if cb(&TransitionProperty::${prop.camel_case}) {
- return true;
- }
- % endif
- % endfor
- false
+impl AnimatableLonghand {
+ /// Returns true if this AnimatableLonghand is one of the discretely animatable properties.
+ pub fn is_discrete(&self) -> bool {
+ match *self {
+ % for prop in data.longhands:
+ % if prop.animation_value_type == "discrete":
+ AnimatableLonghand::${prop.camel_case} => true,
+ % endif
+ % endfor
+ _ => false
+ }
}
- /// Parse a transition-property value.
- pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- let ident = try!(input.expect_ident());
- (match_ignore_ascii_case! { &ident,
- "all" => Ok(TransitionProperty::All),
+ /// Converts from an nsCSSPropertyID. Returns None if nsCSSPropertyID is not an animatable
+ /// longhand in Servo.
+ #[cfg(feature = "gecko")]
+ pub fn from_nscsspropertyid(css_property: nsCSSPropertyID) -> Option<Self> {
+ match css_property {
% for prop in data.longhands:
% if prop.animatable:
- "${prop.name}" => Ok(TransitionProperty::${prop.camel_case}),
+ ${helpers.to_nscsspropertyid(prop.ident)}
+ => Some(AnimatableLonghand::${prop.camel_case}),
% endif
% endfor
- % for prop in data.shorthands_except_all():
- "${prop.name}" => Ok(TransitionProperty::${prop.camel_case}),
+ _ => None
+ }
+ }
+
+ /// Converts from TransitionProperty. Returns None if the property is not an animatable
+ /// longhand.
+ pub fn from_transition_property(transition_property: &TransitionProperty) -> Option<Self> {
+ match *transition_property {
+ % for prop in data.longhands:
+ % if prop.transitionable and prop.animatable:
+ TransitionProperty::${prop.camel_case}
+ => Some(AnimatableLonghand::${prop.camel_case}),
+ % endif
% endfor
- "none" => Err(()),
- _ => {
- match CSSWideKeyword::from_ident(&ident) {
- Some(_) => Err(()),
- None => Ok(TransitionProperty::Unsupported((&*ident).into()))
- }
- }
- }).map_err(|()| SelectorParseError::UnexpectedIdent(ident.into()).into())
+ _ => None
+ }
}
- /// Get a transition property from a property declaration.
+ /// Get an animatable longhand property from a property declaration.
pub fn from_declaration(declaration: &PropertyDeclaration) -> Option<Self> {
use properties::LonghandId;
match *declaration {
% for prop in data.longhands:
% if prop.animatable:
PropertyDeclaration::${prop.camel_case}(..)
- => Some(TransitionProperty::${prop.camel_case}),
+ => Some(AnimatableLonghand::${prop.camel_case}),
% endif
% endfor
PropertyDeclaration::CSSWideKeyword(id, _) |
@@ -138,7 +118,7 @@ impl TransitionProperty {
% for prop in data.longhands:
% if prop.animatable:
LonghandId::${prop.camel_case} =>
- Some(TransitionProperty::${prop.camel_case}),
+ Some(AnimatableLonghand::${prop.camel_case}),
% endif
% endfor
_ => None,
@@ -147,46 +127,36 @@ impl TransitionProperty {
_ => None,
}
}
+}
- /// Returns true if this TransitionProperty is one of the discrete animatable properties and
- /// this TransitionProperty should be a longhand property.
- pub fn is_discrete(&self) -> bool {
- match *self {
+/// Convert to nsCSSPropertyID.
+#[cfg(feature = "gecko")]
+#[allow(non_upper_case_globals)]
+impl<'a> From< &'a AnimatableLonghand> for nsCSSPropertyID {
+ fn from(property: &'a AnimatableLonghand) -> nsCSSPropertyID {
+ match *property {
% for prop in data.longhands:
- % if prop.animation_value_type == "discrete":
- TransitionProperty::${prop.camel_case} => true,
+ % if prop.animatable:
+ AnimatableLonghand::${prop.camel_case}
+ => ${helpers.to_nscsspropertyid(prop.ident)},
% endif
% endfor
- _ => false
- }
- }
-
- /// Return animatable longhands of this shorthand TransitionProperty, except for "all".
- pub fn longhands(&self) -> &'static [TransitionProperty] {
- % for prop in data.shorthands_except_all():
- static ${prop.ident.upper()}: &'static [TransitionProperty] = &[
- % for sub in prop.sub_properties:
- % if sub.animatable:
- TransitionProperty::${sub.camel_case},
- % endif
- % endfor
- ];
- % endfor
- match *self {
- % for prop in data.shorthands_except_all():
- TransitionProperty::${prop.camel_case} => ${prop.ident.upper()},
- % endfor
- _ => panic!("Not allowed to call longhands() for this TransitionProperty")
}
}
+}
- /// Returns true if this TransitionProperty is a shorthand.
- pub fn is_shorthand(&self) -> bool {
- match *self {
- % for prop in data.shorthands_except_all():
- TransitionProperty::${prop.camel_case} => true,
+/// Convert to PropertyDeclarationId.
+#[cfg(feature = "gecko")]
+#[allow(non_upper_case_globals)]
+impl<'a> From<AnimatableLonghand> for PropertyDeclarationId<'a> {
+ fn from(property: AnimatableLonghand) -> PropertyDeclarationId<'a> {
+ match property {
+ % for prop in data.longhands:
+ % if prop.animatable:
+ AnimatableLonghand::${prop.camel_case}
+ => PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}),
+ % endif
% endfor
- _ => false
}
}
}
@@ -195,44 +165,115 @@ impl TransitionProperty {
#[cfg(feature = "gecko")]
pub fn nscsspropertyid_is_animatable(property: nsCSSPropertyID) -> bool {
match property {
- % for prop in data.longhands:
+ % for prop in data.longhands + data.shorthands_except_all():
% if prop.animatable:
${helpers.to_nscsspropertyid(prop.ident)} => true,
% endif
% endfor
- % for prop in data.shorthands_except_all():
- <%
- animatable = "false"
- for sub in prop.sub_properties:
- if sub.animatable:
- animatable = "true"
- break
- %>
- ${helpers.to_nscsspropertyid(prop.ident)} => ${animatable},
- % endfor
_ => false
}
}
-impl ToCss for TransitionProperty {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where W: fmt::Write,
- {
+/// A given transition property, that is either `All`, a transitionable longhand property,
+/// a shorthand with at least one transitionable longhand component, or an unsupported property.
+// NB: This needs to be here because it needs all the longhands generated
+// beforehand.
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss)]
+pub enum TransitionProperty {
+ /// All, any transitionable property changing should generate a transition.
+ All,
+ % for prop in data.longhands + data.shorthands_except_all():
+ % if prop.transitionable:
+ /// ${prop.name}
+ ${prop.camel_case},
+ % endif
+ % endfor
+ /// Unrecognized property which could be any non-transitionable, custom property, or
+ /// unknown property.
+ Unsupported(CustomIdent)
+}
+
+no_viewport_percentage!(TransitionProperty);
+
+impl ComputedValueAsSpecified for TransitionProperty {}
+
+impl TransitionProperty {
+ /// Iterates over each longhand property.
+ pub fn each<F: FnMut(&TransitionProperty) -> ()>(mut cb: F) {
+ % for prop in data.longhands:
+ % if prop.transitionable:
+ cb(&TransitionProperty::${prop.camel_case});
+ % endif
+ % endfor
+ }
+
+ /// Iterates over every longhand property that is not TransitionProperty::All, stopping and
+ /// returning true when the provided callback returns true for the first time.
+ pub fn any<F: FnMut(&TransitionProperty) -> bool>(mut cb: F) -> bool {
+ % for prop in data.longhands:
+ % if prop.transitionable:
+ if cb(&TransitionProperty::${prop.camel_case}) {
+ return true;
+ }
+ % endif
+ % endfor
+ false
+ }
+
+ /// Parse a transition-property value.
+ pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+ let ident = try!(input.expect_ident());
+ let supported = match_ignore_ascii_case! { &ident,
+ "all" => Ok(Some(TransitionProperty::All)),
+ % for prop in data.longhands + data.shorthands_except_all():
+ % if prop.transitionable:
+ "${prop.name}" => Ok(Some(TransitionProperty::${prop.camel_case})),
+ % endif
+ % endfor
+ "none" => Err(()),
+ _ => Ok(None),
+ };
+
+ match supported {
+ Ok(Some(property)) => Ok(property),
+ Ok(None) => CustomIdent::from_ident(ident, &[]).map(TransitionProperty::Unsupported),
+ Err(()) => Err(SelectorParseError::UnexpectedIdent(ident).into()),
+ }
+ }
+
+ /// Return transitionable longhands of this shorthand TransitionProperty, except for "all".
+ pub fn longhands(&self) -> &'static [TransitionProperty] {
+ % for prop in data.shorthands_except_all():
+ % if prop.transitionable:
+ static ${prop.ident.upper()}: &'static [TransitionProperty] = &[
+ % for sub in prop.sub_properties:
+ % if sub.transitionable:
+ TransitionProperty::${sub.camel_case},
+ % endif
+ % endfor
+ ];
+ % endif
+ % endfor
match *self {
- TransitionProperty::All => dest.write_str("all"),
- % for prop in data.longhands:
- % if prop.animatable:
- TransitionProperty::${prop.camel_case} => dest.write_str("${prop.name}"),
+ % for prop in data.shorthands_except_all():
+ % if prop.transitionable:
+ TransitionProperty::${prop.camel_case} => ${prop.ident.upper()},
% endif
% endfor
+ _ => panic!("Not allowed to call longhands() for this TransitionProperty")
+ }
+ }
+
+ /// Returns true if this TransitionProperty is a shorthand.
+ pub fn is_shorthand(&self) -> bool {
+ match *self {
% for prop in data.shorthands_except_all():
- TransitionProperty::${prop.camel_case} => dest.write_str("${prop.name}"),
+ % if prop.transitionable:
+ TransitionProperty::${prop.camel_case} => true,
+ % endif
% endfor
- #[cfg(feature = "gecko")]
- TransitionProperty::Unsupported(ref atom) => serialize_identifier(&atom.to_string(),
- dest),
- #[cfg(feature = "servo")]
- TransitionProperty::Unsupported(ref atom) => serialize_identifier(atom, dest),
+ _ => false
}
}
}
@@ -243,16 +284,12 @@ impl ToCss for TransitionProperty {
impl<'a> From< &'a TransitionProperty> for nsCSSPropertyID {
fn from(transition_property: &'a TransitionProperty) -> nsCSSPropertyID {
match *transition_property {
- % for prop in data.longhands:
- % if prop.animatable:
+ % for prop in data.longhands + data.shorthands_except_all():
+ % if prop.transitionable:
TransitionProperty::${prop.camel_case}
=> ${helpers.to_nscsspropertyid(prop.ident)},
% endif
% endfor
- % for prop in data.shorthands_except_all():
- TransitionProperty::${prop.camel_case}
- => ${helpers.to_nscsspropertyid(prop.ident)},
- % endfor
TransitionProperty::All => nsCSSPropertyID::eCSSPropertyExtra_all_properties,
_ => panic!("Unconvertable Servo transition property: {:?}", transition_property),
}
@@ -265,39 +302,31 @@ impl<'a> From< &'a TransitionProperty> for nsCSSPropertyID {
impl From<nsCSSPropertyID> for TransitionProperty {
fn from(property: nsCSSPropertyID) -> TransitionProperty {
match property {
- % for prop in data.longhands:
- % if prop.animatable:
+ % for prop in data.longhands + data.shorthands_except_all():
+ % if prop.transitionable:
${helpers.to_nscsspropertyid(prop.ident)}
=> TransitionProperty::${prop.camel_case},
% else:
${helpers.to_nscsspropertyid(prop.ident)}
- => TransitionProperty::Unsupported(Atom::from("${prop.ident}")),
+ => TransitionProperty::Unsupported(CustomIdent(Atom::from("${prop.ident}"))),
% endif
% endfor
- % for prop in data.shorthands_except_all():
- ${helpers.to_nscsspropertyid(prop.ident)}
- => TransitionProperty::${prop.camel_case},
- % endfor
nsCSSPropertyID::eCSSPropertyExtra_all_properties => TransitionProperty::All,
_ => panic!("Unconvertable nsCSSPropertyID: {:?}", property),
}
}
}
-/// Convert to PropertyDeclarationId.
+/// Returns true if this nsCSSPropertyID is one of the transitionable properties.
#[cfg(feature = "gecko")]
-#[allow(non_upper_case_globals)]
-impl<'a> From<TransitionProperty> for PropertyDeclarationId<'a> {
- fn from(transition_property: TransitionProperty) -> PropertyDeclarationId<'a> {
- match transition_property {
- % for prop in data.longhands:
- % if prop.animatable:
- TransitionProperty::${prop.camel_case}
- => PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}),
- % endif
- % endfor
- _ => panic!(),
- }
+pub fn nscsspropertyid_is_transitionable(property: nsCSSPropertyID) -> bool {
+ match property {
+ % for prop in data.longhands + data.shorthands_except_all():
+ % if prop.transitionable:
+ ${helpers.to_nscsspropertyid(prop.ident)} => true,
+ % endif
+ % endfor
+ _ => false
}
}
@@ -387,22 +416,20 @@ impl AnimatedProperty {
/// Get an animatable value from a transition-property, an old style, and a
/// new style.
- pub fn from_transition_property(transition_property: &TransitionProperty,
+ pub fn from_animatable_longhand(property: &AnimatableLonghand,
old_style: &ComputedValues,
new_style: &ComputedValues)
-> AnimatedProperty {
- match *transition_property {
- TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
+ match *property {
% for prop in data.longhands:
% if prop.animatable:
- TransitionProperty::${prop.camel_case} => {
+ AnimatableLonghand::${prop.camel_case} => {
AnimatedProperty::${prop.camel_case}(
old_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}().into(),
new_style.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}().into())
}
% endif
% endfor
- ref other => panic!("Can't use TransitionProperty::{:?} here", other),
}
}
}
@@ -411,7 +438,7 @@ impl AnimatedProperty {
/// This HashMap stores the values that are the last AnimationValue to be
/// composed for each TransitionProperty.
#[cfg(feature = "gecko")]
-pub type AnimationValueMap = HashMap<TransitionProperty, AnimationValue>;
+pub type AnimationValueMap = HashMap<AnimatableLonghand, AnimationValue>;
#[cfg(feature = "gecko")]
unsafe impl HasFFI for AnimationValueMap {
type FFIType = RawServoAnimationValueMap;
@@ -579,15 +606,14 @@ impl AnimationValue {
}
}
- /// Get an AnimationValue for a TransitionProperty from a given computed values.
- pub fn from_computed_values(transition_property: &TransitionProperty,
+ /// Get an AnimationValue for an AnimatableLonghand from a given computed values.
+ pub fn from_computed_values(property: &AnimatableLonghand,
computed_values: &ComputedValues)
-> Self {
- match *transition_property {
- TransitionProperty::All => panic!("Can't use TransitionProperty::All here."),
+ match *property {
% for prop in data.longhands:
% if prop.animatable:
- TransitionProperty::${prop.camel_case} => {
+ AnimatableLonghand::${prop.camel_case} => {
AnimationValue::${prop.camel_case}(
% if prop.is_animatable_with_computed_value:
computed_values.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}())
@@ -598,7 +624,6 @@ impl AnimationValue {
}
% endif
% endfor
- ref other => panic!("Can't use TransitionProperty::{:?} here.", other),
}
}
}
@@ -853,6 +878,7 @@ impl <T> Animatable for Option<T>
(&Some(ref this), &Some(ref other)) => {
this.compute_squared_distance(other)
},
+ (&None, &None) => Ok(0.0),
_ => Err(()),
}
}
@@ -916,6 +942,22 @@ impl Animatable for Angle {
}
}
+/// https://drafts.csswg.org/css-transitions/#animtype-percentage
+impl Animatable for Percentage {
+ #[inline]
+ fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+ Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32))
+ }
+
+ #[inline]
+ fn get_zero_value(&self) -> Option<Self> { Some(Percentage(0.)) }
+
+ #[inline]
+ fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+ Ok((self.0 as f64 - other.0 as f64).abs())
+ }
+}
+
/// https://drafts.csswg.org/css-transitions/#animtype-visibility
impl Animatable for Visibility {
#[inline]
@@ -1125,7 +1167,7 @@ impl Animatable for LengthOrPercentage {
},
(LengthOrPercentage::Percentage(ref this),
LengthOrPercentage::Percentage(ref other)) => {
- let diff = (this - other) as f64;
+ let diff = this.0 as f64 - other.0 as f64;
Ok(diff * diff)
},
(this, other) => {
@@ -1210,7 +1252,7 @@ impl Animatable for LengthOrPercentageOrAuto {
},
(LengthOrPercentageOrAuto::Percentage(ref this),
LengthOrPercentageOrAuto::Percentage(ref other)) => {
- let diff = (this - other) as f64;
+ let diff = this.0 as f64 - other.0 as f64;
Ok(diff * diff)
},
(this, other) => {
@@ -1246,7 +1288,14 @@ impl Animatable for LengthOrPercentageOrNone {
(LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
Ok(LengthOrPercentageOrNone::None)
}
- _ => Err(())
+ (this, other) => {
+ let this = <Option<CalcLengthOrPercentage>>::from(this);
+ let other = <Option<CalcLengthOrPercentage>>::from(other);
+ match this.add_weighted(&other, self_portion, other_portion) {
+ Ok(Some(result)) => Ok(LengthOrPercentageOrNone::Calc(result)),
+ _ => Err(()),
+ }
+ },
}
}
@@ -1273,7 +1322,12 @@ impl Animatable for LengthOrPercentageOrNone {
LengthOrPercentageOrNone::Percentage(ref other)) => {
this.compute_distance(other)
},
- _ => Err(())
+ (this, other) => {
+ // If one of the element is Auto, Option<> will be None, and the returned distance is Err(())
+ let this = <Option<CalcLengthOrPercentage>>::from(this);
+ let other = <Option<CalcLengthOrPercentage>>::from(other);
+ this.compute_distance(&other)
+ },
}
}
}
diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs
index b3ada208939..e839985f950 100644
--- a/components/style/properties/longhand/box.mako.rs
+++ b/components/style/properties/longhand/box.mako.rs
@@ -12,7 +12,7 @@
// TODO(SimonSapin): don't parse `inline-table`, since we don't support it
<%helpers:longhand name="display"
need_clone="True"
- animation_value_type="none"
+ animation_value_type="discrete"
custom_cascade="${product == 'servo'}"
spec="https://drafts.csswg.org/css-display/#propdef-display">
<%
@@ -1866,9 +1866,9 @@ ${helpers.single_keyword("-moz-orient",
<%helpers:longhand name="will-change" products="gecko" animation_value_type="none"
spec="https://drafts.csswg.org/css-will-change/#will-change">
- use cssparser::serialize_identifier;
use std::fmt;
use style_traits::ToCss;
+ use values::CustomIdent;
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
@@ -1882,7 +1882,7 @@ ${helpers.single_keyword("-moz-orient",
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum SpecifiedValue {
Auto,
- AnimateableFeatures(Vec<Atom>),
+ AnimateableFeatures(Vec<CustomIdent>),
}
impl ToCss for SpecifiedValue {
@@ -1891,12 +1891,10 @@ ${helpers.single_keyword("-moz-orient",
SpecifiedValue::Auto => dest.write_str("auto"),
SpecifiedValue::AnimateableFeatures(ref features) => {
let (first, rest) = features.split_first().unwrap();
- // handle head element
- serialize_identifier(&*first.to_string(), dest)?;
- // handle tail, precede each with a delimiter
+ first.to_css(dest)?;
for feature in rest {
dest.write_str(", ")?;
- serialize_identifier(&*feature.to_string(), dest)?;
+ feature.to_css(dest)?;
}
Ok(())
}
@@ -1916,17 +1914,12 @@ ${helpers.single_keyword("-moz-orient",
Ok(computed_value::T::Auto)
} else {
input.parse_comma_separated(|i| {
- let ident = i.expect_ident()?;
- let bad_keyword = match_ignore_ascii_case! { &ident,
- "will-change" | "none" | "all" | "auto" |
- "initial" | "inherit" | "unset" | "default" => true,
- _ => false,
- };
- if bad_keyword {
- Err(SelectorParseError::UnexpectedIdent(ident.into()).into())
- } else {
- Ok(Atom::from(ident))
- }
+ CustomIdent::from_ident(i.expect_ident()?, &[
+ "will-change",
+ "none",
+ "all",
+ "auto",
+ ])
}).map(SpecifiedValue::AnimateableFeatures)
}
}
diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs
index ed0da5f44c7..be7a8d1e168 100644
--- a/components/style/properties/longhand/font.mako.rs
+++ b/components/style/properties/longhand/font.mako.rs
@@ -1376,21 +1376,21 @@ ${helpers.single_keyword_system("font-kerning",
return Ok(SpecifiedValue::Value(result))
}
- while let Ok(ident) = input.try(|input| input.expect_ident()) {
- let flag = match_ignore_ascii_case! { &ident,
- "stylistic" => Some(STYLISTIC),
- "historical-forms" => Some(HISTORICAL_FORMS),
- "styleset" => Some(STYLESET),
- "character-variant" => Some(CHARACTER_VARIANT),
- "swash" => Some(SWASH),
- "ornaments" => Some(ORNAMENTS),
- "annotation" => Some(ANNOTATION),
- _ => None,
- };
- let flag = match flag {
- Some(flag) if !result.intersects(flag) => flag,
- _ => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
- };
+ while let Ok(flag) = input.try(|input| {
+ Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
+ "stylistic" => STYLISTIC,
+ "historical-forms" => HISTORICAL_FORMS,
+ "styleset" => STYLESET,
+ "character-variant" => CHARACTER_VARIANT,
+ "swash" => SWASH,
+ "ornaments" => ORNAMENTS,
+ "annotation" => ANNOTATION,
+ _ => return Err(()),
+ })
+ }) {
+ if result.intersects(flag) {
+ return Err(StyleParseError::UnspecifiedError.into())
+ }
result.insert(flag);
}
@@ -1406,9 +1406,9 @@ ${helpers.single_keyword_system("font-kerning",
macro_rules! exclusive_value {
(($value:ident, $set:expr) => $ident:ident) => {
if $value.intersects($set) {
- None
+ return Err(())
} else {
- Some($ident)
+ $ident
}
}
}
@@ -1521,8 +1521,8 @@ macro_rules! exclusive_value {
return Ok(SpecifiedValue::Value(result))
}
- while let Ok(ident) = input.try(|input| input.expect_ident()) {
- let flag = match_ignore_ascii_case! { &ident,
+ while let Ok(flag) = input.try(|input| {
+ Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
"jis78" =>
exclusive_value!((result, ${east_asian_variant_values}) => JIS78),
"jis83" =>
@@ -1541,12 +1541,9 @@ macro_rules! exclusive_value {
exclusive_value!((result, ${east_asian_width_values}) => PROPORTIONAL_WIDTH),
"ruby" =>
exclusive_value!((result, RUBY) => RUBY),
- _ => None,
- };
- let flag = match flag {
- Some(flag) => flag,
- None => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
- };
+ _ => return Err(()),
+ })
+ }) {
result.insert(flag);
}
@@ -1656,6 +1653,10 @@ macro_rules! exclusive_value {
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue::Value(VariantLigatures::empty())
}
+ #[inline]
+ pub fn get_none_specified_value() -> SpecifiedValue {
+ SpecifiedValue::Value(NONE)
+ }
/// normal | none |
/// [ <common-lig-values> ||
@@ -1681,8 +1682,8 @@ macro_rules! exclusive_value {
return Ok(SpecifiedValue::Value(NONE))
}
- while let Ok(ident) = input.try(|input| input.expect_ident()) {
- let flag = match_ignore_ascii_case! { &ident,
+ while let Ok(flag) = input.try(|input| {
+ Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
"common-ligatures" =>
exclusive_value!((result, ${common_lig_values}) => COMMON_LIGATURES),
"no-common-ligatures" =>
@@ -1699,12 +1700,9 @@ macro_rules! exclusive_value {
exclusive_value!((result, ${contextual_alt_values}) => CONTEXTUAL),
"no-contextual" =>
exclusive_value!((result, ${contextual_alt_values}) => NO_CONTEXTUAL),
- _ => None,
- };
- let flag = match flag {
- Some(flag) => flag,
- None => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
- };
+ _ => return Err(()),
+ })
+ }) {
result.insert(flag);
}
@@ -1832,14 +1830,14 @@ macro_rules! exclusive_value {
return Ok(SpecifiedValue::Value(result))
}
- while let Ok(ident) = input.try(|input| input.expect_ident()) {
- let flag = match_ignore_ascii_case! { &ident,
+ while let Ok(flag) = input.try(|input| {
+ Ok(match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
"ordinal" =>
exclusive_value!((result, ORDINAL) => ORDINAL),
"slashed-zero" =>
exclusive_value!((result, SLASHED_ZERO) => SLASHED_ZERO),
"lining-nums" =>
- exclusive_value!((result, ${numeric_figure_values}) => LINING_NUMS ),
+ exclusive_value!((result, ${numeric_figure_values}) => LINING_NUMS),
"oldstyle-nums" =>
exclusive_value!((result, ${numeric_figure_values}) => OLDSTYLE_NUMS),
"proportional-nums" =>
@@ -1850,12 +1848,9 @@ macro_rules! exclusive_value {
exclusive_value!((result, ${numeric_fraction_values}) => DIAGONAL_FRACTIONS),
"stacked-fractions" =>
exclusive_value!((result, ${numeric_fraction_values}) => STACKED_FRACTIONS),
- _ => None,
- };
- let flag = match flag {
- Some(flag) => flag,
- None => return Err(SelectorParseError::UnexpectedIdent(ident).into()),
- };
+ _ => return Err(()),
+ })
+ }) {
result.insert(flag);
}
diff --git a/components/style/properties/longhand/list.mako.rs b/components/style/properties/longhand/list.mako.rs
index 6f25334698e..7b5b2879dd9 100644
--- a/components/style/properties/longhand/list.mako.rs
+++ b/components/style/properties/longhand/list.mako.rs
@@ -63,7 +63,7 @@ ${helpers.single_keyword("list-style-position", "outside inside", animation_valu
pub fn from_gecko_keyword(value: u32) -> Self {
use gecko_bindings::structs;
SpecifiedValue::CounterStyle(if value == structs::NS_STYLE_LIST_STYLE_NONE {
- CounterStyleOrNone::None_
+ CounterStyleOrNone::None
} else {
<%
values = """disc circle square decimal lower-roman
diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs
index 8bd5ec4680d..0ba5e29314e 100644
--- a/components/style/properties/properties.mako.rs
+++ b/components/style/properties/properties.mako.rs
@@ -22,7 +22,7 @@ use app_units::Au;
use cssparser::{Parser, TokenSerializationType, serialize_identifier};
use cssparser::ParserInput;
use error_reporting::ParseErrorReporter;
-#[cfg(feature = "servo")] use euclid::side_offsets::SideOffsets2D;
+#[cfg(feature = "servo")] use euclid::SideOffsets2D;
use computed_values;
use context::QuirksMode;
use font_metrics::FontMetricsProvider;
@@ -31,13 +31,13 @@ use font_metrics::FontMetricsProvider;
#[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide};
use logical_geometry::WritingMode;
use media_queries::Device;
-use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext};
-use properties::animated_properties::TransitionProperty;
+use parser::{Parse, ParserContext};
+use properties::animated_properties::AnimatableLonghand;
#[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont;
use selectors::parser::SelectorParseError;
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
use shared_lock::StylesheetGuards;
-use style_traits::{HasViewportPercentage, ToCss, ParseError, PropertyDeclarationParseError};
+use style_traits::{PARSING_MODE_DEFAULT, HasViewportPercentage, ToCss, ParseError, PropertyDeclarationParseError};
use stylesheets::{CssRuleType, MallocSizeOf, MallocSizeOfFn, Origin, UrlExtraData};
#[cfg(feature = "servo")] use values::Either;
use values::generics::text::LineHeight;
@@ -300,29 +300,25 @@ impl LonghandIdSet {
}
}
- /// Set the corresponding bit of TransitionProperty.
- /// This function will panic if TransitionProperty::All is given.
- pub fn set_transition_property_bit(&mut self, property: &TransitionProperty) {
+ /// Set the corresponding bit of AnimatableLonghand.
+ pub fn set_animatable_longhand_bit(&mut self, property: &AnimatableLonghand) {
match *property {
% for prop in data.longhands:
% if prop.animatable:
- TransitionProperty::${prop.camel_case} => self.insert(LonghandId::${prop.camel_case}),
+ AnimatableLonghand::${prop.camel_case} => self.insert(LonghandId::${prop.camel_case}),
% endif
% endfor
- ref other => unreachable!("Tried to set TransitionProperty::{:?} in a PropertyBitfield", other),
}
}
- /// Return true if the corresponding bit of TransitionProperty is set.
- /// This function will panic if TransitionProperty::All is given.
- pub fn has_transition_property_bit(&self, property: &TransitionProperty) -> bool {
+ /// Return true if the corresponding bit of AnimatableLonghand is set.
+ pub fn has_animatable_longhand_bit(&self, property: &AnimatableLonghand) -> bool {
match *property {
% for prop in data.longhands:
% if prop.animatable:
- TransitionProperty::${prop.camel_case} => self.contains(LonghandId::${prop.camel_case}),
+ AnimatableLonghand::${prop.camel_case} => self.contains(LonghandId::${prop.camel_case}),
% endif
% endfor
- ref other => unreachable!("Tried to get TransitionProperty::{:?} in a PropertyBitfield", other),
}
}
}
@@ -551,6 +547,42 @@ impl LonghandId {
}
}
+ fn shorthands(&self) -> &'static [ShorthandId] {
+ // first generate longhand to shorthands lookup map
+ //
+ // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It
+ // could potentially do so, which would speed up serialization
+ // algorithms and what not, I guess.
+ <%
+ longhand_to_shorthand_map = {}
+ for shorthand in data.shorthands:
+ for sub_property in shorthand.sub_properties:
+ if sub_property.ident not in longhand_to_shorthand_map:
+ longhand_to_shorthand_map[sub_property.ident] = []
+
+ longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
+
+ for shorthand_list in longhand_to_shorthand_map.itervalues():
+ shorthand_list.sort()
+ %>
+
+ // based on lookup results for each longhand, create result arrays
+ % for property in data.longhands:
+ static ${property.ident.upper()}: &'static [ShorthandId] = &[
+ % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
+ ShorthandId::${shorthand},
+ % endfor
+ ];
+ % endfor
+
+ match *self {
+ % for property in data.longhands:
+ LonghandId::${property.camel_case} => ${property.ident.upper()},
+ % endfor
+ }
+ }
+
+
/// If this is a logical property, return the corresponding physical one in the given writing mode.
/// Otherwise, return unchanged.
pub fn to_physical(&self, wm: WritingMode) -> Self {
@@ -885,7 +917,7 @@ impl<'a> PropertyDeclarationId<'a> {
PropertyDeclarationId::Longhand(id) => {
match *other {
PropertyId::Longhand(other_id) => id == other_id,
- PropertyId::Shorthand(shorthand) => shorthand.longhands().contains(&id),
+ PropertyId::Shorthand(shorthand) => self.is_longhand_of(shorthand),
PropertyId::Custom(_) => false,
}
}
@@ -899,7 +931,7 @@ impl<'a> PropertyDeclarationId<'a> {
/// shorthand.
pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
match *self {
- PropertyDeclarationId::Longhand(ref id) => shorthand.longhands().contains(id),
+ PropertyDeclarationId::Longhand(ref id) => id.shorthands().contains(&shorthand),
_ => false,
}
}
@@ -1308,40 +1340,9 @@ impl PropertyDeclaration {
/// The shorthands that this longhand is part of.
pub fn shorthands(&self) -> &'static [ShorthandId] {
- // first generate longhand to shorthands lookup map
- <%
- longhand_to_shorthand_map = {}
- for shorthand in data.shorthands:
- for sub_property in shorthand.sub_properties:
- if sub_property.ident not in longhand_to_shorthand_map:
- longhand_to_shorthand_map[sub_property.ident] = []
-
- longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
-
- for shorthand_list in longhand_to_shorthand_map.itervalues():
- shorthand_list.sort()
- %>
-
- // based on lookup results for each longhand, create result arrays
- % for property in data.longhands:
- static ${property.ident.upper()}: &'static [ShorthandId] = &[
- % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
- ShorthandId::${shorthand},
- % endfor
- ];
- % endfor
-
- match *self {
- % for property in data.longhands:
- PropertyDeclaration::${property.camel_case}(_) => ${property.ident.upper()},
- % endfor
- PropertyDeclaration::CSSWideKeyword(id, _) |
- PropertyDeclaration::WithVariables(id, _) => match id {
- % for property in data.longhands:
- LonghandId::${property.camel_case} => ${property.ident.upper()},
- % endfor
- },
- PropertyDeclaration::Custom(_, _) => &[]
+ match self.id() {
+ PropertyDeclarationId::Longhand(id) => id.shorthands(),
+ PropertyDeclarationId::Custom(..) => &[],
}
}
@@ -2478,6 +2479,10 @@ bitflags! {
/// ::backdrop and all NAC will resolve rem units against
/// the toplevel root element now.
const ALLOW_SET_ROOT_FONT_SIZE = 0x08,
+ /// Whether to convert display:contents into display:inline. This
+ /// is used by Gecko to prevent display:contents on generated
+ /// content.
+ const PROHIBIT_DISPLAY_CONTENTS = 0x10,
}
}
@@ -2850,8 +2855,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device,
{
StyleAdjuster::new(&mut style, is_root_element)
- .adjust(context.layout_parent_style,
- flags.contains(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP));
+ .adjust(context.layout_parent_style, flags);
}
% if product == "gecko":
diff --git a/components/style/properties/shorthand/font.mako.rs b/components/style/properties/shorthand/font.mako.rs
index 009282a1b91..993f7ffd752 100644
--- a/components/style/properties/shorthand/font.mako.rs
+++ b/components/style/properties/shorthand/font.mako.rs
@@ -232,66 +232,105 @@
${'font-variant-numeric' if product == 'gecko' or data.testing else ''}
${'font-variant-position' if product == 'gecko' or data.testing else ''}"
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
- use properties::longhands::font_variant_caps;
<% gecko_sub_properties = "alternates east_asian ligatures numeric position".split() %>
- % if product == "gecko" or data.testing:
- % for prop in gecko_sub_properties:
- use properties::longhands::font_variant_${prop};
- % endfor
- % endif
+ <%
+ sub_properties = ["caps"]
+ if product == "gecko" or data.testing:
+ sub_properties += gecko_sub_properties
+ %>
+
+% for prop in sub_properties:
+ use properties::longhands::font_variant_${prop};
+% endfor
pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-> Result<Longhands, ParseError<'i>> {
- let mut nb_normals = 0;
- let mut caps = None;
- loop {
- // Special-case 'normal' because it is valid in each of
- // all sub properties.
- // Leaves the values to None, 'normal' is the initial value for each of them.
- if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
- nb_normals += 1;
- continue;
- }
- if caps.is_none() {
- if let Ok(value) = input.try(|input| font_variant_caps::parse(context, input)) {
- caps = Some(value);
- continue
+ % for prop in sub_properties:
+ let mut ${prop} = None;
+ % endfor
+
+ if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
+ // Leave the values to None, 'normal' is the initial value for all the sub properties.
+ } else if input.try(|input| input.expect_ident_matching("none")).is_ok() {
+ // The 'none' value sets 'font-variant-ligatures' to 'none' and resets all other sub properties
+ // to their initial value.
+ % if product == "gecko" or data.testing:
+ ligatures = Some(font_variant_ligatures::get_none_specified_value());
+ % endif
+ } else {
+ let mut has_custom_value: bool = false;
+ loop {
+ if input.try(|input| input.expect_ident_matching("normal")).is_ok() ||
+ input.try(|input| input.expect_ident_matching("none")).is_ok() {
+ return Err(StyleParseError::UnspecifiedError.into())
}
+ % for prop in sub_properties:
+ if ${prop}.is_none() {
+ if let Ok(value) = input.try(|i| font_variant_${prop}::parse(context, i)) {
+ has_custom_value = true;
+ ${prop} = Some(value);
+ continue
+ }
+ }
+ % endfor
+
+ break
+ }
+
+ if !has_custom_value {
+ return Err(StyleParseError::UnspecifiedError.into())
}
- break
- }
- #[inline]
- fn count<T>(opt: &Option<T>) -> u8 {
- if opt.is_some() { 1 } else { 0 }
- }
- let count = count(&caps) + nb_normals;
- if count == 0 || count > 1 {
- return Err(StyleParseError::UnspecifiedError.into())
}
+
Ok(expanded! {
- font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
- // FIXME: Bug 1356134 - parse all sub properties.
- % if product == "gecko" or data.testing:
- % for name in gecko_sub_properties:
- font_variant_${name}: font_variant_${name}::get_initial_specified_value(),
- % endfor
- % endif
+ % for prop in sub_properties:
+ font_variant_${prop}: unwrap_or_initial!(font_variant_${prop}, ${prop}),
+ % endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
+ #[allow(unused_assignments)]
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- % if product == "gecko" or data.testing:
- % for name in gecko_sub_properties:
- // FIXME: Bug 1356134 - handle all sub properties.
- if self.font_variant_${name} != &font_variant_${name}::get_initial_specified_value() {
- return Ok(());
+ let has_none_ligatures =
+ % if product == "gecko" or data.testing:
+ self.font_variant_ligatures == &font_variant_ligatures::get_none_specified_value();
+ % else:
+ false;
+ % endif
+
+ const TOTAL_SUBPROPS: usize = ${len(sub_properties)};
+ let mut nb_normals = 0;
+ % for prop in sub_properties:
+ if self.font_variant_${prop} == &font_variant_${prop}::get_initial_specified_value() {
+ nb_normals += 1;
}
% endfor
- % endif
- self.font_variant_caps.to_css(dest)?;
+
+ if nb_normals > 0 && nb_normals == TOTAL_SUBPROPS {
+ dest.write_str("normal")?;
+ } else if has_none_ligatures {
+ if nb_normals == TOTAL_SUBPROPS - 1 {
+ // Serialize to 'none' if 'font-variant-ligatures' is set to 'none' and all other
+ // font feature properties are reset to their initial value.
+ dest.write_str("none")?;
+ } else {
+ return Ok(())
+ }
+ } else {
+ let mut has_any = false;
+ % for prop in sub_properties:
+ if self.font_variant_${prop} != &font_variant_${prop}::get_initial_specified_value() {
+ if has_any {
+ dest.write_str(" ")?;
+ }
+ has_any = true;
+ self.font_variant_${prop}.to_css(dest)?;
+ }
+ % endfor
+ }
Ok(())
}
diff --git a/components/style/properties/shorthand/list.mako.rs b/components/style/properties/shorthand/list.mako.rs
index 4ea2647e141..8f591760ec7 100644
--- a/components/style/properties/shorthand/list.mako.rs
+++ b/components/style/properties/shorthand/list.mako.rs
@@ -62,7 +62,7 @@
list_style_type::SpecifiedValue::none
% else:
use values::generics::CounterStyleOrNone;
- list_style_type::SpecifiedValue::CounterStyle(CounterStyleOrNone::None_)
+ list_style_type::SpecifiedValue::CounterStyle(CounterStyleOrNone::None)
% endif
}
diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs
deleted file mode 100644
index 350608e118b..00000000000
--- a/components/style/restyle_hints.rs
+++ /dev/null
@@ -1,1271 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * 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/. */
-
-//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
-
-#![deny(missing_docs)]
-
-use Atom;
-use LocalName;
-use Namespace;
-use context::{SharedStyleContext, ThreadLocalStyleContext};
-use dom::TElement;
-use element_state::*;
-#[cfg(feature = "gecko")]
-use gecko_bindings::structs::nsRestyleHint;
-#[cfg(feature = "servo")]
-use heapsize::HeapSizeOf;
-use selector_map::{SelectorMap, SelectorMapEntry};
-use selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap, AttrValue};
-use selectors::Element;
-use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
-use selectors::matching::{ElementSelectorFlags, LocalMatchingContext, MatchingContext, MatchingMode};
-use selectors::matching::{RelevantLinkStatus, VisitedHandlingMode, matches_selector};
-use selectors::parser::{AncestorHashes, Combinator, Component};
-use selectors::parser::{Selector, SelectorAndHashes, SelectorIter, SelectorMethods};
-use selectors::visitor::SelectorVisitor;
-use smallvec::SmallVec;
-use std::cell::Cell;
-use std::clone::Clone;
-use std::cmp;
-use std::fmt;
-
-/// When the ElementState of an element (like IN_HOVER_STATE) changes,
-/// certain pseudo-classes (like :hover) may require us to restyle that
-/// element, its siblings, and/or its descendants. Similarly, when various
-/// attributes of an element change, we may also need to restyle things with
-/// id, class, and attribute selectors. Doing this conservatively is
-/// expensive, and so we use RestyleHints to short-circuit work we know is
-/// unnecessary.
-#[derive(Debug, Clone, PartialEq)]
-pub struct RestyleHint {
- /// Depths at which selector matching must be re-run.
- match_under_self: RestyleDepths,
-
- /// Rerun selector matching on all later siblings of the element and all
- /// of their descendants.
- match_later_siblings: bool,
-
- /// Whether the current element and/or all descendants must be recascade.
- recascade: CascadeHint,
-
- /// Levels of the cascade whose rule nodes should be recomputed and
- /// replaced.
- pub replacements: RestyleReplacements,
-}
-
-bitflags! {
- /// Cascade levels that can be updated for an element by simply replacing
- /// their rule node.
- ///
- /// Note that the bit values here must be kept in sync with the Gecko
- /// nsRestyleHint values. If you add more bits with matching values,
- /// please add assertions to assert_restyle_hints_match below.
- pub flags RestyleReplacements: u8 {
- /// Replace the style data coming from CSS transitions without updating
- /// any other style data. This hint is only processed in animation-only
- /// traversal which is prior to normal traversal.
- const RESTYLE_CSS_TRANSITIONS = 0x10,
-
- /// Replace the style data coming from CSS animations without updating
- /// any other style data. This hint is only processed in animation-only
- /// traversal which is prior to normal traversal.
- const RESTYLE_CSS_ANIMATIONS = 0x20,
-
- /// Don't re-run selector-matching on the element, only the style
- /// attribute has changed, and this change didn't have any other
- /// dependencies.
- const RESTYLE_STYLE_ATTRIBUTE = 0x40,
-
- /// Replace the style data coming from SMIL animations without updating
- /// any other style data. This hint is only processed in animation-only
- /// traversal which is prior to normal traversal.
- const RESTYLE_SMIL = 0x80,
- }
-}
-
-/// Eight bit wide bitfield representing depths of a DOM subtree's descendants,
-/// used to represent which elements must have selector matching re-run on them.
-///
-/// The least significant bit indicates that selector matching must be re-run
-/// for the element itself, the second least significant bit for the element's
-/// children, the third its grandchildren, and so on. If the most significant
-/// bit it set, it indicates that that selector matching must be re-run for
-/// elements at that depth and all of their descendants.
-#[derive(Debug, Clone, Copy, PartialEq)]
-struct RestyleDepths(u8);
-
-impl RestyleDepths {
- /// Returns a `RestyleDepths` representing no element depths.
- fn empty() -> Self {
- RestyleDepths(0)
- }
-
- /// Returns a `RestyleDepths` representing the current element depth.
- fn for_self() -> Self {
- RestyleDepths(0x01)
- }
-
- /// Returns a `RestyleDepths` representing the depths of all descendants of
- /// the current element.
- fn for_descendants() -> Self {
- RestyleDepths(0xfe)
- }
-
- /// Returns a `RestyleDepths` representing the current element depth and the
- /// depths of all the current element's descendants.
- fn for_self_and_descendants() -> Self {
- RestyleDepths(0xff)
- }
-
- /// Returns a `RestyleDepths` representing the specified depth, where zero
- /// is the current element depth, one is its children's depths, etc.
- fn for_depth(depth: u32) -> Self {
- RestyleDepths(1u8 << cmp::min(depth, 7))
- }
-
- /// Returns whether this `RestyleDepths` represents the current element
- /// depth and the depths of all the current element's descendants.
- fn is_self_and_descendants(&self) -> bool {
- self.0 == 0xff
- }
-
- /// Returns whether this `RestyleDepths` includes any element depth.
- fn is_any(&self) -> bool {
- self.0 != 0
- }
-
- /// Returns whether this `RestyleDepths` includes the current element depth.
- fn has_self(&self) -> bool {
- (self.0 & 0x01) != 0
- }
-
- /// Returns a new `RestyleDepths` with all depth values represented by this
- /// `RestyleDepths` reduced by one.
- fn propagate(&self) -> Self {
- RestyleDepths((self.0 >> 1) | (self.0 & 0x80))
- }
-
- /// Returns a new `RestyleDepths` that represents the union of the depths
- /// from `self` and `other`.
- fn insert(&mut self, other: RestyleDepths) {
- self.0 |= other.0;
- }
-
- /// Returns whether this `RestyleDepths` includes all depths represented
- /// by `other`.
- fn contains(&self, other: RestyleDepths) -> bool {
- (self.0 & other.0) == other.0
- }
-}
-
-bitflags! {
- /// Flags representing whether the current element or its descendants
- /// must be recascaded.
- ///
- /// FIXME(bholley): This should eventually become more fine-grained.
- pub flags CascadeHint: u8 {
- /// Recascade the current element.
- const RECASCADE_SELF = 0x01,
- /// Recascade all descendant elements.
- const RECASCADE_DESCENDANTS = 0x02,
- }
-}
-
-impl CascadeHint {
- /// Creates a new `CascadeHint` indicating that the current element and all
- /// its descendants must be recascaded.
- fn subtree() -> CascadeHint {
- RECASCADE_SELF | RECASCADE_DESCENDANTS
- }
-
- /// Returns a new `CascadeHint` appropriate for children of the current
- /// element.
- fn propagate(&self) -> Self {
- if self.contains(RECASCADE_DESCENDANTS) {
- CascadeHint::subtree()
- } else {
- CascadeHint::empty()
- }
- }
-}
-
-/// Asserts that all RestyleReplacements have a matching nsRestyleHint value.
-#[cfg(feature = "gecko")]
-#[inline]
-pub fn assert_restyle_hints_match() {
- use gecko_bindings::structs;
-
- macro_rules! check_restyle_hints {
- ( $( $a:ident => $b:ident ),*, ) => {
- if cfg!(debug_assertions) {
- let mut replacements = RestyleReplacements::all();
- $(
- assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b));
- replacements.remove($b);
- )*
- assert_eq!(replacements, RestyleReplacements::empty(),
- "all RestyleReplacements bits should have an assertion");
- }
- }
- }
-
- check_restyle_hints! {
- nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
- nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
- nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
- nsRestyleHint_eRestyle_StyleAttribute_Animations => RESTYLE_SMIL,
- }
-}
-
-impl RestyleHint {
- /// Creates a new, empty `RestyleHint`, which represents no restyling work
- /// needs to be done.
- #[inline]
- pub fn empty() -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::empty(),
- match_later_siblings: false,
- recascade: CascadeHint::empty(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Creates a new `RestyleHint` that indicates selector matching must be
- /// re-run on the element.
- #[inline]
- pub fn for_self() -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::for_self(),
- match_later_siblings: false,
- recascade: CascadeHint::empty(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Creates a new `RestyleHint` that indicates selector matching must be
- /// re-run on all of the element's descendants.
- #[inline]
- pub fn descendants() -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::for_descendants(),
- match_later_siblings: false,
- recascade: CascadeHint::empty(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Creates a new `RestyleHint` that indicates selector matching must be
- /// re-run on the descendants of element at the specified depth, where 0
- /// indicates the element itself, 1 is its children, 2 its grandchildren,
- /// etc.
- #[inline]
- pub fn descendants_at_depth(depth: u32) -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::for_depth(depth),
- match_later_siblings: false,
- recascade: CascadeHint::empty(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Creates a new `RestyleHint` that indicates selector matching must be
- /// re-run on all of the element's later siblings and their descendants.
- #[inline]
- pub fn later_siblings() -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::empty(),
- match_later_siblings: true,
- recascade: CascadeHint::empty(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Creates a new `RestyleHint` that indicates selector matching must be
- /// re-run on the element and all of its descendants.
- #[inline]
- pub fn subtree() -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::for_self_and_descendants(),
- match_later_siblings: false,
- recascade: CascadeHint::empty(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Creates a new `RestyleHint` that indicates selector matching must be
- /// re-run on the element, its descendants, its later siblings, and
- /// their descendants.
- #[inline]
- pub fn subtree_and_later_siblings() -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::for_self_and_descendants(),
- match_later_siblings: true,
- recascade: CascadeHint::empty(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Creates a new `RestyleHint` that indicates the specified rule node
- /// replacements must be performed on the element.
- #[inline]
- pub fn for_replacements(replacements: RestyleReplacements) -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::empty(),
- match_later_siblings: false,
- recascade: CascadeHint::empty(),
- replacements: replacements,
- }
- }
-
- /// Creates a new `RestyleHint` that indicates the element must be
- /// recascaded.
- pub fn recascade_self() -> Self {
- RestyleHint {
- match_under_self: RestyleDepths::empty(),
- match_later_siblings: false,
- recascade: RECASCADE_SELF,
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Returns whether this `RestyleHint` represents no needed restyle work.
- #[inline]
- pub fn is_empty(&self) -> bool {
- *self == RestyleHint::empty()
- }
-
- /// Returns whether this `RestyleHint` represents the maximum possible
- /// restyle work, and thus any `insert()` calls will have no effect.
- #[inline]
- pub fn is_maximum(&self) -> bool {
- self.match_under_self.is_self_and_descendants() &&
- self.match_later_siblings &&
- self.recascade.is_all() &&
- self.replacements.is_all()
- }
-
- /// Returns whether the hint specifies that some work must be performed on
- /// the current element.
- #[inline]
- pub fn affects_self(&self) -> bool {
- self.match_self() || self.has_recascade_self() || !self.replacements.is_empty()
- }
-
- /// Returns whether the hint specifies that the currently element must be
- /// recascaded.
- pub fn has_recascade_self(&self) -> bool {
- self.recascade.contains(RECASCADE_SELF)
- }
-
- /// Returns whether the hint specifies that later siblings must be restyled.
- #[inline]
- pub fn affects_later_siblings(&self) -> bool {
- self.match_later_siblings
- }
-
- /// Returns whether the hint specifies that an animation cascade level must
- /// be replaced.
- #[inline]
- pub fn has_animation_hint(&self) -> bool {
- self.replacements.intersects(RestyleReplacements::for_animations())
- }
-
- /// Returns whether the hint specifies some restyle work other than an
- /// animation cascade level replacement.
- #[inline]
- pub fn has_non_animation_hint(&self) -> bool {
- self.match_under_self.is_any() || self.match_later_siblings ||
- !self.recascade.is_empty() ||
- self.replacements.contains(RESTYLE_STYLE_ATTRIBUTE)
- }
-
- /// Returns whether the hint specifies that selector matching must be re-run
- /// for the element.
- #[inline]
- pub fn match_self(&self) -> bool {
- self.match_under_self.has_self()
- }
-
- /// Returns whether the hint specifies that some cascade levels must be
- /// replaced.
- #[inline]
- pub fn has_replacements(&self) -> bool {
- !self.replacements.is_empty()
- }
-
- /// Returns a new `RestyleHint` appropriate for children of the current
- /// element.
- #[inline]
- pub fn propagate_for_non_animation_restyle(&self) -> Self {
- RestyleHint {
- match_under_self: self.match_under_self.propagate(),
- match_later_siblings: false,
- recascade: self.recascade.propagate(),
- replacements: RestyleReplacements::empty(),
- }
- }
-
- /// Removes all of the animation-related hints.
- #[inline]
- pub fn remove_animation_hints(&mut self) {
- self.replacements.remove(RestyleReplacements::for_animations());
-
- // While RECASCADE_SELF is not animation-specific, we only ever add and
- // process it during traversal. If we are here, removing animation
- // hints, then we are in an animation-only traversal, and we know that
- // any RECASCADE_SELF flag must have been set due to changes in
- // inherited values after restyling for animations, and thus we
- // want to remove it so that we don't later try to restyle the element
- // during a normal restyle. (We could have separate
- // RECASCADE_SELF_NORMAL and RECASCADE_SELF_ANIMATIONS flags to make it
- // clear, but this isn't currently necessary.)
- self.recascade.remove(RECASCADE_SELF);
- }
-
- /// Removes the later siblings hint, and returns whether it was present.
- pub fn remove_later_siblings_hint(&mut self) -> bool {
- let later_siblings = self.match_later_siblings;
- self.match_later_siblings = false;
- later_siblings
- }
-
- /// Unions the specified `RestyleHint` into this one.
- #[inline]
- pub fn insert_from(&mut self, other: &Self) {
- self.match_under_self.insert(other.match_under_self);
- self.match_later_siblings |= other.match_later_siblings;
- self.recascade.insert(other.recascade);
- self.replacements.insert(other.replacements);
- }
-
- /// Unions the specified `RestyleHint` into this one.
- #[inline]
- pub fn insert(&mut self, other: Self) {
- // A later patch should make it worthwhile to have an insert() function
- // that consumes its argument.
- self.insert_from(&other)
- }
-
- /// Inserts the specified `CascadeHint`.
- #[inline]
- pub fn insert_cascade_hint(&mut self, cascade_hint: CascadeHint) {
- self.recascade.insert(cascade_hint);
- }
-
- /// Returns whether this `RestyleHint` represents at least as much restyle
- /// work as the specified one.
- #[inline]
- pub fn contains(&self, other: &Self) -> bool {
- self.match_under_self.contains(other.match_under_self) &&
- (self.match_later_siblings & other.match_later_siblings) == other.match_later_siblings &&
- self.recascade.contains(other.recascade) &&
- self.replacements.contains(other.replacements)
- }
-}
-
-impl RestyleReplacements {
- /// The replacements for the animation cascade levels.
- #[inline]
- pub fn for_animations() -> Self {
- RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
- }
-}
-
-#[cfg(feature = "gecko")]
-impl From<nsRestyleHint> for RestyleReplacements {
- fn from(raw: nsRestyleHint) -> Self {
- Self::from_bits_truncate(raw.0 as u8)
- }
-}
-
-#[cfg(feature = "gecko")]
-impl From<nsRestyleHint> for RestyleHint {
- fn from(raw: nsRestyleHint) -> Self {
- use gecko_bindings::structs::nsRestyleHint_eRestyle_ForceDescendants as eRestyle_ForceDescendants;
- use gecko_bindings::structs::nsRestyleHint_eRestyle_LaterSiblings as eRestyle_LaterSiblings;
- use gecko_bindings::structs::nsRestyleHint_eRestyle_Self as eRestyle_Self;
- use gecko_bindings::structs::nsRestyleHint_eRestyle_SomeDescendants as eRestyle_SomeDescendants;
- use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree as eRestyle_Subtree;
-
- let mut match_under_self = RestyleDepths::empty();
- if (raw.0 & (eRestyle_Self.0 | eRestyle_Subtree.0)) != 0 {
- match_under_self.insert(RestyleDepths::for_self());
- }
- if (raw.0 & (eRestyle_Subtree.0 | eRestyle_SomeDescendants.0)) != 0 {
- match_under_self.insert(RestyleDepths::for_descendants());
- }
-
- let mut recascade = CascadeHint::empty();
- if (raw.0 & eRestyle_ForceDescendants.0) != 0 {
- recascade.insert(CascadeHint::subtree())
- }
-
- RestyleHint {
- match_under_self: match_under_self,
- match_later_siblings: (raw.0 & eRestyle_LaterSiblings.0) != 0,
- recascade: recascade,
- replacements: raw.into(),
- }
- }
-}
-
-#[cfg(feature = "servo")]
-impl HeapSizeOf for RestyleHint {
- fn heap_size_of_children(&self) -> usize { 0 }
-}
-
-/// In order to compute restyle hints, we perform a selector match against a
-/// list of partial selectors whose rightmost simple selector may be sensitive
-/// to the thing being changed. We do this matching twice, once for the element
-/// as it exists now and once for the element as it existed at the time of the
-/// last restyle. If the results of the selector match differ, that means that
-/// the given partial selector is sensitive to the change, and we compute a
-/// restyle hint based on its combinator.
-///
-/// In order to run selector matching against the old element state, we generate
-/// a wrapper for the element which claims to have the old state. This is the
-/// ElementWrapper logic below.
-///
-/// Gecko does this differently for element states, and passes a mask called
-/// mStateMask, which indicates the states that need to be ignored during
-/// selector matching. This saves an ElementWrapper allocation and an additional
-/// selector match call at the expense of additional complexity inside the
-/// selector matching logic. This only works for boolean states though, so we
-/// still need to take the ElementWrapper approach for attribute-dependent
-/// style. So we do it the same both ways for now to reduce complexity, but it's
-/// worth measuring the performance impact (if any) of the mStateMask approach.
-pub trait ElementSnapshot : Sized {
- /// The state of the snapshot, if any.
- fn state(&self) -> Option<ElementState>;
-
- /// If this snapshot contains attribute information.
- fn has_attrs(&self) -> bool;
-
- /// The ID attribute per this snapshot. Should only be called if
- /// `has_attrs()` returns true.
- fn id_attr(&self) -> Option<Atom>;
-
- /// Whether this snapshot contains the class `name`. Should only be called
- /// if `has_attrs()` returns true.
- fn has_class(&self, name: &Atom) -> bool;
-
- /// A callback that should be called for each class of the snapshot. Should
- /// only be called if `has_attrs()` returns true.
- fn each_class<F>(&self, F)
- where F: FnMut(&Atom);
-
- /// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
- fn lang_attr(&self) -> Option<AttrValue>;
-}
-
-#[derive(Clone)]
-struct ElementWrapper<'a, E>
- where E: TElement,
-{
- element: E,
- cached_snapshot: Cell<Option<&'a Snapshot>>,
- snapshot_map: &'a SnapshotMap,
-}
-
-impl<'a, E> ElementWrapper<'a, E>
- where E: TElement,
-{
- /// Trivially constructs an `ElementWrapper`.
- fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {
- ElementWrapper {
- element: el,
- cached_snapshot: Cell::new(None),
- snapshot_map: snapshot_map,
- }
- }
-
- /// Gets the snapshot associated with this element, if any.
- fn snapshot(&self) -> Option<&'a Snapshot> {
- if !self.element.has_snapshot() {
- return None;
- }
-
- if let Some(s) = self.cached_snapshot.get() {
- return Some(s);
- }
-
- let snapshot = self.snapshot_map.get(&self.element);
- debug_assert!(snapshot.is_some(), "has_snapshot lied!");
-
- self.cached_snapshot.set(snapshot);
-
- snapshot
- }
-
- fn state_changes(&self) -> ElementState {
- let snapshot = match self.snapshot() {
- Some(s) => s,
- None => return ElementState::empty(),
- };
-
- match snapshot.state() {
- Some(state) => state ^ self.element.get_state(),
- None => ElementState::empty(),
- }
- }
-
- /// Returns the value of the `xml:lang=""` (or, if appropriate, `lang=""`)
- /// attribute from this element's snapshot or the closest ancestor
- /// element snapshot with the attribute specified.
- fn get_lang(&self) -> Option<AttrValue> {
- let mut current = self.clone();
- loop {
- let lang = match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(),
- _ => current.element.lang_attr(),
- };
- if lang.is_some() {
- return lang;
- }
- match current.parent_element() {
- Some(parent) => current = parent,
- None => return None,
- }
- }
- }
-}
-
-impl<'a, E> fmt::Debug for ElementWrapper<'a, E>
- where E: TElement,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- // Ignore other fields for now, can change later if needed.
- self.element.fmt(f)
- }
-}
-
-#[cfg(feature = "gecko")]
-fn dir_selector_to_state(s: &[u16]) -> ElementState {
- // Jump through some hoops to deal with our Box<[u16]> thing.
- const LTR: [u16; 4] = [b'l' as u16, b't' as u16, b'r' as u16, 0];
- const RTL: [u16; 4] = [b'r' as u16, b't' as u16, b'l' as u16, 0];
- if LTR == *s {
- IN_LTR_STATE
- } else if RTL == *s {
- IN_RTL_STATE
- } else {
- // :dir(something-random) is a valid selector, but shouldn't
- // match anything.
- ElementState::empty()
- }
-}
-
-impl<'a, E> Element for ElementWrapper<'a, E>
- where E: TElement,
-{
- type Impl = SelectorImpl;
-
- fn match_non_ts_pseudo_class<F>(&self,
- pseudo_class: &NonTSPseudoClass,
- context: &mut LocalMatchingContext<Self::Impl>,
- relevant_link: &RelevantLinkStatus,
- _setter: &mut F)
- -> bool
- where F: FnMut(&Self, ElementSelectorFlags),
- {
- // Some pseudo-classes need special handling to evaluate them against
- // the snapshot.
- match *pseudo_class {
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozAny(ref selectors) => {
- use selectors::matching::matches_complex_selector;
- return selectors.iter().any(|s| {
- matches_complex_selector(s, 0, self, context, _setter)
- })
- }
-
- // :dir is implemented in terms of state flags, but which state flag
- // it maps to depends on the argument to :dir. That means we can't
- // just add its state flags to the NonTSPseudoClass, because if we
- // added all of them there, and tested via intersects() here, we'd
- // get incorrect behavior for :not(:dir()) cases.
- //
- // FIXME(bz): How can I set this up so once Servo adds :dir()
- // support we don't forget to update this code?
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::Dir(ref s) => {
- let selector_flag = dir_selector_to_state(s);
- if selector_flag.is_empty() {
- // :dir() with some random argument; does not match.
- return false;
- }
- let state = match self.snapshot().and_then(|s| s.state()) {
- Some(snapshot_state) => snapshot_state,
- None => self.element.get_state(),
- };
- return state.contains(selector_flag);
- }
-
- // For :link and :visited, we don't actually want to test the element
- // state directly. Instead, we use the `relevant_link` to determine if
- // they match.
- NonTSPseudoClass::Link => {
- return relevant_link.is_unvisited(self, context.shared);
- }
- NonTSPseudoClass::Visited => {
- return relevant_link.is_visited(self, context.shared);
- }
-
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozTableBorderNonzero => {
- if let Some(snapshot) = self.snapshot() {
- if snapshot.has_other_pseudo_class_state() {
- return snapshot.mIsTableBorderNonzero();
- }
- }
- }
-
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozBrowserFrame => {
- if let Some(snapshot) = self.snapshot() {
- if snapshot.has_other_pseudo_class_state() {
- return snapshot.mIsMozBrowserFrame();
- }
- }
- }
-
- // :lang() needs to match using the closest ancestor xml:lang="" or
- // lang="" attribtue from snapshots.
- NonTSPseudoClass::Lang(ref lang_arg) => {
- return self.element.match_element_lang(Some(self.get_lang()), lang_arg);
- }
-
- _ => {}
- }
-
- let flag = pseudo_class.state_flag();
- if flag.is_empty() {
- return self.element.match_non_ts_pseudo_class(pseudo_class,
- context,
- relevant_link,
- &mut |_, _| {})
- }
- match self.snapshot().and_then(|s| s.state()) {
- Some(snapshot_state) => snapshot_state.intersects(flag),
- None => {
- self.element.match_non_ts_pseudo_class(pseudo_class,
- context,
- relevant_link,
- &mut |_, _| {})
- }
- }
- }
-
- fn match_pseudo_element(&self,
- pseudo_element: &PseudoElement,
- context: &mut MatchingContext)
- -> bool
- {
- self.element.match_pseudo_element(pseudo_element, context)
- }
-
- fn is_link(&self) -> bool {
- self.element.is_link()
- }
-
- fn parent_element(&self) -> Option<Self> {
- self.element.parent_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-
- fn first_child_element(&self) -> Option<Self> {
- self.element.first_child_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-
- fn last_child_element(&self) -> Option<Self> {
- self.element.last_child_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-
- fn prev_sibling_element(&self) -> Option<Self> {
- self.element.prev_sibling_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-
- fn next_sibling_element(&self) -> Option<Self> {
- self.element.next_sibling_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-
- fn is_html_element_in_html_document(&self) -> bool {
- self.element.is_html_element_in_html_document()
- }
-
- fn get_local_name(&self) -> &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName {
- self.element.get_local_name()
- }
-
- fn get_namespace(&self) -> &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl {
- self.element.get_namespace()
- }
-
- fn attr_matches(&self,
- ns: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- operation: &AttrSelectorOperation<&AttrValue>)
- -> bool {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => {
- snapshot.attr_matches(ns, local_name, operation)
- }
- _ => self.element.attr_matches(ns, local_name, operation)
- }
- }
-
- fn get_id(&self) -> Option<Atom> {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs()
- => snapshot.id_attr(),
- _ => self.element.get_id()
- }
- }
-
- fn has_class(&self, name: &Atom) -> bool {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs()
- => snapshot.has_class(name),
- _ => self.element.has_class(name)
- }
- }
-
- fn is_empty(&self) -> bool {
- self.element.is_empty()
- }
-
- fn is_root(&self) -> bool {
- self.element.is_root()
- }
-
- fn pseudo_element_originating_element(&self) -> Option<Self> {
- self.element.closest_non_native_anonymous_ancestor()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-}
-
-fn selector_to_state(sel: &Component<SelectorImpl>) -> ElementState {
- match *sel {
- // FIXME(bz): How can I set this up so once Servo adds :dir() support we
- // don't forget to update this code?
- #[cfg(feature = "gecko")]
- Component::NonTSPseudoClass(NonTSPseudoClass::Dir(ref s)) => dir_selector_to_state(s),
- Component::NonTSPseudoClass(ref pc) => pc.state_flag(),
- _ => ElementState::empty(),
- }
-}
-
-fn is_attr_based_selector(sel: &Component<SelectorImpl>) -> bool {
- match *sel {
- Component::ID(_) |
- Component::Class(_) |
- Component::AttributeInNoNamespaceExists { .. } |
- Component::AttributeInNoNamespace { .. } |
- Component::AttributeOther(_) => true,
- Component::NonTSPseudoClass(ref pc) => pc.is_attr_based(),
- _ => false,
- }
-}
-
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-/// The characteristics that a selector is sensitive to.
-pub struct Sensitivities {
- /// The states which the selector is sensitive to.
- pub states: ElementState,
- /// Whether the selector is sensitive to attributes.
- pub attrs: bool,
-}
-
-impl Sensitivities {
- fn is_empty(&self) -> bool {
- self.states.is_empty() && !self.attrs
- }
-
- fn new() -> Sensitivities {
- Sensitivities {
- states: ElementState::empty(),
- attrs: false,
- }
- }
-
- fn sensitive_to(&self, attrs: bool, states: ElementState) -> bool {
- (attrs && self.attrs) || self.states.intersects(states)
- }
-}
-
-/// Mapping between (partial) CompoundSelectors (and the combinator to their
-/// right) and the states and attributes they depend on.
-///
-/// In general, for all selectors in all applicable stylesheets of the form:
-///
-/// |a _ b _ c _ d _ e|
-///
-/// Where:
-/// * |b| and |d| are simple selectors that depend on state (like :hover) or
-/// attributes (like [attr...], .foo, or #foo).
-/// * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on
-/// state or attributes.
-///
-/// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|,
-/// even though those selectors may not appear on their own in any stylesheet.
-/// This allows us to quickly scan through the dependency sites of all style
-/// rules and determine the maximum effect that a given state or attribute
-/// change may have on the style of elements in the document.
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub struct Dependency {
- /// The dependency selector.
- #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
- selector: Selector<SelectorImpl>,
- /// The offset into the selector that we should match on.
- selector_offset: usize,
- /// The ancestor hashes associated with the above selector at the given
- /// offset.
- #[cfg_attr(feature = "servo", ignore_heap_size_of = "No heap data")]
- hashes: AncestorHashes,
- /// The hint associated with this dependency.
- pub hint: RestyleHint,
- /// The sensitivities associated with this dependency.
- pub sensitivities: Sensitivities,
-}
-
-impl SelectorMapEntry for Dependency {
- fn selector(&self) -> SelectorIter<SelectorImpl> {
- self.selector.iter_from(self.selector_offset)
- }
-
- fn hashes(&self) -> &AncestorHashes {
- &self.hashes
- }
-}
-
-/// The following visitor visits all the simple selectors for a given complex
-/// selector, taking care of :not and :any combinators, collecting whether any
-/// of them is sensitive to attribute or state changes.
-struct SensitivitiesVisitor {
- sensitivities: Sensitivities,
-}
-
-impl SelectorVisitor for SensitivitiesVisitor {
- type Impl = SelectorImpl;
- fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
- self.sensitivities.states.insert(selector_to_state(s));
- self.sensitivities.attrs |= is_attr_based_selector(s);
- true
- }
-}
-
-/// A set of dependencies for a given stylist.
-///
-/// Note that we can have many dependencies, often more than the total number
-/// of selectors given that we can get multiple partial selectors for a given
-/// selector. As such, we want all the usual optimizations, including the
-/// SelectorMap and the bloom filter.
-#[derive(Debug)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub struct DependencySet {
- /// This is for all other normal element's selectors/selector parts.
- dependencies: SelectorMap<Dependency>,
-}
-
-/// The data that we need to compute a given restyle hint.
-pub enum HintComputationContext<'a, E: 'a>
- where E: TElement,
-{
- /// The data we need to compute a restyle hint for the root of the
- /// traversal.
- ///
- /// We don't bother with the bloom filter there for multiple reasons:
- ///
- /// * The root of the traversal uses to be the root of the document, so we
- /// don't gain much using a bloom filter.
- ///
- /// * The chances that a non-root-element root of the traversal has a
- /// snapshot is quite low.
- Root,
-
- /// The data we need to compute a restyle hint for a child.
- ///
- /// This needs a full-blown style context in order to get the selector
- /// filters up-to-date, and the dom depth in order to insert into the filter
- /// properly if needed.
- Child {
- /// The thread-local context, that holds the bloom filter alive.
- local_context: &'a mut ThreadLocalStyleContext<E>,
- /// The dom depth of this element.
- dom_depth: usize,
- }
-}
-
-impl DependencySet {
- /// Adds a selector to this `DependencySet`.
- pub fn note_selector(&mut self, selector_and_hashes: &SelectorAndHashes<SelectorImpl>) {
- let mut combinator = None;
- let mut iter = selector_and_hashes.selector.iter();
- let mut index = 0;
- let mut child_combinators_seen = 0;
- let mut saw_descendant_combinator = false;
-
- loop {
- let sequence_start = index;
- let mut visitor = SensitivitiesVisitor {
- sensitivities: Sensitivities::new()
- };
-
- // Visit all the simple selectors in this sequence.
- //
- // Note that this works because we can't have combinators nested
- // inside simple selectors (i.e. in :not() or :-moz-any()). If we
- // ever support that we'll need to visit complex selectors as well.
- for ss in &mut iter {
- ss.visit(&mut visitor);
- index += 1; // Account for the simple selector.
- }
-
- // Keep track of how many child combinators we've encountered,
- // and whether we've encountered a descendant combinator at all.
- match combinator {
- Some(Combinator::Child) => child_combinators_seen += 1,
- Some(Combinator::Descendant) => saw_descendant_combinator = true,
- _ => {}
- }
-
- // If we found a sensitivity, add an entry in the dependency set.
- if !visitor.sensitivities.is_empty() {
- // Compute a RestyleHint given the current combinator and the
- // tracked number of child combinators and presence of a
- // descendant combinator.
- let hint = match combinator {
- // NB: RestyleHint::subtree() and not
- // RestyleHint::descendants() is needed to handle properly
- // eager pseudos, otherwise we may leave a stale style on
- // the parent.
- Some(Combinator::PseudoElement) => RestyleHint::subtree(),
- Some(Combinator::Child) if !saw_descendant_combinator => {
- RestyleHint::descendants_at_depth(child_combinators_seen)
- }
- Some(Combinator::Child) |
- Some(Combinator::Descendant) => RestyleHint::descendants(),
- Some(Combinator::NextSibling) |
- Some(Combinator::LaterSibling) => RestyleHint::later_siblings(),
- None => RestyleHint::for_self(),
- };
-
- // Reuse the bloom hashes if this is the base selector. Otherwise,
- // rebuild them.
- let hashes = if sequence_start == 0 {
- selector_and_hashes.hashes.clone()
- } else {
- let seq_iter = selector_and_hashes.selector.iter_from(sequence_start);
- AncestorHashes::from_iter(seq_iter)
- };
-
- self.dependencies.insert(Dependency {
- sensitivities: visitor.sensitivities,
- hint: hint,
- selector: selector_and_hashes.selector.clone(),
- selector_offset: sequence_start,
- hashes: hashes,
- });
- }
-
- combinator = iter.next_sequence();
- if combinator.is_none() {
- break;
- }
-
- index += 1; // Account for the combinator.
- }
- }
-
- /// Create an empty `DependencySet`.
- pub fn new() -> Self {
- DependencySet {
- dependencies: SelectorMap::new(),
- }
- }
-
- /// Return the total number of dependencies that this set contains.
- pub fn len(&self) -> usize {
- self.dependencies.len()
- }
-
- /// Clear this dependency set.
- pub fn clear(&mut self) {
- self.dependencies = SelectorMap::new();
- }
-
- /// Compute a restyle hint given an element and a snapshot, per the rules
- /// explained in the rest of the documentation.
- pub fn compute_hint<'a, E>(
- &self,
- el: &E,
- shared_context: &SharedStyleContext,
- hint_context: HintComputationContext<'a, E>)
- -> RestyleHint
- where E: TElement,
- {
- debug_assert!(el.has_snapshot(), "Shouldn't be here!");
- let snapshot_el =
- ElementWrapper::new(*el, shared_context.snapshot_map);
- let snapshot =
- snapshot_el.snapshot().expect("has_snapshot lied so badly");
-
- let state_changes = snapshot_el.state_changes();
- let attrs_changed = snapshot.has_attrs();
- if state_changes.is_empty() && !attrs_changed {
- return RestyleHint::empty();
- }
-
- let mut hint = RestyleHint::empty();
-
- // If we are sensitive to visitedness and the visited state changed, we
- // force a restyle here. Matching doesn't depend on the actual visited
- // state at all, so we can't look at matching results to decide what to
- // do for this case.
- if state_changes.intersects(IN_VISITED_OR_UNVISITED_STATE) {
- trace!(" > visitedness change, force subtree restyle");
- // We can't just return here because there may also be attribute
- // changes as well that imply additional hints.
- hint = RestyleHint::subtree();
- }
-
- // Compute whether the snapshot has any different id or class attributes
- // from the element. If it does, we need to pass those to the lookup, so
- // that we get all the possible applicable selectors from the rulehash.
- let mut additional_id = None;
- let mut additional_classes = SmallVec::<[Atom; 8]>::new();
- if attrs_changed {
- let id = snapshot.id_attr();
- if id.is_some() && id != el.get_id() {
- additional_id = id;
- }
-
- snapshot.each_class(|c| {
- if !el.has_class(c) {
- additional_classes.push(c.clone())
- }
- });
- }
-
- let bloom_filter = match hint_context {
- HintComputationContext::Root => None,
- HintComputationContext::Child { mut local_context, dom_depth } => {
- local_context
- .bloom_filter
- .insert_parents_recovering(*el, dom_depth);
- local_context.bloom_filter.assert_complete(*el);
- Some(local_context.bloom_filter.filter())
- }
- };
-
- let lookup_element = if el.implemented_pseudo_element().is_some() {
- el.closest_non_native_anonymous_ancestor().unwrap()
- } else {
- *el
- };
-
- self.dependencies
- .lookup_with_additional(lookup_element, additional_id, &additional_classes, &mut |dep| {
- trace!("scanning dependency: {:?}", dep);
-
- if !dep.sensitivities.sensitive_to(attrs_changed,
- state_changes) {
- trace!(" > non-sensitive");
- return true;
- }
-
- if hint.contains(&dep.hint) {
- trace!(" > hint was already there");
- return true;
- }
-
- // NOTE(emilio): We can't use the bloom filter for snapshots, given
- // that arbitrary elements in the parent chain may have mutated
- // their id's/classes, which means that they won't be in the
- // filter, and as such we may fast-reject selectors incorrectly.
- //
- // We may be able to improve this if we record as we go down the
- // tree whether any parent had a snapshot, and whether those
- // snapshots were taken due to an element class/id change, but it's
- // not clear we _need_ it right now.
- let mut then_context =
- MatchingContext::new_for_visited(MatchingMode::Normal, None,
- VisitedHandlingMode::AllLinksUnvisited,
- shared_context.quirks_mode);
- let matched_then =
- matches_selector(&dep.selector,
- dep.selector_offset,
- &dep.hashes,
- &snapshot_el,
- &mut then_context,
- &mut |_, _| {});
- let mut now_context =
- MatchingContext::new_for_visited(MatchingMode::Normal, bloom_filter,
- VisitedHandlingMode::AllLinksUnvisited,
- shared_context.quirks_mode);
- let matches_now =
- matches_selector(&dep.selector,
- dep.selector_offset,
- &dep.hashes,
- el,
- &mut now_context,
- &mut |_, _| {});
-
- // Check for mismatches in both the match result and also the status
- // of whether a relevant link was found.
- if matched_then != matches_now ||
- then_context.relevant_link_found != now_context.relevant_link_found {
- hint.insert_from(&dep.hint);
- return !hint.is_maximum()
- }
-
- // If there is a relevant link, then we also matched in visited
- // mode. Match again in this mode to ensure this also matches.
- // Note that we never actually match directly against the element's
- // true visited state at all, since that would expose us to timing
- // attacks. The matching process only considers the relevant link
- // state and visited handling mode when deciding if visited
- // matches. Instead, we are rematching here in case there is some
- // :visited selector whose matching result changed for some _other_
- // element state or attribute.
- if now_context.relevant_link_found &&
- dep.sensitivities.states.intersects(IN_VISITED_OR_UNVISITED_STATE) {
- then_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
- let matched_then =
- matches_selector(&dep.selector,
- dep.selector_offset,
- &dep.hashes,
- &snapshot_el,
- &mut then_context,
- &mut |_, _| {});
- now_context.visited_handling = VisitedHandlingMode::RelevantLinkVisited;
- let matches_now =
- matches_selector(&dep.selector,
- dep.selector_offset,
- &dep.hashes,
- el,
- &mut now_context,
- &mut |_, _| {});
- if matched_then != matches_now {
- hint.insert_from(&dep.hint);
- return !hint.is_maximum()
- }
- }
-
- !hint.is_maximum()
- });
-
- debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",
- hint, el, el.get_state(), self.len());
-
- hint
- }
-}
diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs
index be563d2230d..198999f61b1 100644
--- a/components/style/rule_tree/mod.rs
+++ b/components/style/rule_tree/mod.rs
@@ -6,17 +6,18 @@
//! The rule tree.
+use applicable_declarations::ApplicableDeclarationList;
#[cfg(feature = "servo")]
use heapsize::HeapSizeOf;
use properties::{AnimationRules, Importance, LonghandIdSet, PropertyDeclarationBlock};
use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
use smallvec::SmallVec;
use std::io::{self, Write};
+use std::mem;
use std::ptr;
use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use stylearc::{Arc, NonZeroPtrMut};
use stylesheets::StyleRule;
-use stylist::ApplicableDeclarationList;
use thread_state;
/// The rule tree, the structure servo uses to preserve the results of selector
@@ -229,7 +230,7 @@ impl RuleTree {
guards: &StylesheetGuards)
-> StrongRuleNode
{
- let rules = applicable_declarations.drain().map(|d| (d.source, d.level));
+ let rules = applicable_declarations.drain().map(|d| d.order_and_level());
let rule_node = self.insert_ordered_rules_with_important(rules, guards);
rule_node
}
@@ -425,10 +426,18 @@ pub enum CascadeLevel {
/// User-agent important rules.
UAImportant,
/// Transitions
+ ///
+ /// NB: If this changes from being last, change from_byte below.
Transitions,
}
impl CascadeLevel {
+ /// Converts a raw byte to a CascadeLevel.
+ pub unsafe fn from_byte(byte: u8) -> Self {
+ debug_assert!(byte <= CascadeLevel::Transitions as u8);
+ mem::transmute(byte)
+ }
+
/// Select a lock guard for this level
pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
match *self {
@@ -563,6 +572,17 @@ impl RuleNode {
self.parent.is_none()
}
+ fn next_sibling(&self) -> Option<WeakRuleNode> {
+ // We use acquire semantics here to ensure proper synchronization while
+ // inserting in the child list.
+ let ptr = self.next_sibling.load(Ordering::Acquire);
+ if ptr.is_null() {
+ None
+ } else {
+ Some(WeakRuleNode::from_ptr(ptr))
+ }
+ }
+
/// Remove this rule node from the child list.
///
/// This method doesn't use proper synchronization, and it's expected to be
@@ -628,7 +648,7 @@ impl RuleNode {
let _ = write!(writer, "\n");
for child in self.iter_children() {
- child.get().dump(guards, writer, indent + INDENT_INCREMENT);
+ child.upgrade().get().dump(guards, writer, indent + INDENT_INCREMENT);
}
}
@@ -683,31 +703,30 @@ impl StrongRuleNode {
WeakRuleNode::from_ptr(self.ptr())
}
- fn next_sibling(&self) -> Option<WeakRuleNode> {
- // We use acquire semantics here to ensure proper synchronization while
- // inserting in the child list.
- let ptr = self.get().next_sibling.load(Ordering::Acquire);
- if ptr.is_null() {
- None
- } else {
- Some(WeakRuleNode::from_ptr(ptr))
- }
- }
-
fn parent(&self) -> Option<&StrongRuleNode> {
self.get().parent.as_ref()
}
- fn ensure_child(&self,
- root: WeakRuleNode,
- source: StyleSource,
- level: CascadeLevel) -> StrongRuleNode {
+ fn ensure_child(
+ &self,
+ root: WeakRuleNode,
+ source: StyleSource,
+ level: CascadeLevel
+ ) -> StrongRuleNode {
let mut last = None;
- // TODO(emilio): We could avoid all the refcount churn here.
+
+ // NB: This is an iterator over _weak_ nodes.
+ //
+ // It's fine though, because nothing can make us GC while this happens,
+ // and this happens to be hot.
+ //
+ // TODO(emilio): We could actually make this even less hot returning a
+ // WeakRuleNode, and implementing this on WeakRuleNode itself...
for child in self.get().iter_children() {
- if child.get().level == level &&
- child.get().source.as_ref().unwrap().ptr_equals(&source) {
- return child;
+ let child_node = unsafe { &*child.ptr() };
+ if child_node.level == level &&
+ child_node.source.as_ref().unwrap().ptr_equals(&source) {
+ return child.upgrade();
}
last = Some(child);
}
@@ -719,11 +738,12 @@ impl StrongRuleNode {
let new_ptr: *mut RuleNode = &mut *node;
loop {
- let strong;
+ let next;
{
- let next_sibling_ptr = match last {
- Some(ref l) => &l.get().next_sibling,
+ let last_node = last.as_ref().map(|l| unsafe { &*l.ptr() });
+ let next_sibling_ptr = match last_node {
+ Some(ref l) => &l.next_sibling,
None => &self.get().first_child,
};
@@ -748,17 +768,17 @@ impl StrongRuleNode {
// Existing is not null: some thread inserted a child node since
// we accessed `last`.
- strong = WeakRuleNode::from_ptr(existing).upgrade();
+ next = WeakRuleNode::from_ptr(existing);
- if strong.get().source.as_ref().unwrap().ptr_equals(&source) {
+ if unsafe { &*next.ptr() }.source.as_ref().unwrap().ptr_equals(&source) {
// That node happens to be for the same style source, use
// that, and let node fall out of scope.
- return strong;
+ return next.upgrade();
}
}
// Try again inserting after the new last child.
- last = Some(strong);
+ last = Some(next);
}
}
@@ -1357,12 +1377,11 @@ struct RuleChildrenListIter {
}
impl Iterator for RuleChildrenListIter {
- type Item = StrongRuleNode;
+ type Item = WeakRuleNode;
fn next(&mut self) -> Option<Self::Item> {
self.current.take().map(|current| {
- let current = current.upgrade();
- self.current = current.next_sibling();
+ self.current = unsafe { &*current.ptr() }.next_sibling();
current
})
}
diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs
index a65b664ad38..48d25231b25 100644
--- a/components/style/selector_map.rs
+++ b/components/style/selector_map.rs
@@ -6,6 +6,8 @@
//! name, ids and hash.
use {Atom, LocalName};
+use applicable_declarations::ApplicableDeclarationBlock;
+use context::QuirksMode;
use dom::TElement;
use fnv::FnvHashMap;
use pdqsort::sort_by;
@@ -15,10 +17,10 @@ use selectors::matching::{matches_selector, MatchingContext, ElementSelectorFlag
use selectors::parser::{AncestorHashes, Component, Combinator, SelectorAndHashes, SelectorIter};
use selectors::parser::LocalName as LocalNameSelector;
use smallvec::VecLike;
-use std::borrow::Borrow;
use std::collections::HashMap;
+use std::collections::hash_map;
use std::hash::Hash;
-use stylist::{ApplicableDeclarationBlock, Rule};
+use stylist::Rule;
/// A trait to abstract over a given selector map entry.
pub trait SelectorMapEntry : Sized + Clone {
@@ -65,9 +67,9 @@ impl SelectorMapEntry for SelectorAndHashes<SelectorImpl> {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct SelectorMap<T: SelectorMapEntry> {
/// A hash from an ID to rules which contain that ID selector.
- pub id_hash: FnvHashMap<Atom, Vec<T>>,
+ pub id_hash: MaybeCaseInsensitiveHashMap<Atom, Vec<T>>,
/// A hash from a class name to rules which contain that class selector.
- pub class_hash: FnvHashMap<Atom, Vec<T>>,
+ pub class_hash: MaybeCaseInsensitiveHashMap<Atom, Vec<T>>,
/// A hash from local name to rules which contain that local name selector.
pub local_name_hash: FnvHashMap<LocalName, Vec<T>>,
/// Rules that don't have ID, class, or element selectors.
@@ -85,8 +87,8 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
/// Trivially constructs an empty `SelectorMap`.
pub fn new() -> Self {
SelectorMap {
- id_hash: HashMap::default(),
- class_hash: HashMap::default(),
+ id_hash: MaybeCaseInsensitiveHashMap::new(),
+ class_hash: MaybeCaseInsensitiveHashMap::new(),
local_name_hash: HashMap::default(),
other: Vec::new(),
count: 0,
@@ -114,6 +116,7 @@ impl SelectorMap<Rule> {
rule_hash_target: &E,
matching_rules_list: &mut V,
context: &mut MatchingContext,
+ quirks_mode: QuirksMode,
flags_setter: &mut F,
cascade_level: CascadeLevel)
where E: TElement,
@@ -127,32 +130,35 @@ impl SelectorMap<Rule> {
// At the end, we're going to sort the rules that we added, so remember where we began.
let init_len = matching_rules_list.len();
if let Some(id) = rule_hash_target.get_id() {
- SelectorMap::get_matching_rules_from_hash(element,
- &self.id_hash,
- &id,
- matching_rules_list,
- context,
- flags_setter,
- cascade_level)
+ if let Some(rules) = self.id_hash.get(&id, quirks_mode) {
+ SelectorMap::get_matching_rules(element,
+ rules,
+ matching_rules_list,
+ context,
+ flags_setter,
+ cascade_level)
+ }
}
rule_hash_target.each_class(|class| {
- SelectorMap::get_matching_rules_from_hash(element,
- &self.class_hash,
- class,
- matching_rules_list,
- context,
- flags_setter,
- cascade_level);
+ if let Some(rules) = self.class_hash.get(&class, quirks_mode) {
+ SelectorMap::get_matching_rules(element,
+ rules,
+ matching_rules_list,
+ context,
+ flags_setter,
+ cascade_level)
+ }
});
- SelectorMap::get_matching_rules_from_hash(element,
- &self.local_name_hash,
- rule_hash_target.get_local_name(),
- matching_rules_list,
- context,
- flags_setter,
- cascade_level);
+ if let Some(rules) = self.local_name_hash.get(rule_hash_target.get_local_name()) {
+ SelectorMap::get_matching_rules(element,
+ rules,
+ matching_rules_list,
+ context,
+ flags_setter,
+ cascade_level)
+ }
SelectorMap::get_matching_rules(element,
&self.other,
@@ -163,13 +169,7 @@ impl SelectorMap<Rule> {
// Sort only the rules we just added.
sort_by_key(&mut matching_rules_list[init_len..],
- |block| (block.specificity, block.source_order));
- }
-
- /// Check whether we have rules for the given id
- #[inline]
- pub fn has_rules_for_id(&self, id: &Atom) -> bool {
- self.id_hash.get(id).is_some()
+ |block| (block.specificity, block.source_order()));
}
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
@@ -190,35 +190,11 @@ impl SelectorMap<Rule> {
}
sort_by_key(&mut rules_list,
- |block| (block.specificity, block.source_order));
+ |block| (block.specificity, block.source_order()));
rules_list
}
- fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
- element: &E,
- hash: &FnvHashMap<Str, Vec<Rule>>,
- key: &BorrowedStr,
- matching_rules: &mut Vector,
- context: &mut MatchingContext,
- flags_setter: &mut F,
- cascade_level: CascadeLevel)
- where E: TElement,
- Str: Borrow<BorrowedStr> + Eq + Hash,
- BorrowedStr: Eq + Hash,
- Vector: VecLike<ApplicableDeclarationBlock>,
- F: FnMut(&E, ElementSelectorFlags),
- {
- if let Some(rules) = hash.get(key) {
- SelectorMap::get_matching_rules(element,
- rules,
- matching_rules,
- context,
- flags_setter,
- cascade_level)
- }
- }
-
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
fn get_matching_rules<E, V, F>(element: &E,
rules: &[Rule],
@@ -246,16 +222,16 @@ impl SelectorMap<Rule> {
impl<T: SelectorMapEntry> SelectorMap<T> {
/// Inserts into the correct hash, trying id, class, and localname.
- pub fn insert(&mut self, entry: T) {
+ pub fn insert(&mut self, entry: T, quirks_mode: QuirksMode) {
self.count += 1;
if let Some(id_name) = get_id_name(entry.selector()) {
- find_push(&mut self.id_hash, id_name, entry);
+ self.id_hash.entry(id_name, quirks_mode).or_insert_with(Vec::new).push(entry);
return;
}
if let Some(class_name) = get_class_name(entry.selector()) {
- find_push(&mut self.class_hash, class_name, entry);
+ self.class_hash.entry(class_name, quirks_mode).or_insert_with(Vec::new).push(entry);
return;
}
@@ -292,13 +268,13 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
/// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
/// but that function is extremely hot and I'd rather not rearrange it.
#[inline]
- pub fn lookup<E, F>(&self, element: E, f: &mut F) -> bool
+ pub fn lookup<E, F>(&self, element: E, quirks_mode: QuirksMode, f: &mut F) -> bool
where E: TElement,
F: FnMut(&T) -> bool
{
// Id.
if let Some(id) = element.get_id() {
- if let Some(v) = self.id_hash.get(&id) {
+ if let Some(v) = self.id_hash.get(&id, quirks_mode) {
for entry in v.iter() {
if !f(&entry) {
return false;
@@ -311,7 +287,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
let mut done = false;
element.each_class(|class| {
if !done {
- if let Some(v) = self.class_hash.get(class) {
+ if let Some(v) = self.class_hash.get(class, quirks_mode) {
for entry in v.iter() {
if !f(&entry) {
done = true;
@@ -354,7 +330,8 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
#[inline]
pub fn lookup_with_additional<E, F>(&self,
element: E,
- additional_id: Option<Atom>,
+ quirks_mode: QuirksMode,
+ additional_id: Option<&Atom>,
additional_classes: &[Atom],
f: &mut F)
-> bool
@@ -362,13 +339,13 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
F: FnMut(&T) -> bool
{
// Do the normal lookup.
- if !self.lookup(element, f) {
+ if !self.lookup(element, quirks_mode, f) {
return false;
}
// Check the additional id.
if let Some(id) = additional_id {
- if let Some(v) = self.id_hash.get(&id) {
+ if let Some(v) = self.id_hash.get(id, quirks_mode) {
for entry in v.iter() {
if !f(&entry) {
return false;
@@ -379,7 +356,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
// Check the additional classes.
for class in additional_classes {
- if let Some(v) = self.class_hash.get(class) {
+ if let Some(v) = self.class_hash.get(class, quirks_mode) {
for entry in v.iter() {
if !f(&entry) {
return false;
@@ -471,3 +448,42 @@ fn find_push<Str: Eq + Hash, V>(map: &mut FnvHashMap<Str, Vec<V>>,
value: V) {
map.entry(key).or_insert_with(Vec::new).push(value)
}
+
+/// Wrapper for FnvHashMap that does ASCII-case-insensitive lookup in quirks mode.
+#[derive(Debug)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+pub struct MaybeCaseInsensitiveHashMap<K: Hash + Eq, V>(FnvHashMap<K, V>);
+
+impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
+ /// Empty map
+ pub fn new() -> Self {
+ MaybeCaseInsensitiveHashMap(FnvHashMap::default())
+ }
+
+ /// HashMap::entry
+ pub fn entry(&mut self, mut key: Atom, quirks_mode: QuirksMode) -> hash_map::Entry<Atom, V> {
+ if quirks_mode == QuirksMode::Quirks {
+ key = key.to_ascii_lowercase()
+ }
+ self.0.entry(key)
+ }
+
+ /// HashMap::iter
+ pub fn iter(&self) -> hash_map::Iter<Atom, V> {
+ self.0.iter()
+ }
+
+ /// HashMap::clear
+ pub fn clear(&mut self) {
+ self.0.clear()
+ }
+
+ /// HashMap::get
+ pub fn get(&self, key: &Atom, quirks_mode: QuirksMode) -> Option<&V> {
+ if quirks_mode == QuirksMode::Quirks {
+ self.0.get(&key.to_ascii_lowercase())
+ } else {
+ self.0.get(key)
+ }
+ }
+}
diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs
index 289884c28f3..652daa5417c 100644
--- a/components/style/servo/selector_parser.rs
+++ b/components/style/servo/selector_parser.rs
@@ -6,16 +6,16 @@
//! Servo's selector parser.
-use {Atom, Prefix, Namespace, LocalName};
+use {Atom, Prefix, Namespace, LocalName, CaseSensitivityExt};
use attr::{AttrIdentifier, AttrValue};
use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
use dom::{OpaqueNode, TElement, TNode};
use element_state::ElementState;
use fnv::FnvHashMap;
-use restyle_hints::ElementSnapshot;
+use invalidation::element::element_wrapper::ElementSnapshot;
use selector_parser::{AttrValue as SelectorAttrValue, ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::Element;
-use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
+use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
use selectors::parser::{SelectorMethods, SelectorParseError};
use selectors::visitor::SelectorVisitor;
use std::ascii::AsciiExt;
@@ -545,6 +545,12 @@ pub struct ServoElementSnapshot {
pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
/// Whether this element is an HTML element in an HTML document.
pub is_html_element_in_html_document: bool,
+ /// Whether the class attribute changed or not.
+ pub class_changed: bool,
+ /// Whether the id attribute changed or not.
+ pub id_changed: bool,
+ /// Whether other attributes other than id or class changed or not.
+ pub other_attributes_changed: bool,
}
impl ServoElementSnapshot {
@@ -554,9 +560,27 @@ impl ServoElementSnapshot {
state: None,
attrs: None,
is_html_element_in_html_document: is_html_element_in_html_document,
+ class_changed: false,
+ id_changed: false,
+ other_attributes_changed: false,
}
}
+ /// Returns whether the id attribute changed or not.
+ pub fn id_changed(&self) -> bool {
+ self.id_changed
+ }
+
+ /// Returns whether the class attribute changed or not.
+ pub fn class_changed(&self) -> bool {
+ self.class_changed
+ }
+
+ /// Returns whether other attributes other than id or class changed or not.
+ pub fn other_attr_changed(&self) -> bool {
+ self.other_attributes_changed
+ }
+
fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
self.attrs.as_ref().unwrap().iter()
.find(|&&(ref ident, _)| ident.local_name == *name &&
@@ -584,9 +608,9 @@ impl ElementSnapshot for ServoElementSnapshot {
self.get_attr(&ns!(), &local_name!("id")).map(|v| v.as_atom().clone())
}
- fn has_class(&self, name: &Atom) -> bool {
+ fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
self.get_attr(&ns!(), &local_name!("class"))
- .map_or(false, |v| v.as_tokens().iter().any(|atom| atom == name))
+ .map_or(false, |v| v.as_tokens().iter().any(|atom| case_sensitivity.eq_atom(atom, name)))
}
fn each_class<F>(&self, mut callback: F)
diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs
index 3c17f576156..a1cedbd4b73 100644
--- a/components/style/sharing/mod.rs
+++ b/components/style/sharing/mod.rs
@@ -65,6 +65,7 @@
//! elements makes sense.
use Atom;
+use applicable_declarations::ApplicableDeclarationBlock;
use bit_vec::BitVec;
use bloom::StyleBloom;
use cache::{LRUCache, LRUCacheMutIterator};
@@ -78,7 +79,7 @@ use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode, StyleRelati
use smallvec::SmallVec;
use std::mem;
use std::ops::Deref;
-use stylist::{ApplicableDeclarationBlock, Stylist};
+use stylist::Stylist;
mod checks;
@@ -376,38 +377,38 @@ impl<E: TElement> StyleSharingTarget<E> {
// We used to have pseudos (because we had styles).
// Check for damage from the set of pseudos changing or
// pseudos being restyled.
- let (styles, restyle_data) = data.styles_and_restyle_mut();
- if let Some(restyle_data) = restyle_data {
- let old_pseudos = &styles.pseudos;
- let new_pseudos = &shared_style.pseudos;
- if !old_pseudos.has_same_pseudos_as(new_pseudos) {
- restyle_data.damage |= RestyleDamage::reconstruct();
- } else {
- // It's a bit unfortunate that we have to keep
- // mapping PseudoElements back to indices
- // here....
- for pseudo in old_pseudos.keys() {
- let old_values =
- old_pseudos.get(&pseudo).unwrap().values.as_ref().map(|v| &**v);
- let new_values =
- new_pseudos.get(&pseudo).unwrap().values();
- self.element.accumulate_damage(
- &shared_context,
- Some(restyle_data),
- old_values,
- new_values,
- Some(&pseudo)
- );
- }
+ let (styles, mut restyle_data) = data.styles_and_restyle_mut();
+ let old_pseudos = &styles.pseudos;
+ let new_pseudos = &shared_style.pseudos;
+
+ if !old_pseudos.has_same_pseudos_as(new_pseudos) {
+ restyle_data.damage |= RestyleDamage::reconstruct();
+ } else {
+ // It's a bit unfortunate that we have to keep
+ // mapping PseudoElements back to indices
+ // here....
+ for pseudo in old_pseudos.keys() {
+ let old_values =
+ old_pseudos.get(&pseudo).unwrap().values.as_ref().map(|v| &**v);
+ let new_values =
+ new_pseudos.get(&pseudo).unwrap().values();
+ self.element.accumulate_damage(
+ &shared_context,
+ restyle_data,
+ old_values,
+ new_values,
+ Some(&pseudo)
+ );
}
}
}
- let old_values = data.get_styles_mut()
- .and_then(|s| s.primary.values.take());
+ let old_values =
+ data.get_styles_mut().and_then(|s| s.primary.values.take());
+
self.element.accumulate_damage(
&shared_context,
- data.get_restyle_mut(),
+ &mut data.restyle,
old_values.as_ref().map(|v| &**v),
shared_style.primary.values(),
None
@@ -596,9 +597,6 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
match sharing_result {
Ok(shared_style) => {
// Yay, cache hit. Share the style.
-
- debug_assert_eq!(data.has_styles(), data.has_restyle());
-
let child_cascade_requirement =
target.accumulate_damage_when_sharing(shared_context,
&shared_style,
diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs
index 8d148c5360c..19ceecedca7 100644
--- a/components/style/style_adjuster.rs
+++ b/components/style/style_adjuster.rs
@@ -6,7 +6,10 @@
//! for it to adhere to the CSS spec.
use app_units::Au;
-use properties::{self, ComputedValues, StyleBuilder};
+use properties::{self, CascadeFlags, ComputedValues};
+use properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, StyleBuilder};
+#[cfg(feature = "gecko")]
+use properties::PROHIBIT_DISPLAY_CONTENTS;
use properties::longhands::display::computed_value::T as display;
use properties::longhands::float::computed_value::T as float;
use properties::longhands::overflow_x::computed_value::T as overflow;
@@ -54,7 +57,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// https://drafts.csswg.org/css2/visuren.html#dis-pos-flo
fn blockify_if_necessary(&mut self,
layout_parent_style: &ComputedValues,
- skip_root_and_element_display_fixup: bool) {
+ flags: CascadeFlags) {
let mut blockify = false;
macro_rules! blockify_if {
($if_what:expr) => {
@@ -64,7 +67,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
}
}
- if !skip_root_and_element_display_fixup {
+ if !flags.contains(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) {
blockify_if!(self.is_root_element);
blockify_if!(layout_parent_style.get_box().clone_display().is_item_container());
}
@@ -275,6 +278,19 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
}
}
+ /// Native anonymous content converts display:contents into display:inline.
+ #[cfg(feature = "gecko")]
+ fn adjust_for_prohibited_display_contents(&mut self, flags: CascadeFlags) {
+ // TODO: We should probably convert display:contents into display:none
+ // in some cases too: https://drafts.csswg.org/css-display/#unbox
+ if !flags.contains(PROHIBIT_DISPLAY_CONTENTS) ||
+ self.style.get_box().clone_display() != display::contents {
+ return;
+ }
+
+ self.style.mutate_box().set_display(display::inline);
+ }
+
/// -moz-center, -moz-left and -moz-right are used for HTML's alignment.
///
/// This is covering the <div align="right"><table>...</table></div> case.
@@ -305,10 +321,13 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// `nsStyleContext::ApplyStyleFixups`.
pub fn adjust(&mut self,
layout_parent_style: &ComputedValues,
- skip_root_and_element_display_fixup: bool) {
+ flags: CascadeFlags) {
+ #[cfg(feature = "gecko")]
+ {
+ self.adjust_for_prohibited_display_contents(flags);
+ }
self.adjust_for_top_layer();
- self.blockify_if_necessary(layout_parent_style,
- skip_root_and_element_display_fixup);
+ self.blockify_if_necessary(layout_parent_style, flags);
self.adjust_for_position();
self.adjust_for_overflow();
#[cfg(feature = "gecko")]
diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs
index 2f63ec5ad1a..da5311f3da7 100644
--- a/components/style/stylesheets/keyframes_rule.rs
+++ b/components/style/stylesheets/keyframes_rule.rs
@@ -7,17 +7,17 @@
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser, ParserInput};
use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule, SourceLocation};
use error_reporting::{NullReporter, ContextualParseError};
-use parser::{PARSING_MODE_DEFAULT, ParserContext, log_css_error};
+use parser::{ParserContext, log_css_error};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
use properties::{PropertyDeclarationId, LonghandId, SourcePropertyDeclaration};
use properties::LonghandIdSet;
-use properties::animated_properties::TransitionProperty;
+use properties::animated_properties::AnimatableLonghand;
use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
use selectors::parser::SelectorParseError;
use shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
use std::borrow::Cow;
use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseError};
use stylearc::Arc;
use stylesheets::{CssRuleType, Stylesheet};
use stylesheets::rule_parser::VendorPrefix;
@@ -205,9 +205,10 @@ impl Keyframe {
/// Parse a CSS keyframe.
pub fn parse<'i>(css: &'i str, parent_stylesheet: &Stylesheet)
-> Result<Arc<Locked<Self>>, ParseError<'i>> {
+ let url_data = parent_stylesheet.url_data.read();
let error_reporter = NullReporter;
let context = ParserContext::new(parent_stylesheet.origin,
- &parent_stylesheet.url_data,
+ &url_data,
&error_reporter,
Some(CssRuleType::Keyframe),
PARSING_MODE_DEFAULT,
@@ -336,14 +337,14 @@ pub struct KeyframesAnimation {
/// The difference steps of the animation.
pub steps: Vec<KeyframesStep>,
/// The properties that change in this animation.
- pub properties_changed: Vec<TransitionProperty>,
+ pub properties_changed: Vec<AnimatableLonghand>,
/// Vendor prefix type the @keyframes has.
pub vendor_prefix: Option<VendorPrefix>,
}
/// Get all the animated properties in a keyframes animation.
fn get_animated_properties(keyframes: &[Arc<Locked<Keyframe>>], guard: &SharedRwLockReadGuard)
- -> Vec<TransitionProperty> {
+ -> Vec<AnimatableLonghand> {
let mut ret = vec![];
let mut seen = LonghandIdSet::new();
// NB: declarations are already deduplicated, so we don't have to check for
@@ -354,9 +355,12 @@ fn get_animated_properties(keyframes: &[Arc<Locked<Keyframe>>], guard: &SharedRw
for &(ref declaration, importance) in block.declarations().iter() {
assert!(!importance.important());
- if let Some(property) = TransitionProperty::from_declaration(declaration) {
- if !seen.has_transition_property_bit(&property) {
- seen.set_transition_property_bit(&property);
+ if let Some(property) = AnimatableLonghand::from_declaration(declaration) {
+ // Skip the 'display' property because although it is animatable from SMIL,
+ // it should not be animatable from CSS Animations or Web Animations.
+ if property != AnimatableLonghand::Display &&
+ !seen.has_animatable_longhand_bit(&property) {
+ seen.set_animatable_longhand_bit(&property);
ret.push(property);
}
}
@@ -371,7 +375,7 @@ impl KeyframesAnimation {
///
/// This will return a keyframe animation with empty steps and
/// properties_changed if the list of keyframes is empty, or there are no
- // animated properties obtained from the keyframes.
+ /// animated properties obtained from the keyframes.
///
/// Otherwise, this will compute and sort the steps used for the animation,
/// and return the animation object.
diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs
index df1bd2ae3c2..e5e6aa521f9 100644
--- a/components/style/stylesheets/mod.rs
+++ b/components/style/stylesheets/mod.rs
@@ -24,9 +24,10 @@ pub mod viewport_rule;
use cssparser::{parse_one_rule, Parser, ParserInput};
use error_reporting::NullReporter;
-use parser::{ParserContext, PARSING_MODE_DEFAULT};
+use parser::ParserContext;
use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt;
+use style_traits::PARSING_MODE_DEFAULT;
use stylearc::Arc;
pub use self::counter_style_rule::CounterStyleRule;
@@ -224,10 +225,11 @@ impl CssRule {
state: Option<State>,
loader: Option<&StylesheetLoader>
) -> Result<(Self, State), SingleRuleParseError> {
+ let url_data = parent_stylesheet.url_data.read();
let error_reporter = NullReporter;
let context = ParserContext::new(
parent_stylesheet.origin,
- &parent_stylesheet.url_data,
+ &url_data,
&error_reporter,
None,
PARSING_MODE_DEFAULT,
diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs
index f8f536b4a06..0cecb8bf36a 100644
--- a/components/style/stylesheets/rule_parser.rs
+++ b/components/style/stylesheets/rule_parser.rs
@@ -30,6 +30,7 @@ use stylesheets::keyframes_rule::parse_keyframe_list;
use stylesheets::loader::NoOpLoader;
use stylesheets::stylesheet::{Namespaces, Stylesheet};
use stylesheets::supports_rule::SupportsCondition;
+use stylesheets::viewport_rule;
use values::CustomIdent;
use values::KeyframesName;
use values::specified::url::SpecifiedUrl;
@@ -179,7 +180,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
media: media,
shared_lock: self.shared_lock.clone(),
origin: self.context.stylesheet_origin,
- url_data: self.context.url_data.clone(),
+ url_data: RwLock::new(self.context.url_data.clone()),
namespaces: RwLock::new(Namespaces::default()),
dirty_on_viewport_size_change: AtomicBool::new(false),
disabled: AtomicBool::new(false),
@@ -256,15 +257,19 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
}
}
+pub struct QualifiedRuleParserPrelude {
+ selectors: SelectorList<SelectorImpl>,
+ source_location: SourceLocation,
+}
impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
- type Prelude = SelectorList<SelectorImpl>;
+ type Prelude = QualifiedRuleParserPrelude;
type QualifiedRule = CssRule;
type Error = SelectorParseError<'i, StyleParseError<'i>>;
#[inline]
fn parse_prelude<'t>(&mut self, input: &mut Parser<'i, 't>)
- -> Result<SelectorList<SelectorImpl>, ParseError<'i>> {
+ -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
self.state = State::Body;
// "Freeze" the namespace map (no more namespace rules can be parsed
@@ -280,7 +285,7 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
#[inline]
fn parse_block<'t>(
&mut self,
- prelude: SelectorList<SelectorImpl>,
+ prelude: QualifiedRuleParserPrelude,
input: &mut Parser<'i, 't>
) -> Result<CssRule, ParseError<'i>> {
QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input)
@@ -325,17 +330,6 @@ impl<'a, 'b> NestedRuleParser<'a, 'b> {
}
}
-#[cfg(feature = "servo")]
-fn is_viewport_enabled() -> bool {
- use servo_config::prefs::PREFS;
- PREFS.get("layout.viewport.enabled").as_boolean().unwrap_or(false)
-}
-
-#[cfg(not(feature = "servo"))]
-fn is_viewport_enabled() -> bool {
- false // Gecko doesn't support @viewport.
-}
-
impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
type Prelude = AtRulePrelude;
type AtRule = CssRule;
@@ -380,7 +374,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
Ok(AtRuleType::WithBlock(AtRulePrelude::CounterStyle(name)))
},
"viewport" => {
- if is_viewport_enabled() {
+ if viewport_rule::enabled() {
Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
} else {
Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
@@ -492,33 +486,38 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
}
impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
- type Prelude = SelectorList<SelectorImpl>;
+ type Prelude = QualifiedRuleParserPrelude;
type QualifiedRule = CssRule;
type Error = SelectorParseError<'i, StyleParseError<'i>>;
fn parse_prelude<'t>(&mut self, input: &mut Parser<'i, 't>)
- -> Result<SelectorList<SelectorImpl>, ParseError<'i>> {
+ -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
let selector_parser = SelectorParser {
stylesheet_origin: self.stylesheet_origin,
namespaces: self.context.namespaces.unwrap(),
};
- SelectorList::parse(&selector_parser, input)
+ let location = get_location_with_offset(input.current_source_location(),
+ self.context.line_number_offset);
+ let selectors = SelectorList::parse(&selector_parser, input)?;
+
+ Ok(QualifiedRuleParserPrelude {
+ selectors: selectors,
+ source_location: location,
+ })
}
fn parse_block<'t>(
&mut self,
- prelude: SelectorList<SelectorImpl>,
+ prelude: QualifiedRuleParserPrelude,
input: &mut Parser<'i, 't>
) -> Result<CssRule, ParseError<'i>> {
- let location = get_location_with_offset(input.current_source_location(),
- self.context.line_number_offset);
let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style));
let declarations = parse_property_declaration_list(&context, input);
Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
- selectors: prelude,
+ selectors: prelude.selectors,
block: Arc::new(self.shared_lock.wrap(declarations)),
- source_location: location,
+ source_location: prelude.source_location,
}))))
}
}
diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs
index cbcaf9e0123..05dd2e71676 100644
--- a/components/style/stylesheets/rules_iterator.rs
+++ b/components/style/stylesheets/rules_iterator.rs
@@ -68,8 +68,7 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
}
let rule;
- let sub_iter;
- {
+ let sub_iter = {
let mut nested_iter = self.stack.last_mut().unwrap();
rule = match nested_iter.next() {
Some(r) => r,
@@ -79,7 +78,16 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
}
};
- sub_iter = match *rule {
+ match *rule {
+ CssRule::Namespace(_) |
+ CssRule::Style(_) |
+ CssRule::FontFace(_) |
+ CssRule::CounterStyle(_) |
+ CssRule::Viewport(_) |
+ CssRule::Keyframes(_) |
+ CssRule::Page(_) => {
+ return Some(rule)
+ },
CssRule::Import(ref import_rule) => {
let import_rule = import_rule.read_with(self.guard);
if !C::process_import(self.guard,
@@ -88,7 +96,7 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
import_rule) {
continue;
}
- Some(import_rule.stylesheet.rules.read_with(self.guard).0.iter())
+ import_rule.stylesheet.rules.read_with(self.guard).0.iter()
}
CssRule::Document(ref doc_rule) => {
let doc_rule = doc_rule.read_with(self.guard);
@@ -98,7 +106,7 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
doc_rule) {
continue;
}
- Some(doc_rule.rules.read_with(self.guard).0.iter())
+ doc_rule.rules.read_with(self.guard).0.iter()
}
CssRule::Media(ref lock) => {
let media_rule = lock.read_with(self.guard);
@@ -108,7 +116,7 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
media_rule) {
continue;
}
- Some(media_rule.rules.read_with(self.guard).0.iter())
+ media_rule.rules.read_with(self.guard).0.iter()
}
CssRule::Supports(ref lock) => {
let supports_rule = lock.read_with(self.guard);
@@ -118,22 +126,12 @@ impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
supports_rule) {
continue;
}
- Some(supports_rule.rules.read_with(self.guard).0.iter())
+ supports_rule.rules.read_with(self.guard).0.iter()
}
- CssRule::Namespace(_) |
- CssRule::Style(_) |
- CssRule::FontFace(_) |
- CssRule::CounterStyle(_) |
- CssRule::Viewport(_) |
- CssRule::Keyframes(_) |
- CssRule::Page(_) => None,
- };
- }
-
- if let Some(sub_iter) = sub_iter {
- self.stack.push(sub_iter);
- }
+ }
+ };
+ self.stack.push(sub_iter);
return Some(rule);
}
diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs
index aefcb0ff926..477c35f8e64 100644
--- a/components/style/stylesheets/stylesheet.rs
+++ b/components/style/stylesheets/stylesheet.rs
@@ -9,10 +9,11 @@ use error_reporting::{ParseErrorReporter, ContextualParseError};
use fnv::FnvHashMap;
use media_queries::{MediaList, Device};
use parking_lot::RwLock;
-use parser::{PARSING_MODE_DEFAULT, ParserContext, log_css_error};
+use parser::{ParserContext, log_css_error};
use shared_lock::{DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
+use style_traits::PARSING_MODE_DEFAULT;
use stylearc::Arc;
use stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
use stylesheets::loader::StylesheetLoader;
@@ -52,7 +53,7 @@ pub struct Stylesheet {
/// The origin of this stylesheet.
pub origin: Origin,
/// The url data this stylesheet should use.
- pub url_data: UrlExtraData,
+ pub url_data: RwLock<UrlExtraData>,
/// The lock used for objects inside this stylesheet
pub shared_lock: SharedRwLock,
/// The namespaces that apply to this stylesheet.
@@ -69,17 +70,15 @@ impl Stylesheet {
/// Updates an empty stylesheet from a given string of text.
pub fn update_from_str(existing: &Stylesheet,
css: &str,
- url_data: &UrlExtraData,
+ url_data: UrlExtraData,
stylesheet_loader: Option<&StylesheetLoader>,
error_reporter: &ParseErrorReporter,
line_number_offset: u64) {
let namespaces = RwLock::new(Namespaces::default());
- // FIXME: we really should update existing.url_data with the given url_data,
- // otherwise newly inserted rule may not have the right base url.
let (rules, dirty_on_viewport_size_change) =
Stylesheet::parse_rules(
css,
- url_data,
+ &url_data,
existing.origin,
&mut *namespaces.write(),
&existing.shared_lock,
@@ -89,6 +88,7 @@ impl Stylesheet {
line_number_offset
);
+ *existing.url_data.write() = url_data;
mem::swap(&mut *existing.namespaces.write(), &mut *namespaces.write());
existing.dirty_on_viewport_size_change
.store(dirty_on_viewport_size_change, Ordering::Release);
@@ -184,7 +184,7 @@ impl Stylesheet {
Stylesheet {
origin: origin,
- url_data: url_data,
+ url_data: RwLock::new(url_data),
namespaces: namespaces,
rules: CssRules::new(rules, &shared_lock),
media: media,
@@ -287,7 +287,7 @@ impl Clone for Stylesheet {
rules: Arc::new(lock.wrap(cloned_rules)),
media: Arc::new(lock.wrap(cloned_media)),
origin: self.origin,
- url_data: self.url_data.clone(),
+ url_data: RwLock::new((*self.url_data.read()).clone()),
shared_lock: lock,
namespaces: RwLock::new((*self.namespaces.read()).clone()),
dirty_on_viewport_size_change: AtomicBool::new(
diff --git a/components/style/stylesheets/viewport_rule.rs b/components/style/stylesheets/viewport_rule.rs
index e9af7a74673..8f48ac15bb5 100644
--- a/components/style/stylesheets/viewport_rule.rs
+++ b/components/style/stylesheets/viewport_rule.rs
@@ -12,7 +12,7 @@ use context::QuirksMode;
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, parse_important};
use cssparser::ToCss as ParserToCss;
use error_reporting::ContextualParseError;
-use euclid::size::TypedSize2D;
+use euclid::TypedSize2D;
use font_metrics::get_metrics_provider_for_product;
use media_queries::Device;
use parser::{Parse, ParserContext, log_css_error};
@@ -31,6 +31,19 @@ use stylesheets::{Stylesheet, Origin};
use values::computed::{Context, ToComputedValue};
use values::specified::{NoCalcLength, LengthOrPercentageOrAuto, ViewportPercentageLength};
+/// Whether parsing and processing of `@viewport` rules is enabled.
+#[cfg(feature = "servo")]
+pub fn enabled() -> bool {
+ use servo_config::prefs::PREFS;
+ PREFS.get("layout.viewport.enabled").as_boolean().unwrap_or(false)
+}
+
+/// Whether parsing and processing of `@viewport` rules is enabled.
+#[cfg(not(feature = "servo"))]
+pub fn enabled() -> bool {
+ false // Gecko doesn't support @viewport.
+}
+
macro_rules! declare_viewport_descriptor {
( $( $variant_name: expr => $variant: ident($data: ident), )+ ) => {
declare_viewport_descriptor_inner!([] [ $( $variant_name => $variant($data), )+ ] 0);
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index 497b51732d7..35a257dcabe 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -5,8 +5,9 @@
//! Selector matching.
use {Atom, LocalName, Namespace};
+use applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
use bit_vec::BitVec;
-use context::{QuirksMode, SharedStyleContext};
+use context::QuirksMode;
use data::ComputedStyle;
use dom::TElement;
use element_state::ElementState;
@@ -14,13 +15,13 @@ use error_reporting::create_error_reporter;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")]
use gecko_bindings::structs::{nsIAtom, StyleRuleInclusion};
+use invalidation::element::invalidation_map::InvalidationMap;
use invalidation::media_queries::EffectiveMediaQueryResults;
use media_queries::Device;
use properties::{self, CascadeFlags, ComputedValues};
use properties::{AnimationRules, PropertyDeclarationBlock};
#[cfg(feature = "servo")]
use properties::INHERIT_ALL;
-use restyle_hints::{HintComputationContext, DependencySet, RestyleHint};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_map::{SelectorMap, SelectorMapEntry};
use selector_parser::{SelectorImpl, PseudoElement};
@@ -33,7 +34,8 @@ use selectors::parser::{SelectorIter, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use sink::Push;
-use smallvec::{SmallVec, VecLike};
+use smallvec::VecLike;
+use std::fmt::Debug;
#[cfg(feature = "servo")]
use std::marker::PhantomData;
use style_traits::viewport::ViewportConstraints;
@@ -48,15 +50,6 @@ use thread_state;
pub use ::fnv::FnvHashMap;
-/// List of applicable declaration. This is a transient structure that shuttles
-/// declarations between selector matching and inserting into the rule tree, and
-/// therefore we want to avoid heap-allocation where possible.
-///
-/// In measurements on wikipedia, we pretty much never have more than 8 applicable
-/// declarations, so we could consider making this 8 entries instead of 16.
-/// However, it may depend a lot on workload, and stack space is cheap.
-pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;
-
/// This structure holds all the selectors and device characteristics
/// for a given document. The selectors are converted into `Rule`s
/// (defined in rust-selectors), and introduced in a `SelectorMap`
@@ -121,10 +114,10 @@ pub struct Stylist {
/// A monotonically increasing counter to represent the order on which a
/// style rule appears in a stylesheet, needed to sort them by source order.
- rules_source_order: usize,
+ rules_source_order: u32,
- /// Selector dependencies used to compute restyle hints.
- dependencies: DependencySet,
+ /// The invalidation map for this document.
+ invalidation_map: InvalidationMap,
/// The attribute local names that appear in attribute selectors. Used
/// to avoid taking element snapshots when an irrelevant attribute changes.
@@ -256,7 +249,7 @@ impl Stylist {
precomputed_pseudo_element_decls: Default::default(),
rules_source_order: 0,
rule_tree: RuleTree::new(),
- dependencies: DependencySet::new(),
+ invalidation_map: InvalidationMap::new(),
attribute_dependencies: BloomFilter::new(),
style_attribute_dependency: false,
state_dependencies: ElementState::empty(),
@@ -291,16 +284,16 @@ impl Stylist {
self.num_rebuilds
}
- /// Returns the number of dependencies in the DependencySet.
- pub fn num_dependencies(&self) -> usize {
- self.dependencies.len()
- }
-
/// Returns the number of revalidation_selectors.
pub fn num_revalidation_selectors(&self) -> usize {
self.selectors_for_cache_revalidation.len()
}
+ /// Gets a reference to the invalidation map.
+ pub fn invalidation_map(&self) -> &InvalidationMap {
+ &self.invalidation_map
+ }
+
/// Clear the stylist's state, effectively resetting it to more or less
/// the state Stylist::new creates.
///
@@ -331,7 +324,7 @@ impl Stylist {
self.precomputed_pseudo_element_decls = Default::default();
self.rules_source_order = 0;
// We want to keep rule_tree around across stylist rebuilds.
- self.dependencies.clear();
+ self.invalidation_map.clear();
self.attribute_dependencies.clear();
self.style_attribute_dependency = false;
self.state_dependencies = ElementState::empty();
@@ -368,14 +361,30 @@ impl Stylist {
self.num_rebuilds += 1;
- let cascaded_rule = ViewportRule {
- declarations: viewport_rule::Cascade::from_stylesheets(
- doc_stylesheets.clone(), guards.author, &self.device
- ).finish(),
- };
+ self.viewport_constraints = None;
- self.viewport_constraints =
- ViewportConstraints::maybe_new(&self.device, &cascaded_rule, self.quirks_mode);
+ if viewport_rule::enabled() {
+ // TODO(emilio): This doesn't look so efficient.
+ //
+ // Presumably when we properly implement this we can at least have a
+ // bit on the stylesheet that says whether it contains viewport
+ // rules to skip it entirely?
+ //
+ // Processing it with the rest of rules seems tricky since it
+ // overrides the viewport size which may change the evaluation of
+ // media queries (or may not? how are viewport units in media
+ // queries defined?)
+ let cascaded_rule = ViewportRule {
+ declarations: viewport_rule::Cascade::from_stylesheets(
+ doc_stylesheets.clone(), guards.author, &self.device
+ ).finish()
+ };
+
+ self.viewport_constraints =
+ ViewportConstraints::maybe_new(&self.device,
+ &cascaded_rule,
+ self.quirks_mode)
+ }
if let Some(ref constraints) = self.viewport_constraints {
self.device.account_for_viewport_rule(constraints);
@@ -468,15 +477,18 @@ impl Stylist {
self.element_map.borrow_for_origin(&stylesheet.origin)
};
- map.insert(Rule::new(selector_and_hashes.selector.clone(),
- selector_and_hashes.hashes.clone(),
- locked.clone(),
- self.rules_source_order));
+ map.insert(
+ Rule::new(selector_and_hashes.selector.clone(),
+ selector_and_hashes.hashes.clone(),
+ locked.clone(),
+ self.rules_source_order),
+ self.quirks_mode);
- self.dependencies.note_selector(selector_and_hashes);
+ self.invalidation_map.note_selector(selector_and_hashes, self.quirks_mode);
if needs_revalidation(&selector_and_hashes.selector) {
self.selectors_for_cache_revalidation.insert(
- RevalidationSelectorAndHashes::new(&selector_and_hashes));
+ RevalidationSelectorAndHashes::new(&selector_and_hashes),
+ self.quirks_mode);
}
selector_and_hashes.selector.visit(&mut AttributeAndStateDependencyVisitor {
attribute_dependencies: &mut self.attribute_dependencies,
@@ -568,7 +580,7 @@ impl Stylist {
// FIXME(emilio): When we've taken rid of the cascade we can just
// use into_iter.
self.rule_tree.insert_ordered_rules_with_important(
- declarations.into_iter().map(|a| (a.source.clone(), a.level)),
+ declarations.into_iter().map(|a| (a.source.clone(), a.level())),
guards)
}
None => self.rule_tree.root(),
@@ -754,7 +766,7 @@ impl Stylist {
let rule_node =
self.rule_tree.insert_ordered_rules_with_important(
- declarations.into_iter().map(|a| (a.source, a.level)),
+ declarations.into_iter().map(|a| a.order_and_level()),
guards);
if rule_node == self.rule_tree.root() {
None
@@ -921,10 +933,38 @@ impl Stylist {
self.quirks_mode = quirks_mode;
}
+ /// Returns the correspond PerPseudoElementSelectorMap given PseudoElement.
+ fn get_map(&self,
+ pseudo_element: Option<&PseudoElement>) -> Option<&PerPseudoElementSelectorMap>
+ {
+ match pseudo_element {
+ Some(pseudo) => self.pseudos_map.get(pseudo),
+ None => Some(&self.element_map),
+ }
+ }
+
+ /// Returns the rule hash target given an element.
+ fn rule_hash_target<E>(&self, element: E) -> E
+ where E: TElement
+ {
+ let is_implemented_pseudo =
+ element.implemented_pseudo_element().is_some();
+
+ // NB: This causes use to rule has pseudo selectors based on the
+ // properties of the originating element (which is fine, given the
+ // find_first_from_right usage).
+ if is_implemented_pseudo {
+ element.closest_non_native_anonymous_ancestor().unwrap()
+ } else {
+ element
+ }
+ }
+
/// Returns the applicable CSS declarations for the given element by
/// treating us as an XBL stylesheet-only stylist.
pub fn push_applicable_declarations_as_xbl_only_stylist<E, V>(&self,
element: &E,
+ pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V)
where E: TElement,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
@@ -933,12 +973,21 @@ impl Stylist {
MatchingContext::new(MatchingMode::Normal, None, self.quirks_mode);
let mut dummy_flag_setter = |_: &E, _: ElementSelectorFlags| {};
- self.element_map.author.get_all_matching_rules(element,
- element,
- applicable_declarations,
- &mut matching_context,
- &mut dummy_flag_setter,
- CascadeLevel::XBL);
+ let map = match self.get_map(pseudo_element) {
+ Some(map) => map,
+ None => return,
+ };
+ let rule_hash_target = self.rule_hash_target(*element);
+
+ // nsXBLPrototypeResources::ComputeServoStyleSet() added XBL stylesheets under author
+ // (doc) level.
+ map.author.get_all_matching_rules(element,
+ &rule_hash_target,
+ applicable_declarations,
+ &mut matching_context,
+ self.quirks_mode,
+ &mut dummy_flag_setter,
+ CascadeLevel::XBL);
}
/// Returns the applicable CSS declarations for the given element.
@@ -959,7 +1008,7 @@ impl Stylist {
context: &mut MatchingContext,
flags_setter: &mut F)
where E: TElement,
- V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + ::std::fmt::Debug,
+ V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + Debug,
F: FnMut(&E, ElementSelectorFlags),
{
debug_assert!(!self.is_device_dirty);
@@ -970,22 +1019,11 @@ impl Stylist {
"Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.map_or(true, |p| !p.is_precomputed()));
- let map = match pseudo_element {
- Some(pseudo) => self.pseudos_map.get(pseudo).unwrap(),
- None => &self.element_map,
- };
-
- let is_implemented_pseudo =
- element.implemented_pseudo_element().is_some();
-
- // NB: This causes use to rule has pseudo selectors based on the
- // properties of the originating element (which is fine, given the
- // find_first_from_right usage).
- let rule_hash_target = if is_implemented_pseudo {
- element.closest_non_native_anonymous_ancestor().unwrap()
- } else {
- *element
+ let map = match self.get_map(pseudo_element) {
+ Some(map) => map,
+ None => return,
};
+ let rule_hash_target = self.rule_hash_target(*element);
debug!("Determining if style is shareable: pseudo: {}",
pseudo_element.is_some());
@@ -997,6 +1035,7 @@ impl Stylist {
&rule_hash_target,
applicable_declarations,
context,
+ self.quirks_mode,
flags_setter,
CascadeLevel::UANormal);
debug!("UA normal: {:?}", context.relations);
@@ -1011,7 +1050,7 @@ impl Stylist {
if applicable_declarations.len() != length_before_preshints {
if cfg!(debug_assertions) {
for declaration in &applicable_declarations[length_before_preshints..] {
- assert_eq!(declaration.level, CascadeLevel::PresHints);
+ assert_eq!(declaration.level(), CascadeLevel::PresHints);
}
}
// Note the existence of presentational attributes so that the
@@ -1036,6 +1075,7 @@ impl Stylist {
&rule_hash_target,
applicable_declarations,
context,
+ self.quirks_mode,
flags_setter,
CascadeLevel::UserNormal);
debug!("user normal: {:?}", context.relations);
@@ -1045,7 +1085,8 @@ impl Stylist {
// Step 3b: XBL rules.
let cut_off_inheritance =
- rule_hash_target.get_declarations_from_xbl_bindings(applicable_declarations);
+ rule_hash_target.get_declarations_from_xbl_bindings(pseudo_element,
+ applicable_declarations);
debug!("XBL: {:?}", context.relations);
if rule_hash_target.matches_user_and_author_rules() && !only_default_rules {
@@ -1057,13 +1098,18 @@ impl Stylist {
&rule_hash_target,
applicable_declarations,
context,
+ self.quirks_mode,
flags_setter,
CascadeLevel::AuthorNormal);
debug!("author normal: {:?}", context.relations);
} else {
- debug!("Skipping author normal rules due to cut off inheritance");
+ debug!("skipping author normal rules due to cut off inheritance");
}
+ } else {
+ debug!("skipping author normal rules");
+ }
+ if !only_default_rules {
// Step 4: Normal style attributes.
if let Some(sa) = style_attribute {
Push::push(
@@ -1071,7 +1117,6 @@ impl Stylist {
ApplicableDeclarationBlock::from_declarations(sa.clone(),
CascadeLevel::StyleAttributeNormal));
}
-
debug!("style attr: {:?}", context.relations);
// Step 5: SMIL override.
@@ -1095,7 +1140,7 @@ impl Stylist {
}
debug!("animation: {:?}", context.relations);
} else {
- debug!("skipping non-agent rules");
+ debug!("skipping style attr and SMIL & animation rules");
}
//
@@ -1167,32 +1212,21 @@ impl Stylist {
// the lookups, which means that the bitvecs are comparable. We verify
// this in the caller by asserting that the bitvecs are same-length.
let mut results = BitVec::new();
- self.selectors_for_cache_revalidation.lookup(*element, &mut |selector_and_hashes| {
- results.push(matches_selector(&selector_and_hashes.selector,
- selector_and_hashes.selector_offset,
- &selector_and_hashes.hashes,
- element,
- &mut matching_context,
- flags_setter));
- true
- });
+ self.selectors_for_cache_revalidation.lookup(
+ *element, self.quirks_mode, &mut |selector_and_hashes| {
+ results.push(matches_selector(&selector_and_hashes.selector,
+ selector_and_hashes.selector_offset,
+ &selector_and_hashes.hashes,
+ element,
+ &mut matching_context,
+ flags_setter));
+ true
+ }
+ );
results
}
- /// Given an element, and a snapshot table that represents a previous state
- /// of the tree, compute the appropriate restyle hint, that is, the kind of
- /// restyle we need to do.
- pub fn compute_restyle_hint<'a, E>(&self,
- element: &E,
- shared_context: &SharedStyleContext,
- context: HintComputationContext<'a, E>)
- -> RestyleHint
- where E: TElement,
- {
- self.dependencies.compute_hint(element, shared_context, context)
- }
-
/// Computes styles for a given declaration with parent_style.
pub fn compute_for_declarations(&self,
guards: &StylesheetGuards,
@@ -1206,7 +1240,7 @@ impl Stylist {
CascadeLevel::StyleAttributeNormal)
];
let rule_node =
- self.rule_tree.insert_ordered_rules(v.into_iter().map(|a| (a.source, a.level)));
+ self.rule_tree.insert_ordered_rules(v.into_iter().map(|a| a.order_and_level()));
// This currently ignores visited styles. It appears to be used for
// font styles in <canvas> via Servo_StyleSet_ResolveForDeclarations.
@@ -1497,11 +1531,13 @@ pub struct Rule {
/// The ancestor hashes associated with the selector.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "No heap data")]
pub hashes: AncestorHashes,
+ /// The source order this style rule appears in. Note that we only use
+ /// three bytes to store this value in ApplicableDeclarationsBlock, so
+ /// we could repurpose that storage here if we needed to.
+ pub source_order: u32,
/// The actual style rule.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
pub style_rule: Arc<Locked<StyleRule>>,
- /// The source order this style rule appears in.
- pub source_order: usize,
}
impl SelectorMapEntry for Rule {
@@ -1525,19 +1561,18 @@ impl Rule {
pub fn to_applicable_declaration_block(&self,
level: CascadeLevel)
-> ApplicableDeclarationBlock {
- ApplicableDeclarationBlock {
- source: StyleSource::Style(self.style_rule.clone()),
- source_order: self.source_order,
- specificity: self.specificity(),
- level: level,
- }
+ let source = StyleSource::Style(self.style_rule.clone());
+ ApplicableDeclarationBlock::new(source,
+ self.source_order,
+ level,
+ self.specificity())
}
/// Creates a new Rule.
pub fn new(selector: Selector<SelectorImpl>,
hashes: AncestorHashes,
style_rule: Arc<Locked<StyleRule>>,
- source_order: usize)
+ source_order: u32)
-> Self
{
Rule {
@@ -1548,38 +1583,3 @@ impl Rule {
}
}
}
-
-/// A property declaration together with its precedence among rules of equal
-/// specificity so that we can sort them.
-///
-/// This represents the declarations in a given declaration block for a given
-/// importance.
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Debug, Clone, PartialEq)]
-pub struct ApplicableDeclarationBlock {
- /// The style source, either a style rule, or a property declaration block.
- #[cfg_attr(feature = "servo", ignore_heap_size_of = "Arc")]
- pub source: StyleSource,
- /// The source order of this block.
- pub source_order: usize,
- /// The specificity of the selector this block is represented by.
- pub specificity: u32,
- /// The cascade level this applicable declaration block is in.
- pub level: CascadeLevel,
-}
-
-impl ApplicableDeclarationBlock {
- /// Constructs an applicable declaration block from a given property
- /// declaration block and importance.
- #[inline]
- pub fn from_declarations(declarations: Arc<Locked<PropertyDeclarationBlock>>,
- level: CascadeLevel)
- -> Self {
- ApplicableDeclarationBlock {
- source: StyleSource::Declarations(declarations),
- source_order: 0,
- specificity: 0,
- level: level,
- }
- }
-}
diff --git a/components/style/traversal.rs b/components/style/traversal.rs
index 32a525d4b45..004c2485afd 100644
--- a/components/style/traversal.rs
+++ b/components/style/traversal.rs
@@ -6,12 +6,10 @@
use atomic_refcell::AtomicRefCell;
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
-use data::{ElementData, ElementStyles, StoredRestyleHint};
+use data::{ElementData, ElementStyles};
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
+use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
use matching::{ChildCascadeRequirement, MatchMethods};
-use restyle_hints::{CascadeHint, HintComputationContext, RECASCADE_SELF};
-use restyle_hints::{RECASCADE_DESCENDANTS, RestyleHint};
-use selector_parser::RestyleDamage;
use sharing::{StyleSharingBehavior, StyleSharingTarget};
#[cfg(feature = "servo")] use servo_config::opts;
use smallvec::SmallVec;
@@ -242,24 +240,11 @@ pub trait DomTraversal<E: TElement> : Sync {
};
}
- // Expand the snapshot, if any. This is normally handled by the parent, so
- // we need a special case for the root.
- //
- // Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which
- // we propagate to the next sibling element.
+ // Look at whether there has been any attribute or state change, and
+ // invalidate our style, and the one of our siblings and descendants as
+ // needed.
if let Some(mut data) = root.mutate_data() {
- let later_siblings =
- data.compute_final_hint(root,
- shared_context,
- HintComputationContext::Root);
- if later_siblings {
- if let Some(next) = root.next_sibling_element() {
- if let Some(mut next_data) = next.mutate_data() {
- let hint = StoredRestyleHint::subtree_and_later_siblings();
- next_data.ensure_restyle().hint.insert(hint);
- }
- }
- }
+ data.invalidate_style_if_needed(root, shared_context);
}
PreTraverseToken {
@@ -310,10 +295,8 @@ pub trait DomTraversal<E: TElement> : Sync {
if el.is_native_anonymous() {
if let Some(parent) = el.traversal_parent() {
let parent_data = parent.borrow_data().unwrap();
- let going_to_reframe = parent_data.get_restyle().map_or(false, |r| {
- (r.damage | r.damage_handled())
- .contains(RestyleDamage::reconstruct())
- });
+ let going_to_reframe =
+ parent_data.restyle.reconstructed_self_or_ancestor();
let mut is_before_or_after_pseudo = false;
if let Some(pseudo) = el.implemented_pseudo_element() {
@@ -351,9 +334,8 @@ pub trait DomTraversal<E: TElement> : Sync {
Some(d) => d,
None => return false,
};
- return data.get_restyle()
- .map_or(false, |r| r.hint.has_animation_hint() ||
- r.hint.has_recascade_self());
+ return data.restyle.hint.has_animation_hint() ||
+ data.restyle.hint.has_recascade_self();
}
// If the dirty descendants bit is set, we need to traverse no
@@ -374,18 +356,15 @@ pub trait DomTraversal<E: TElement> : Sync {
return true;
}
- // Check the restyle data.
- if let Some(r) = data.get_restyle() {
- // If we have a restyle hint or need to recascade, we need to
- // visit the element.
- //
- // Note that this is different than checking has_current_styles(),
- // since that can return true even if we have a restyle hint
- // indicating that the element's descendants (but not necessarily
- // the element) need restyling.
- if !r.hint.is_empty() {
- return true;
- }
+ // If we have a restyle hint or need to recascade, we need to
+ // visit the element.
+ //
+ // Note that this is different than checking has_current_styles(),
+ // since that can return true even if we have a restyle hint
+ // indicating that the element's descendants (but not necessarily
+ // the element) need restyling.
+ if !data.restyle.hint.is_empty() {
+ return true;
}
// Servo uses the post-order traversal for flow construction, so
@@ -395,7 +374,7 @@ pub trait DomTraversal<E: TElement> : Sync {
// We also need to traverse nodes with explicit damage and no other
// restyle data, so that this damage can be cleared.
if (cfg!(feature = "servo") || traversal_flags.for_reconstruct()) &&
- data.get_restyle().map_or(false, |r| !r.damage.is_empty()) {
+ !data.restyle.damage.is_empty() {
return true;
}
@@ -668,12 +647,9 @@ pub fn recalc_style_at<E, D>(traversal: &D,
context.thread_local.statistics.elements_traversed += 1;
debug_assert!(!element.has_snapshot() || element.handled_snapshot(),
"Should've handled snapshots here already");
- debug_assert!(data.get_restyle().map_or(true, |r| {
- !r.has_sibling_invalidations()
- }), "Should've computed the final hint and handled later_siblings already");
let compute_self = !element.has_current_styles(data);
- let mut cascade_hint = CascadeHint::empty();
+ let mut hint = RestyleHint::empty();
debug!("recalc_style_at: {:?} (compute_self={:?}, dirty_descendants={:?}, data={:?})",
element, compute_self, element.has_dirty_descendants(), data);
@@ -682,10 +658,10 @@ pub fn recalc_style_at<E, D>(traversal: &D,
if compute_self {
match compute_style(traversal, traversal_data, context, element, data) {
ChildCascadeRequirement::MustCascadeChildren => {
- cascade_hint |= RECASCADE_SELF;
+ hint |= RECASCADE_SELF;
}
ChildCascadeRequirement::MustCascadeDescendants => {
- cascade_hint |= RECASCADE_SELF | RECASCADE_DESCENDANTS;
+ hint |= RECASCADE_SELF | RECASCADE_DESCENDANTS;
}
ChildCascadeRequirement::CanSkipCascade => {}
};
@@ -693,7 +669,7 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// We must always cascade native anonymous subtrees, since they inherit styles
// from their first non-NAC ancestor.
if element.is_native_anonymous() {
- cascade_hint |= RECASCADE_SELF;
+ hint |= RECASCADE_SELF;
}
// If we're restyling this element to display:none, throw away all style
@@ -707,24 +683,21 @@ pub fn recalc_style_at<E, D>(traversal: &D,
// Now that matching and cascading is done, clear the bits corresponding to
// those operations and compute the propagated restyle hint.
- let mut propagated_hint = match data.get_restyle_mut() {
- None => StoredRestyleHint::empty(),
- Some(r) => {
- debug_assert!(context.shared.traversal_flags.for_animation_only() ||
- !r.hint.has_animation_hint(),
- "animation restyle hint should be handled during \
- animation-only restyles");
- r.hint.propagate(&context.shared.traversal_flags)
- },
+ let mut propagated_hint = {
+ debug_assert!(context.shared.traversal_flags.for_animation_only() ||
+ !data.restyle.hint.has_animation_hint(),
+ "animation restyle hint should be handled during \
+ animation-only restyles");
+ data.restyle.hint.propagate(&context.shared.traversal_flags)
};
// FIXME(bholley): Need to handle explicitly-inherited reset properties
// somewhere.
- propagated_hint.insert_cascade_hint(cascade_hint);
+ propagated_hint.insert(hint);
- trace!("propagated_hint={:?}, cascade_hint={:?}, \
+ trace!("propagated_hint={:?} \
is_display_none={:?}, implementing_pseudo={:?}",
- propagated_hint, cascade_hint,
+ propagated_hint,
data.styles().is_display_none(),
element.implemented_pseudo_element());
debug_assert!(element.has_current_styles(data) ||
@@ -739,29 +712,30 @@ pub fn recalc_style_at<E, D>(traversal: &D,
element.has_dirty_descendants()
};
- // Preprocess children, propagating restyle hints and handling sibling relationships.
+ // Preprocess children, propagating restyle hints and handling sibling
+ // relationships.
if traversal.should_traverse_children(&mut context.thread_local,
element,
&data,
DontLog) &&
(has_dirty_descendants_for_this_restyle ||
!propagated_hint.is_empty()) {
- let damage_handled = data.get_restyle().map_or(RestyleDamage::empty(), |r| {
- r.damage_handled() | r.damage.handled_for_descendants()
- });
-
- preprocess_children::<E, D>(context,
- traversal_data,
- element,
- propagated_hint,
- damage_handled);
+ let reconstructed_ancestor =
+ data.restyle.reconstructed_self_or_ancestor();
+
+ preprocess_children::<E, D>(
+ context,
+ element,
+ propagated_hint,
+ reconstructed_ancestor,
+ )
}
// If we are in a restyle for reconstruction, drop the existing restyle
// data here, since we won't need to perform a post-traversal to pick up
// any change hints.
if context.shared.traversal_flags.for_reconstruct() {
- data.clear_restyle();
+ data.clear_restyle_state();
}
if context.shared.traversal_flags.for_animation_only() {
@@ -803,10 +777,19 @@ fn compute_style<E, D>(_traversal: &D,
use sharing::StyleSharingResult::*;
context.thread_local.statistics.elements_styled += 1;
- let kind = data.restyle_kind();
+ let kind = data.restyle_kind(context.shared);
+
+ debug!("compute_style: {:?} (kind={:?})", element, kind);
+
+ if data.has_styles() {
+ data.restyle.set_restyled();
+ }
match kind {
MatchAndCascade => {
+ debug_assert!(!context.shared.traversal_flags.for_animation_only(),
+ "MatchAndCascade shouldn't be processed during \
+ animation-only traversal");
// Ensure the bloom filter is up to date.
context.thread_local.bloom_filter
.insert_parents_recovering(element,
@@ -852,13 +835,15 @@ fn compute_style<E, D>(_traversal: &D,
}
}
-fn preprocess_children<E, D>(context: &mut StyleContext<E>,
- parent_traversal_data: &PerLevelTraversalData,
- element: E,
- mut propagated_hint: StoredRestyleHint,
- damage_handled: RestyleDamage)
- where E: TElement,
- D: DomTraversal<E>,
+fn preprocess_children<E, D>(
+ context: &mut StyleContext<E>,
+ element: E,
+ propagated_hint: RestyleHint,
+ reconstructed_ancestor: bool,
+)
+where
+ E: TElement,
+ D: DomTraversal<E>,
{
trace!("preprocess_children: {:?}", element);
@@ -878,42 +863,24 @@ fn preprocess_children<E, D>(context: &mut StyleContext<E>,
continue;
}
- // Handle element snapshots and sibling restyle hints.
- //
- // NB: This will be a no-op if there's no restyle data and no snapshot.
- let later_siblings =
- child_data.compute_final_hint(child,
- &context.shared,
- HintComputationContext::Child {
- local_context: &mut context.thread_local,
- dom_depth: parent_traversal_data.current_dom_depth + 1,
- });
-
- trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}, later_siblings: {:?}",
+ trace!(" > {:?} -> {:?} + {:?}, pseudo: {:?}",
child,
- child_data.get_restyle().map(|r| &r.hint),
+ child_data.restyle.hint,
propagated_hint,
- child.implemented_pseudo_element(),
- later_siblings);
+ child.implemented_pseudo_element());
- // If the child doesn't have pre-existing RestyleData and we don't have
- // any reason to create one, avoid the useless allocation and move on to
- // the next child.
- if propagated_hint.is_empty() && damage_handled.is_empty() && !child_data.has_restyle() {
- continue;
- }
-
- let mut restyle_data = child_data.ensure_restyle();
-
- // Propagate the parent and sibling restyle hint.
- restyle_data.hint.insert_from(&propagated_hint);
-
- if later_siblings {
- propagated_hint.insert(RestyleHint::subtree().into());
+ // Propagate the parent restyle hint, that may make us restyle the whole
+ // subtree.
+ if reconstructed_ancestor {
+ child_data.restyle.set_reconstructed_ancestor();
}
+ child_data.restyle.hint.insert(propagated_hint);
- // Store the damage already handled by ancestors.
- restyle_data.set_damage_handled(damage_handled);
+ // Handle element snapshots and invalidation of descendants and siblings
+ // as needed.
+ //
+ // NB: This will be a no-op if there's no snapshot.
+ child_data.invalidate_style_if_needed(child, &context.shared);
}
}
diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs
index 3d59a51ff42..be803379c65 100644
--- a/components/style/values/computed/length.rs
+++ b/components/style/values/computed/length.rs
@@ -11,7 +11,8 @@ use style_traits::ToCss;
use style_traits::values::specified::AllowedLengthType;
use super::{Number, ToComputedValue, Context};
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
-use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength};
+use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
+use values::specified::length::{Percentage, ViewportPercentageLength};
pub use super::image::Image;
pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
@@ -65,20 +66,20 @@ impl ToComputedValue for specified::Length {
pub struct CalcLengthOrPercentage {
pub clamping_mode: AllowedLengthType,
length: Au,
- pub percentage: Option<CSSFloat>,
+ pub percentage: Option<Percentage>,
}
impl CalcLengthOrPercentage {
/// Returns a new `CalcLengthOrPercentage`.
#[inline]
- pub fn new(length: Au, percentage: Option<CSSFloat>) -> Self {
+ pub fn new(length: Au, percentage: Option<Percentage>) -> Self {
Self::with_clamping_mode(length, percentage, AllowedLengthType::All)
}
/// Returns a new `CalcLengthOrPercentage` with a specific clamping mode.
#[inline]
pub fn with_clamping_mode(length: Au,
- percentage: Option<CSSFloat>,
+ percentage: Option<Percentage>,
clamping_mode: AllowedLengthType)
-> Self {
Self {
@@ -106,7 +107,7 @@ impl CalcLengthOrPercentage {
#[inline]
#[allow(missing_docs)]
pub fn percentage(&self) -> CSSFloat {
- self.percentage.unwrap_or(0.)
+ self.percentage.map_or(0., |p| p.0)
}
/// If there are special rules for computing percentages in a value (e.g. the height property),
@@ -114,7 +115,7 @@ impl CalcLengthOrPercentage {
pub fn to_used_value(&self, container_len: Option<Au>) -> Option<Au> {
match (container_len, self.percentage) {
(Some(len), Some(percent)) => {
- Some(self.clamping_mode.clamp(self.length + len.scale_by(percent)))
+ Some(self.clamping_mode.clamp(self.length + len.scale_by(percent.0)))
},
(_, None) => Some(self.length()),
_ => None,
@@ -157,11 +158,30 @@ impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
}
}
+impl From<LengthOrPercentageOrNone> for Option<CalcLengthOrPercentage> {
+ fn from(len: LengthOrPercentageOrNone) -> Option<CalcLengthOrPercentage> {
+ match len {
+ LengthOrPercentageOrNone::Percentage(this) => {
+ Some(CalcLengthOrPercentage::new(Au(0), Some(this)))
+ }
+ LengthOrPercentageOrNone::Length(this) => {
+ Some(CalcLengthOrPercentage::new(this, None))
+ }
+ LengthOrPercentageOrNone::Calc(this) => {
+ Some(this)
+ }
+ LengthOrPercentageOrNone::None => {
+ None
+ }
+ }
+ }
+}
+
impl ToCss for CalcLengthOrPercentage {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match (self.length, self.percentage) {
- (l, Some(p)) if l == Au(0) => write!(dest, "{}%", p * 100.),
- (l, Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p * 100.),
+ (l, Some(p)) if l == Au(0) => p.to_css(dest),
+ (l, Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p.0 * 100.),
(l, None) => write!(dest, "{}px", Au::to_px(l)),
}
}
@@ -213,12 +233,12 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
}
}
-#[derive(PartialEq, Clone, Copy)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, PartialEq, ToCss)]
pub enum LengthOrPercentage {
Length(Au),
- Percentage(CSSFloat),
+ Percentage(Percentage),
Calc(CalcLengthOrPercentage),
}
@@ -250,7 +270,7 @@ impl LengthOrPercentage {
use self::LengthOrPercentage::*;
match *self {
Length(Au(0)) => true,
- Percentage(p) => p == 0.0,
+ Percentage(p) => p.0 == 0.0,
Length(_) | Calc(_) => false
}
}
@@ -260,7 +280,7 @@ impl LengthOrPercentage {
use self::LengthOrPercentage::*;
match *self {
Length(l) => (l, NotNaN::new(0.0).unwrap()),
- Percentage(p) => (Au(0), NotNaN::new(p).unwrap()),
+ Percentage(p) => (Au(0), NotNaN::new(p.0).unwrap()),
Calc(c) => (c.unclamped_length(), NotNaN::new(c.percentage()).unwrap()),
}
}
@@ -269,7 +289,7 @@ impl LengthOrPercentage {
pub fn to_used_value(&self, containing_length: Au) -> Au {
match *self {
LengthOrPercentage::Length(length) => length,
- LengthOrPercentage::Percentage(p) => containing_length.scale_by(p),
+ LengthOrPercentage::Percentage(p) => containing_length.scale_by(p.0),
LengthOrPercentage::Calc(ref calc) => {
calc.to_used_value(Some(containing_length)).unwrap()
},
@@ -281,7 +301,7 @@ impl fmt::Debug for LengthOrPercentage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentage::Length(length) => write!(f, "{:?}", length),
- LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage.0 * 100.),
LengthOrPercentage::Calc(calc) => write!(f, "{:?}", calc),
}
}
@@ -296,7 +316,7 @@ impl ToComputedValue for specified::LengthOrPercentage {
LengthOrPercentage::Length(value.to_computed_value(context))
}
specified::LengthOrPercentage::Percentage(value) => {
- LengthOrPercentage::Percentage(value.0)
+ LengthOrPercentage::Percentage(value)
}
specified::LengthOrPercentage::Calc(ref calc) => {
LengthOrPercentage::Calc(calc.to_computed_value(context))
@@ -312,7 +332,7 @@ impl ToComputedValue for specified::LengthOrPercentage {
)
}
LengthOrPercentage::Percentage(value) => {
- specified::LengthOrPercentage::Percentage(specified::Percentage(value))
+ specified::LengthOrPercentage::Percentage(value)
}
LengthOrPercentage::Calc(ref calc) => {
specified::LengthOrPercentage::Calc(
@@ -323,23 +343,12 @@ impl ToComputedValue for specified::LengthOrPercentage {
}
}
-impl ToCss for LengthOrPercentage {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- match *self {
- LengthOrPercentage::Length(length) => length.to_css(dest),
- LengthOrPercentage::Percentage(percentage)
- => write!(dest, "{}%", percentage * 100.),
- LengthOrPercentage::Calc(calc) => calc.to_css(dest),
- }
- }
-}
-
-#[derive(PartialEq, Clone, Copy)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, PartialEq, ToCss)]
pub enum LengthOrPercentageOrAuto {
Length(Au),
- Percentage(CSSFloat),
+ Percentage(Percentage),
Auto,
Calc(CalcLengthOrPercentage),
}
@@ -353,7 +362,7 @@ impl LengthOrPercentageOrAuto {
use self::LengthOrPercentageOrAuto::*;
match *self {
Length(Au(0)) => true,
- Percentage(p) => p == 0.0,
+ Percentage(p) => p.0 == 0.0,
Length(_) | Calc(_) | Auto => false
}
}
@@ -363,7 +372,7 @@ impl fmt::Debug for LengthOrPercentageOrAuto {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentageOrAuto::Length(length) => write!(f, "{:?}", length),
- LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage.0 * 100.),
LengthOrPercentageOrAuto::Auto => write!(f, "auto"),
LengthOrPercentageOrAuto::Calc(calc) => write!(f, "{:?}", calc),
}
@@ -380,7 +389,7 @@ impl ToComputedValue for specified::LengthOrPercentageOrAuto {
LengthOrPercentageOrAuto::Length(value.to_computed_value(context))
}
specified::LengthOrPercentageOrAuto::Percentage(value) => {
- LengthOrPercentageOrAuto::Percentage(value.0)
+ LengthOrPercentageOrAuto::Percentage(value)
}
specified::LengthOrPercentageOrAuto::Auto => {
LengthOrPercentageOrAuto::Auto
@@ -401,7 +410,7 @@ impl ToComputedValue for specified::LengthOrPercentageOrAuto {
)
}
LengthOrPercentageOrAuto::Percentage(value) => {
- specified::LengthOrPercentageOrAuto::Percentage(specified::Percentage(value))
+ specified::LengthOrPercentageOrAuto::Percentage(value)
}
LengthOrPercentageOrAuto::Calc(calc) => {
specified::LengthOrPercentageOrAuto::Calc(
@@ -412,24 +421,12 @@ impl ToComputedValue for specified::LengthOrPercentageOrAuto {
}
}
-impl ToCss for LengthOrPercentageOrAuto {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- match *self {
- LengthOrPercentageOrAuto::Length(length) => length.to_css(dest),
- LengthOrPercentageOrAuto::Percentage(percentage)
- => write!(dest, "{}%", percentage * 100.),
- LengthOrPercentageOrAuto::Auto => dest.write_str("auto"),
- LengthOrPercentageOrAuto::Calc(calc) => calc.to_css(dest),
- }
- }
-}
-
-#[derive(PartialEq, Clone, Copy)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, PartialEq, ToCss)]
pub enum LengthOrPercentageOrAutoOrContent {
Length(Au),
- Percentage(CSSFloat),
+ Percentage(Percentage),
Calc(CalcLengthOrPercentage),
Auto,
Content
@@ -439,7 +436,7 @@ impl fmt::Debug for LengthOrPercentageOrAutoOrContent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentageOrAutoOrContent::Length(length) => write!(f, "{:?}", length),
- LengthOrPercentageOrAutoOrContent::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ LengthOrPercentageOrAutoOrContent::Percentage(percentage) => write!(f, "{}%", percentage.0 * 100.),
LengthOrPercentageOrAutoOrContent::Calc(calc) => write!(f, "{:?}", calc),
LengthOrPercentageOrAutoOrContent::Auto => write!(f, "auto"),
LengthOrPercentageOrAutoOrContent::Content => write!(f, "content")
@@ -457,7 +454,7 @@ impl ToComputedValue for specified::LengthOrPercentageOrAutoOrContent {
LengthOrPercentageOrAutoOrContent::Length(value.to_computed_value(context))
},
specified::LengthOrPercentageOrAutoOrContent::Percentage(value) => {
- LengthOrPercentageOrAutoOrContent::Percentage(value.0)
+ LengthOrPercentageOrAutoOrContent::Percentage(value)
},
specified::LengthOrPercentageOrAutoOrContent::Calc(ref calc) => {
LengthOrPercentageOrAutoOrContent::Calc(calc.to_computed_value(context))
@@ -487,7 +484,7 @@ impl ToComputedValue for specified::LengthOrPercentageOrAutoOrContent {
)
}
LengthOrPercentageOrAutoOrContent::Percentage(value) => {
- specified::LengthOrPercentageOrAutoOrContent::Percentage(specified::Percentage(value))
+ specified::LengthOrPercentageOrAutoOrContent::Percentage(value)
}
LengthOrPercentageOrAutoOrContent::Calc(calc) => {
specified::LengthOrPercentageOrAutoOrContent::Calc(
@@ -498,25 +495,12 @@ impl ToComputedValue for specified::LengthOrPercentageOrAutoOrContent {
}
}
-impl ToCss for LengthOrPercentageOrAutoOrContent {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- match *self {
- LengthOrPercentageOrAutoOrContent::Length(length) => length.to_css(dest),
- LengthOrPercentageOrAutoOrContent::Percentage(percentage)
- => write!(dest, "{}%", percentage * 100.),
- LengthOrPercentageOrAutoOrContent::Calc(calc) => calc.to_css(dest),
- LengthOrPercentageOrAutoOrContent::Auto => dest.write_str("auto"),
- LengthOrPercentageOrAutoOrContent::Content => dest.write_str("content")
- }
- }
-}
-
-#[derive(PartialEq, Clone, Copy)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Clone, Copy, PartialEq, ToCss)]
pub enum LengthOrPercentageOrNone {
Length(Au),
- Percentage(CSSFloat),
+ Percentage(Percentage),
Calc(CalcLengthOrPercentage),
None,
}
@@ -527,7 +511,7 @@ impl LengthOrPercentageOrNone {
match *self {
LengthOrPercentageOrNone::None => None,
LengthOrPercentageOrNone::Length(length) => Some(length),
- LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent)),
+ LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent.0)),
LengthOrPercentageOrNone::Calc(ref calc) => calc.to_used_value(Some(containing_length)),
}
}
@@ -537,7 +521,7 @@ impl fmt::Debug for LengthOrPercentageOrNone {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LengthOrPercentageOrNone::Length(length) => write!(f, "{:?}", length),
- LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage.0 * 100.),
LengthOrPercentageOrNone::Calc(calc) => write!(f, "{:?}", calc),
LengthOrPercentageOrNone::None => write!(f, "none"),
}
@@ -554,7 +538,7 @@ impl ToComputedValue for specified::LengthOrPercentageOrNone {
LengthOrPercentageOrNone::Length(value.to_computed_value(context))
}
specified::LengthOrPercentageOrNone::Percentage(value) => {
- LengthOrPercentageOrNone::Percentage(value.0)
+ LengthOrPercentageOrNone::Percentage(value)
}
specified::LengthOrPercentageOrNone::Calc(ref calc) => {
LengthOrPercentageOrNone::Calc(calc.to_computed_value(context))
@@ -575,7 +559,7 @@ impl ToComputedValue for specified::LengthOrPercentageOrNone {
)
}
LengthOrPercentageOrNone::Percentage(value) => {
- specified::LengthOrPercentageOrNone::Percentage(specified::Percentage(value))
+ specified::LengthOrPercentageOrNone::Percentage(value)
}
LengthOrPercentageOrNone::Calc(calc) => {
specified::LengthOrPercentageOrNone::Calc(
@@ -586,18 +570,6 @@ impl ToComputedValue for specified::LengthOrPercentageOrNone {
}
}
-impl ToCss for LengthOrPercentageOrNone {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- match *self {
- LengthOrPercentageOrNone::Length(length) => length.to_css(dest),
- LengthOrPercentageOrNone::Percentage(percentage) =>
- write!(dest, "{}%", percentage * 100.),
- LengthOrPercentageOrNone::Calc(calc) => calc.to_css(dest),
- LengthOrPercentageOrNone::None => dest.write_str("none"),
- }
- }
-}
-
/// A computed `<length>` value.
pub type Length = Au;
diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs
index ff682255d99..7b95372d8a7 100644
--- a/components/style/values/computed/mod.rs
+++ b/components/style/values/computed/mod.rs
@@ -6,7 +6,7 @@
use Atom;
use context::QuirksMode;
-use euclid::size::Size2D;
+use euclid::Size2D;
use font_metrics::FontMetricsProvider;
use media_queries::Device;
use num_traits::Zero;
diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs
index 97f14b73f04..98037ca21b9 100644
--- a/components/style/values/computed/position.rs
+++ b/components/style/values/computed/position.rs
@@ -11,6 +11,7 @@ use std::fmt;
use style_traits::ToCss;
use values::computed::LengthOrPercentage;
use values::generics::position::Position as GenericPosition;
+use values::specified::length::Percentage;
/// The computed value of a CSS `<position>`
pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
@@ -25,7 +26,10 @@ impl Position {
/// `50% 50%`
#[inline]
pub fn center() -> Self {
- Self::new(LengthOrPercentage::Percentage(0.5), LengthOrPercentage::Percentage(0.5))
+ Self::new(
+ LengthOrPercentage::Percentage(Percentage(0.5)),
+ LengthOrPercentage::Percentage(Percentage(0.5)),
+ )
}
/// `0% 0%`
diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs
index b64f6d6f277..5354717bc7c 100644
--- a/components/style/values/computed/transform.rs
+++ b/components/style/values/computed/transform.rs
@@ -8,6 +8,7 @@ use properties::animated_properties::Animatable;
use values::computed::{Length, LengthOrPercentage, Number};
use values::generics::transform::TimingFunction as GenericTimingFunction;
use values::generics::transform::TransformOrigin as GenericTransformOrigin;
+use values::specified::length::Percentage;
/// The computed value of a CSS `<transform-origin>`
pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
@@ -20,9 +21,9 @@ impl TransformOrigin {
#[inline]
pub fn initial_value() -> Self {
Self::new(
- LengthOrPercentage::Percentage(0.5),
- LengthOrPercentage::Percentage(0.5),
- Length::from_px(0)
+ LengthOrPercentage::Percentage(Percentage(0.5)),
+ LengthOrPercentage::Percentage(Percentage(0.5)),
+ Length::from_px(0),
)
}
}
diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs
index cd8e315f3b3..1d54a90dbca 100644
--- a/components/style/values/generics/basic_shape.rs
+++ b/components/style/values/generics/basic_shape.rs
@@ -92,7 +92,7 @@ pub struct Ellipse<H, V, LengthOrPercentage> {
/// https://drafts.csswg.org/css-shapes/#typedef-shape-radius
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
pub enum ShapeRadius<LengthOrPercentage> {
Length(LengthOrPercentage),
ClosestSide,
@@ -161,17 +161,6 @@ impl<L> Default for ShapeRadius<L> {
fn default() -> Self { ShapeRadius::ClosestSide }
}
-impl<L: ToCss> ToCss for ShapeRadius<L> {
- #[inline]
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- match *self {
- ShapeRadius::Length(ref lop) => lop.to_css(dest),
- ShapeRadius::ClosestSide => dest.write_str("closest-side"),
- ShapeRadius::FarthestSide => dest.write_str("farthest-side"),
- }
- }
-}
-
impl<L: ToCss> ToCss for Polygon<L> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
dest.write_str("polygon(")?;
diff --git a/components/style/values/generics/gecko.rs b/components/style/values/generics/gecko.rs
index 52c7b130992..31c62078861 100644
--- a/components/style/values/generics/gecko.rs
+++ b/components/style/values/generics/gecko.rs
@@ -5,15 +5,13 @@
//! Generic types for legacy Gecko-only properties that should probably be
//! unshipped at some point in the future.
-use std::fmt;
-use style_traits::ToCss;
-
/// A generic value for scroll snap points.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
pub enum ScrollSnapPoint<LengthOrPercentage> {
/// `none`
None,
/// `repeat(<length-or-percentage>)`
+ #[css(function)]
Repeat(LengthOrPercentage)
}
@@ -33,22 +31,3 @@ impl<L> ScrollSnapPoint<L> {
}
}
}
-
-impl<L> ToCss for ScrollSnapPoint<L>
-where
- L: ToCss,
-{
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- match *self {
- ScrollSnapPoint::None => dest.write_str("none"),
- ScrollSnapPoint::Repeat(ref length) => {
- dest.write_str("repeat(")?;
- length.to_css(dest)?;
- dest.write_str(")")
- },
- }
- }
-}
diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs
index f56941645fb..3cb10439598 100644
--- a/components/style/values/generics/grid.rs
+++ b/components/style/values/generics/grid.rs
@@ -24,7 +24,7 @@ pub struct GridLine {
/// A custom identifier for named lines.
///
/// https://drafts.csswg.org/css-grid/#grid-placement-slot
- pub ident: Option<String>,
+ pub ident: Option<CustomIdent>,
/// Denotes the nth grid line from grid item's placement.
pub line_num: Option<Integer>,
}
@@ -62,7 +62,7 @@ impl ToCss for GridLine {
if let Some(ref s) = self.ident {
dest.write_str(" ")?;
- serialize_identifier(s, dest)?;
+ s.to_css(dest)?;
}
Ok(())
@@ -100,12 +100,10 @@ impl Parse for GridLine {
grid_line.line_num = Some(i);
} else if let Ok(name) = input.try(|i| i.expect_ident()) {
- if val_before_span || grid_line.ident.is_some() ||
- CustomIdent::from_ident((&*name).into(), &[]).is_err() {
- return Err(StyleParseError::UnspecifiedError.into())
+ if val_before_span || grid_line.ident.is_some() {
+ return Err(StyleParseError::UnspecifiedError.into());
}
-
- grid_line.ident = Some(name.into_owned());
+ grid_line.ident = Some(CustomIdent::from_ident(name, &[])?);
} else {
break
}
diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs
index 7730cbea9f9..20094e75935 100644
--- a/components/style/values/generics/image.rs
+++ b/components/style/values/generics/image.rs
@@ -69,7 +69,7 @@ pub enum GradientKind<LineDirection, Length, LengthOrPercentage, Position> {
}
/// A radial gradient's ending shape.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum EndingShape<Length, LengthOrPercentage> {
/// A circular gradient.
@@ -89,7 +89,7 @@ pub enum Circle<Length> {
}
/// An ellipse shape.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Ellipse<LengthOrPercentage> {
/// An ellipse pair of radii.
@@ -285,30 +285,26 @@ pub trait LineDirection {
where W: fmt::Write;
}
-impl<L, LoP> ToCss for EndingShape<L, LoP>
- where L: ToCss, LoP: ToCss,
+impl<L> ToCss for Circle<L>
+where
+ L: ToCss,
{
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
match *self {
- EndingShape::Circle(Circle::Extent(ShapeExtent::FarthestCorner)) |
- EndingShape::Circle(Circle::Extent(ShapeExtent::Cover)) => {
+ Circle::Extent(ShapeExtent::FarthestCorner) |
+ Circle::Extent(ShapeExtent::Cover) => {
dest.write_str("circle")
},
- EndingShape::Circle(Circle::Extent(keyword)) => {
+ Circle::Extent(keyword) => {
dest.write_str("circle ")?;
keyword.to_css(dest)
},
- EndingShape::Circle(Circle::Radius(ref length)) => {
+ Circle::Radius(ref length) => {
length.to_css(dest)
},
- EndingShape::Ellipse(Ellipse::Extent(keyword)) => {
- keyword.to_css(dest)
- },
- EndingShape::Ellipse(Ellipse::Radii(ref x, ref y)) => {
- x.to_css(dest)?;
- dest.write_str(" ")?;
- y.to_css(dest)
- },
}
}
}
diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs
index 4d0a3315560..3d218b0bdf5 100644
--- a/components/style/values/generics/mod.rs
+++ b/components/style/values/generics/mod.rs
@@ -54,13 +54,14 @@ impl SymbolsType {
///
/// Since wherever <counter-style> is used, 'none' is a valid value as
/// well, we combine them into one type to make code simpler.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum CounterStyleOrNone {
- /// none
- None_,
- /// <counter-style-name>
+ /// `none`
+ None,
+ /// `<counter-style-name>`
Name(CustomIdent),
- /// symbols()
+ /// `symbols()`
+ #[css(function)]
Symbols(SymbolsType, Symbols),
}
@@ -84,7 +85,7 @@ impl Parse for CounterStyleOrNone {
return Ok(CounterStyleOrNone::Name(name));
}
if input.try(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(CounterStyleOrNone::None_);
+ return Ok(CounterStyleOrNone::None);
}
if input.try(|i| i.expect_function_matching("symbols")).is_ok() {
return input.parse_nested_block(|input| {
@@ -108,23 +109,6 @@ impl Parse for CounterStyleOrNone {
}
}
-impl ToCss for CounterStyleOrNone {
- #[inline]
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- match self {
- &CounterStyleOrNone::None_ => dest.write_str("none"),
- &CounterStyleOrNone::Name(ref name) => name.to_css(dest),
- &CounterStyleOrNone::Symbols(ref symbols_type, ref symbols) => {
- dest.write_str("symbols(")?;
- symbols_type.to_css(dest)?;
- dest.write_str(" ")?;
- symbols.to_css(dest)?;
- dest.write_str(")")
- }
- }
- }
-}
-
/// A settings tag, defined by a four-character tag and a setting value
///
/// For font-feature-settings, this is a tag and an integer,
diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs
index f1cf9bc2f61..c4a2bae3f07 100644
--- a/components/style/values/specified/calc.rs
+++ b/components/style/values/specified/calc.rs
@@ -15,7 +15,8 @@ use style_traits::{HasViewportPercentage, ToCss, ParseError, StyleParseError};
use style_traits::values::specified::AllowedLengthType;
use values::{CSSInteger, CSSFloat};
use values::specified::{Angle, Time};
-use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength};
+use values::specified::length::{FontRelativeLength, NoCalcLength};
+use values::specified::length::{Percentage, ViewportPercentageLength};
/// A node inside a `Calc` expression's AST.
#[derive(Clone, Debug)]
@@ -74,7 +75,7 @@ pub struct CalcLengthOrPercentage {
pub ex: Option<CSSFloat>,
pub ch: Option<CSSFloat>,
pub rem: Option<CSSFloat>,
- pub percentage: Option<CSSFloat>,
+ pub percentage: Option<Percentage>,
#[cfg(feature = "gecko")]
pub mozmm: Option<CSSFloat>,
}
@@ -128,7 +129,7 @@ impl ToCss for CalcLengthOrPercentage {
if let Some(val) = self.percentage {
first_value_check!();
- try!(write!(dest, "{}%", val * 100.));
+ val.to_css(dest)?;
}
write!(dest, ")")
@@ -298,7 +299,9 @@ impl CalcNode {
{
match *self {
CalcNode::Percentage(pct) => {
- ret.percentage = Some(ret.percentage.unwrap_or(0.) + pct * factor)
+ ret.percentage = Some(Percentage(
+ ret.percentage.map_or(0., |p| p.0) + pct * factor,
+ ));
}
CalcNode::Length(ref l) => {
match *l {
diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs
index d2e6e45d407..cc1655ce33e 100644
--- a/components/style/values/specified/length.rs
+++ b/components/style/values/specified/length.rs
@@ -8,7 +8,7 @@
use app_units::Au;
use cssparser::{Parser, Token, BasicParseError};
-use euclid::size::Size2D;
+use euclid::Size2D;
use font_metrics::FontMetricsQueryResult;
use parser::{Parse, ParserContext};
use std::{cmp, fmt, mem};
@@ -611,9 +611,9 @@ impl Length {
-> Result<Length, ParseError<'i>> {
let token = try!(input.next());
match token {
- Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+ Token::Dimension(ref value, ref unit) if num_context.is_ok(context.parsing_mode, value.value) =>
Length::parse_dimension(context, value.value, unit),
- Token::Number(ref value) if num_context.is_ok(value.value) => {
+ Token::Number(ref value) if num_context.is_ok(context.parsing_mode, value.value) => {
if value.value != 0. &&
!context.parsing_mode.allows_unitless_lengths() &&
!allow_quirks.allowed(context.quirks_mode) {
@@ -701,7 +701,7 @@ impl<T: Parse> Either<Length, T> {
/// As of today, only `-moz-image-rect` supports percentages without length.
/// This is not a regression, and that's a non-standard extension anyway, so I'm
/// not implementing it for now.
-#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct Percentage(pub CSSFloat);
@@ -715,11 +715,12 @@ impl ToCss for Percentage {
impl Percentage {
/// Parse a specific kind of percentage.
- pub fn parse_with_clamping_mode<'i, 't>(input: &mut Parser<'i, 't>,
- context: AllowedNumericType)
+ pub fn parse_with_clamping_mode<'i, 't>(context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ num_context: AllowedNumericType)
-> Result<Self, ParseError<'i>> {
match try!(input.next()) {
- Token::Percentage(ref value) if context.is_ok(value.unit_value) => {
+ Token::Percentage(ref value) if num_context.is_ok(context.parsing_mode, value.unit_value) => {
Ok(Percentage(value.unit_value))
}
t => Err(BasicParseError::UnexpectedToken(t).into())
@@ -727,8 +728,10 @@ impl Percentage {
}
/// Parses a percentage token, but rejects it if it's negative.
- pub fn parse_non_negative<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(input, AllowedNumericType::NonNegative)
+ pub fn parse_non_negative<'i, 't>(context: &ParserContext,
+ input: &mut Parser<'i, 't>)
+ -> Result<Self, ParseError<'i>> {
+ Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
}
/// 0%
@@ -746,8 +749,8 @@ impl Percentage {
impl Parse for Percentage {
#[inline]
- fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(input, AllowedNumericType::All)
+ fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+ Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
}
}
@@ -801,11 +804,11 @@ impl LengthOrPercentage {
{
let token = try!(input.next());
match token {
- Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+ Token::Dimension(ref value, ref unit) if num_context.is_ok(context.parsing_mode, value.value) =>
NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentage::Length),
- Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
+ Token::Percentage(ref value) if num_context.is_ok(context.parsing_mode, value.unit_value) =>
return Ok(LengthOrPercentage::Percentage(Percentage(value.unit_value))),
- Token::Number(value) if num_context.is_ok(value.value) => {
+ Token::Number(value) if num_context.is_ok(context.parsing_mode, value.value) => {
if value.value != 0. &&
!context.parsing_mode.allows_unitless_lengths() &&
!allow_quirks.allowed(context.quirks_mode) {
@@ -935,11 +938,11 @@ impl LengthOrPercentageOrAuto {
-> Result<Self, ParseError<'i>> {
let token = try!(input.next());
match token {
- Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+ Token::Dimension(ref value, ref unit) if num_context.is_ok(context.parsing_mode, value.value) =>
NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentageOrAuto::Length),
- Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
+ Token::Percentage(ref value) if num_context.is_ok(context.parsing_mode, value.unit_value) =>
Ok(LengthOrPercentageOrAuto::Percentage(Percentage(value.unit_value))),
- Token::Number(ref value) if num_context.is_ok(value.value) => {
+ Token::Number(ref value) if num_context.is_ok(context.parsing_mode, value.value) => {
if value.value != 0. &&
!context.parsing_mode.allows_unitless_lengths() &&
!allow_quirks.allowed(context.quirks_mode) {
@@ -1031,11 +1034,11 @@ impl LengthOrPercentageOrNone {
{
let token = try!(input.next());
match token {
- Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+ Token::Dimension(ref value, ref unit) if num_context.is_ok(context.parsing_mode, value.value) =>
NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentageOrNone::Length),
- Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
+ Token::Percentage(ref value) if num_context.is_ok(context.parsing_mode, value.unit_value) =>
Ok(LengthOrPercentageOrNone::Percentage(Percentage(value.unit_value))),
- Token::Number(value) if num_context.is_ok(value.value) => {
+ Token::Number(value) if num_context.is_ok(context.parsing_mode, value.value) => {
if value.value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
!allow_quirks.allowed(context.quirks_mode) {
return Err(StyleParseError::UnspecifiedError.into())
@@ -1113,10 +1116,10 @@ impl LengthOrPercentageOrAutoOrContent {
let num_context = AllowedLengthType::NonNegative;
let token = try!(input.next());
match token {
- Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+ Token::Dimension(ref value, ref unit) if num_context.is_ok(context.parsing_mode, value.value) =>
NoCalcLength::parse_dimension(context, value.value, unit)
.map(LengthOrPercentageOrAutoOrContent::Length),
- Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
+ Token::Percentage(ref value) if num_context.is_ok(context.parsing_mode, value.unit_value) =>
Ok(LengthOrPercentageOrAutoOrContent::Percentage(Percentage(value.unit_value))),
Token::Number(ref value) if value.value == 0. =>
Ok(Self::zero()),
diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs
index 80ce0db08d0..112a3c38bc6 100644
--- a/components/style/values/specified/mod.rs
+++ b/components/style/values/specified/mod.rs
@@ -120,7 +120,7 @@ pub fn parse_number_with_clamping_mode<'i, 't>(context: &ParserContext,
clamping_mode: AllowedNumericType)
-> Result<Number, ParseError<'i>> {
match try!(input.next()) {
- Token::Number(ref value) if clamping_mode.is_ok(value.value) => {
+ Token::Number(ref value) if clamping_mode.is_ok(context.parsing_mode, value.value) => {
Ok(Number {
value: value.value.min(f32::MAX).max(f32::MIN),
calc_clamping_mode: None,
@@ -364,14 +364,21 @@ impl Time {
input: &mut Parser<'i, 't>,
clamping_mode: AllowedNumericType)
-> Result<Self, ParseError<'i>> {
+ use style_traits::PARSING_MODE_DEFAULT;
+
match input.next() {
- Ok(Token::Dimension(ref value, ref unit)) if clamping_mode.is_ok(value.value) => {
+ // Note that we generally pass ParserContext to is_ok() to check
+ // that the ParserMode of the ParserContext allows all numeric
+ // values for SMIL regardless of clamping_mode, but in this Time
+ // value case, the value does not animate for SMIL at all, so we use
+ // PARSING_MODE_DEFAULT directly.
+ Ok(Token::Dimension(ref value, ref unit)) if clamping_mode.is_ok(PARSING_MODE_DEFAULT, value.value) => {
Time::parse_dimension(value.value, &unit, /* from_calc = */ false)
.map_err(|()| StyleParseError::UnspecifiedError.into())
}
Ok(Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => {
match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) {
- Ok(time) if clamping_mode.is_ok(time.seconds) => Ok(time),
+ Ok(time) if clamping_mode.is_ok(PARSING_MODE_DEFAULT, time.seconds) => Ok(time),
_ => Err(StyleParseError::UnspecifiedError.into()),
}
}
@@ -457,21 +464,13 @@ impl Number {
#[allow(missing_docs)]
pub fn parse_non_negative<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-> Result<Number, ParseError<'i>> {
- if context.parsing_mode.allows_all_numeric_values() {
- parse_number(context, input)
- } else {
- parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
- }
+ parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
}
#[allow(missing_docs)]
pub fn parse_at_least_one<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
-> Result<Number, ParseError<'i>> {
- if context.parsing_mode.allows_all_numeric_values() {
- parse_number(context, input)
- } else {
- parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
- }
+ parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
}
}
@@ -522,7 +521,7 @@ impl NumberOrPercentage {
input: &mut Parser<'i, 't>,
type_: AllowedNumericType)
-> Result<Self, ParseError<'i>> {
- if let Ok(per) = input.try(|i| Percentage::parse_with_clamping_mode(i, type_)) {
+ if let Ok(per) = input.try(|i| Percentage::parse_with_clamping_mode(context, i, type_)) {
return Ok(NumberOrPercentage::Percentage(per));
}
diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs
index 2cd206f2a21..ccbc861b3c1 100644
--- a/components/style/values/specified/position.rs
+++ b/components/style/values/specified/position.rs
@@ -224,22 +224,22 @@ impl<S: Side> ToComputedValue for PositionComponent<S> {
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
PositionComponent::Center => {
- ComputedLengthOrPercentage::Percentage(0.5)
+ ComputedLengthOrPercentage::Percentage(Percentage(0.5))
},
PositionComponent::Side(ref keyword, None) => {
- let p = if keyword.is_start() { 0. } else { 1. };
+ let p = Percentage(if keyword.is_start() { 0. } else { 1. });
ComputedLengthOrPercentage::Percentage(p)
},
PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
match length.to_computed_value(context) {
ComputedLengthOrPercentage::Length(length) => {
- ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-length, Some(1.0)))
+ ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-length, Some(Percentage(1.0))))
},
ComputedLengthOrPercentage::Percentage(p) => {
- ComputedLengthOrPercentage::Percentage(1.0 - p)
+ ComputedLengthOrPercentage::Percentage(Percentage(1.0 - p.0))
},
ComputedLengthOrPercentage::Calc(calc) => {
- let p = 1. - calc.percentage.unwrap_or(0.);
+ let p = Percentage(1. - calc.percentage.map_or(0., |p| p.0));
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-calc.unclamped_length(), Some(p)))
},
}
diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs
index d3188e9c876..e4f709ecab9 100644
--- a/components/style/values/specified/transform.rs
+++ b/components/style/values/specified/transform.rs
@@ -14,7 +14,7 @@ use values::computed::transform::TimingFunction as ComputedTimingFunction;
use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction};
use values::generics::transform::{TimingKeyword, TransformOrigin as GenericTransformOrigin};
use values::specified::{Integer, Number};
-use values::specified::length::{Length, LengthOrPercentage};
+use values::specified::length::{Length, LengthOrPercentage, Percentage};
use values::specified::position::{Side, X, Y};
/// The specified value of a CSS `<transform-origin>`
@@ -107,13 +107,13 @@ impl<S> ToComputedValue for OriginComponent<S>
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
OriginComponent::Center => {
- ComputedLengthOrPercentage::Percentage(0.5)
+ ComputedLengthOrPercentage::Percentage(Percentage(0.5))
},
OriginComponent::Length(ref length) => {
length.to_computed_value(context)
},
OriginComponent::Side(ref keyword) => {
- let p = if keyword.is_start() { 0. } else { 1. };
+ let p = Percentage(if keyword.is_start() { 0. } else { 1. });
ComputedLengthOrPercentage::Percentage(p)
},
}
diff --git a/components/style_derive/lib.rs b/components/style_derive/lib.rs
index 0f39a7deca1..6264e8186e1 100644
--- a/components/style_derive/lib.rs
+++ b/components/style_derive/lib.rs
@@ -25,7 +25,7 @@ pub fn derive_to_computed_value(stream: TokenStream) -> TokenStream {
to_computed_value::derive(input).to_string().parse().unwrap()
}
-#[proc_macro_derive(ToCss)]
+#[proc_macro_derive(ToCss, attributes(css))]
pub fn derive_to_css(stream: TokenStream) -> TokenStream {
let input = syn::parse_derive_input(&stream.to_string()).unwrap();
to_css::derive(input).to_string().parse().unwrap()
diff --git a/components/style_derive/to_css.rs b/components/style_derive/to_css.rs
index f85fe1ce418..8531b39ab46 100644
--- a/components/style_derive/to_css.rs
+++ b/components/style_derive/to_css.rs
@@ -16,24 +16,61 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_variant(&input, &style, |bindings, variant| {
- if bindings.is_empty() {
- let identifier = to_css_identifier(variant.ident.as_ref());
- return Some(quote! {
+ let mut identifier = to_css_identifier(variant.ident.as_ref());
+ let mut expr = if bindings.is_empty() {
+ quote! {
::std::fmt::Write::write_str(dest, #identifier)
- });
- }
- let (first, rest) = bindings.split_first().expect("unit variants are not yet supported");
- where_clause.predicates.push(where_predicate(first.field.ty.clone()));
- let mut expr = quote! {
- ::style_traits::ToCss::to_css(#first, dest)
+ }
+ } else {
+ let (first, rest) = bindings.split_first().expect("unit variants are not yet supported");
+ where_clause.predicates.push(where_predicate(first.field.ty.clone()));
+ let mut expr = quote! {
+ ::style_traits::ToCss::to_css(#first, dest)
+ };
+ for binding in rest {
+ where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
+ expr = quote! {
+ #expr?;
+ ::std::fmt::Write::write_str(dest, " ")?;
+ ::style_traits::ToCss::to_css(#binding, dest)
+ };
+ }
+ expr
};
- for binding in rest {
- where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
+ let mut css_attrs = variant.attrs.iter().filter(|attr| attr.name() == "css");
+ let is_function = css_attrs.next().map_or(false, |attr| {
+ match attr.value {
+ syn::MetaItem::List(ref ident, ref items) if ident.as_ref() == "css" => {
+ let mut nested = items.iter();
+ let is_function = nested.next().map_or(false, |attr| {
+ match *attr {
+ syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref ident)) => {
+ if ident.as_ref() != "function" {
+ panic!("only `#[css(function)]` is supported for now")
+ }
+ true
+ },
+ _ => panic!("only `#[css(<ident>)]` is supported for now"),
+ }
+ });
+ if nested.next().is_some() {
+ panic!("only `#[css()]` or `#[css(<ident>)]` is supported for now")
+ }
+ is_function
+ },
+ _ => panic!("only `#[css(...)]` is supported for now"),
+ }
+ });
+ if css_attrs.next().is_some() {
+ panic!("only a single `#[css(...)]` attribute is supported for now");
+ }
+ if is_function {
+ identifier.push_str("(");
expr = quote! {
+ ::std::fmt::Write::write_str(dest, #identifier)?;
#expr?;
- ::std::fmt::Write::write_str(dest, " ")?;
- ::style_traits::ToCss::to_css(#binding, dest)
- };
+ ::std::fmt::Write::write_str(dest, ")")
+ }
}
Some(expr)
});
diff --git a/components/style_traits/Cargo.toml b/components/style_traits/Cargo.toml
index e298fe53053..62a6a44337c 100644
--- a/components/style_traits/Cargo.toml
+++ b/components/style_traits/Cargo.toml
@@ -16,8 +16,9 @@ gecko = []
[dependencies]
app_units = "0.4.1"
+bitflags = "0.7"
cssparser = "0.14.0"
-euclid = "0.13"
+euclid = "0.14.4"
heapsize = {version = "0.4", optional = true}
heapsize_derive = {version = "0.1", optional = true}
selectors = { path = "../selectors" }
diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs
index 87aa6b330fb..ed085a12993 100644
--- a/components/style_traits/lib.rs
+++ b/components/style_traits/lib.rs
@@ -14,6 +14,7 @@
#![cfg_attr(feature = "servo", feature(plugin))]
extern crate app_units;
+#[macro_use] extern crate bitflags;
#[macro_use] extern crate cssparser;
extern crate euclid;
#[cfg(feature = "servo")] extern crate heapsize;
@@ -140,3 +141,32 @@ impl<'a> From<PropertyDeclarationParseError> for ParseError<'a> {
cssparser::ParseError::Custom(SelectorParseError::Custom(StyleParseError::PropertyDeclaration(this)))
}
}
+
+bitflags! {
+ /// The mode to use when parsing values.
+ pub flags ParsingMode: u8 {
+ /// In CSS, lengths must have units, except for zero values, where the unit can be omitted.
+ /// https://www.w3.org/TR/css3-values/#lengths
+ const PARSING_MODE_DEFAULT = 0x00,
+ /// In SVG, a coordinate or length value without a unit identifier (e.g., "25") is assumed
+ /// to be in user units (px).
+ /// https://www.w3.org/TR/SVG/coords.html#Units
+ const PARSING_MODE_ALLOW_UNITLESS_LENGTH = 0x01,
+ /// In SVG, out-of-range values are not treated as an error in parsing.
+ /// https://www.w3.org/TR/SVG/implnote.html#RangeClamping
+ const PARSING_MODE_ALLOW_ALL_NUMERIC_VALUES = 0x02,
+ }
+}
+
+impl ParsingMode {
+ /// Whether the parsing mode allows unitless lengths for non-zero values to be intpreted as px.
+ pub fn allows_unitless_lengths(&self) -> bool {
+ self.intersects(PARSING_MODE_ALLOW_UNITLESS_LENGTH)
+ }
+
+ /// Whether the parsing mode allows all numeric values.
+ pub fn allows_all_numeric_values(&self) -> bool {
+ self.intersects(PARSING_MODE_ALLOW_ALL_NUMERIC_VALUES)
+ }
+}
+
diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs
index 6c31da16664..3a587ac81f9 100644
--- a/components/style_traits/values.rs
+++ b/components/style_traits/values.rs
@@ -204,6 +204,7 @@ macro_rules! __define_css_keyword_enum__actual {
/// Helper types for the handling of specified values.
pub mod specified {
+ use ParsingMode;
use app_units::Au;
use std::cmp;
@@ -228,7 +229,10 @@ pub mod specified {
impl AllowedLengthType {
/// Whether value is valid for this allowed length type.
#[inline]
- pub fn is_ok(&self, value: f32) -> bool {
+ pub fn is_ok(&self, parsing_mode: ParsingMode, value: f32) -> bool {
+ if parsing_mode.allows_all_numeric_values() {
+ return true;
+ }
match *self {
AllowedLengthType::All => true,
AllowedLengthType::NonNegative => value >= 0.,
@@ -261,7 +265,10 @@ pub mod specified {
impl AllowedNumericType {
/// Whether the value fits the rules of this numeric type.
#[inline]
- pub fn is_ok(&self, val: f32) -> bool {
+ pub fn is_ok(&self, parsing_mode: ParsingMode, val: f32) -> bool {
+ if parsing_mode.allows_all_numeric_values() {
+ return true;
+ }
match *self {
AllowedNumericType::All => true,
AllowedNumericType::NonNegative => val >= 0.0,
diff --git a/components/style_traits/viewport.rs b/components/style_traits/viewport.rs
index 9a77a554f29..c3061a30d22 100644
--- a/components/style_traits/viewport.rs
+++ b/components/style_traits/viewport.rs
@@ -6,10 +6,9 @@
use {CSSPixel, PinchZoomFactor, ParseError};
use cssparser::{Parser, ToCss, ParseError as CssParseError, BasicParseError};
-use euclid::size::TypedSize2D;
+use euclid::TypedSize2D;
use std::ascii::AsciiExt;
use std::fmt;
-use values::specified::AllowedLengthType;
define_css_keyword_enum!(UserZoom:
"zoom" => Zoom,
@@ -141,12 +140,18 @@ impl Zoom {
///
/// https://drafts.csswg.org/css-device-adapt/#descdef-viewport-zoom
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Zoom, ParseError<'i>> {
+ use PARSING_MODE_DEFAULT;
use cssparser::Token;
+ use values::specified::AllowedLengthType::NonNegative;
match try!(input.next()) {
- Token::Percentage(ref value) if AllowedLengthType::NonNegative.is_ok(value.unit_value) =>
+ // TODO: This parse() method should take ParserContext as an
+ // argument, and pass ParsingMode owned by the ParserContext to
+ // is_ok() instead of using PARSING_MODE_DEFAULT directly.
+ // In order to do so, we might want to move these stuff into style::stylesheets::viewport_rule.
+ Token::Percentage(ref value) if NonNegative.is_ok(PARSING_MODE_DEFAULT, value.unit_value) =>
Ok(Zoom::Percentage(value.unit_value)),
- Token::Number(ref value) if AllowedLengthType::NonNegative.is_ok(value.value) =>
+ Token::Number(ref value) if NonNegative.is_ok(PARSING_MODE_DEFAULT, value.value) =>
Ok(Zoom::Number(value.value)),
Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
Ok(Zoom::Auto),
diff --git a/components/webdriver_server/Cargo.toml b/components/webdriver_server/Cargo.toml
index 7038a7c30d4..cf835159f7a 100644
--- a/components/webdriver_server/Cargo.toml
+++ b/components/webdriver_server/Cargo.toml
@@ -12,7 +12,7 @@ path = "lib.rs"
[dependencies]
base64 = "0.5.2"
cookie = "0.6"
-euclid = "0.13"
+euclid = "0.14"
hyper = "0.10"
image = "0.12"
ipc-channel = "0.7"