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 {
// 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(..) => {

View file

@ -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()),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
}
};

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

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)]
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);

View file

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

View file

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

View file

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

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"
}
],
"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
}
}

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>