Implement getComputedStyle

This commit is contained in:
David Zbarsky 2015-07-27 23:05:18 -04:00
parent 416931f4be
commit e484d6b5e3
24 changed files with 886 additions and 152 deletions

View file

@ -618,7 +618,7 @@ impl BlockFlow {
pub fn transform_requires_layer(&self) -> bool { pub fn transform_requires_layer(&self) -> bool {
// Check if the transform matrix is 2D or 3D // 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 { for transform in transform_list {
match transform { match transform {
&transform::ComputedOperation::Perspective(..) => { &transform::ComputedOperation::Perspective(..) => {

View file

@ -1117,7 +1117,7 @@ impl<'a> FlowConstructor<'a> {
fn build_flow_for_list_item(&mut self, node: &ThreadSafeLayoutNode, flotation: float::T) fn build_flow_for_list_item(&mut self, node: &ThreadSafeLayoutNode, flotation: float::T)
-> ConstructionResult { -> ConstructionResult {
let flotation = FloatKind::from_property(flotation); 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) => { Some(ref url) => {
let image_info = box ImageFragmentInfo::new(node, let image_info = box ImageFragmentInfo::new(node,
Some((*url).clone()), Some((*url).clone()),

View file

@ -361,7 +361,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Implements background image, per spec: // Implements background image, per spec:
// http://www.w3.org/TR/CSS21/colors.html#background // http://www.w3.org/TR/CSS21/colors.html#background
let background = style.get_background(); let background = style.get_background();
match background.background_image { match background.background_image.0 {
None => {} None => {}
Some(computed::Image::LinearGradient(ref gradient)) => { Some(computed::Image::LinearGradient(ref gradient)) => {
self.build_display_list_for_background_linear_gradient(display_list, self.build_display_list_for_background_linear_gradient(display_list,
@ -668,7 +668,7 @@ impl FragmentDisplayListBuilding for Fragment {
absolute_bounds: &Rect<Au>, absolute_bounds: &Rect<Au>,
clip: &ClippingRegion) { clip: &ClippingRegion) {
// NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back). // 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, let bounds = shadow_bounds(&absolute_bounds.translate(&Point2D::new(box_shadow.offset_x,
box_shadow.offset_y)), box_shadow.offset_y)),
box_shadow.blur_radius, box_shadow.blur_radius,
@ -863,7 +863,7 @@ impl FragmentDisplayListBuilding for Fragment {
-> ClippingRegion { -> ClippingRegion {
// Account for `clip` per CSS 2.1 § 11.1.2. // Account for `clip` per CSS 2.1 § 11.1.2.
let style_clip_rect = match (self.style().get_box().position, 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, (position::T::absolute, Some(style_clip_rect)) => style_clip_rect,
_ => return (*parent_clip).clone(), _ => return (*parent_clip).clone(),
}; };
@ -1147,7 +1147,7 @@ impl FragmentDisplayListBuilding for Fragment {
let mut transform = Matrix4::identity(); 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 = self.style().get_effects().transform_origin;
let transform_origin = let transform_origin =
Point3D::new(model::specified(transform_origin.horizontal, Point3D::new(model::specified(transform_origin.horizontal,

View file

@ -1989,7 +1989,7 @@ impl Fragment {
if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal { if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal {
return true return true
} }
if self.style().get_effects().transform.is_some() { if self.style().get_effects().transform.0.is_some() {
return true return true
} }
match self.style().get_used_transform_style() { match self.style().get_used_transform_style() {
@ -2036,7 +2036,7 @@ impl Fragment {
let mut overflow = border_box; let mut overflow = border_box;
// Box shadows cause us to draw outside our 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 offset = Point2D::new(box_shadow.offset_x, box_shadow.offset_y);
let inflation = box_shadow.spread_radius + box_shadow.blur_radius * let inflation = box_shadow.spread_radius + box_shadow.blur_radius *
BLUR_INFLATION_FACTOR; BLUR_INFLATION_FACTOR;

View file

@ -10,6 +10,7 @@
use animation; use animation;
use construct::ConstructionResult; use construct::ConstructionResult;
use context::{SharedLayoutContext, heap_size_of_local_context}; use context::{SharedLayoutContext, heap_size_of_local_context};
use cssparser::ToCss;
use data::LayoutDataWrapper; use data::LayoutDataWrapper;
use display_list_builder::ToGfxColor; use display_list_builder::ToGfxColor;
use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; 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::{Animation, ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse}; use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse};
use script::layout_interface::{NewLayoutTaskInfo, Msg, Reflow, ReflowGoal, ReflowQueryType}; 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::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, StylesheetLoadResponder}; use script_traits::{ScriptControlChan, StylesheetLoadResponder};
use selectors::parser::PseudoElement;
use serde::json; use serde::json;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cell::Cell; use std::cell::Cell;
@ -67,20 +69,23 @@ use std::mem::transmute;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::mpsc::{channel, Sender, Receiver, Select}; use std::sync::mpsc::{channel, Sender, Receiver, Select};
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
use string_cache::Atom;
use style::computed_values::{filter, mix_blend_mode}; use style::computed_values::{filter, mix_blend_mode};
use style::media_queries::{MediaType, MediaQueryList, Device}; use style::media_queries::{MediaType, MediaQueryList, Device};
use style::properties::style_structs; use style::properties::style_structs;
use style::properties::longhands::{display, position};
use style::selector_matching::Stylist; use style::selector_matching::Stylist;
use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt}; use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt};
use url::Url; use url::Url;
use util::cursor::Cursor; use util::cursor::Cursor;
use util::geometry::{Au, MAX_RECT}; use util::geometry::{Au, MAX_RECT, ZERO_POINT};
use util::logical_geometry::LogicalPoint; use util::logical_geometry::{LogicalPoint, WritingMode};
use util::mem::HeapSizeOf; use util::mem::HeapSizeOf;
use util::opts; use util::opts;
use util::task::spawn_named_with_send_on_failure; use util::task::spawn_named_with_send_on_failure;
use util::task_state; use util::task_state;
use util::workqueue::WorkQueue; use util::workqueue::WorkQueue;
use wrapper::ThreadSafeLayoutNode;
/// The number of screens of data we're allowed to generate display lists for in each direction. /// 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; 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. /// A queued response for the client {top, left, width, height} of a node in pixels.
pub client_rect_response: Rect<i32>, 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. /// The list of currently-running animations.
pub running_animations: Vec<Animation>, pub running_animations: Vec<Animation>,
@ -372,6 +380,7 @@ impl LayoutTask {
content_box_response: Rect::zero(), content_box_response: Rect::zero(),
content_boxes_response: Vec::new(), content_boxes_response: Vec::new(),
client_rect_response: Rect::zero(), client_rect_response: Rect::zero(),
resolved_style_response: None,
running_animations: Vec::new(), running_animations: Vec::new(),
visible_rects: Arc::new(HashMap::with_hash_state(Default::default())), visible_rects: Arc::new(HashMap::with_hash_state(Default::default())),
new_animations_receiver: new_animations_receiver, new_animations_receiver: new_animations_receiver,
@ -864,6 +873,127 @@ impl LayoutTask {
rw_data.client_rect_response = iterator.client_rect; 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, fn compute_abs_pos_and_build_display_list<'a>(&'a self,
data: &Reflow, data: &Reflow,
layout_root: &mut FlowRef, layout_root: &mut FlowRef,
@ -1052,15 +1182,14 @@ impl LayoutTask {
let mut root_flow = (*rw_data.root_flow.as_ref().unwrap()).clone(); let mut root_flow = (*rw_data.root_flow.as_ref().unwrap()).clone();
match data.query_type { match data.query_type {
ReflowQueryType::ContentBoxQuery(node) => { ReflowQueryType::ContentBoxQuery(node) =>
self.process_content_box_request(node, &mut root_flow, &mut rw_data) self.process_content_box_request(node, &mut root_flow, &mut rw_data),
} ReflowQueryType::ContentBoxesQuery(node) =>
ReflowQueryType::ContentBoxesQuery(node) => { self.process_content_boxes_request(node, &mut root_flow, &mut rw_data),
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::NodeGeometryQuery(node) => { ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) =>
self.process_node_geometry_request(node, &mut root_flow, &mut rw_data) self.process_resolved_style_request(node, pseudo, property, &mut root_flow, &mut rw_data),
}
ReflowQueryType::NoQuery => {} 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. /// Requests the node containing the point of interest.
fn hit_test(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()> { 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)); 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 // The default computed value for background-color is transparent (see
// http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we // http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we
// need to propagate the background color from the root HTML/Body // need to propagate the background color from the root HTML/Body

View file

@ -167,8 +167,8 @@ impl TextRunScanner {
white_space::T::pre => CompressionMode::CompressNone, white_space::T::pre => CompressionMode::CompressNone,
}; };
text_transform = inherited_text_style.text_transform; text_transform = inherited_text_style.text_transform;
letter_spacing = inherited_text_style.letter_spacing; letter_spacing = inherited_text_style.letter_spacing.0;
word_spacing = inherited_text_style.word_spacing.unwrap_or(Au(0)); word_spacing = inherited_text_style.word_spacing.0.unwrap_or(Au(0));
text_rendering = inherited_text_style.text_rendering; text_rendering = inherited_text_style.text_rendering;
} }

View file

@ -63,6 +63,7 @@ use msg::constellation_msg::ConstellationChan;
use net_traits::image::base::Image; use net_traits::image::base::Image;
use profile_traits::mem::ProfilerChan; use profile_traits::mem::ProfilerChan;
use util::str::{LengthOrPercentageOrAuto}; use util::str::{LengthOrPercentageOrAuto};
use selectors::parser::PseudoElement;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cell::{Cell, UnsafeCell, RefCell}; use std::cell::{Cell, UnsafeCell, RefCell};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -304,6 +305,7 @@ no_jsmanaged_fields!(LineCapStyle, LineJoinStyle, CompositionOrBlending);
no_jsmanaged_fields!(RepetitionStyle); no_jsmanaged_fields!(RepetitionStyle);
no_jsmanaged_fields!(WebGLError); no_jsmanaged_fields!(WebGLError);
no_jsmanaged_fields!(ProfilerChan); no_jsmanaged_fields!(ProfilerChan);
no_jsmanaged_fields!(PseudoElement);
impl JSTraceable for Box<ScriptChan+Send> { impl JSTraceable for Box<ScriptChan+Send> {
#[inline] #[inline]

View file

@ -11,9 +11,10 @@ use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::document::DocumentHelpers; use dom::document::DocumentHelpers;
use dom::element::{ElementHelpers, StylePriority}; use dom::element::{ElementHelpers, StylePriority};
use dom::htmlelement::HTMLElement; 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 dom::window::{Window, WindowHelpers};
use util::str::DOMString; use util::str::DOMString;
use selectors::parser::PseudoElement;
use string_cache::Atom; use string_cache::Atom;
use style::properties::{is_supported_property, longhands_from_shorthand, parse_style_attribute}; use style::properties::{is_supported_property, longhands_from_shorthand, parse_style_attribute};
use style::properties::PropertyDeclaration; use style::properties::PropertyDeclaration;
@ -27,6 +28,7 @@ pub struct CSSStyleDeclaration {
reflector_: Reflector, reflector_: Reflector,
owner: JS<HTMLElement>, owner: JS<HTMLElement>,
readonly: bool, readonly: bool,
pseudo: Option<PseudoElement>,
} }
#[derive(PartialEq)] #[derive(PartialEq)]
@ -56,17 +58,20 @@ fn serialize_list(list: &Vec<PropertyDeclaration>) -> DOMString {
impl CSSStyleDeclaration { impl CSSStyleDeclaration {
pub fn new_inherited(owner: &HTMLElement, pub fn new_inherited(owner: &HTMLElement,
pseudo: Option<PseudoElement>,
modification_access: CSSModificationAccess) -> CSSStyleDeclaration { modification_access: CSSModificationAccess) -> CSSStyleDeclaration {
CSSStyleDeclaration { CSSStyleDeclaration {
reflector_: Reflector::new(), reflector_: Reflector::new(),
owner: JS::from_ref(owner), owner: JS::from_ref(owner),
pseudo: pseudo,
readonly: modification_access == CSSModificationAccess::Readonly, readonly: modification_access == CSSModificationAccess::Readonly,
} }
} }
pub fn new(global: &Window, owner: &HTMLElement, pub fn new(global: &Window, owner: &HTMLElement,
pseudo: Option<PseudoElement>,
modification_access: CSSModificationAccess) -> Root<CSSStyleDeclaration> { 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), GlobalRef::Window(global),
CSSStyleDeclarationBinding::Wrap) CSSStyleDeclarationBinding::Wrap)
} }
@ -75,6 +80,7 @@ impl CSSStyleDeclaration {
trait PrivateCSSStyleDeclarationHelpers { trait PrivateCSSStyleDeclarationHelpers {
fn get_declaration(self, property: &Atom) -> Option<PropertyDeclaration>; fn get_declaration(self, property: &Atom) -> Option<PropertyDeclaration>;
fn get_important_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 { impl<'a> PrivateCSSStyleDeclarationHelpers for &'a CSSStyleDeclaration {
@ -89,6 +95,13 @@ impl<'a> PrivateCSSStyleDeclarationHelpers for &'a CSSStyleDeclaration {
let element = ElementCast::from_ref(owner.r()); let element = ElementCast::from_ref(owner.r());
element.get_important_inline_style_declaration(property).map(|decl| decl.clone()) 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 { impl<'a> CSSStyleDeclarationMethods for &'a CSSStyleDeclaration {
@ -129,6 +142,11 @@ impl<'a> CSSStyleDeclarationMethods for &'a CSSStyleDeclaration {
// Step 1 // Step 1
let property = Atom::from_slice(&property.to_ascii_lowercase()); 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 // Step 2
let longhand_properties = longhands_from_shorthand(&property); let longhand_properties = longhands_from_shorthand(&property);
if let Some(longhand_properties) = longhand_properties { if let Some(longhand_properties) = longhand_properties {

View file

@ -135,7 +135,7 @@ impl<'a> HTMLElementMethods for &'a HTMLElement {
fn Style(self) -> Root<CSSStyleDeclaration> { fn Style(self) -> Root<CSSStyleDeclaration> {
self.style_decl.or_init(|| { self.style_decl.or_init(|| {
let global = window_from_node(self); let global = window_from_node(self);
CSSStyleDeclaration::new(global.r(), self, CSSModificationAccess::ReadWrite) CSSStyleDeclaration::new(global.r(), self, None, CSSModificationAccess::ReadWrite)
}) })
} }

View file

@ -90,6 +90,13 @@ partial interface Window {
/*[Replaceable]*/ readonly attribute Performance performance; /*[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 // http://dev.w3.org/csswg/cssom-view/#extensions-to-the-window-interface
partial interface Window { partial interface Window {
//MediaQueryList matchMedia(DOMString query); //MediaQueryList matchMedia(DOMString query);

View file

@ -20,9 +20,11 @@ use dom::bindings::utils::{GlobalStaticData, Reflectable, WindowProxyHandler};
use dom::browsercontext::BrowsingContext; use dom::browsercontext::BrowsingContext;
use dom::console::Console; use dom::console::Console;
use dom::crypto::Crypto; use dom::crypto::Crypto;
use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration};
use dom::document::{Document, DocumentHelpers}; use dom::document::{Document, DocumentHelpers};
use dom::element::Element; use dom::element::Element;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId}; use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::location::Location; use dom::location::Location;
use dom::navigator::Navigator; use dom::navigator::Navigator;
use dom::node::{window_from_node, TrustedNodeAddress, NodeHelpers}; use dom::node::{window_from_node, TrustedNodeAddress, NodeHelpers};
@ -30,7 +32,7 @@ use dom::performance::Performance;
use dom::screen::Screen; use dom::screen::Screen;
use dom::storage::Storage; use dom::storage::Storage;
use layout_interface::{ReflowGoal, ReflowQueryType, LayoutRPC, LayoutChan, Reflow, Msg}; 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 page::Page;
use script_task::{TimerSource, ScriptChan, ScriptPort, NonWorkerScriptChan}; use script_task::{TimerSource, ScriptChan, ScriptPort, NonWorkerScriptChan};
use script_task::ScriptMsg; use script_task::ScriptMsg;
@ -47,6 +49,7 @@ use net_traits::ResourceTask;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask}; use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask};
use net_traits::storage_task::{StorageTask, StorageType}; use net_traits::storage_task::{StorageTask, StorageType};
use profile_traits::mem; use profile_traits::mem;
use string_cache::Atom;
use util::geometry::{self, Au, MAX_RECT}; use util::geometry::{self, Au, MAX_RECT};
use util::{breakpoint, opts}; use util::{breakpoint, opts};
use util::str::{DOMString,HTML_SPACE_CHARACTERS}; 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::jsapi::{JS_GC, JS_GetRuntime, JSAutoCompartment, JSAutoRequest};
use js::rust::Runtime; use js::rust::Runtime;
use js::rust::CompileOptionsWrapper; use js::rust::CompileOptionsWrapper;
use selectors::parser::PseudoElement;
use url::{Url, UrlParser}; use url::{Url, UrlParser};
use libc; use libc;
use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD}; use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD};
use std::ascii::AsciiExt;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cell::{Cell, Ref, RefMut, RefCell}; use std::cell::{Cell, Ref, RefMut, RefCell};
use std::collections::HashSet; use std::collections::HashSet;
@ -539,6 +544,23 @@ impl<'a> WindowMethods for &'a Window {
chan.send(Err(WebDriverJSError::Timeout)).unwrap(); 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 { pub trait WindowHelpers {
@ -553,6 +575,8 @@ pub trait WindowHelpers {
fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au>; fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au>;
fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec<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 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 handle_reflow_complete_msg(self, reflow_id: u32);
fn set_fragment_name(self, fragment: Option<String>); fn set_fragment_name(self, fragment: Option<String>);
fn steal_fragment_name(self) -> 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 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) { fn handle_reflow_complete_msg(self, reflow_id: u32) {
let last_reflow_id = self.last_reflow_id.get(); let last_reflow_id = self.last_reflow_id.get();
if last_reflow_id == reflow_id { 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::ContentBoxQuery(_n) => "\tContentBoxQuery",
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery", ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
ReflowQueryType::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
}); });
debug_msg.push_str(match *reason { debug_msg.push_str(match *reason {

View file

@ -21,8 +21,10 @@ use net_traits::PendingAsyncLoad;
use profile_traits::mem::ReportsChan; use profile_traits::mem::ReportsChan;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, ScriptControlChan}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, ScriptControlChan};
use script_traits::{OpaqueScriptLayoutChannel, StylesheetLoadResponder, UntrustedNodeAddress}; use script_traits::{OpaqueScriptLayoutChannel, StylesheetLoadResponder, UntrustedNodeAddress};
use selectors::parser::PseudoElement;
use std::any::Any; use std::any::Any;
use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::mpsc::{channel, Receiver, Sender};
use string_cache::Atom;
use style::animation::PropertyAnimation; use style::animation::PropertyAnimation;
use style::media_queries::MediaQueryList; use style::media_queries::MediaQueryList;
use style::stylesheets::Stylesheet; use style::stylesheets::Stylesheet;
@ -100,8 +102,11 @@ pub trait LayoutRPC {
/// Requests the node containing the point of interest /// Requests the node containing the point of interest
fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>; fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>;
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>; 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 ContentBoxResponse(pub Rect<Au>);
pub struct ContentBoxesResponse(pub Vec<Rect<Au>>); pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
pub struct NodeGeometryResponse { pub struct NodeGeometryResponse {
@ -109,6 +114,7 @@ pub struct NodeGeometryResponse {
} }
pub struct HitTestResponse(pub UntrustedNodeAddress); pub struct HitTestResponse(pub UntrustedNodeAddress);
pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>); pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>);
pub struct ResolvedStyleResponse(pub Option<String>);
/// Why we're doing reflow. /// Why we're doing reflow.
#[derive(PartialEq, Copy, Clone, Debug)] #[derive(PartialEq, Copy, Clone, Debug)]
@ -126,6 +132,7 @@ pub enum ReflowQueryType {
ContentBoxQuery(TrustedNodeAddress), ContentBoxQuery(TrustedNodeAddress),
ContentBoxesQuery(TrustedNodeAddress), ContentBoxesQuery(TrustedNodeAddress),
NodeGeometryQuery(TrustedNodeAddress), NodeGeometryQuery(TrustedNodeAddress),
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
} }
/// Information needed for a reflow. /// Information needed for a reflow.

View file

@ -92,6 +92,14 @@ impl PropertyAnimation {
new_style.$structname().$field) 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 => { TransitionProperty::TextShadow => {
AnimatedProperty::TextShadow(old_style.get_effects().text_shadow.clone(), AnimatedProperty::TextShadow(old_style.get_effects().text_shadow.clone(),
new_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(), AnimatedProperty::Transform(old_style.get_effects().transform.clone(),
new_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], [BorderTopWidth; get_border; border_top_width],
[Bottom; get_positionoffsets; bottom], [Bottom; get_positionoffsets; bottom],
[Color; get_color; color], [Color; get_color; color],
[Clip; get_effects; clip],
[FontSize; get_font; font_size], [FontSize; get_font; font_size],
[FontWeight; get_font; font_weight], [FontWeight; get_font; font_weight],
[Height; get_box; height], [Height; get_box; height],
[Left; get_positionoffsets; bottom], [Left; get_positionoffsets; bottom],
[LetterSpacing; get_inheritedtext; letter_spacing],
[LineHeight; get_inheritedbox; line_height], [LineHeight; get_inheritedbox; line_height],
[MarginBottom; get_margin; margin_bottom], [MarginBottom; get_margin; margin_bottom],
[MarginLeft; get_margin; margin_left], [MarginLeft; get_margin; margin_left],
@ -145,7 +155,6 @@ impl PropertyAnimation {
[VerticalAlign; get_box; vertical_align], [VerticalAlign; get_box; vertical_align],
[Visibility; get_inheritedbox; visibility], [Visibility; get_inheritedbox; visibility],
[Width; get_box; width], [Width; get_box; width],
[WordSpacing; get_inheritedtext; word_spacing],
[ZIndex; get_box; z_index]); [ZIndex; get_box; z_index]);
let property_animation = PropertyAnimation { 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!( match_property!(
[BackgroundColor; mutate_background; background_color], [BackgroundColor; mutate_background; background_color],
@ -202,12 +226,10 @@ impl PropertyAnimation {
[BorderTopWidth; mutate_border; border_top_width], [BorderTopWidth; mutate_border; border_top_width],
[Bottom; mutate_positionoffsets; bottom], [Bottom; mutate_positionoffsets; bottom],
[Color; mutate_color; color], [Color; mutate_color; color],
[Clip; mutate_effects; clip],
[FontSize; mutate_font; font_size], [FontSize; mutate_font; font_size],
[FontWeight; mutate_font; font_weight], [FontWeight; mutate_font; font_weight],
[Height; mutate_box; height], [Height; mutate_box; height],
[Left; mutate_positionoffsets; bottom], [Left; mutate_positionoffsets; bottom],
[LetterSpacing; mutate_inheritedtext; letter_spacing],
[LineHeight; mutate_inheritedbox; line_height], [LineHeight; mutate_inheritedbox; line_height],
[MarginBottom; mutate_margin; margin_bottom], [MarginBottom; mutate_margin; margin_bottom],
[MarginLeft; mutate_margin; margin_left], [MarginLeft; mutate_margin; margin_left],
@ -232,7 +254,6 @@ impl PropertyAnimation {
[VerticalAlign; mutate_box; vertical_align], [VerticalAlign; mutate_box; vertical_align],
[Visibility; mutate_inheritedbox; visibility], [Visibility; mutate_inheritedbox; visibility],
[Width; mutate_box; width], [Width; mutate_box; width],
[WordSpacing; mutate_inheritedtext; word_spacing],
[ZIndex; mutate_box; z_index]); [ZIndex; mutate_box; z_index]);
} }
@ -765,7 +786,7 @@ fn interpolate_transform_list(from_list: &Vec<TransformOperation>,
result.push_all(from_list); result.push_all(from_list);
} }
Some(result) TransformList(Some(result))
} }
/// Build an equivalent 'identity transform function list' based /// Build an equivalent 'identity transform function list' based
@ -809,7 +830,7 @@ impl Interpolate for TransformList {
#[inline] #[inline]
fn interpolate(&self, other: &TransformList, time: f32) -> Option<TransformList> { fn interpolate(&self, other: &TransformList, time: f32) -> Option<TransformList> {
// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms // 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)) => { (&Some(ref from_list), &Some(ref to_list)) => {
// Two lists of transforms // Two lists of transforms
interpolate_transform_list(from_list, &to_list, time) 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); let from_list = build_identity_transform_list(to_list);
interpolate_transform_list(&from_list, to_list, time) interpolate_transform_list(&from_list, to_list, time)
} }
(&None, &None) => { _ => {
// http://dev.w3.org/csswg/css-transforms/#none-none-animation // http://dev.w3.org/csswg/css-transforms/#none-none-animation
None TransformList(None)
} }
}; };

View file

@ -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 } pub fn get_initial_value() -> computed_value::T { computed_value::T::Normal }
impl ToComputedValue for SpecifiedValue { 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] #[inline]
pub fn get_initial_value() -> computed_value::T { computed_value::T::baseline } pub fn get_initial_value() -> computed_value::T { computed_value::T::baseline }
@ -1067,7 +1087,20 @@ pub mod longhands {
pub mod computed_value { pub mod computed_value {
use url::Url; 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 { impl ToComputedValue for SpecifiedValue {
@ -1076,8 +1109,8 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T { fn to_computed_value(&self, _context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue::None => None, SpecifiedValue::None => computed_value::T(None),
SpecifiedValue::Url(ref url) => Some(url.clone()), SpecifiedValue::Url(ref url) => computed_value::T(Some(url.clone())),
} }
} }
} }
@ -1091,7 +1124,7 @@ pub mod longhands {
} }
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
</%self:longhand> </%self:longhand>
@ -1249,7 +1282,20 @@ pub mod longhands {
pub mod computed_value { pub mod computed_value {
use values::computed; 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)] #[derive(Clone, PartialEq)]
@ -1266,7 +1312,7 @@ pub mod longhands {
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
if input.try(|input| input.expect_ident_matching("none")).is_ok() { if input.try(|input| input.expect_ident_matching("none")).is_ok() {
@ -1281,8 +1327,9 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue(None) => None, SpecifiedValue(None) => computed_value::T(None),
SpecifiedValue(Some(ref image)) => Some(image.to_computed_value(context)), 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 { impl SpecifiedValue {
fn new(first: specified::PositionComponent, second: specified::PositionComponent) fn new(first: specified::PositionComponent, second: specified::PositionComponent)
-> Result<SpecifiedValue, ()> { -> 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)] #[derive(Clone, PartialEq, Debug)]
pub struct SpecifiedExplicitSize { pub struct SpecifiedExplicitSize {
pub width: specified::LengthOrPercentageOrAuto, 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)] #[derive(Clone, PartialEq, Debug)]
pub enum SpecifiedValue { pub enum SpecifiedValue {
Explicit(SpecifiedExplicitSize), 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] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
computed_value::T::Weight400 // normal computed_value::T::Weight400 // normal
@ -1909,12 +1993,22 @@ pub mod longhands {
pub mod computed_value { pub mod computed_value {
use util::geometry::Au; 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] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
@ -1923,8 +2017,9 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue::Normal => None, SpecifiedValue::Normal => computed_value::T(None),
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context)) SpecifiedValue::Specified(l) =>
computed_value::T(Some(l.to_computed_value(context)))
} }
} }
} }
@ -1960,12 +2055,22 @@ pub mod longhands {
pub mod computed_value { pub mod computed_value {
use util::geometry::Au; 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] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
@ -1974,8 +2079,9 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue::Normal => None, SpecifiedValue::Normal => computed_value::T(None),
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context)) 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" <%self:longhand name="-servo-text-decorations-in-effect"
derived_from="display text-decoration"> derived_from="display text-decoration">
use cssparser::RGBA; use cssparser::{RGBA, ToCss};
use std::fmt;
use values::computed::ComputedValueAsSpecified; use values::computed::ComputedValueAsSpecified;
impl ComputedValueAsSpecified for SpecifiedValue {} impl ComputedValueAsSpecified for SpecifiedValue {}
@ -2114,6 +2222,13 @@ pub mod longhands {
pub type T = super::SpecifiedValue; 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] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
SpecifiedValue { 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 { impl ToComputedValue for SpecifiedValue {
type ComputedValue = computed_value::T; type ComputedValue = computed_value::T;
@ -2370,12 +2493,22 @@ pub mod longhands {
pub mod computed_value { pub mod computed_value {
use util::geometry::Au; 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] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
@ -2384,8 +2517,9 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue::Auto => None, SpecifiedValue::Auto => computed_value::T(None),
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context)) SpecifiedValue::Specified(l) =>
computed_value::T(Some(l.to_computed_value(context)))
} }
} }
} }
@ -2420,12 +2554,22 @@ pub mod longhands {
} }
pub mod computed_value { 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] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
@ -2434,8 +2578,9 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, _context: &Context) -> computed_value::T { fn to_computed_value(&self, _context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue::Auto => None, SpecifiedValue::Auto => computed_value::T(None),
SpecifiedValue::Specified(count) => Some(count) SpecifiedValue::Specified(count) =>
computed_value::T(Some(count))
} }
} }
} }
@ -2476,12 +2621,22 @@ pub mod longhands {
pub mod computed_value { pub mod computed_value {
use util::geometry::Au; 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] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
@ -2490,8 +2645,9 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
match *self { match *self {
SpecifiedValue::Normal => None, SpecifiedValue::Normal => computed_value::T(None),
SpecifiedValue::Specified(l) => Some(l.to_computed_value(context)) SpecifiedValue::Specified(l) =>
computed_value::T(Some(l.to_computed_value(context)))
} }
} }
} }
@ -2611,7 +2767,8 @@ pub mod longhands {
use values::computed; use values::computed;
use std::fmt; use std::fmt;
pub type T = Vec<BoxShadow>; #[derive(Clone, PartialEq)]
pub struct T(pub Vec<BoxShadow>);
#[derive(Clone, PartialEq, Copy)] #[derive(Clone, PartialEq, Copy)]
pub struct BoxShadow { 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] #[inline]
pub fn get_initial_value() -> computed_value::T { 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, ()> { pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
@ -2653,7 +2845,7 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { 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 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)] #[derive(Clone, Debug, PartialEq, Copy)]
@ -2780,7 +3003,7 @@ pub mod longhands {
try!(dest.write_str("auto, ")); try!(dest.write_str("auto, "));
} }
if let Some(bottom) = self.right { if let Some(bottom) = self.bottom {
try!(bottom.to_css(dest)); try!(bottom.to_css(dest));
try!(dest.write_str(", ")); try!(dest.write_str(", "));
} else { } else {
@ -2806,7 +3029,7 @@ pub mod longhands {
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
impl ToComputedValue for SpecifiedValue { impl ToComputedValue for SpecifiedValue {
@ -2814,12 +3037,12 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { 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), top: value.top.to_computed_value(context),
right: value.right.map(|right| right.to_computed_value(context)), right: value.right.map(|right| right.to_computed_value(context)),
bottom: value.bottom.map(|bottom| bottom.to_computed_value(context)), bottom: value.bottom.map(|bottom| bottom.to_computed_value(context)),
left: value.left.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 { impl ToCss for SpecifiedValue {
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 {
let mut iter = self.0.iter(); 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 { impl ToCss for SpecifiedValue {
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 {
let mut iter = self.0.iter(); 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 { impl ToCss for SpecifiedFilter {
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 { match *self {
@ -3253,7 +3548,8 @@ pub mod longhands {
Perspective(computed::Length), 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; pub use self::computed_value::ComputedMatrix as SpecifiedMatrix;
@ -3290,6 +3586,13 @@ pub mod longhands {
Perspective(specified::Length), 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 { impl ToCss for SpecifiedOperation {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
// TODO(pcwalton) // TODO(pcwalton)
@ -3316,7 +3619,7 @@ pub mod longhands {
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {
None computed_value::T(None)
} }
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
@ -3548,7 +3851,7 @@ pub mod longhands {
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> computed_value::T { fn to_computed_value(&self, context: &Context) -> computed_value::T {
if self.0.is_empty() { if self.0.is_empty() {
return None return computed_value::T(None)
} }
let mut result = vec!(); let mut result = vec!();
@ -3577,7 +3880,7 @@ pub mod longhands {
}; };
} }
Some(result) computed_value::T(Some(result))
} }
} }
</%self:longhand> </%self:longhand>
@ -3612,6 +3915,16 @@ pub mod longhands {
depth: Length, 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 { impl ToCss for SpecifiedValue {
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 {
try!(self.horizontal.to_css(dest)); 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)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct SpecifiedValue { pub struct SpecifiedValue {
horizontal: LengthOrPercentage, horizontal: LengthOrPercentage,
@ -5542,7 +5863,7 @@ impl ComputedValues {
#[inline] #[inline]
pub fn is_multicol(&self) -> bool { pub fn is_multicol(&self) -> bool {
let style = self.get_column(); 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] #[inline]
@ -5560,13 +5881,13 @@ impl ComputedValues {
// TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported. // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
if effects.opacity < 1.0 || if effects.opacity < 1.0 ||
!effects.filter.is_empty() || !effects.filter.is_empty() ||
effects.clip.is_some() { effects.clip.0.is_some() {
effects.mix_blend_mode != mix_blend_mode::T::normal || effects.mix_blend_mode != mix_blend_mode::T::normal ||
return transform_style::T::flat; return transform_style::T::flat;
} }
if effects.transform_style == transform_style::T::auto { if effects.transform_style == transform_style::T::auto {
if effects.transform.is_some() { if effects.transform.0.is_some() {
return transform_style::T::flat; return transform_style::T::flat;
} }
if effects.perspective != computed::LengthOrNone::None { if effects.perspective != computed::LengthOrNone::None {
@ -5579,17 +5900,28 @@ impl ComputedValues {
} }
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
#[inline] #[inline]
pub fn get_${style_struct.name.lower()} pub fn get_${style_struct.name.lower()}
<'a>(&'a self) -> &'a style_structs::${style_struct.name} { <'a>(&'a self) -> &'a style_structs::${style_struct.name} {
&*self.${style_struct.ident} &*self.${style_struct.ident}
} }
#[inline] #[inline]
pub fn mutate_${style_struct.name.lower()} pub fn mutate_${style_struct.name.lower()}
<'a>(&'a mut self) -> &'a mut style_structs::${style_struct.name} { <'a>(&'a mut self) -> &'a mut style_structs::${style_struct.name} {
&mut *Arc::make_unique(&mut self.${style_struct.ident}) &mut *Arc::make_unique(&mut self.${style_struct.ident})
} }
% endfor % 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
}
}
} }

View file

@ -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)] #[derive(PartialEq, Clone, Copy)]
pub enum LengthOrPercentageOrAuto { pub enum LengthOrPercentageOrAuto {
Length(Au), 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)] #[derive(PartialEq, Clone, Copy)]
pub enum LengthOrPercentageOrNone { pub enum LengthOrPercentageOrNone {
Length(Au), 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)] #[derive(PartialEq, Clone, Copy)]
pub enum LengthOrNone { pub enum LengthOrNone {
Length(Au), 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 { impl ToComputedValue for specified::Image {
type ComputedValue = Image; type ComputedValue = Image;
@ -1116,6 +1157,19 @@ pub mod computed {
pub stops: Vec<ColorStop>, 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 { impl fmt::Debug for LinearGradient {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = write!(f, "{:?}", self.angle_or_corner); let _ = write!(f, "{:?}", self.angle_or_corner);
@ -1137,6 +1191,17 @@ pub mod computed {
pub position: Option<LengthOrPercentage>, 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 { impl fmt::Debug for ColorStop {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let _ = write!(f, "{:?}", self.color); let _ = write!(f, "{:?}", self.color);

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::ToCss;
use euclid::length::Length; use euclid::length::Length;
use euclid::point::Point2D; use euclid::point::Point2D;
use euclid::rect::Rect; use euclid::rect::Rect;
@ -122,7 +124,14 @@ impl Encodable for Au {
impl fmt::Debug for Au { impl fmt::Debug for Au {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}px", self.to_f64_px()) 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 { impl Add for Au {
type Output = Au; type Output = Au;

View file

@ -1,9 +1,8 @@
[events-006.htm] [events-006.htm]
type: testharness type: testharness
expected: TIMEOUT
[transition padding-left on :before / events] [transition padding-left on :before / events]
expected: NOTRUN expected: FAIL
[transition padding-left on :after / events] [transition padding-left on :after / events]
expected: NOTRUN expected: FAIL

View file

@ -1,15 +1,14 @@
[pseudo-elements-001.htm] [pseudo-elements-001.htm]
type: testharness type: testharness
expected: TIMEOUT
[transition padding-left on :before / values] [transition padding-left on :before / values]
expected: NOTRUN expected: FAIL
[transition padding-left on :after / values] [transition padding-left on :after / values]
expected: NOTRUN expected: FAIL
[transition padding-left on :before, changing content / values] [transition padding-left on :before, changing content / values]
expected: NOTRUN expected: FAIL
[transition padding-left on :after, changing content / values] [transition padding-left on :after, changing content / values]
expected: NOTRUN expected: FAIL

View file

@ -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

View file

@ -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

View file

@ -1,5 +0,0 @@
[del_effect.html]
type: testharness
[HTML Test: Text in the del element should be 'line-through']
expected: FAIL

View file

@ -1,5 +0,0 @@
[ins_effect.html]
type: testharness
[HTML Test: Text in the ins element should be 'underline']
expected: FAIL

View file

@ -533,6 +533,12 @@
"url": "/_mozilla/mozilla/getBoundingClientRect.html" "url": "/_mozilla/mozilla/getBoundingClientRect.html"
} }
], ],
"mozilla/getComputedStyle.html": [
{
"path": "mozilla/getComputedStyle.html",
"url": "/_mozilla/mozilla/getComputedStyle.html"
}
],
"mozilla/getPropertyPriority.html": [ "mozilla/getPropertyPriority.html": [
{ {
"path": "mozilla/getPropertyPriority.html", "path": "mozilla/getPropertyPriority.html",
@ -974,4 +980,4 @@
"rev": null, "rev": null,
"url_base": "/_mozilla/", "url_base": "/_mozilla/",
"version": 2 "version": 2
} }

View file

@ -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>