aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Zbarsky <dzbarsky@gmail.com>2015-07-27 23:05:18 -0400
committerDavid Zbarsky <dzbarsky@gmail.com>2015-07-29 20:17:50 -0400
commite484d6b5e3f378a33fabf5ff64cb9a7f76f3ba68 (patch)
tree49235a86cc3949b27d2262c8bfb11af5c964a249
parent416931f4be43826d3b2a96905c22f626c88b603c (diff)
downloadservo-e484d6b5e3f378a33fabf5ff64cb9a7f76f3ba68.tar.gz
servo-e484d6b5e3f378a33fabf5ff64cb9a7f76f3ba68.zip
Implement getComputedStyle
-rw-r--r--components/layout/block.rs2
-rw-r--r--components/layout/construct.rs2
-rw-r--r--components/layout/display_list_builder.rs8
-rw-r--r--components/layout/fragment.rs4
-rw-r--r--components/layout/layout_task.rs262
-rw-r--r--components/layout/text.rs4
-rw-r--r--components/script/dom/bindings/trace.rs2
-rw-r--r--components/script/dom/cssstyledeclaration.rs22
-rw-r--r--components/script/dom/htmlelement.rs2
-rw-r--r--components/script/dom/webidls/Window.webidl7
-rw-r--r--components/script/dom/window.rs38
-rw-r--r--components/script/layout_interface.rs7
-rw-r--r--components/style/animation.rs43
-rw-r--r--components/style/properties.mako.rs442
-rw-r--r--components/style/values.rs65
-rw-r--r--components/util/geometry.rs11
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/events-006.htm.ini5
-rw-r--r--tests/wpt/metadata-css/css21_dev/html4/pseudo-elements-001.htm.ini9
-rw-r--r--tests/wpt/metadata/dom/nodes/Element-classlist.html.ini20
-rw-r--r--tests/wpt/metadata/html/dom/elements/global-attributes/id-attribute.html.ini20
-rw-r--r--tests/wpt/metadata/html/semantics/edits/the-del-element/del_effect.html.ini5
-rw-r--r--tests/wpt/metadata/html/semantics/edits/the-ins-element/ins_effect.html.ini5
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json8
-rw-r--r--tests/wpt/mozilla/tests/mozilla/getComputedStyle.html45
24 files changed, 886 insertions, 152 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs
index 91166c6cdc2..c7304865ecb 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -618,7 +618,7 @@ impl BlockFlow {
pub fn transform_requires_layer(&self) -> bool {
// Check if the transform matrix is 2D or 3D
- if let Some(ref transform_list) = self.fragment.style().get_effects().transform {
+ if let Some(ref transform_list) = self.fragment.style().get_effects().transform.0 {
for transform in transform_list {
match transform {
&transform::ComputedOperation::Perspective(..) => {
diff --git a/components/layout/construct.rs b/components/layout/construct.rs
index 7e53bfee798..89e88847a22 100644
--- a/components/layout/construct.rs
+++ b/components/layout/construct.rs
@@ -1117,7 +1117,7 @@ impl<'a> FlowConstructor<'a> {
fn build_flow_for_list_item(&mut self, node: &ThreadSafeLayoutNode, flotation: float::T)
-> ConstructionResult {
let flotation = FloatKind::from_property(flotation);
- let marker_fragment = match node.style().get_list().list_style_image {
+ let marker_fragment = match node.style().get_list().list_style_image.0 {
Some(ref url) => {
let image_info = box ImageFragmentInfo::new(node,
Some((*url).clone()),
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index c63e7e1f5ae..9569947da13 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -361,7 +361,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Implements background image, per spec:
// http://www.w3.org/TR/CSS21/colors.html#background
let background = style.get_background();
- match background.background_image {
+ match background.background_image.0 {
None => {}
Some(computed::Image::LinearGradient(ref gradient)) => {
self.build_display_list_for_background_linear_gradient(display_list,
@@ -668,7 +668,7 @@ impl FragmentDisplayListBuilding for Fragment {
absolute_bounds: &Rect<Au>,
clip: &ClippingRegion) {
// NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back).
- for box_shadow in style.get_effects().box_shadow.iter().rev() {
+ 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)),
box_shadow.blur_radius,
@@ -863,7 +863,7 @@ impl FragmentDisplayListBuilding for Fragment {
-> ClippingRegion {
// Account for `clip` per CSS 2.1 § 11.1.2.
let style_clip_rect = match (self.style().get_box().position,
- self.style().get_effects().clip) {
+ self.style().get_effects().clip.0) {
(position::T::absolute, Some(style_clip_rect)) => style_clip_rect,
_ => return (*parent_clip).clone(),
};
@@ -1147,7 +1147,7 @@ impl FragmentDisplayListBuilding for Fragment {
let mut transform = Matrix4::identity();
- if let Some(ref operations) = self.style().get_effects().transform {
+ if let Some(ref operations) = self.style().get_effects().transform.0 {
let transform_origin = self.style().get_effects().transform_origin;
let transform_origin =
Point3D::new(model::specified(transform_origin.horizontal,
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index edc4e0fb66a..721d7ea1573 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -1989,7 +1989,7 @@ impl Fragment {
if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal {
return true
}
- if self.style().get_effects().transform.is_some() {
+ if self.style().get_effects().transform.0.is_some() {
return true
}
match self.style().get_used_transform_style() {
@@ -2036,7 +2036,7 @@ impl Fragment {
let mut overflow = border_box;
// Box shadows cause us to draw outside our border box.
- for box_shadow in self.style().get_effects().box_shadow.iter() {
+ for box_shadow in self.style().get_effects().box_shadow.0.iter() {
let offset = Point2D::new(box_shadow.offset_x, box_shadow.offset_y);
let inflation = box_shadow.spread_radius + box_shadow.blur_radius *
BLUR_INFLATION_FACTOR;
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index dec05eef49f..363e35cc72e 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -10,6 +10,7 @@
use animation;
use construct::ConstructionResult;
use context::{SharedLayoutContext, heap_size_of_local_context};
+use cssparser::ToCss;
use data::LayoutDataWrapper;
use display_list_builder::ToGfxColor;
use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
@@ -55,9 +56,10 @@ use script::dom::node::{LayoutData, Node};
use script::layout_interface::{Animation, ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse};
use script::layout_interface::{NewLayoutTaskInfo, Msg, Reflow, ReflowGoal, ReflowQueryType};
-use script::layout_interface::{ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
+use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, StylesheetLoadResponder};
+use selectors::parser::PseudoElement;
use serde::json;
use std::borrow::ToOwned;
use std::cell::Cell;
@@ -67,20 +69,23 @@ use std::mem::transmute;
use std::ops::{Deref, DerefMut};
use std::sync::mpsc::{channel, Sender, Receiver, Select};
use std::sync::{Arc, Mutex, MutexGuard};
+use string_cache::Atom;
use style::computed_values::{filter, mix_blend_mode};
use style::media_queries::{MediaType, MediaQueryList, Device};
use style::properties::style_structs;
+use style::properties::longhands::{display, position};
use style::selector_matching::Stylist;
use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt};
use url::Url;
use util::cursor::Cursor;
-use util::geometry::{Au, MAX_RECT};
-use util::logical_geometry::LogicalPoint;
+use util::geometry::{Au, MAX_RECT, ZERO_POINT};
+use util::logical_geometry::{LogicalPoint, WritingMode};
use util::mem::HeapSizeOf;
use util::opts;
use util::task::spawn_named_with_send_on_failure;
use util::task_state;
use util::workqueue::WorkQueue;
+use wrapper::ThreadSafeLayoutNode;
/// The number of screens of data we're allowed to generate display lists for in each direction.
pub const DISPLAY_PORT_SIZE_FACTOR: i32 = 8;
@@ -129,6 +134,9 @@ pub struct LayoutTaskData {
/// A queued response for the client {top, left, width, height} of a node in pixels.
pub client_rect_response: Rect<i32>,
+ /// A queued response for the resolved style property of an element.
+ pub resolved_style_response: Option<String>,
+
/// The list of currently-running animations.
pub running_animations: Vec<Animation>,
@@ -372,6 +380,7 @@ impl LayoutTask {
content_box_response: Rect::zero(),
content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(),
+ resolved_style_response: None,
running_animations: Vec::new(),
visible_rects: Arc::new(HashMap::with_hash_state(Default::default())),
new_animations_receiver: new_animations_receiver,
@@ -864,6 +873,127 @@ impl LayoutTask {
rw_data.client_rect_response = iterator.client_rect;
}
+ // Compute the resolved value of property for a given (pseudo)element.
+ // Stores the result in rw_data.resolved_style_response.
+ // https://drafts.csswg.org/cssom/#resolved-value
+ fn process_resolved_style_request<'a>(&'a self,
+ requested_node: TrustedNodeAddress,
+ pseudo: &Option<PseudoElement>,
+ property: &Atom,
+ layout_root: &mut FlowRef,
+ rw_data: &mut RWGuard<'a>) {
+ // FIXME: Isolate this transmutation into a "bridge" module.
+ // FIXME(rust#16366): The following line had to be moved because of a
+ // rustc bug. It should be in the next unsafe block.
+ let node: LayoutJS<Node> = unsafe {
+ LayoutJS::from_trusted_node_address(requested_node)
+ };
+ let node: &LayoutNode = unsafe {
+ transmute(&node)
+ };
+
+ let layout_node = ThreadSafeLayoutNode::new(node);
+ let layout_node = match pseudo {
+ &Some(PseudoElement::Before) => layout_node.get_before_pseudo(),
+ &Some(PseudoElement::After) => layout_node.get_after_pseudo(),
+ _ => Some(layout_node)
+ };
+
+ let layout_node = match layout_node {
+ None => {
+ // The pseudo doesn't exist, return nothing. Chrome seems to query
+ // the element itself in this case, Firefox uses the resolved value.
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29006
+ rw_data.resolved_style_response = None;
+ return;
+ }
+ Some(layout_node) => layout_node
+ };
+
+ let style = &*layout_node.style();
+
+ let positioned = match style.get_box().position {
+ position::computed_value::T::relative |
+ /*position::computed_value::T::sticky |*/
+ position::computed_value::T::fixed |
+ position::computed_value::T::absolute => true,
+ _ => false
+ };
+
+ //TODO: determine whether requested property applies to the element.
+ // eg. width does not apply to non-replaced inline elements.
+ // Existing browsers disagree about when left/top/right/bottom apply
+ // (Chrome seems to think they never apply and always returns resolved values).
+ // There are probably other quirks.
+ let applies = true;
+
+ // TODO: we will return neither the computed nor used value for margin and padding.
+ // Firefox returns blank strings for the computed value of shorthands,
+ // so this should be web-compatible.
+ match property.clone() {
+ atom!("margin-bottom") | atom!("margin-top") |
+ atom!("margin-left") | atom!("margin-right") |
+ atom!("padding-bottom") | atom!("padding-top") |
+ atom!("padding-left") | atom!("padding-right")
+ if applies && style.get_box().display != display::computed_value::T::none => {
+ let (margin_padding, side) = match *property {
+ atom!("margin-bottom") => (MarginPadding::Margin, Side::Bottom),
+ atom!("margin-top") => (MarginPadding::Margin, Side::Top),
+ atom!("margin-left") => (MarginPadding::Margin, Side::Left),
+ atom!("margin-right") => (MarginPadding::Margin, Side::Right),
+ atom!("padding-bottom") => (MarginPadding::Padding, Side::Bottom),
+ atom!("padding-top") => (MarginPadding::Padding, Side::Top),
+ atom!("padding-left") => (MarginPadding::Padding, Side::Left),
+ atom!("padding-right") => (MarginPadding::Padding, Side::Right),
+ _ => unreachable!()
+ };
+ let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
+ let mut iterator =
+ MarginRetrievingFragmentBorderBoxIterator::new(requested_node,
+ side,
+ margin_padding,
+ style.writing_mode);
+ sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
+ rw_data.resolved_style_response = iterator.result.map(|r| r.to_css_string());
+ },
+
+ atom!("bottom") | atom!("top") | atom!("right") |
+ atom!("left") | atom!("width") | atom!("height")
+ if applies && positioned && style.get_box().display != display::computed_value::T::none => {
+ let layout_data = layout_node.borrow_layout_data();
+ let position = layout_data.as_ref().map(|layout_data| {
+ match layout_data.data.flow_construction_result {
+ ConstructionResult::Flow(ref flow_ref, _) =>
+ flow::base(flow_ref.deref()).stacking_relative_position,
+ // TODO search parents until we find node with a flow ref.
+ _ => ZERO_POINT
+ }
+ }).unwrap_or(ZERO_POINT);
+ let property = match *property {
+ atom!("bottom") => PositionProperty::Bottom,
+ atom!("top") => PositionProperty::Top,
+ atom!("left") => PositionProperty::Left,
+ atom!("right") => PositionProperty::Right,
+ atom!("width") => PositionProperty::Width,
+ atom!("height") => PositionProperty::Height,
+ _ => unreachable!()
+ };
+ let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
+ let mut iterator =
+ PositionRetrievingFragmentBorderBoxIterator::new(requested_node,
+ property,
+ position);
+ sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
+ rw_data.resolved_style_response = iterator.result.map(|r| r.to_css_string());
+ },
+ // FIXME: implement used value computation for line-height
+ property => {
+ rw_data.resolved_style_response = style.computed_value_to_string(property.as_slice());
+ }
+ };
+ }
+
+
fn compute_abs_pos_and_build_display_list<'a>(&'a self,
data: &Reflow,
layout_root: &mut FlowRef,
@@ -1052,15 +1182,14 @@ impl LayoutTask {
let mut root_flow = (*rw_data.root_flow.as_ref().unwrap()).clone();
match data.query_type {
- ReflowQueryType::ContentBoxQuery(node) => {
- self.process_content_box_request(node, &mut root_flow, &mut rw_data)
- }
- ReflowQueryType::ContentBoxesQuery(node) => {
- self.process_content_boxes_request(node, &mut root_flow, &mut rw_data)
- }
- ReflowQueryType::NodeGeometryQuery(node) => {
- self.process_node_geometry_request(node, &mut root_flow, &mut rw_data)
- }
+ ReflowQueryType::ContentBoxQuery(node) =>
+ self.process_content_box_request(node, &mut root_flow, &mut rw_data),
+ ReflowQueryType::ContentBoxesQuery(node) =>
+ self.process_content_boxes_request(node, &mut root_flow, &mut rw_data),
+ ReflowQueryType::NodeGeometryQuery(node) =>
+ self.process_node_geometry_request(node, &mut root_flow, &mut rw_data),
+ ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) =>
+ self.process_resolved_style_request(node, pseudo, property, &mut root_flow, &mut rw_data),
ReflowQueryType::NoQuery => {}
}
@@ -1308,6 +1437,13 @@ impl LayoutRPC for LayoutRPCImpl {
}
}
+ /// Retrieves the resolved value for a CSS style property.
+ fn resolved_style(&self) -> ResolvedStyleResponse {
+ let &LayoutRPCImpl(ref rw_data) = self;
+ let rw_data = rw_data.lock().unwrap();
+ ResolvedStyleResponse(rw_data.resolved_style_response.clone())
+ }
+
/// Requests the node containing the point of interest.
fn hit_test(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()> {
let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y));
@@ -1459,6 +1595,108 @@ impl FragmentBorderBoxIterator for FragmentLocatingFragmentIterator {
}
}
+enum Side {
+ Left,
+ Right,
+ Bottom,
+ Top
+}
+
+enum MarginPadding {
+ Margin,
+ Padding
+}
+
+enum PositionProperty {
+ Left,
+ Right,
+ Top,
+ Bottom,
+ Width,
+ Height,
+}
+
+struct PositionRetrievingFragmentBorderBoxIterator {
+ node_address: OpaqueNode,
+ result: Option<Au>,
+ position: Point2D<Au>,
+ property: PositionProperty,
+}
+
+impl PositionRetrievingFragmentBorderBoxIterator {
+ fn new(node_address: OpaqueNode,
+ property: PositionProperty,
+ position: Point2D<Au>) -> PositionRetrievingFragmentBorderBoxIterator {
+ PositionRetrievingFragmentBorderBoxIterator {
+ node_address: node_address,
+ position: position,
+ property: property,
+ result: None,
+ }
+ }
+}
+
+impl FragmentBorderBoxIterator for PositionRetrievingFragmentBorderBoxIterator {
+ fn process(&mut self, _: &Fragment, border_box: &Rect<Au>) {
+ self.result =
+ Some(match self.property {
+ PositionProperty::Left => self.position.x,
+ PositionProperty::Top => self.position.y,
+ PositionProperty::Width => border_box.size.width,
+ PositionProperty::Height => border_box.size.height,
+ // TODO: the following 2 calculations are completely wrong.
+ // They should return the difference between the parent's and this
+ // fragment's border boxes.
+ PositionProperty::Right => border_box.max_x() + self.position.x,
+ PositionProperty::Bottom => border_box.max_y() + self.position.y,
+ });
+ }
+
+ fn should_process(&mut self, fragment: &Fragment) -> bool {
+ fragment.contains_node(self.node_address)
+ }
+}
+
+struct MarginRetrievingFragmentBorderBoxIterator {
+ node_address: OpaqueNode,
+ result: Option<Au>,
+ writing_mode: WritingMode,
+ margin_padding: MarginPadding,
+ side: Side,
+}
+
+impl MarginRetrievingFragmentBorderBoxIterator {
+ fn new(node_address: OpaqueNode, side: Side, margin_padding:
+ MarginPadding, writing_mode: WritingMode) -> MarginRetrievingFragmentBorderBoxIterator {
+ MarginRetrievingFragmentBorderBoxIterator {
+ node_address: node_address,
+ side: side,
+ margin_padding: margin_padding,
+ result: None,
+ writing_mode: writing_mode,
+ }
+ }
+}
+
+impl FragmentBorderBoxIterator for MarginRetrievingFragmentBorderBoxIterator {
+ fn process(&mut self, fragment: &Fragment, _: &Rect<Au>) {
+ let rect = match self.margin_padding {
+ MarginPadding::Margin => &fragment.margin,
+ MarginPadding::Padding => &fragment.border_padding
+ };
+ self.result = Some(match self.side {
+ Side::Left => rect.left(self.writing_mode),
+ Side::Right => rect.right(self.writing_mode),
+ Side::Bottom => rect.bottom(self.writing_mode),
+ Side::Top => rect.top(self.writing_mode)
+ });
+ }
+
+ fn should_process(&mut self, fragment: &Fragment) -> bool {
+ fragment.contains_node(self.node_address)
+ }
+}
+
// The default computed value for background-color is transparent (see
// http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we
// need to propagate the background color from the root HTML/Body
diff --git a/components/layout/text.rs b/components/layout/text.rs
index 8ff70d70a07..88756a4f51d 100644
--- a/components/layout/text.rs
+++ b/components/layout/text.rs
@@ -167,8 +167,8 @@ impl TextRunScanner {
white_space::T::pre => CompressionMode::CompressNone,
};
text_transform = inherited_text_style.text_transform;
- letter_spacing = inherited_text_style.letter_spacing;
- word_spacing = inherited_text_style.word_spacing.unwrap_or(Au(0));
+ letter_spacing = inherited_text_style.letter_spacing.0;
+ word_spacing = inherited_text_style.word_spacing.0.unwrap_or(Au(0));
text_rendering = inherited_text_style.text_rendering;
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index ad8da0e41b0..741921af652 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -63,6 +63,7 @@ use msg::constellation_msg::ConstellationChan;
use net_traits::image::base::Image;
use profile_traits::mem::ProfilerChan;
use util::str::{LengthOrPercentageOrAuto};
+use selectors::parser::PseudoElement;
use serde::{Deserialize, Serialize};
use std::cell::{Cell, UnsafeCell, RefCell};
use std::collections::{HashMap, HashSet};
@@ -304,6 +305,7 @@ no_jsmanaged_fields!(LineCapStyle, LineJoinStyle, CompositionOrBlending);
no_jsmanaged_fields!(RepetitionStyle);
no_jsmanaged_fields!(WebGLError);
no_jsmanaged_fields!(ProfilerChan);
+no_jsmanaged_fields!(PseudoElement);
impl JSTraceable for Box<ScriptChan+Send> {
#[inline]
diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs
index d3c1af656d4..99e70f9c387 100644
--- a/components/script/dom/cssstyledeclaration.rs
+++ b/components/script/dom/cssstyledeclaration.rs
@@ -11,9 +11,10 @@ use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::document::DocumentHelpers;
use dom::element::{ElementHelpers, StylePriority};
use dom::htmlelement::HTMLElement;
-use dom::node::{window_from_node, document_from_node, NodeDamage};
+use dom::node::{window_from_node, document_from_node, NodeDamage, NodeHelpers};
use dom::window::{Window, WindowHelpers};
use util::str::DOMString;
+use selectors::parser::PseudoElement;
use string_cache::Atom;
use style::properties::{is_supported_property, longhands_from_shorthand, parse_style_attribute};
use style::properties::PropertyDeclaration;
@@ -27,6 +28,7 @@ pub struct CSSStyleDeclaration {
reflector_: Reflector,
owner: JS<HTMLElement>,
readonly: bool,
+ pseudo: Option<PseudoElement>,
}
#[derive(PartialEq)]
@@ -56,17 +58,20 @@ fn serialize_list(list: &Vec<PropertyDeclaration>) -> DOMString {
impl CSSStyleDeclaration {
pub fn new_inherited(owner: &HTMLElement,
+ pseudo: Option<PseudoElement>,
modification_access: CSSModificationAccess) -> CSSStyleDeclaration {
CSSStyleDeclaration {
reflector_: Reflector::new(),
owner: JS::from_ref(owner),
+ pseudo: pseudo,
readonly: modification_access == CSSModificationAccess::Readonly,
}
}
pub fn new(global: &Window, owner: &HTMLElement,
+ pseudo: Option<PseudoElement>,
modification_access: CSSModificationAccess) -> Root<CSSStyleDeclaration> {
- reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner, modification_access),
+ reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner, pseudo, modification_access),
GlobalRef::Window(global),
CSSStyleDeclarationBinding::Wrap)
}
@@ -75,6 +80,7 @@ impl CSSStyleDeclaration {
trait PrivateCSSStyleDeclarationHelpers {
fn get_declaration(self, property: &Atom) -> Option<PropertyDeclaration>;
fn get_important_declaration(self, property: &Atom) -> Option<PropertyDeclaration>;
+ fn get_computed_style(self, property: &Atom) -> Option<DOMString>;
}
impl<'a> PrivateCSSStyleDeclarationHelpers for &'a CSSStyleDeclaration {
@@ -89,6 +95,13 @@ impl<'a> PrivateCSSStyleDeclarationHelpers for &'a CSSStyleDeclaration {
let element = ElementCast::from_ref(owner.r());
element.get_important_inline_style_declaration(property).map(|decl| decl.clone())
}
+
+ fn get_computed_style(self, property: &Atom) -> Option<DOMString> {
+ let owner = self.owner.root();
+ let node = NodeCast::from_ref(owner.r());
+ let addr = node.to_trusted_node_address();
+ window_from_node(owner.r()).resolved_style_query(addr, self.pseudo.clone(), property)
+ }
}
impl<'a> CSSStyleDeclarationMethods for &'a CSSStyleDeclaration {
@@ -129,6 +142,11 @@ impl<'a> CSSStyleDeclarationMethods for &'a CSSStyleDeclaration {
// Step 1
let property = Atom::from_slice(&property.to_ascii_lowercase());
+ if self.readonly {
+ // Readonly style declarations are used for getComputedStyle.
+ return self.get_computed_style(&property).unwrap_or("".to_owned());
+ }
+
// Step 2
let longhand_properties = longhands_from_shorthand(&property);
if let Some(longhand_properties) = longhand_properties {
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index 85574373c30..513a9d39775 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -135,7 +135,7 @@ impl<'a> HTMLElementMethods for &'a HTMLElement {
fn Style(self) -> Root<CSSStyleDeclaration> {
self.style_decl.or_init(|| {
let global = window_from_node(self);
- CSSStyleDeclaration::new(global.r(), self, CSSModificationAccess::ReadWrite)
+ CSSStyleDeclaration::new(global.r(), self, None, CSSModificationAccess::ReadWrite)
})
}
diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl
index 54c112cec41..1fa85ffbe63 100644
--- a/components/script/dom/webidls/Window.webidl
+++ b/components/script/dom/webidls/Window.webidl
@@ -90,6 +90,13 @@ partial interface Window {
/*[Replaceable]*/ readonly attribute Performance performance;
};
+// https://drafts.csswg.org/cssom/#extensions-to-the-window-interface
+partial interface Window {
+ //CSSStyleDeclaration getComputedStyle(Element elt, optional DOMString? pseudoElt);
+ [NewObject]
+ CSSStyleDeclaration getComputedStyle(HTMLElement elt, optional DOMString pseudoElt);
+};
+
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-window-interface
partial interface Window {
//MediaQueryList matchMedia(DOMString query);
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index e00164d3c93..d4f2a36eabf 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -20,9 +20,11 @@ use dom::bindings::utils::{GlobalStaticData, Reflectable, WindowProxyHandler};
use dom::browsercontext::BrowsingContext;
use dom::console::Console;
use dom::crypto::Crypto;
+use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration};
use dom::document::{Document, DocumentHelpers};
use dom::element::Element;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
+use dom::htmlelement::HTMLElement;
use dom::location::Location;
use dom::navigator::Navigator;
use dom::node::{window_from_node, TrustedNodeAddress, NodeHelpers};
@@ -30,7 +32,7 @@ use dom::performance::Performance;
use dom::screen::Screen;
use dom::storage::Storage;
use layout_interface::{ReflowGoal, ReflowQueryType, LayoutRPC, LayoutChan, Reflow, Msg};
-use layout_interface::{ContentBoxResponse, ContentBoxesResponse, ScriptReflow};
+use layout_interface::{ContentBoxResponse, ContentBoxesResponse, ResolvedStyleResponse, ScriptReflow};
use page::Page;
use script_task::{TimerSource, ScriptChan, ScriptPort, NonWorkerScriptChan};
use script_task::ScriptMsg;
@@ -47,6 +49,7 @@ use net_traits::ResourceTask;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask};
use net_traits::storage_task::{StorageTask, StorageType};
use profile_traits::mem;
+use string_cache::Atom;
use util::geometry::{self, Au, MAX_RECT};
use util::{breakpoint, opts};
use util::str::{DOMString,HTML_SPACE_CHARACTERS};
@@ -58,10 +61,12 @@ use js::jsapi::{JSContext, HandleValue};
use js::jsapi::{JS_GC, JS_GetRuntime, JSAutoCompartment, JSAutoRequest};
use js::rust::Runtime;
use js::rust::CompileOptionsWrapper;
+use selectors::parser::PseudoElement;
use url::{Url, UrlParser};
use libc;
use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD};
+use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::{Cell, Ref, RefMut, RefCell};
use std::collections::HashSet;
@@ -539,6 +544,23 @@ impl<'a> WindowMethods for &'a Window {
chan.send(Err(WebDriverJSError::Timeout)).unwrap();
}
}
+
+ // https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle
+ fn GetComputedStyle(self,
+ element: &HTMLElement,
+ pseudo: Option<DOMString>) -> Root<CSSStyleDeclaration> {
+ // Steps 1-4.
+ let pseudo = match pseudo.map(|s| s.to_ascii_lowercase()) {
+ Some(ref pseudo) if pseudo == ":before" || pseudo == "::before" =>
+ Some(PseudoElement::Before),
+ Some(ref pseudo) if pseudo == ":after" || pseudo == "::after" =>
+ Some(PseudoElement::After),
+ _ => None
+ };
+
+ // Step 5.
+ CSSStyleDeclaration::new(self, element, pseudo, CSSModificationAccess::Readonly)
+ }
}
pub trait WindowHelpers {
@@ -553,6 +575,8 @@ pub trait WindowHelpers {
fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au>;
fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>>;
fn client_rect_query(self, node_geometry_request: TrustedNodeAddress) -> Rect<i32>;
+ fn resolved_style_query(self, element: TrustedNodeAddress,
+ pseudo: Option<PseudoElement>, property: &Atom) -> Option<String>;
fn handle_reflow_complete_msg(self, reflow_id: u32);
fn set_fragment_name(self, fragment: Option<String>);
fn steal_fragment_name(self) -> Option<String>;
@@ -791,6 +815,17 @@ impl<'a> WindowHelpers for &'a Window {
self.layout_rpc.node_geometry().client_rect
}
+ fn resolved_style_query(self,
+ element: TrustedNodeAddress,
+ pseudo: Option<PseudoElement>,
+ property: &Atom) -> Option<String> {
+ self.reflow(ReflowGoal::ForScriptQuery,
+ ReflowQueryType::ResolvedStyleQuery(element, pseudo, property.clone()),
+ ReflowReason::Query);
+ let ResolvedStyleResponse(resolved) = self.layout_rpc.resolved_style();
+ resolved
+ }
+
fn handle_reflow_complete_msg(self, reflow_id: u32) {
let last_reflow_id = self.last_reflow_id.get();
if last_reflow_id == reflow_id {
@@ -1098,6 +1133,7 @@ fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason:
ReflowQueryType::ContentBoxQuery(_n) => "\tContentBoxQuery",
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
+ ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
});
debug_msg.push_str(match *reason {
diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs
index d94f6e4a581..092938b8797 100644
--- a/components/script/layout_interface.rs
+++ b/components/script/layout_interface.rs
@@ -21,8 +21,10 @@ use net_traits::PendingAsyncLoad;
use profile_traits::mem::ReportsChan;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, ScriptControlChan};
use script_traits::{OpaqueScriptLayoutChannel, StylesheetLoadResponder, UntrustedNodeAddress};
+use selectors::parser::PseudoElement;
use std::any::Any;
use std::sync::mpsc::{channel, Receiver, Sender};
+use string_cache::Atom;
use style::animation::PropertyAnimation;
use style::media_queries::MediaQueryList;
use style::stylesheets::Stylesheet;
@@ -100,8 +102,11 @@ pub trait LayoutRPC {
/// Requests the node containing the point of interest
fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>;
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
+ /// Query layout for the resolved value of a given CSS property
+ fn resolved_style(&self) -> ResolvedStyleResponse;
}
+
pub struct ContentBoxResponse(pub Rect<Au>);
pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
pub struct NodeGeometryResponse {
@@ -109,6 +114,7 @@ pub struct NodeGeometryResponse {
}
pub struct HitTestResponse(pub UntrustedNodeAddress);
pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>);
+pub struct ResolvedStyleResponse(pub Option<String>);
/// Why we're doing reflow.
#[derive(PartialEq, Copy, Clone, Debug)]
@@ -126,6 +132,7 @@ pub enum ReflowQueryType {
ContentBoxQuery(TrustedNodeAddress),
ContentBoxesQuery(TrustedNodeAddress),
NodeGeometryQuery(TrustedNodeAddress),
+ ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
}
/// Information needed for a reflow.
diff --git a/components/style/animation.rs b/components/style/animation.rs
index f094229b931..3065ec5cd4f 100644
--- a/components/style/animation.rs
+++ b/components/style/animation.rs
@@ -92,6 +92,14 @@ impl PropertyAnimation {
new_style.$structname().$field)
}
)*
+ TransitionProperty::Clip => {
+ AnimatedProperty::Clip(old_style.get_effects().clip.0,
+ new_style.get_effects().clip.0)
+ }
+ TransitionProperty::LetterSpacing => {
+ AnimatedProperty::LetterSpacing(old_style.get_inheritedtext().letter_spacing.0,
+ new_style.get_inheritedtext().letter_spacing.0)
+ }
TransitionProperty::TextShadow => {
AnimatedProperty::TextShadow(old_style.get_effects().text_shadow.clone(),
new_style.get_effects().text_shadow.clone())
@@ -100,6 +108,10 @@ impl PropertyAnimation {
AnimatedProperty::Transform(old_style.get_effects().transform.clone(),
new_style.get_effects().transform.clone())
}
+ TransitionProperty::WordSpacing => {
+ AnimatedProperty::WordSpacing(old_style.get_inheritedtext().word_spacing.0,
+ new_style.get_inheritedtext().word_spacing.0)
+ }
}
}
}
@@ -117,12 +129,10 @@ impl PropertyAnimation {
[BorderTopWidth; get_border; border_top_width],
[Bottom; get_positionoffsets; bottom],
[Color; get_color; color],
- [Clip; get_effects; clip],
[FontSize; get_font; font_size],
[FontWeight; get_font; font_weight],
[Height; get_box; height],
[Left; get_positionoffsets; bottom],
- [LetterSpacing; get_inheritedtext; letter_spacing],
[LineHeight; get_inheritedbox; line_height],
[MarginBottom; get_margin; margin_bottom],
[MarginLeft; get_margin; margin_left],
@@ -145,7 +155,6 @@ impl PropertyAnimation {
[VerticalAlign; get_box; vertical_align],
[Visibility; get_inheritedbox; visibility],
[Width; get_box; width],
- [WordSpacing; get_inheritedtext; word_spacing],
[ZIndex; get_box; z_index]);
let property_animation = PropertyAnimation {
@@ -186,7 +195,22 @@ impl PropertyAnimation {
}
}
)*
- }
+ AnimatedProperty::Clip(ref start, ref end) => {
+ if let Some(value) = start.interpolate(end, progress) {
+ style.mutate_effects().clip.0 = value
+ }
+ }
+ AnimatedProperty::LetterSpacing(ref start, ref end) => {
+ if let Some(value) = start.interpolate(end, progress) {
+ style.mutate_inheritedtext().letter_spacing.0 = value
+ }
+ }
+ AnimatedProperty::WordSpacing(ref start, ref end) => {
+ if let Some(value) = start.interpolate(end, progress) {
+ style.mutate_inheritedtext().word_spacing.0 = value
+ }
+ }
+ }
});
match_property!(
[BackgroundColor; mutate_background; background_color],
@@ -202,12 +226,10 @@ impl PropertyAnimation {
[BorderTopWidth; mutate_border; border_top_width],
[Bottom; mutate_positionoffsets; bottom],
[Color; mutate_color; color],
- [Clip; mutate_effects; clip],
[FontSize; mutate_font; font_size],
[FontWeight; mutate_font; font_weight],
[Height; mutate_box; height],
[Left; mutate_positionoffsets; bottom],
- [LetterSpacing; mutate_inheritedtext; letter_spacing],
[LineHeight; mutate_inheritedbox; line_height],
[MarginBottom; mutate_margin; margin_bottom],
[MarginLeft; mutate_margin; margin_left],
@@ -232,7 +254,6 @@ impl PropertyAnimation {
[VerticalAlign; mutate_box; vertical_align],
[Visibility; mutate_inheritedbox; visibility],
[Width; mutate_box; width],
- [WordSpacing; mutate_inheritedtext; word_spacing],
[ZIndex; mutate_box; z_index]);
}
@@ -765,7 +786,7 @@ fn interpolate_transform_list(from_list: &Vec<TransformOperation>,
result.push_all(from_list);
}
- Some(result)
+ TransformList(Some(result))
}
/// Build an equivalent 'identity transform function list' based
@@ -809,7 +830,7 @@ impl Interpolate for TransformList {
#[inline]
fn interpolate(&self, other: &TransformList, time: f32) -> Option<TransformList> {
// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
- let result = match (self, other) {
+ let result = match (&self.0, &other.0) {
(&Some(ref from_list), &Some(ref to_list)) => {
// Two lists of transforms
interpolate_transform_list(from_list, &to_list, time)
@@ -824,9 +845,9 @@ impl Interpolate for TransformList {
let from_list = build_identity_transform_list(to_list);
interpolate_transform_list(&from_list, to_list, time)
}
- (&None, &None) => {
+ _ => {
// http://dev.w3.org/csswg/css-transforms/#none-none-animation
- None
+ TransformList(None)
}
};
diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs
index 354d956bf58..7b8e89a1a2a 100644
--- a/components/style/properties.mako.rs
+++ b/components/style/properties.mako.rs
@@ -655,7 +655,16 @@ pub mod longhands {
}
}
}
- #[inline]
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match *self {
+ computed_value::T::Normal => dest.write_str("normal"),
+ computed_value::T::Length(length) => length.to_css(dest),
+ computed_value::T::Number(number) => write!(dest, "{}", number),
+ }
+ }
+ }
+ #[inline]
pub fn get_initial_value() -> computed_value::T { computed_value::T::Normal }
impl ToComputedValue for SpecifiedValue {
@@ -749,6 +758,17 @@ pub mod longhands {
}
}
}
+ impl ::cssparser::ToCss for T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match *self {
+ % for keyword in vertical_align_keywords:
+ T::${to_rust_ident(keyword)} => dest.write_str("${keyword}"),
+ % endfor
+ T::Length(value) => value.to_css(dest),
+ T::Percentage(percentage) => write!(dest, "{}%", percentage * 100.),
+ }
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T { computed_value::T::baseline }
@@ -1067,7 +1087,20 @@ pub mod longhands {
pub mod computed_value {
use url::Url;
- pub type T = Option<Url>;
+ use cssparser::{ToCss, Token};
+ use std::fmt;
+
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<Url>);
+
+ impl ToCss for T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("none"),
+ Some(ref url) => Token::Url(url.to_string().into()).to_css(dest)
+ }
+ }
+ }
}
impl ToComputedValue for SpecifiedValue {
@@ -1076,8 +1109,8 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T {
match *self {
- SpecifiedValue::None => None,
- SpecifiedValue::Url(ref url) => Some(url.clone()),
+ SpecifiedValue::None => computed_value::T(None),
+ SpecifiedValue::Url(ref url) => computed_value::T(Some(url.clone())),
}
}
}
@@ -1091,7 +1124,7 @@ pub mod longhands {
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
</%self:longhand>
@@ -1249,7 +1282,20 @@ pub mod longhands {
pub mod computed_value {
use values::computed;
- pub type T = Option<computed::Image>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<computed::Image>);
+ }
+
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("none"),
+ Some(computed::Image::Url(ref url)) =>
+ ::cssparser::Token::Url(url.to_string().into()).to_css(dest),
+ Some(computed::Image::LinearGradient(ref gradient)) =>
+ gradient.to_css(dest)
+ }
+ }
}
#[derive(Clone, PartialEq)]
@@ -1266,7 +1312,7 @@ pub mod longhands {
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
@@ -1281,8 +1327,9 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
- SpecifiedValue(None) => None,
- SpecifiedValue(Some(ref image)) => Some(image.to_computed_value(context)),
+ SpecifiedValue(None) => computed_value::T(None),
+ SpecifiedValue(Some(ref image)) =>
+ computed_value::T(Some(image.to_computed_value(context))),
}
}
}
@@ -1318,6 +1365,15 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(self.horizontal.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.vertical.to_css(dest));
+ Ok(())
+ }
+ }
+
impl SpecifiedValue {
fn new(first: specified::PositionComponent, second: specified::PositionComponent)
-> Result<SpecifiedValue, ()> {
@@ -1424,6 +1480,16 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match *self {
+ computed_value::T::Explicit(ref size) => size.to_css(dest),
+ computed_value::T::Cover => dest.write_str("cover"),
+ computed_value::T::Contain => dest.write_str("contain"),
+ }
+ }
+ }
+
#[derive(Clone, PartialEq, Debug)]
pub struct SpecifiedExplicitSize {
pub width: specified::LengthOrPercentageOrAuto,
@@ -1438,6 +1504,15 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::ExplicitSize {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(self.width.to_css(dest));
+ try!(dest.write_str(" "));
+ self.height.to_css(dest)
+ }
+ }
+
+
#[derive(Clone, PartialEq, Debug)]
pub enum SpecifiedValue {
Explicit(SpecifiedExplicitSize),
@@ -1727,6 +1802,15 @@ pub mod longhands {
}
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match *self {
+ % for weight in range(100, 901, 100):
+ computed_value::T::Weight${weight} => dest.write_str("${weight}"),
+ % endfor
+ }
+ }
+ }
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::Weight400 // normal
@@ -1909,12 +1993,22 @@ pub mod longhands {
pub mod computed_value {
use util::geometry::Au;
- pub type T = Option<Au>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<Au>);
+ }
+
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("normal"),
+ Some(l) => l.to_css(dest),
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
impl ToComputedValue for SpecifiedValue {
@@ -1923,8 +2017,9 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
- SpecifiedValue::Normal => None,
- SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
+ SpecifiedValue::Normal => computed_value::T(None),
+ SpecifiedValue::Specified(l) =>
+ computed_value::T(Some(l.to_computed_value(context)))
}
}
}
@@ -1960,12 +2055,22 @@ pub mod longhands {
pub mod computed_value {
use util::geometry::Au;
- pub type T = Option<Au>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<Au>);
+ }
+
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("normal"),
+ Some(l) => l.to_css(dest),
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
impl ToComputedValue for SpecifiedValue {
@@ -1974,8 +2079,9 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
- SpecifiedValue::Normal => None,
- SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
+ SpecifiedValue::Normal => computed_value::T(None),
+ SpecifiedValue::Specified(l) =>
+ computed_value::T(Some(l.to_computed_value(context)))
}
}
}
@@ -2098,7 +2204,9 @@ pub mod longhands {
<%self:longhand name="-servo-text-decorations-in-effect"
derived_from="display text-decoration">
- use cssparser::RGBA;
+ use cssparser::{RGBA, ToCss};
+ use std::fmt;
+
use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {}
@@ -2114,6 +2222,13 @@ pub mod longhands {
pub type T = super::SpecifiedValue;
}
+ impl ToCss for SpecifiedValue {
+ fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
+ // Web compat doesn't matter here.
+ Ok(())
+ }
+ }
+
#[inline]
pub fn get_initial_value() -> computed_value::T {
SpecifiedValue {
@@ -2233,6 +2348,14 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(self.horizontal.to_css(dest));
+ try!(dest.write_str(" "));
+ self.vertical.to_css(dest)
+ }
+ }
+
impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T;
@@ -2370,12 +2493,22 @@ pub mod longhands {
pub mod computed_value {
use util::geometry::Au;
- pub type T = Option<Au>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<Au>);
+ }
+
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("auto"),
+ Some(l) => l.to_css(dest),
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
impl ToComputedValue for SpecifiedValue {
@@ -2384,8 +2517,9 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
- SpecifiedValue::Auto => None,
- SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
+ SpecifiedValue::Auto => computed_value::T(None),
+ SpecifiedValue::Specified(l) =>
+ computed_value::T(Some(l.to_computed_value(context)))
}
}
}
@@ -2420,12 +2554,22 @@ pub mod longhands {
}
pub mod computed_value {
- pub type T = Option<u32>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<u32>);
+ }
+
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("auto"),
+ Some(count) => write!(dest, "{}", count),
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
impl ToComputedValue for SpecifiedValue {
@@ -2434,8 +2578,9 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T {
match *self {
- SpecifiedValue::Auto => None,
- SpecifiedValue::Specified(count) => Some(count)
+ SpecifiedValue::Auto => computed_value::T(None),
+ SpecifiedValue::Specified(count) =>
+ computed_value::T(Some(count))
}
}
}
@@ -2476,12 +2621,22 @@ pub mod longhands {
pub mod computed_value {
use util::geometry::Au;
- pub type T = Option<Au>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<Au>);
+ }
+
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("normal"),
+ Some(l) => l.to_css(dest),
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
impl ToComputedValue for SpecifiedValue {
@@ -2490,8 +2645,9 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self {
- SpecifiedValue::Normal => None,
- SpecifiedValue::Specified(l) => Some(l.to_computed_value(context))
+ SpecifiedValue::Normal => computed_value::T(None),
+ SpecifiedValue::Specified(l) =>
+ computed_value::T(Some(l.to_computed_value(context)))
}
}
}
@@ -2611,7 +2767,8 @@ pub mod longhands {
use values::computed;
use std::fmt;
- pub type T = Vec<BoxShadow>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Vec<BoxShadow>);
#[derive(Clone, PartialEq, Copy)]
pub struct BoxShadow {
@@ -2635,9 +2792,44 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ let mut iter = self.0.iter();
+ if let Some(shadow) = iter.next() {
+ try!(shadow.to_css(dest));
+ } else {
+ try!(dest.write_str("none"));
+ return Ok(())
+ }
+ for shadow in iter {
+ try!(dest.write_str(", "));
+ try!(shadow.to_css(dest));
+ }
+ Ok(())
+ }
+ }
+
+ impl ToCss for computed_value::BoxShadow {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ if self.inset {
+ try!(dest.write_str("inset "));
+ }
+ try!(self.blur_radius.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.spread_radius.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.offset_x.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.offset_y.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.color.to_css(dest));
+ Ok(())
+ }
+ }
+
#[inline]
pub fn get_initial_value() -> computed_value::T {
- Vec::new()
+ computed_value::T(Vec::new())
}
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
@@ -2653,7 +2845,7 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
- self.0.iter().map(|value| compute_one_box_shadow(value, context)).collect()
+ computed_value::T(self.0.iter().map(|value| compute_one_box_shadow(value, context)).collect())
}
}
@@ -2752,7 +2944,38 @@ pub mod longhands {
pub left: Au,
}
- pub type T = Option<ClipRect>;
+ #[derive(Clone, PartialEq)]
+ pub struct T(pub Option<ClipRect>);
+ }
+
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self.0 {
+ None => dest.write_str("auto"),
+ Some(rect) => {
+ try!(dest.write_str("rect("));
+ try!(rect.top.to_css(dest));
+ try!(dest.write_str(", "));
+ if let Some(right) = rect.right {
+ try!(right.to_css(dest));
+ try!(dest.write_str(", "));
+ } else {
+ try!(dest.write_str("auto, "));
+ }
+
+ if let Some(bottom) = rect.bottom {
+ try!(bottom.to_css(dest));
+ try!(dest.write_str(", "));
+ } else {
+ try!(dest.write_str("auto, "));
+ }
+
+ try!(rect.left.to_css(dest));
+ try!(dest.write_str(")"));
+ Ok(())
+ }
+ }
+ }
}
#[derive(Clone, Debug, PartialEq, Copy)]
@@ -2780,7 +3003,7 @@ pub mod longhands {
try!(dest.write_str("auto, "));
}
- if let Some(bottom) = self.right {
+ if let Some(bottom) = self.bottom {
try!(bottom.to_css(dest));
try!(dest.write_str(", "));
} else {
@@ -2806,7 +3029,7 @@ pub mod longhands {
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
impl ToComputedValue for SpecifiedValue {
@@ -2814,12 +3037,12 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
- self.0.map(|value| computed_value::ClipRect {
+ computed_value::T(self.0.map(|value| computed_value::ClipRect {
top: value.top.to_computed_value(context),
right: value.right.map(|right| right.to_computed_value(context)),
bottom: value.bottom.map(|bottom| bottom.to_computed_value(context)),
left: value.left.to_computed_value(context),
- })
+ }))
}
}
@@ -2903,6 +3126,36 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ let mut iter = self.0.iter();
+ if let Some(shadow) = iter.next() {
+ try!(shadow.to_css(dest));
+ } else {
+ try!(dest.write_str("none"));
+ return Ok(())
+ }
+ for shadow in iter {
+ try!(dest.write_str(", "));
+ try!(shadow.to_css(dest));
+ }
+ Ok(())
+ }
+ }
+
+ impl ToCss for computed_value::TextShadow {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(self.offset_x.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.offset_y.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.blur_radius.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.color.to_css(dest));
+ Ok(())
+ }
+ }
+
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut iter = self.0.iter();
@@ -3103,6 +3356,23 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ let mut iter = self.filters.iter();
+ if let Some(filter) = iter.next() {
+ try!(filter.to_css(dest));
+ } else {
+ try!(dest.write_str("none"));
+ return Ok(())
+ }
+ for filter in iter {
+ try!(dest.write_str(" "));
+ try!(filter.to_css(dest));
+ }
+ Ok(())
+ }
+ }
+
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let mut iter = self.0.iter();
@@ -3120,6 +3390,31 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::Filter {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match *self {
+ computed_value::Filter::Blur(value) => {
+ try!(dest.write_str("blur("));
+ try!(value.to_css(dest));
+ try!(dest.write_str(")"));
+ }
+ computed_value::Filter::Brightness(value) => try!(write!(dest, "brightness({})", value)),
+ computed_value::Filter::Contrast(value) => try!(write!(dest, "contrast({})", value)),
+ computed_value::Filter::Grayscale(value) => try!(write!(dest, "grayscale({})", value)),
+ computed_value::Filter::HueRotate(value) => {
+ try!(dest.write_str("hue-rotate("));
+ try!(value.to_css(dest));
+ try!(dest.write_str(")"));
+ }
+ computed_value::Filter::Invert(value) => try!(write!(dest, "invert({})", value)),
+ computed_value::Filter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
+ computed_value::Filter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
+ computed_value::Filter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
+ }
+ Ok(())
+ }
+ }
+
impl ToCss for SpecifiedFilter {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
@@ -3253,7 +3548,8 @@ pub mod longhands {
Perspective(computed::Length),
}
- pub type T = Option<Vec<ComputedOperation>>;
+ #[derive(Clone, Debug, PartialEq)]
+ pub struct T(pub Option<Vec<ComputedOperation>>);
}
pub use self::computed_value::ComputedMatrix as SpecifiedMatrix;
@@ -3290,6 +3586,13 @@ pub mod longhands {
Perspective(specified::Length),
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
+ // TODO(pcwalton)
+ Ok(())
+ }
+ }
+
impl ToCss for SpecifiedOperation {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
// TODO(pcwalton)
@@ -3316,7 +3619,7 @@ pub mod longhands {
#[inline]
pub fn get_initial_value() -> computed_value::T {
- None
+ computed_value::T(None)
}
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
@@ -3548,7 +3851,7 @@ pub mod longhands {
#[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T {
if self.0.is_empty() {
- return None
+ return computed_value::T(None)
}
let mut result = vec!();
@@ -3577,7 +3880,7 @@ pub mod longhands {
};
}
- Some(result)
+ computed_value::T(Some(result))
}
}
</%self:longhand>
@@ -3612,6 +3915,16 @@ pub mod longhands {
depth: Length,
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(self.horizontal.to_css(dest));
+ try!(dest.write_str(" "));
+ try!(self.vertical.to_css(dest));
+ try!(dest.write_str(" "));
+ self.depth.to_css(dest)
+ }
+ }
+
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.horizontal.to_css(dest));
@@ -3742,6 +4055,14 @@ pub mod longhands {
}
}
+ impl ToCss for computed_value::T {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(self.horizontal.to_css(dest));
+ try!(dest.write_str(" "));
+ self.vertical.to_css(dest)
+ }
+ }
+
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SpecifiedValue {
horizontal: LengthOrPercentage,
@@ -5542,7 +5863,7 @@ impl ComputedValues {
#[inline]
pub fn is_multicol(&self) -> bool {
let style = self.get_column();
- style.column_count.is_some() || style.column_width.is_some()
+ style.column_count.0.is_some() || style.column_width.0.is_some()
}
#[inline]
@@ -5560,13 +5881,13 @@ impl ComputedValues {
// TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
if effects.opacity < 1.0 ||
!effects.filter.is_empty() ||
- effects.clip.is_some() {
+ effects.clip.0.is_some() {
effects.mix_blend_mode != mix_blend_mode::T::normal ||
return transform_style::T::flat;
}
if effects.transform_style == transform_style::T::auto {
- if effects.transform.is_some() {
+ if effects.transform.0.is_some() {
return transform_style::T::flat;
}
if effects.perspective != computed::LengthOrNone::None {
@@ -5579,17 +5900,28 @@ impl ComputedValues {
}
% for style_struct in STYLE_STRUCTS:
- #[inline]
- pub fn get_${style_struct.name.lower()}
- <'a>(&'a self) -> &'a style_structs::${style_struct.name} {
- &*self.${style_struct.ident}
- }
- #[inline]
- pub fn mutate_${style_struct.name.lower()}
- <'a>(&'a mut self) -> &'a mut style_structs::${style_struct.name} {
- &mut *Arc::make_unique(&mut self.${style_struct.ident})
- }
+ #[inline]
+ pub fn get_${style_struct.name.lower()}
+ <'a>(&'a self) -> &'a style_structs::${style_struct.name} {
+ &*self.${style_struct.ident}
+ }
+ #[inline]
+ pub fn mutate_${style_struct.name.lower()}
+ <'a>(&'a mut self) -> &'a mut style_structs::${style_struct.name} {
+ &mut *Arc::make_unique(&mut self.${style_struct.ident})
+ }
% endfor
+
+ pub fn computed_value_to_string(&self, name: &str) -> Option<String> {
+ match name {
+ % for style_struct in STYLE_STRUCTS:
+ % for longhand in style_struct.longhands:
+ "${longhand.name}" => Some(self.${style_struct.ident}.${longhand.ident}.to_css_string()),
+ % endfor
+ % endfor
+ _ => None
+ }
+ }
}
diff --git a/components/style/values.rs b/components/style/values.rs
index 93d6ccc7e6c..b03a239ae49 100644
--- a/components/style/values.rs
+++ b/components/style/values.rs
@@ -975,6 +975,16 @@ pub mod computed {
}
}
+ impl ::cssparser::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.),
+ }
+ }
+ }
+
#[derive(PartialEq, Clone, Copy)]
pub enum LengthOrPercentageOrAuto {
Length(Au),
@@ -1010,6 +1020,17 @@ pub mod computed {
}
}
+ impl ::cssparser::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"),
+ }
+ }
+ }
+
#[derive(PartialEq, Clone, Copy)]
pub enum LengthOrPercentageOrNone {
Length(Au),
@@ -1045,6 +1066,17 @@ pub mod computed {
}
}
+ impl ::cssparser::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::None => dest.write_str("none"),
+ }
+ }
+ }
+
#[derive(PartialEq, Clone, Copy)]
pub enum LengthOrNone {
Length(Au),
@@ -1075,6 +1107,15 @@ pub mod computed {
}
}
+ impl ::cssparser::ToCss for LengthOrNone {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ match self {
+ &LengthOrNone::Length(length) => length.to_css(dest),
+ &LengthOrNone::None => dest.write_str("none"),
+ }
+ }
+ }
+
impl ToComputedValue for specified::Image {
type ComputedValue = Image;
@@ -1116,6 +1157,19 @@ pub mod computed {
pub stops: Vec<ColorStop>,
}
+ impl ::cssparser::ToCss for LinearGradient {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(dest.write_str("linear-gradient("));
+ try!(self.angle_or_corner.to_css(dest));
+ for stop in self.stops.iter() {
+ try!(dest.write_str(", "));
+ try!(stop.to_css(dest));
+ }
+ try!(dest.write_str(")"));
+ Ok(())
+ }
+ }
+
impl fmt::Debug for LinearGradient {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = write!(f, "{:?}", self.angle_or_corner);
@@ -1137,6 +1191,17 @@ pub mod computed {
pub position: Option<LengthOrPercentage>,
}
+ impl ::cssparser::ToCss for ColorStop {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ try!(self.color.to_css(dest));
+ if let Some(position) = self.position {
+ try!(dest.write_str(" "));
+ try!(position.to_css(dest));
+ }
+ Ok(())
+ }
+ }
+
impl fmt::Debug for ColorStop {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = write!(f, "{:?}", self.color);
diff --git a/components/util/geometry.rs b/components/util/geometry.rs
index 1ed0565e3b4..8af8dce27ab 100644
--- a/components/util/geometry.rs
+++ b/components/util/geometry.rs
@@ -2,6 +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 cssparser::ToCss;
+
use euclid::length::Length;
use euclid::point::Point2D;
use euclid::rect::Rect;
@@ -122,7 +124,14 @@ impl Encodable for Au {
impl fmt::Debug for Au {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}px", self.to_f64_px())
- }}
+ }
+}
+
+impl ToCss for Au {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+ write!(dest, "{}px", self.to_f64_px())
+ }
+}
impl Add for Au {
type Output = Au;
diff --git a/tests/wpt/metadata-css/css21_dev/html4/events-006.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/events-006.htm.ini
index 6d28ce473f0..d50f4dc4c63 100644
--- a/tests/wpt/metadata-css/css21_dev/html4/events-006.htm.ini
+++ b/tests/wpt/metadata-css/css21_dev/html4/events-006.htm.ini
@@ -1,9 +1,8 @@
[events-006.htm]
type: testharness
- expected: TIMEOUT
[transition padding-left on :before / events]
- expected: NOTRUN
+ expected: FAIL
[transition padding-left on :after / events]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/metadata-css/css21_dev/html4/pseudo-elements-001.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/pseudo-elements-001.htm.ini
index 42591fa1ae7..784c08016a2 100644
--- a/tests/wpt/metadata-css/css21_dev/html4/pseudo-elements-001.htm.ini
+++ b/tests/wpt/metadata-css/css21_dev/html4/pseudo-elements-001.htm.ini
@@ -1,15 +1,14 @@
[pseudo-elements-001.htm]
type: testharness
- expected: TIMEOUT
[transition padding-left on :before / values]
- expected: NOTRUN
+ expected: FAIL
[transition padding-left on :after / values]
- expected: NOTRUN
+ expected: FAIL
[transition padding-left on :before, changing content / values]
- expected: NOTRUN
+ expected: FAIL
[transition padding-left on :after, changing content / values]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/metadata/dom/nodes/Element-classlist.html.ini b/tests/wpt/metadata/dom/nodes/Element-classlist.html.ini
deleted file mode 100644
index de405c47bc8..00000000000
--- a/tests/wpt/metadata/dom/nodes/Element-classlist.html.ini
+++ /dev/null
@@ -1,20 +0,0 @@
-[Element-classlist.html]
- type: testharness
- [CSS .foo selectors must not match elements without any class]
- expected: FAIL
-
- [computed style must update when setting .className]
- expected: FAIL
-
- [classList.add must not cause the CSS selector to stop matching]
- expected: FAIL
-
- [classList.remove must not break case-sensitive CSS selector matching]
- expected: FAIL
-
- [classList.toggle must not break case-sensitive CSS selector matching]
- expected: FAIL
-
- [CSS class selectors must stop matching when all classes have been removed]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/html/dom/elements/global-attributes/id-attribute.html.ini b/tests/wpt/metadata/html/dom/elements/global-attributes/id-attribute.html.ini
deleted file mode 100644
index 2a63d763353..00000000000
--- a/tests/wpt/metadata/html/dom/elements/global-attributes/id-attribute.html.ini
+++ /dev/null
@@ -1,20 +0,0 @@
-[id-attribute.html]
- type: testharness
- [User agents must associate the element with an id value for purposes of CSS.]
- expected: FAIL
-
- [Association for CSS is exact and therefore case-sensitive.]
- expected: FAIL
-
- [Spaces are allowed in an id and still make an association.]
- expected: FAIL
-
- [Non-ASCII is allowed in an id and still make an association for CSS.]
- expected: FAIL
-
- [After setting id via id attribute, CSS association is via the new ID.]
- expected: FAIL
-
- [After setting id via setAttribute attribute, CSS association is via the new ID.]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/html/semantics/edits/the-del-element/del_effect.html.ini b/tests/wpt/metadata/html/semantics/edits/the-del-element/del_effect.html.ini
deleted file mode 100644
index e060d1a2697..00000000000
--- a/tests/wpt/metadata/html/semantics/edits/the-del-element/del_effect.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[del_effect.html]
- type: testharness
- [HTML Test: Text in the del element should be 'line-through']
- expected: FAIL
-
diff --git a/tests/wpt/metadata/html/semantics/edits/the-ins-element/ins_effect.html.ini b/tests/wpt/metadata/html/semantics/edits/the-ins-element/ins_effect.html.ini
deleted file mode 100644
index 6fd52bc7e8b..00000000000
--- a/tests/wpt/metadata/html/semantics/edits/the-ins-element/ins_effect.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[ins_effect.html]
- type: testharness
- [HTML Test: Text in the ins element should be 'underline']
- expected: FAIL
-
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index eb000fe529f..075f3da0da4 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -533,6 +533,12 @@
"url": "/_mozilla/mozilla/getBoundingClientRect.html"
}
],
+ "mozilla/getComputedStyle.html": [
+ {
+ "path": "mozilla/getComputedStyle.html",
+ "url": "/_mozilla/mozilla/getComputedStyle.html"
+ }
+ ],
"mozilla/getPropertyPriority.html": [
{
"path": "mozilla/getPropertyPriority.html",
@@ -974,4 +980,4 @@
"rev": null,
"url_base": "/_mozilla/",
"version": 2
-} \ No newline at end of file
+}
diff --git a/tests/wpt/mozilla/tests/mozilla/getComputedStyle.html b/tests/wpt/mozilla/tests/mozilla/getComputedStyle.html
new file mode 100644
index 00000000000..410a8fd79c6
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/getComputedStyle.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ #foo:before {
+ color: red;
+ }
+
+ #foo {
+ width: 50px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="foo"></div>
+ <script>
+ test(function() {
+ var div = document.getElementById("foo");
+ var cs = getComputedStyle(div);
+ assert_equals(cs.getPropertyValue("left"), "auto");
+ assert_equals(cs.getPropertyValue("right"), "auto");
+ assert_equals(cs.getPropertyValue("top"), "auto");
+ assert_equals(cs.getPropertyValue("bottom"), "auto");
+ assert_equals(cs.getPropertyValue("width"), "50px");
+ assert_equals(cs.getPropertyValue("height"), "auto");
+ assert_equals(cs.getPropertyValue("color"), "rgb(0, 0, 0)");
+ }, "Element's resolved values");
+
+ test(function() {
+ var div = document.getElementById("foo");
+ assert_equals(getComputedStyle(div, ':before').getPropertyValue("color"), "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(div, '::before').getPropertyValue("color"), "rgb(255, 0, 0)");
+ }, "Existing :before pseudoelement");
+
+ test(function() {
+ var div = document.getElementById("foo");
+ assert_equals(getComputedStyle(div, ':after').getPropertyValue("color"), "");
+ assert_equals(getComputedStyle(div, '::after').getPropertyValue("color"), "");
+ }, "Missing :after pseudoelement");
+
+ </script>
+ </body>
+</html>