aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <metajack+bors@gmail.com>2014-12-18 11:54:52 -0700
committerbors-servo <metajack+bors@gmail.com>2014-12-18 11:54:52 -0700
commit824788649cd338c044d9396166af5b0f378d6685 (patch)
tree8fe0f79b3cfe8c8c400ef62f5f334addac5843d5
parent1f342638c46d6b43bca4cfbd405aceedc0465a85 (diff)
parent2f5786d6ca02894407b6d1083ecfed3c036769de (diff)
downloadservo-824788649cd338c044d9396166af5b0f378d6685.tar.gz
servo-824788649cd338c044d9396166af5b0f378d6685.zip
auto merge of #4342 : jdm/servo/cssom, r=jdm,metajack
This does not implement any notion of CSSStyleDeclaration objects that do not have an owning element; there's no actual CSS object model in play here. This does support setting and getting properties of the style attribute for HTMLElement, and tries to implement the ambiguous CSS value serialization spec.
-rw-r--r--components/script/dom/cssstyledeclaration.rs350
-rw-r--r--components/script/dom/element.rs73
-rw-r--r--components/script/dom/htmlelement.rs18
-rw-r--r--components/script/dom/webidls/CSSStyleDeclaration.webidl149
-rw-r--r--components/script/dom/webidls/ElementCSSInlineStyle.webidl11
-rw-r--r--components/script/dom/webidls/HTMLElement.webidl1
-rw-r--r--components/script/lib.rs3
-rw-r--r--components/script/tests.rs6
-rw-r--r--components/style/legacy.rs3
-rw-r--r--components/style/lib.rs5
-rw-r--r--components/style/properties/common_types.rs241
-rw-r--r--components/style/properties/mod.rs.mako331
-rw-r--r--tests/content/test_interfaces.html3
-rw-r--r--tests/html/cssprops.js534
-rw-r--r--tests/html/test_htmlelement_style.html12
-rw-r--r--tests/html/test_style.html12
16 files changed, 1705 insertions, 47 deletions
diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs
new file mode 100644
index 00000000000..99cb65c4807
--- /dev/null
+++ b/components/script/dom/cssstyledeclaration.rs
@@ -0,0 +1,350 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::{mod, CSSStyleDeclarationMethods};
+use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
+use dom::bindings::error::ErrorResult;
+use dom::bindings::global;
+use dom::bindings::js::{JS, JSRef, OptionalRootedRootable, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::document::DocumentHelpers;
+use dom::element::{Element, ElementHelpers};
+use dom::htmlelement::HTMLElement;
+use dom::node::{window_from_node, document_from_node, NodeDamage, Node};
+use dom::window::Window;
+use servo_util::str::DOMString;
+use string_cache::Atom;
+use style::{is_supported_property, longhands_from_shorthand, parse_style_attribute};
+use style::PropertyDeclaration;
+
+use std::ascii::AsciiExt;
+
+#[dom_struct]
+pub struct CSSStyleDeclaration {
+ reflector_: Reflector,
+ owner: JS<HTMLElement>,
+}
+
+macro_rules! css_properties(
+ ( $([$getter:ident, $setter:ident, $cssprop:expr]),* ) => (
+ $(
+ fn $getter(self) -> DOMString {
+ self.GetPropertyValue($cssprop.to_string())
+ }
+ fn $setter(self, value: DOMString) {
+ self.SetPropertyValue($cssprop.to_string(), value).unwrap();
+ }
+ )*
+ );
+)
+
+fn serialize_list(list: &Vec<PropertyDeclaration>) -> DOMString {
+ let mut result = String::new();
+ for declaration in list.iter() {
+ result.push_str(serialize_value(declaration).as_slice());
+ result.push_str(" ");
+ }
+ result
+}
+
+fn serialize_value(declaration: &PropertyDeclaration) -> DOMString {
+ declaration.value()
+}
+
+impl CSSStyleDeclaration {
+ pub fn new_inherited(owner: JSRef<HTMLElement>) -> CSSStyleDeclaration {
+ CSSStyleDeclaration {
+ reflector_: Reflector::new(),
+ owner: JS::from_rooted(owner),
+ }
+ }
+
+ pub fn new(global: JSRef<Window>, owner: JSRef<HTMLElement>) -> Temporary<CSSStyleDeclaration> {
+ reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner),
+ global::Window(global),
+ CSSStyleDeclarationBinding::Wrap)
+ }
+}
+
+trait PrivateCSSStyleDeclarationHelpers {
+ fn get_declaration(self, property: &Atom) -> Option<PropertyDeclaration>;
+}
+
+impl<'a> PrivateCSSStyleDeclarationHelpers for JSRef<'a, CSSStyleDeclaration> {
+ fn get_declaration(self, property: &Atom) -> Option<PropertyDeclaration> {
+ let owner = self.owner.root();
+ let element: JSRef<Element> = ElementCast::from_ref(*owner);
+ element.get_inline_style_declaration(property).map(|decl| decl.clone())
+ }
+}
+
+impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> {
+ fn Length(self) -> u32 {
+ let owner = self.owner.root();
+ let elem: JSRef<Element> = ElementCast::from_ref(*owner);
+ let len = match *elem.style_attribute().borrow() {
+ Some(ref declarations) => declarations.normal.len() + declarations.important.len(),
+ None => 0
+ };
+ len as u32
+ }
+
+ fn Item(self, index: u32) -> DOMString {
+ let owner = self.owner.root();
+ let elem: JSRef<Element> = ElementCast::from_ref(*owner);
+ let style_attribute = elem.style_attribute().borrow();
+ let result = style_attribute.as_ref().and_then(|declarations| {
+ if index as uint > declarations.normal.len() {
+ declarations.important
+ .get(index as uint - declarations.normal.len())
+ .map(|decl| format!("{} !important", decl))
+ } else {
+ declarations.normal
+ .get(index as uint)
+ .map(|decl| format!("{}", decl))
+ }
+ });
+
+ result.unwrap_or("".to_string())
+ }
+
+ // http://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
+ fn GetPropertyValue(self, property: DOMString) -> DOMString {
+ // Step 1
+ let property = Atom::from_slice(property.as_slice().to_ascii_lower().as_slice());
+
+ // Step 2
+ let longhand_properties = longhands_from_shorthand(property.as_slice());
+ if let Some(longhand_properties) = longhand_properties {
+ // Step 2.1
+ let mut list = vec!();
+
+ // Step 2.2
+ for longhand in longhand_properties.iter() {
+ // Step 2.2.1
+ let declaration = self.get_declaration(&Atom::from_slice(longhand.as_slice()));
+
+ // Step 2.2.2 & 2.2.3
+ match declaration {
+ Some(declaration) => list.push(declaration),
+ None => return "".to_string(),
+ }
+ }
+
+ // Step 2.3
+ return serialize_list(&list);
+ }
+
+ // Step 3 & 4
+ if let Some(ref declaration) = self.get_declaration(&property) {
+ serialize_value(declaration)
+ } else {
+ "".to_string()
+ }
+ }
+
+ // http://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
+ fn SetProperty(self, property: DOMString, value: DOMString,
+ priority: DOMString) -> ErrorResult {
+ //TODO: disallow modifications if readonly flag is set
+
+ // Step 2
+ let property = property.as_slice().to_ascii_lower();
+
+ // Step 3
+ if !is_supported_property(property.as_slice()) {
+ return Ok(());
+ }
+
+ // Step 4
+ if value.is_empty() {
+ self.RemoveProperty(property);
+ return Ok(());
+ }
+
+ // Step 5
+ let priority = priority.as_slice().to_ascii_lower();
+ if priority.as_slice() != "!important" && !priority.is_empty() {
+ return Ok(());
+ }
+
+ // Step 6
+ let mut synthesized_declaration = String::from_str(property.as_slice());
+ synthesized_declaration.push_str(": ");
+ synthesized_declaration.push_str(value.as_slice());
+
+ let owner = self.owner.root();
+ let window = window_from_node(*owner).root();
+ let page = window.page();
+ let decl_block = parse_style_attribute(synthesized_declaration.as_slice(),
+ &page.get_url());
+
+ // Step 7
+ if decl_block.normal.len() == 0 {
+ return Ok(());
+ }
+
+ let owner = self.owner.root();
+ let element: JSRef<Element> = ElementCast::from_ref(*owner);
+
+ // Step 8
+ for decl in decl_block.normal.iter() {
+ // Step 9
+ element.update_inline_style(decl.clone(), !priority.is_empty());
+ }
+
+ let document = document_from_node(element).root();
+ let node: JSRef<Node> = NodeCast::from_ref(element);
+ document.content_changed(node, NodeDamage::NodeStyleDamaged);
+ Ok(())
+ }
+
+ // http://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
+ fn SetPropertyValue(self, property: DOMString, value: DOMString) -> ErrorResult {
+ self.SetProperty(property, value, "".to_string())
+ }
+
+ // http://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
+ fn RemoveProperty(self, property: DOMString) -> DOMString {
+ //TODO: disallow modifications if readonly flag is set
+
+ // Step 2
+ let property = property.as_slice().to_ascii_lower();
+
+ // Step 3
+ let value = self.GetPropertyValue(property.clone());
+
+ let longhand_properties = longhands_from_shorthand(property.as_slice());
+ match longhand_properties {
+ Some(longhands) => {
+ // Step 4
+ for longhand in longhands.iter() {
+ self.RemoveProperty(longhand.clone());
+ }
+ }
+
+ None => {
+ // Step 5
+ let owner = self.owner.root();
+ let elem: JSRef<Element> = ElementCast::from_ref(*owner);
+ elem.remove_inline_style_property(property)
+ }
+ }
+
+ // Step 6
+ value
+ }
+
+ // http://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
+ fn CssFloat(self) -> DOMString {
+ self.GetPropertyValue("float".to_string())
+ }
+
+ // http://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
+ fn SetCssFloat(self, value: DOMString) -> ErrorResult {
+ self.SetPropertyValue("float".to_string(), value)
+ }
+
+ fn IndexedGetter(self, index: u32, found: &mut bool) -> DOMString {
+ let rval = self.Item(index);
+ *found = index < self.Length();
+ rval
+ }
+
+ css_properties!(
+ [Color, SetColor, "color"],
+ [Background, SetBackground, "background"],
+ [BackgroundColor, SetBackgroundColor, "background-color"],
+ [BackgroundPosition, SetBackgroundPosition, "background-position"],
+ [BackgroundImage, SetBackgroundImage, "background-image"],
+ [BackgroundRepeat, SetBackgroundRepeat, "background-repeat"],
+ [BackgroundAttachment, SetBackgroundAttachment, "background-attachment"],
+ [Border, SetBorder, "border"],
+ [BorderColor, SetBorderColor, "border-color"],
+ [BorderRadius, SetBorderRadius, "border-radius"],
+ [BorderStyle, SetBorderStyle, "border-style"],
+ [BorderWidth, SetBorderWidth, "border-width"],
+ [BorderBottom, SetBorderBottom, "border-bottom"],
+ [BorderBottomColor, SetBorderBottomColor, "border-bottom-color"],
+ [BorderBottomStyle, SetBorderBottomStyle, "border-bottom-style"],
+ [BorderBottomWidth, SetBorderBottomWidth, "border-bottom-width"],
+ [BorderLeft, SetBorderLeft, "border-left"],
+ [BorderLeftColor, SetBorderLeftColor, "border-left-color"],
+ [BorderLeftStyle, SetBorderLeftStyle, "border-left-style"],
+ [BorderLeftWidth, SetBorderLeftWidth, "border-left-width"],
+ [BorderRight, SetBorderRight, "border-right"],
+ [BorderRightColor, SetBorderRightColor, "border-right-color"],
+ [BorderRightStyle, SetBorderRightStyle, "border-right-style"],
+ [BorderRightWidth, SetBorderRightWidth, "border-right-width"],
+ [BorderTop, SetBorderTop, "border-top"],
+ [BorderTopColor, SetBorderTopColor, "border-top-color"],
+ [BorderTopStyle, SetBorderTopStyle, "border-top-style"],
+ [BorderTopWidth, SetBorderTopWidth, "border-top-width"],
+ [Content, SetContent, "content"],
+ [Display, SetDisplay, "display"],
+ [Opacity, SetOpacity, "opacity"],
+ [Width, SetWidth, "width"],
+ [MinWidth, SetMinWidth, "min-width"],
+ [MaxWidth, SetMaxWidth, "max-width"],
+ [Height, SetHeight, "height"],
+ [MinHeight, SetMinHeight, "min-height"],
+ [MaxHeight, SetMaxHeight, "max-height"],
+ [Clear, SetClear, "clear"],
+ [Direction, SetDirection, "direction"],
+ [LineHeight, SetLineHeight, "line-height"],
+ [VerticalAlign, SetVerticalAlign, "vertical-align"],
+ [ListStyle, SetListStyle, "list-style"],
+ [ListStylePosition, SetListStylePosition, "list-style-position"],
+ [ListStyleType, SetListStyleType, "list-style-type"],
+ [ListStyleImage, SetListStyleImage, "list-style-image"],
+ [Visibility, SetVisibility, "visibility"],
+ [Cursor, SetCursor, "cursor"],
+ [BoxShadow, SetBoxShadow, "box-shadow"],
+ [BoxSizing, SetBoxSizing, "box-sizing"],
+ [Overflow, SetOverflow, "overflow"],
+ [OverflowWrap, SetOverflowWrap, "overflow-wrap"],
+ [TableLayout, SetTableLayout, "table-layout"],
+ [EmptyCells, SetEmptyCells, "empty-cells"],
+ [CaptionSide, SetCaptionSide, "caption-side"],
+ [WhiteSpace, SetWhiteSpace, "white-space"],
+ [WritingMode, SetWritingMode, "writing-mode"],
+ [LetterSpacing, SetLetterSpacing, "letter-spacing"],
+ [WordSpacing, SetWordSpacing, "word-spacing"],
+ [WordWrap, SetWordWrap, "word-wrap"],
+ [TextAlign, SetTextAlign, "text-align"],
+ [TextDecoration, SetTextDecoration, "text-decoration"],
+ [TextIndent, SetTextIndent, "text-indent"],
+ [TextOrientation, SetTextOrientation, "text-orientation"],
+ [TextTransform, SetTextTransform, "text-transform"],
+ [Font, SetFont, "font"],
+ [FontFamily, SetFontFamily, "font-family"],
+ [FontSize, SetFontSize, "font-size"],
+ [FontStyle, SetFontStyle, "font-style"],
+ [FontVariant, SetFontVariant, "font-variant"],
+ [FontWeight, SetFontWeight, "font-weight"],
+ [Margin, SetMargin, "margin"],
+ [MarginBottom, SetMarginBottom, "margin-bottom"],
+ [MarginLeft, SetMarginLeft, "margin-left"],
+ [MarginRight, SetMarginRight, "margin-right"],
+ [MarginTop, SetMarginTop, "margin-top"],
+ [Padding, SetPadding, "padding"],
+ [PaddingBottom, SetPaddingBottom, "padding-bottom"],
+ [PaddingLeft, SetPaddingLeft, "padding-left"],
+ [PaddingRight, SetPaddingRight, "padding-right"],
+ [PaddingTop, SetPaddingTop, "padding-top"],
+ [Outline, SetOutline, "outline"],
+ [Position, SetPosition, "position"],
+ [Bottom, SetBottom, "bottom"],
+ [Left, SetLeft, "left"],
+ [Right, SetRight, "right"],
+ [Top, SetTop, "top"],
+ [ZIndex, SetZIndex, "z-index"]
+ )
+}
+
+impl Reflectable for CSSStyleDeclaration {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index da1ff1d408f..00f481dcd32 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -60,6 +60,7 @@ use std::ascii::AsciiExt;
use std::cell::{Ref, RefMut};
use std::default::Default;
use std::mem;
+use std::sync::Arc;
use string_cache::{Atom, Namespace, QualName};
use url::UrlParser;
@@ -465,6 +466,9 @@ pub trait ElementHelpers<'a> {
fn style_attribute(self) -> &'a DOMRefCell<Option<style::PropertyDeclarationBlock>>;
fn summarize(self) -> Vec<AttrInfo>;
fn is_void(self) -> bool;
+ fn remove_inline_style_property(self, property: DOMString);
+ fn update_inline_style(self, property_decl: style::PropertyDeclaration, important: bool);
+ fn get_inline_style_declaration(self, property: &Atom) -> Option<style::PropertyDeclaration>;
}
impl<'a> ElementHelpers<'a> for JSRef<'a, Element> {
@@ -522,6 +526,75 @@ impl<'a> ElementHelpers<'a> for JSRef<'a, Element> {
_ => false
}
}
+
+ fn remove_inline_style_property(self, property: DOMString) {
+ let mut inline_declarations = self.style_attribute.borrow_mut();
+ inline_declarations.as_mut().map(|declarations| {
+ let index = declarations.normal
+ .iter()
+ .position(|decl| decl.name() == property);
+ match index {
+ Some(index) => {
+ declarations.normal.make_unique().remove(index);
+ return;
+ }
+ None => ()
+ }
+
+ let index = declarations.important
+ .iter()
+ .position(|decl| decl.name() == property);
+ match index {
+ Some(index) => {
+ declarations.important.make_unique().remove(index);
+ return;
+ }
+ None => ()
+ }
+ });
+ }
+
+ fn update_inline_style(self, property_decl: style::PropertyDeclaration, important: bool) {
+ let mut inline_declarations = self.style_attribute().borrow_mut();
+ if let Some(ref mut declarations) = *inline_declarations.deref_mut() {
+ let existing_declarations = if important {
+ declarations.important.make_unique()
+ } else {
+ declarations.normal.make_unique()
+ };
+
+ for declaration in existing_declarations.iter_mut() {
+ if declaration.name() == property_decl.name() {
+ *declaration = property_decl;
+ return;
+ }
+ }
+ existing_declarations.push(property_decl);
+ return;
+ }
+
+ let (important, normal) = if important {
+ (vec!(property_decl), vec!())
+ } else {
+ (vec!(), vec!(property_decl))
+ };
+
+ *inline_declarations = Some(style::PropertyDeclarationBlock {
+ important: Arc::new(important),
+ normal: Arc::new(normal),
+ });
+ }
+
+ fn get_inline_style_declaration(self, property: &Atom) -> Option<style::PropertyDeclaration> {
+ let inline_declarations = self.style_attribute.borrow();
+ inline_declarations.as_ref().and_then(|declarations| {
+ declarations.normal
+ .iter()
+ .chain(declarations.important.iter())
+ .find(|decl| decl.matches(property.as_slice()))
+ .map(|decl| decl.clone())
+ })
+ }
}
pub trait AttributeHandlers {
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index f8a8ea7ce5c..8fb0f2c4be5 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -12,8 +12,9 @@ use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived};
use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLInputElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived};
-use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::js::{JSRef, Temporary, MutNullableJS};
use dom::bindings::utils::{Reflectable, Reflector};
+use dom::cssstyledeclaration::CSSStyleDeclaration;
use dom::document::Document;
use dom::element::{Element, ElementTypeId, ActivationElementHelpers};
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
@@ -24,9 +25,12 @@ use servo_util::str::DOMString;
use string_cache::Atom;
+use std::default::Default;
+
#[dom_struct]
pub struct HTMLElement {
- element: Element
+ element: Element,
+ style_decl: MutNullableJS<CSSStyleDeclaration>,
}
impl HTMLElementDerived for EventTarget {
@@ -42,7 +46,8 @@ impl HTMLElementDerived for EventTarget {
impl HTMLElement {
pub fn new_inherited(type_id: ElementTypeId, tag_name: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLElement {
HTMLElement {
- element: Element::new_inherited(type_id, tag_name, ns!(HTML), prefix, document)
+ element: Element::new_inherited(type_id, tag_name, ns!(HTML), prefix, document),
+ style_decl: Default::default(),
}
}
@@ -65,6 +70,13 @@ impl<'a> PrivateHTMLElementHelpers for JSRef<'a, HTMLElement> {
}
impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
+ fn Style(self) -> Temporary<CSSStyleDeclaration> {
+ self.style_decl.or_init(|| {
+ let global = window_from_node(self).root();
+ CSSStyleDeclaration::new(*global, self)
+ })
+ }
+
make_getter!(Title)
make_setter!(SetTitle, "title")
diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl
new file mode 100644
index 00000000000..8ef419df6a5
--- /dev/null
+++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl
@@ -0,0 +1,149 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
+ *
+ * Copyright © 2013 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
+ */
+
+interface CSSStyleDeclaration {
+ //[SetterThrows]
+ // attribute DOMString cssText;
+ readonly attribute unsigned long length;
+ getter DOMString item(unsigned long index);
+ DOMString getPropertyValue(DOMString property);
+ //DOMString getPropertyPriority(DOMString property);
+ [Throws]
+ void setProperty(DOMString property, [TreatNullAs=EmptyString] DOMString value,
+ [TreatNullAs=EmptyString] optional DOMString priority = "");
+ [Throws]
+ void setPropertyValue(DOMString property, [TreatNullAs=EmptyString] DOMString value);
+ //[Throws]
+ //void setPropertyPriority(DOMString property, [TreatNullAs=EmptyString] DOMString priority);
+ DOMString removeProperty(DOMString property);
+ //readonly attribute CSSRule? parentRule;
+ [SetterThrows]
+ attribute DOMString cssFloat;
+};
+
+partial interface CSSStyleDeclaration {
+ [TreatNullAs=EmptyString] attribute DOMString background;
+ [TreatNullAs=EmptyString] attribute DOMString backgroundColor;
+ [TreatNullAs=EmptyString] attribute DOMString backgroundPosition;
+ [TreatNullAs=EmptyString] attribute DOMString backgroundRepeat;
+ [TreatNullAs=EmptyString] attribute DOMString backgroundImage;
+ [TreatNullAs=EmptyString] attribute DOMString backgroundAttachment;
+
+ [TreatNullAs=EmptyString] attribute DOMString border;
+ [TreatNullAs=EmptyString] attribute DOMString borderColor;
+ [TreatNullAs=EmptyString] attribute DOMString borderRadius;
+ [TreatNullAs=EmptyString] attribute DOMString borderStyle;
+ [TreatNullAs=EmptyString] attribute DOMString borderWidth;
+ [TreatNullAs=EmptyString] attribute DOMString borderBottom;
+ [TreatNullAs=EmptyString] attribute DOMString borderBottomColor;
+ [TreatNullAs=EmptyString] attribute DOMString borderBottomStyle;
+ [TreatNullAs=EmptyString] attribute DOMString borderBottomWidth;
+ [TreatNullAs=EmptyString] attribute DOMString borderLeft;
+ [TreatNullAs=EmptyString] attribute DOMString borderLeftColor;
+ [TreatNullAs=EmptyString] attribute DOMString borderLeftStyle;
+ [TreatNullAs=EmptyString] attribute DOMString borderLeftWidth;
+ [TreatNullAs=EmptyString] attribute DOMString borderRight;
+ [TreatNullAs=EmptyString] attribute DOMString borderRightColor;
+ [TreatNullAs=EmptyString] attribute DOMString borderRightStyle;
+ [TreatNullAs=EmptyString] attribute DOMString borderRightWidth;
+ [TreatNullAs=EmptyString] attribute DOMString borderTop;
+ [TreatNullAs=EmptyString] attribute DOMString borderTopColor;
+ [TreatNullAs=EmptyString] attribute DOMString borderTopStyle;
+ [TreatNullAs=EmptyString] attribute DOMString borderTopWidth;
+
+ [TreatNullAs=EmptyString] attribute DOMString content;
+
+ [TreatNullAs=EmptyString] attribute DOMString color;
+
+ [TreatNullAs=EmptyString] attribute DOMString display;
+
+ [TreatNullAs=EmptyString] attribute DOMString opacity;
+
+ [TreatNullAs=EmptyString] attribute DOMString visibility;
+
+ [TreatNullAs=EmptyString] attribute DOMString cursor;
+
+ [TreatNullAs=EmptyString] attribute DOMString boxSizing;
+ [TreatNullAs=EmptyString] attribute DOMString boxShadow;
+
+ //[TreatNullAs=EmptyString] attribute DOMString float; //XXXjdm need BinaryName annotation
+
+ [TreatNullAs=EmptyString] attribute DOMString clear;
+
+ [TreatNullAs=EmptyString] attribute DOMString direction;
+
+ [TreatNullAs=EmptyString] attribute DOMString lineHeight;
+
+ [TreatNullAs=EmptyString] attribute DOMString verticalAlign;
+
+ [TreatNullAs=EmptyString] attribute DOMString listStyle;
+ [TreatNullAs=EmptyString] attribute DOMString listStylePosition;
+ [TreatNullAs=EmptyString] attribute DOMString listStyleType;
+ [TreatNullAs=EmptyString] attribute DOMString listStyleImage;
+
+ [TreatNullAs=EmptyString] attribute DOMString overflow;
+ [TreatNullAs=EmptyString] attribute DOMString overflowWrap;
+
+ [TreatNullAs=EmptyString] attribute DOMString tableLayout;
+ [TreatNullAs=EmptyString] attribute DOMString emptyCells;
+ [TreatNullAs=EmptyString] attribute DOMString captionSide;
+
+ [TreatNullAs=EmptyString] attribute DOMString whiteSpace;
+
+ [TreatNullAs=EmptyString] attribute DOMString writingMode;
+
+ [TreatNullAs=EmptyString] attribute DOMString letterSpacing;
+ [TreatNullAs=EmptyString] attribute DOMString wordSpacing;
+ [TreatNullAs=EmptyString] attribute DOMString wordWrap;
+
+ [TreatNullAs=EmptyString] attribute DOMString textAlign;
+ [TreatNullAs=EmptyString] attribute DOMString textDecoration;
+ [TreatNullAs=EmptyString] attribute DOMString textIndent;
+ [TreatNullAs=EmptyString] attribute DOMString textOrientation;
+ [TreatNullAs=EmptyString] attribute DOMString textTransform;
+
+ [TreatNullAs=EmptyString] attribute DOMString font;
+ [TreatNullAs=EmptyString] attribute DOMString fontFamily;
+ [TreatNullAs=EmptyString] attribute DOMString fontSize;
+ [TreatNullAs=EmptyString] attribute DOMString fontStyle;
+ [TreatNullAs=EmptyString] attribute DOMString fontVariant;
+ [TreatNullAs=EmptyString] attribute DOMString fontWeight;
+
+ [TreatNullAs=EmptyString] attribute DOMString margin;
+ [TreatNullAs=EmptyString] attribute DOMString marginBottom;
+ [TreatNullAs=EmptyString] attribute DOMString marginLeft;
+ [TreatNullAs=EmptyString] attribute DOMString marginRight;
+ [TreatNullAs=EmptyString] attribute DOMString marginTop;
+
+ [TreatNullAs=EmptyString] attribute DOMString padding;
+ [TreatNullAs=EmptyString] attribute DOMString paddingBottom;
+ [TreatNullAs=EmptyString] attribute DOMString paddingLeft;
+ [TreatNullAs=EmptyString] attribute DOMString paddingRight;
+ [TreatNullAs=EmptyString] attribute DOMString paddingTop;
+
+ [TreatNullAs=EmptyString] attribute DOMString outline;
+
+ [TreatNullAs=EmptyString] attribute DOMString position;
+
+ [TreatNullAs=EmptyString] attribute DOMString top;
+ [TreatNullAs=EmptyString] attribute DOMString right;
+ [TreatNullAs=EmptyString] attribute DOMString left;
+ [TreatNullAs=EmptyString] attribute DOMString bottom;
+
+ [TreatNullAs=EmptyString] attribute DOMString height;
+ [TreatNullAs=EmptyString] attribute DOMString minHeight;
+ [TreatNullAs=EmptyString] attribute DOMString maxHeight;
+
+ [TreatNullAs=EmptyString] attribute DOMString width;
+ [TreatNullAs=EmptyString] attribute DOMString minWidth;
+ [TreatNullAs=EmptyString] attribute DOMString maxWidth;
+
+ [TreatNullAs=EmptyString] attribute DOMString zIndex;
+};
diff --git a/components/script/dom/webidls/ElementCSSInlineStyle.webidl b/components/script/dom/webidls/ElementCSSInlineStyle.webidl
new file mode 100644
index 00000000000..bf7a7b92b9e
--- /dev/null
+++ b/components/script/dom/webidls/ElementCSSInlineStyle.webidl
@@ -0,0 +1,11 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//http://dev.w3.org/csswg/cssom/#elementcssinlinestyle
+
+[NoInterfaceObject]
+interface ElementCSSInlineStyle {
+ [SameObject/*, PutForwards=cssText*/] readonly attribute CSSStyleDeclaration style;
+};
diff --git a/components/script/dom/webidls/HTMLElement.webidl b/components/script/dom/webidls/HTMLElement.webidl
index 359ef11f0a7..39c7699900d 100644
--- a/components/script/dom/webidls/HTMLElement.webidl
+++ b/components/script/dom/webidls/HTMLElement.webidl
@@ -46,3 +46,4 @@ interface HTMLElement : Element {
//readonly attribute boolean? commandChecked;
};
HTMLElement implements GlobalEventHandlers;
+HTMLElement implements ElementCSSInlineStyle;
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 26f773f6006..b62b137a38e 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -5,7 +5,7 @@
#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]
-#![feature(default_type_params, globs, macro_rules, phase, unsafe_destructor)]
+#![feature(default_type_params, globs, macro_rules, phase, unsafe_destructor, if_let)]
#![deny(unused_imports)]
#![deny(unused_variables)]
@@ -88,6 +88,7 @@ pub mod dom {
pub mod browsercontext;
pub mod canvasrenderingcontext2d;
pub mod characterdata;
+ pub mod cssstyledeclaration;
pub mod domrect;
pub mod domrectlist;
pub mod domstringmap;
diff --git a/components/script/tests.rs b/components/script/tests.rs
index 6675fefd07b..8f7bbda8385 100644
--- a/components/script/tests.rs
+++ b/components/script/tests.rs
@@ -41,8 +41,8 @@ macro_rules! sizeof_checker (
sizeof_checker!(size_event_target, EventTarget, 56)
sizeof_checker!(size_node, Node, 304)
sizeof_checker!(size_element, Element, 448)
-sizeof_checker!(size_htmlelement, HTMLElement, 448)
-sizeof_checker!(size_div, HTMLDivElement, 448)
-sizeof_checker!(size_span, HTMLSpanElement, 448)
+sizeof_checker!(size_htmlelement, HTMLElement, 464)
+sizeof_checker!(size_div, HTMLDivElement, 464)
+sizeof_checker!(size_span, HTMLSpanElement, 464)
sizeof_checker!(size_text, Text, 336)
sizeof_checker!(size_characterdata, CharacterData, 336)
diff --git a/components/style/legacy.rs b/components/style/legacy.rs
index 357609cc534..e668b1203ad 100644
--- a/components/style/legacy.rs
+++ b/components/style/legacy.rs
@@ -6,6 +6,7 @@
//! `<input size>`, and so forth.
use node::{TElement, TElementAttributes, TNode};
+use properties::common_types::specified::CSSColor;
use properties::DeclaredValue::SpecifiedValue;
use properties::PropertyDeclaration::*;
use properties::{CSSFloat, specified};
@@ -211,7 +212,7 @@ impl PresentationalHintSynthesis for Stylist {
None => {}
Some(color) => {
matching_rules_list.vec_push(DeclarationBlock::from_declaration(
- BackgroundColorDeclaration(SpecifiedValue(Color::RGBA(color)))));
+ BackgroundColorDeclaration(SpecifiedValue(CSSColor { parsed: Color::RGBA(color), authored: None }))));
*shareable = false
}
}
diff --git a/components/style/lib.rs b/components/style/lib.rs
index 6d256792028..fec6c2d5b61 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -5,7 +5,7 @@
#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]
-#![feature(globs, macro_rules)]
+#![feature(globs, macro_rules, if_let)]
#![deny(unused_imports)]
#![deny(unused_variables)]
@@ -42,7 +42,8 @@ pub use selector_matching::{CommonStyleAffectingAttributeInfo, CommonStyleAffect
pub use selector_matching::{matches, matches_simple_selector, common_style_affecting_attributes};
pub use selector_matching::{rare_style_affecting_attributes};
pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE, SELECTOR_WHITESPACE};
-pub use properties::{cascade, cascade_anonymous, computed};
+pub use properties::{cascade, cascade_anonymous, computed, longhands_from_shorthand};
+pub use properties::is_supported_property;
pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs};
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult};
diff --git a/components/style/properties/common_types.rs b/components/style/properties/common_types.rs
index 69b3576f410..9dcd65d6fc5 100644
--- a/components/style/properties/common_types.rs
+++ b/components/style/properties/common_types.rs
@@ -13,14 +13,71 @@ pub type CSSFloat = f64;
pub mod specified {
use std::ascii::AsciiExt;
use std::f64::consts::PI;
+ use std::fmt;
+ use std::fmt::{Formatter, FormatError, Show};
use url::Url;
+ use cssparser;
use cssparser::ast;
use cssparser::ast::*;
use parsing_utils::{mod, BufferedIter, ParserIter};
use super::{Au, CSSFloat};
- pub use cssparser::Color as CSSColor;
- #[deriving(Clone, Show)]
+ #[deriving(Clone, PartialEq)]
+ pub struct CSSColor {
+ pub parsed: cssparser::Color,
+ pub authored: Option<String>,
+ }
+ impl CSSColor {
+ pub fn parse(component_value: &ComponentValue) -> Result<CSSColor, ()> {
+ let parsed = cssparser::Color::parse(component_value);
+ parsed.map(|parsed| {
+ let authored = match component_value {
+ &Ident(ref s) => Some(s.clone()),
+ _ => None,
+ };
+ CSSColor {
+ parsed: parsed,
+ authored: authored,
+ }
+ })
+ }
+ }
+ impl fmt::Show for CSSColor {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.authored {
+ Some(ref s) => write!(f, "{}", s),
+ None => write!(f, "{}", self.parsed),
+ }
+ }
+ }
+
+ #[deriving(Clone)]
+ pub struct CSSRGBA {
+ pub parsed: cssparser::RGBA,
+ pub authored: Option<String>,
+ }
+ impl fmt::Show for CSSRGBA {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.authored {
+ Some(ref s) => write!(f, "{}", s),
+ None => write!(f, "{}", self.parsed),
+ }
+ }
+ }
+
+ #[deriving(Clone, PartialEq)]
+ pub struct CSSImage(pub Option<Image>);
+ impl fmt::Show for CSSImage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let &CSSImage(ref url) = self;
+ match url {
+ &Some(ref image) => write!(f, "{}", image),
+ &None => write!(f, "none"),
+ }
+ }
+ }
+
+ #[deriving(Clone, PartialEq)]
pub enum Length {
Au(Au), // application units
Em(CSSFloat),
@@ -40,6 +97,17 @@ pub mod specified {
// Vmin(CSSFloat),
// Vmax(CSSFloat),
}
+ impl fmt::Show for Length {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ &Length::Au(length) => write!(f, "{}", length),
+ &Length::Em(length) => write!(f, "{}em", length),
+ &Length::Ex(length) => write!(f, "{}ex", length),
+ &Length::Rem(length) => write!(f, "{}rem", length),
+ &Length::ServoCharacterWidth(_) => panic!("internal CSS values should never be serialized"),
+ }
+ }
+ }
const AU_PER_PX: CSSFloat = 60.;
const AU_PER_IN: CSSFloat = AU_PER_PX * 96.;
const AU_PER_CM: CSSFloat = AU_PER_IN / 2.54;
@@ -83,12 +151,19 @@ pub mod specified {
}
}
- #[deriving(Clone, Show)]
+ #[deriving(Clone, PartialEq)]
pub enum LengthOrPercentage {
Length(Length),
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
}
-
+ impl fmt::Show for LengthOrPercentage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &LengthOrPercentage::Length(length) => write!(f, "{}", length),
+ &LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ }
+ }
+ }
impl LengthOrPercentage {
fn parse_internal(input: &ComponentValue, negative_ok: bool)
-> Result<LengthOrPercentage, ()> {
@@ -120,6 +195,15 @@ pub mod specified {
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
Auto,
}
+ impl fmt::Show for LengthOrPercentageOrAuto {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &LengthOrPercentageOrAuto::Length(length) => write!(f, "{}", length),
+ &LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ &LengthOrPercentageOrAuto::Auto => write!(f, "auto"),
+ }
+ }
+ }
impl LengthOrPercentageOrAuto {
fn parse_internal(input: &ComponentValue, negative_ok: bool)
-> Result<LengthOrPercentageOrAuto, ()> {
@@ -151,6 +235,15 @@ pub mod specified {
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
None,
}
+ impl fmt::Show for LengthOrPercentageOrNone {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &LengthOrPercentageOrNone::Length(length) => write!(f, "{}", length),
+ &LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ &LengthOrPercentageOrNone::None => write!(f, "none"),
+ }
+ }
+ }
impl LengthOrPercentageOrNone {
fn parse_internal(input: &ComponentValue, negative_ok: bool)
-> Result<LengthOrPercentageOrNone, ()> {
@@ -219,6 +312,13 @@ pub mod specified {
#[deriving(Clone, PartialEq, PartialOrd)]
pub struct Angle(pub CSSFloat);
+ impl Show for Angle {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
+ let Angle(value) = *self;
+ write!(f, "{}", value)
+ }
+ }
+
impl Angle {
pub fn radians(self) -> f64 {
let Angle(radians) = self;
@@ -247,12 +347,21 @@ pub mod specified {
}
/// Specified values for an image according to CSS-IMAGES.
- #[deriving(Clone)]
+ #[deriving(Clone, PartialEq)]
pub enum Image {
Url(Url),
LinearGradient(LinearGradient),
}
+ impl Show for Image {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
+ match self {
+ &Image::Url(ref url) => write!(f, "url(\"{}\")", url),
+ &Image::LinearGradient(ref grad) => write!(f, "linear-gradient({})", grad),
+ }
+ }
+ }
+
impl Image {
pub fn from_component_value(component_value: &ComponentValue, base_url: &Url)
-> Result<Image,()> {
@@ -287,7 +396,7 @@ pub mod specified {
}
/// Specified values for a CSS linear gradient.
- #[deriving(Clone)]
+ #[deriving(Clone, PartialEq)]
pub struct LinearGradient {
/// The angle or corner of the gradient.
pub angle_or_corner: AngleOrCorner,
@@ -296,6 +405,16 @@ pub mod specified {
pub stops: Vec<ColorStop>,
}
+ impl Show for LinearGradient {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
+ let _ = write!(f, "{}", self.angle_or_corner);
+ for stop in self.stops.iter() {
+ let _ = write!(f, ", {}", stop);
+ }
+ Ok(())
+ }
+ }
+
/// Specified values for an angle or a corner in a linear gradient.
#[deriving(Clone, PartialEq)]
pub enum AngleOrCorner {
@@ -303,8 +422,17 @@ pub mod specified {
Corner(HorizontalDirection, VerticalDirection),
}
+ impl Show for AngleOrCorner {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
+ match self {
+ &AngleOrCorner::Angle(angle) => write!(f, "{}", angle),
+ &AngleOrCorner::Corner(horiz, vert) => write!(f, "to {} {}", horiz, vert),
+ }
+ }
+ }
+
/// Specified values for one color stop in a linear gradient.
- #[deriving(Clone)]
+ #[deriving(Clone, PartialEq)]
pub struct ColorStop {
/// The color of this stop.
pub color: CSSColor,
@@ -314,18 +442,46 @@ pub mod specified {
pub position: Option<LengthOrPercentage>,
}
+ impl Show for ColorStop {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
+ let _ = write!(f, "{}", self.color);
+ self.position.map(|pos| {
+ let _ = write!(f, " {}", pos);
+ });
+ Ok(())
+ }
+ }
+
#[deriving(Clone, PartialEq)]
pub enum HorizontalDirection {
Left,
Right,
}
+ impl Show for HorizontalDirection {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
+ match self {
+ &HorizontalDirection::Left => write!(f, "left"),
+ &HorizontalDirection::Right => write!(f, "right"),
+ }
+ }
+ }
+
#[deriving(Clone, PartialEq)]
pub enum VerticalDirection {
Top,
Bottom,
}
+ impl Show for VerticalDirection {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
+ match self {
+ &VerticalDirection::Top => write!(f, "top"),
+ &VerticalDirection::Bottom => write!(f, "bottom"),
+ }
+ }
+ }
+
fn parse_color_stop(source: ParserIter) -> Result<ColorStop,()> {
let color = match source.next() {
Some(color) => try!(CSSColor::parse(color)),
@@ -463,9 +619,9 @@ pub mod computed {
pub use super::specified::{Angle, AngleOrCorner, HorizontalDirection};
pub use super::specified::{VerticalDirection};
pub use cssparser::Color as CSSColor;
- pub use super::super::longhands::computed_as_specified as compute_CSSColor;
use super::*;
use super::super::longhands;
+ use std::fmt;
use url::Url;
pub struct Context {
@@ -490,6 +646,12 @@ pub mod computed {
#[allow(non_snake_case)]
#[inline]
+ pub fn compute_CSSColor(value: specified::CSSColor, _context: &computed::Context) -> CSSColor {
+ value.parsed
+ }
+
+ #[allow(non_snake_case)]
+ #[inline]
pub fn compute_Au(value: specified::Length, context: &Context) -> Au {
compute_Au_with_font_size(value, context.font_size, context.root_font_size)
}
@@ -518,11 +680,19 @@ pub mod computed {
}
}
- #[deriving(PartialEq, Clone, Show)]
+ #[deriving(PartialEq, Clone)]
pub enum LengthOrPercentage {
Length(Au),
Percentage(CSSFloat),
}
+ impl fmt::Show for LengthOrPercentage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &LengthOrPercentage::Length(length) => write!(f, "{}", length),
+ &LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ }
+ }
+ }
#[allow(non_snake_case)]
pub fn compute_LengthOrPercentage(value: specified::LengthOrPercentage, context: &Context)
@@ -535,12 +705,21 @@ pub mod computed {
}
}
- #[deriving(PartialEq, Clone, Show)]
+ #[deriving(PartialEq, Clone)]
pub enum LengthOrPercentageOrAuto {
Length(Au),
Percentage(CSSFloat),
Auto,
}
+ impl fmt::Show for LengthOrPercentageOrAuto {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &LengthOrPercentageOrAuto::Length(length) => write!(f, "{}", length),
+ &LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ &LengthOrPercentageOrAuto::Auto => write!(f, "auto"),
+ }
+ }
+ }
#[allow(non_snake_case)]
pub fn compute_LengthOrPercentageOrAuto(value: specified::LengthOrPercentageOrAuto,
context: &Context) -> LengthOrPercentageOrAuto {
@@ -554,12 +733,21 @@ pub mod computed {
}
}
- #[deriving(PartialEq, Clone, Show)]
+ #[deriving(PartialEq, Clone)]
pub enum LengthOrPercentageOrNone {
Length(Au),
Percentage(CSSFloat),
None,
}
+ impl fmt::Show for LengthOrPercentageOrNone {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &LengthOrPercentageOrNone::Length(length) => write!(f, "{}", length),
+ &LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
+ &LengthOrPercentageOrNone::None => write!(f, "none"),
+ }
+ }
+ }
#[allow(non_snake_case)]
pub fn compute_LengthOrPercentageOrNone(value: specified::LengthOrPercentageOrNone,
context: &Context) -> LengthOrPercentageOrNone {
@@ -580,6 +768,15 @@ pub mod computed {
LinearGradient(LinearGradient),
}
+ impl fmt::Show for Image {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &Image::Url(ref url) => write!(f, "url(\"{}\")", url),
+ &Image::LinearGradient(ref grad) => write!(f, "linear-gradient({})", grad),
+ }
+ }
+ }
+
/// Computed values for a CSS linear gradient.
#[deriving(Clone, PartialEq)]
pub struct LinearGradient {
@@ -590,6 +787,16 @@ pub mod computed {
pub stops: Vec<ColorStop>,
}
+ impl fmt::Show for LinearGradient {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let _ = write!(f, "{}", self.angle_or_corner);
+ for stop in self.stops.iter() {
+ let _ = write!(f, ", {}", stop);
+ }
+ Ok(())
+ }
+ }
+
/// Computed values for one color stop in a linear gradient.
#[deriving(Clone, PartialEq)]
pub struct ColorStop {
@@ -601,6 +808,16 @@ pub mod computed {
pub position: Option<LengthOrPercentage>,
}
+ impl fmt::Show for ColorStop {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let _ = write!(f, "{}", self.color);
+ self.position.map(|pos| {
+ let _ = write!(f, " {}", pos);
+ });
+ Ok(())
+ }
+ }
+
impl LinearGradient {
pub fn compute(value: specified::LinearGradient, context: &Context) -> LinearGradient {
let specified::LinearGradient {
@@ -611,7 +828,7 @@ pub mod computed {
angle_or_corner: angle_or_corner,
stops: stops.into_iter().map(|stop| {
ColorStop {
- color: stop.color,
+ color: stop.color.parsed,
position: match stop.position {
None => None,
Some(value) => Some(compute_LengthOrPercentage(value, context)),
diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako
index f82c821a8bb..8b7abd23ecf 100644
--- a/components/style/properties/mod.rs.mako
+++ b/components/style/properties/mod.rs.mako
@@ -5,6 +5,8 @@
// This file is a Mako template: http://www.makotemplates.org/
pub use std::ascii::AsciiExt;
+use std::fmt;
+use std::fmt::Show;
use servo_util::logical_geometry::{WritingMode, LogicalMargin};
use sync::Arc;
@@ -159,13 +161,23 @@ pub mod longhands {
<%self:single_component_value name="${name}" experimental="${experimental}">
${caller.body()}
pub mod computed_value {
+ use std::fmt;
#[allow(non_camel_case_types)]
- #[deriving(PartialEq, Clone, FromPrimitive, Show)]
+ #[deriving(PartialEq, Clone, FromPrimitive)]
pub enum T {
% for value in values.split():
${to_rust_ident(value)},
% endfor
}
+ impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ % for value in values.split():
+ &T::${to_rust_ident(value)} => write!(f, "${value}"),
+ % endfor
+ }
+ }
+ }
}
pub type SpecifiedValue = computed_value::T;
#[inline] pub fn get_initial_value() -> computed_value::T {
@@ -455,11 +467,21 @@ pub mod longhands {
pub use super::computed_as_specified as to_computed_value;
pub type SpecifiedValue = computed_value::T;
pub mod computed_value {
+ use std::fmt;
+
#[deriving(PartialEq, Clone)]
pub enum T {
Auto,
Number(i32),
}
+ impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &T::Auto => write!(f, "auto"),
+ &T::Number(number) => write!(f, "{}", number),
+ }
+ }
+ }
impl T {
pub fn number_or_zero(self) -> i32 {
@@ -538,12 +560,23 @@ pub mod longhands {
${switch_to_style_struct("InheritedBox")}
<%self:single_component_value name="line-height">
+ use std::fmt;
#[deriving(Clone)]
pub enum SpecifiedValue {
Normal,
Length(specified::Length),
Number(CSSFloat),
- // percentage are the same as em.
+ Percentage(CSSFloat),
+ }
+ impl fmt::Show for SpecifiedValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &SpecifiedValue::Normal => write!(f, "normal"),
+ &SpecifiedValue::Length(length) => write!(f, "{}", length),
+ &SpecifiedValue::Number(number) => write!(f, "{}", number),
+ &SpecifiedValue::Percentage(number) => write!(f, "{}%", number * 100.),
+ }
+ }
}
/// normal | <number> | <length> | <percentage>
pub fn from_component_value(input: &ComponentValue, _base_url: &Url)
@@ -552,7 +585,7 @@ pub mod longhands {
&ast::Number(ref value) if value.value >= 0. =>
Ok(SpecifiedValue::Number(value.value)),
&ast::Percentage(ref value) if value.value >= 0. =>
- Ok(SpecifiedValue::Length(specified::Length::Em(value.value / 100.))),
+ Ok(SpecifiedValue::Percentage(value.value / 100.)),
&Dimension(ref value, ref unit) if value.value >= 0. =>
specified::Length::parse_dimension(value.value, unit.as_slice())
.map(SpecifiedValue::Length),
@@ -563,12 +596,22 @@ pub mod longhands {
}
pub mod computed_value {
use super::super::{Au, CSSFloat};
+ use std::fmt;
#[deriving(PartialEq, Clone)]
pub enum T {
Normal,
Length(Au),
Number(CSSFloat),
}
+ impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &T::Normal => write!(f, "normal"),
+ &T::Length(length) => write!(f, "{}%", length),
+ &T::Number(number) => write!(f, "{}", number),
+ }
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T { T::Normal }
@@ -579,6 +622,7 @@ pub mod longhands {
SpecifiedValue::Normal => T::Normal,
SpecifiedValue::Length(value) => T::Length(computed::compute_Au(value, context)),
SpecifiedValue::Number(value) => T::Number(value),
+ SpecifiedValue::Percentage(value) => T::Length(computed::compute_Au(specified::Length::Em(value), context)),
}
}
</%self:single_component_value>
@@ -586,6 +630,7 @@ pub mod longhands {
${switch_to_style_struct("Box")}
<%self:single_component_value name="vertical-align">
+ use std::fmt;
<% vertical_align_keywords = (
"baseline sub super top text-top middle bottom text-bottom".split()) %>
#[allow(non_camel_case_types)]
@@ -596,6 +641,16 @@ pub mod longhands {
% endfor
LengthOrPercentage(specified::LengthOrPercentage),
}
+ impl fmt::Show for SpecifiedValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ % for keyword in vertical_align_keywords:
+ &SpecifiedValue::${to_rust_ident(keyword)} => write!(f, "${keyword}"),
+ % endfor
+ &SpecifiedValue::LengthOrPercentage(lop) => write!(f, "{}", lop),
+ }
+ }
+ }
/// baseline | sub | super | top | text-top | middle | bottom | text-bottom
/// | <percentage> | <length>
pub fn from_component_value(input: &ComponentValue, _base_url: &Url)
@@ -615,6 +670,7 @@ pub mod longhands {
}
pub mod computed_value {
use super::super::{Au, CSSFloat};
+ use std::fmt;
#[allow(non_camel_case_types)]
#[deriving(PartialEq, Clone)]
pub enum T {
@@ -624,6 +680,17 @@ pub mod longhands {
Length(Au),
Percentage(CSSFloat),
}
+ impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ % for keyword in vertical_align_keywords:
+ &T::${to_rust_ident(keyword)} => write!(f, "${keyword}"),
+ % endfor
+ &T::Length(length) => write!(f, "{}", length),
+ &T::Percentage(number) => write!(f, "{}%", number),
+ }
+ }
+ }
}
#[inline]
pub fn get_initial_value() -> computed_value::T { T::baseline }
@@ -660,10 +727,18 @@ pub mod longhands {
<%self:longhand name="content">
pub use super::computed_as_specified as to_computed_value;
pub mod computed_value {
+ use std::fmt;
#[deriving(PartialEq, Clone)]
pub enum ContentItem {
StringContent(String),
}
+ impl fmt::Show for ContentItem {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &ContentItem::StringContent(ref s) => write!(f, "\"{}\"", s),
+ }
+ }
+ }
#[allow(non_camel_case_types)]
#[deriving(PartialEq, Clone)]
pub enum T {
@@ -671,6 +746,20 @@ pub mod longhands {
none,
Content(Vec<ContentItem>),
}
+ impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &T::normal => write!(f, "normal"),
+ &T::none => write!(f, "none"),
+ &T::Content(ref content) => {
+ for c in content.iter() {
+ let _ = write!(f, "{}", c);
+ }
+ Ok(())
+ }
+ }
+ }
+ }
}
pub type SpecifiedValue = computed_value::T;
#[inline] pub fn get_initial_value() -> computed_value::T { T::normal }
@@ -748,13 +837,13 @@ pub mod longhands {
<%self:single_component_value name="background-image">
use super::common_types::specified as common_specified;
+ use super::super::common_types::specified::CSSImage as CSSImage;
pub mod computed_value {
use super::super::super::common_types::computed;
- #[deriving(Clone, PartialEq)]
pub type T = Option<computed::Image>;
}
#[deriving(Clone)]
- pub type SpecifiedValue = Option<common_specified::Image>;
+ pub type SpecifiedValue = common_specified::CSSImage;
#[inline]
pub fn get_initial_value() -> computed_value::T {
None
@@ -763,13 +852,13 @@ pub mod longhands {
-> Result<SpecifiedValue, ()> {
match component_value {
&ast::Ident(ref value) if value.as_slice().eq_ignore_ascii_case("none") => {
- Ok(None)
+ Ok(CSSImage(None))
}
_ => {
match common_specified::Image::from_component_value(component_value,
base_url) {
Err(err) => Err(err),
- Ok(result) => Ok(Some(result)),
+ Ok(result) => Ok(CSSImage(Some(result))),
}
}
}
@@ -777,21 +866,29 @@ pub mod longhands {
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
match value {
- None => None,
- Some(image) => Some(image.to_computed_value(context)),
+ CSSImage(None) => None,
+ CSSImage(Some(image)) => Some(image.to_computed_value(context)),
}
}
</%self:single_component_value>
<%self:longhand name="background-position">
+ use std::fmt;
+
pub mod computed_value {
use super::super::super::common_types::computed::LengthOrPercentage;
+ use std::fmt;
#[deriving(PartialEq, Clone)]
pub struct T {
pub horizontal: LengthOrPercentage,
pub vertical: LengthOrPercentage,
}
+ impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} {}", self.horizontal, self.vertical)
+ }
+ }
}
#[deriving(Clone)]
@@ -799,6 +896,11 @@ pub mod longhands {
pub horizontal: specified::LengthOrPercentage,
pub vertical: specified::LengthOrPercentage,
}
+ impl fmt::Show for SpecifiedValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} {}", self.horizontal, self.vertical)
+ }
+ }
impl SpecifiedValue {
fn new(first: specified::PositionComponent, second: specified::PositionComponent)
@@ -902,19 +1004,32 @@ pub mod longhands {
${new_style_struct("Color", is_inherited=True)}
<%self:raw_longhand name="color">
- pub use super::computed_as_specified as to_computed_value;
- pub type SpecifiedValue = RGBA;
+ use super::super::common_types::specified::{CSSColor, CSSRGBA};
+ #[inline]
+ pub fn to_computed_value(value: SpecifiedValue, _context: &computed::Context)
+ -> computed_value::T {
+ value.parsed
+ }
+
+ pub type SpecifiedValue = CSSRGBA;
pub mod computed_value {
- pub type T = super::SpecifiedValue;
+ use cssparser;
+ pub type T = cssparser::RGBA;
}
#[inline] pub fn get_initial_value() -> computed_value::T {
RGBA { red: 0., green: 0., blue: 0., alpha: 1. } /* black */
}
pub fn parse_specified(input: &[ComponentValue], _base_url: &Url)
-> Result<DeclaredValue<SpecifiedValue>, ()> {
- match one_component_value(input).and_then(Color::parse) {
- Ok(Color::RGBA(rgba)) => Ok(DeclaredValue::SpecifiedValue(rgba)),
- Ok(Color::CurrentColor) => Ok(DeclaredValue::Inherit),
+ match one_component_value(input).and_then(CSSColor::parse) {
+ Ok(CSSColor { parsed: Color::RGBA(rgba), authored }) => {
+ let rgba = CSSRGBA {
+ parsed: rgba,
+ authored: authored,
+ };
+ Ok(DeclaredValue::SpecifiedValue(rgba))
+ }
+ Ok(CSSColor { parsed: Color::CurrentColor, .. }) => Ok(DeclaredValue::Inherit),
Err(()) => Err(()),
}
}
@@ -927,6 +1042,7 @@ pub mod longhands {
<%self:longhand name="font-family">
pub use super::computed_as_specified as to_computed_value;
pub mod computed_value {
+ use std::fmt;
#[deriving(PartialEq, Clone)]
pub enum FontFamily {
FamilyName(String),
@@ -944,7 +1060,22 @@ pub mod longhands {
}
}
}
+ impl fmt::Show for FontFamily {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &FontFamily::FamilyName(ref name) => write!(f, "{}", name),
+ }
+ }
+ }
pub type T = Vec<FontFamily>;
+ /*impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ for font in self.iter() {
+ write!(f, "{}", font);
+ }
+ Ok(())
+ }
+ }*/
}
pub type SpecifiedValue = computed_value::T;
@@ -998,6 +1129,7 @@ pub mod longhands {
${single_keyword("font-variant", "normal small-caps")}
<%self:single_component_value name="font-weight">
+ use std::fmt;
#[deriving(Clone)]
pub enum SpecifiedValue {
Bolder,
@@ -1006,6 +1138,17 @@ pub mod longhands {
SpecifiedWeight${weight},
% endfor
}
+ impl fmt::Show for SpecifiedValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &SpecifiedValue::Bolder => write!(f, "bolder"),
+ &SpecifiedValue::Lighter => write!(f, "lighter"),
+ % for weight in range(100, 901, 100):
+ &SpecifiedValue::SpecifiedWeight${weight} => write!(f, "{}", ${weight}i),
+ % endfor
+ }
+ }
+ }
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
pub fn from_component_value(input: &ComponentValue, _base_url: &Url)
-> Result<SpecifiedValue, ()> {
@@ -1035,12 +1178,22 @@ pub mod longhands {
}
}
pub mod computed_value {
+ use std::fmt;
#[deriving(PartialEq, Clone)]
pub enum T {
% for weight in range(100, 901, 100):
Weight${weight},
% endfor
}
+ impl fmt::Show for T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ % for weight in range(100, 901, 100):
+ &T::Weight${weight} => write!(f, "{}", ${weight}i),
+ % endfor
+ }
+ }
+ }
impl T {
pub fn is_bold(self) -> bool {
match self {
@@ -1193,6 +1346,7 @@ pub mod longhands {
<%self:longhand name="text-decoration">
pub use super::computed_as_specified as to_computed_value;
+ use std::fmt;
#[deriving(PartialEq, Clone)]
pub struct SpecifiedValue {
pub underline: bool,
@@ -1201,6 +1355,29 @@ pub mod longhands {
// 'blink' is accepted in the parser but ignored.
// Just not blinking the text is a conforming implementation per CSS 2.1.
}
+ impl fmt::Show for SpecifiedValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut space = false;
+ if self.underline {
+ let _ = write!(f, "underline");
+ space = true;
+ }
+ if self.overline {
+ if space {
+ let _ = write!(f, " ");
+ }
+ let _ = write!(f, "overline");
+ space = true;
+ }
+ if self.line_through {
+ if space {
+ let _ = write!(f, " ");
+ }
+ let _ = write!(f, "line-through");
+ }
+ Ok(())
+ }
+ }
pub mod computed_value {
pub type T = super::SpecifiedValue;
#[allow(non_upper_case_globals)]
@@ -1518,6 +1695,7 @@ pub mod longhands {
<%self:longhand name="box-shadow">
use cssparser;
+ use std::fmt;
pub type SpecifiedValue = Vec<SpecifiedBoxShadow>;
@@ -1531,9 +1709,24 @@ pub mod longhands {
pub inset: bool,
}
+ impl fmt::Show for SpecifiedBoxShadow {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.inset {
+ let _ = write!(f, "inset ");
+ }
+ let _ = write!(f, "{} {} {} {}", self.offset_x, self.offset_y,
+ self.blur_radius, self.spread_radius);
+ if let Some(ref color) = self.color {
+ let _ = write!(f, "{}", color);
+ }
+ Ok(())
+ }
+ }
+
pub mod computed_value {
use super::super::Au;
use super::super::super::computed;
+ use std::fmt;
pub type T = Vec<BoxShadow>;
@@ -1546,6 +1739,17 @@ pub mod longhands {
pub color: computed::CSSColor,
pub inset: bool,
}
+
+ impl fmt::Show for BoxShadow {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.inset {
+ let _ = write!(f, "inset ");
+ }
+ let _ = write!(f, "{} {} {} {} {}", self.offset_x, self.offset_y,
+ self.blur_radius, self.spread_radius, self.color);
+ Ok(())
+ }
+ }
}
#[inline]
@@ -1571,7 +1775,7 @@ pub mod longhands {
offset_y: computed::compute_Au(value.offset_y, context),
blur_radius: computed::compute_Au(value.blur_radius, context),
spread_radius: computed::compute_Au(value.spread_radius, context),
- color: value.color.unwrap_or(cssparser::Color::CurrentColor),
+ color: value.color.map(|color| color.parsed).unwrap_or(cssparser::Color::CurrentColor),
inset: value.inset,
}
}).collect()
@@ -1627,8 +1831,8 @@ pub mod longhands {
// Try to parse a color.
match specified::CSSColor::parse(value) {
- Ok(the_color) if color.is_none() => {
- color = Some(the_color);
+ Ok(ref the_color) if color.is_none() => {
+ color = Some(the_color.clone());
continue
}
Ok(_) => return Err(()),
@@ -1693,9 +1897,9 @@ pub mod shorthands {
// three values set top, (left, right) and bottom
// four values set them in order
let top = iter.next().unwrap_or(None);
- let right = iter.next().unwrap_or(top);
- let bottom = iter.next().unwrap_or(top);
- let left = iter.next().unwrap_or(right);
+ let right = iter.next().unwrap_or(top.clone());
+ let bottom = iter.next().unwrap_or(top.clone());
+ let left = iter.next().unwrap_or(right.clone());
if top.is_some() && right.is_some() && bottom.is_some() && left.is_some()
&& iter.next().is_none() {
Ok(Longhands {
@@ -1896,7 +2100,7 @@ pub mod shorthands {
Longhands {
% for side in ["top", "right", "bottom", "left"]:
% for prop in ["color", "style", "width"]:
- ${"border_%s_%s: %s," % (side, prop, prop)}
+ ${"border_%s_%s: %s.clone()," % (side, prop, prop)}
% endfor
% endfor
}
@@ -2314,6 +2518,16 @@ pub enum DeclaredValue<T> {
// depending on whether the property is inherited.
}
+impl<T: Show> DeclaredValue<T> {
+ pub fn specified_value(&self) -> Option<String> {
+ match self {
+ &DeclaredValue::SpecifiedValue(ref inner) => Some(format!("{}", inner)),
+ &DeclaredValue::Initial => None,
+ &DeclaredValue::Inherit => Some("inherit".to_string()),
+ }
+ }
+}
+
#[deriving(Clone)]
pub enum PropertyDeclaration {
% for property in LONGHANDS:
@@ -2329,8 +2543,43 @@ pub enum PropertyDeclarationParseResult {
ValidOrIgnoredDeclaration,
}
-
impl PropertyDeclaration {
+ pub fn name(&self) -> String {
+ match self {
+ % for property in LONGHANDS:
+ % if property.derived_from is None:
+ &PropertyDeclaration::${property.camel_case}Declaration(..) => "${property.name}".to_string(),
+ % endif
+ % endfor
+ _ => "".to_string(),
+ }
+ }
+
+ pub fn value(&self) -> String {
+ match self {
+ % for property in LONGHANDS:
+ % if property.derived_from is None:
+ &PropertyDeclaration::${property.camel_case}Declaration(ref value) =>
+ value.specified_value()
+ .unwrap_or_else(|| format!("{}", longhands::${property.ident}::get_initial_value())),
+ % endif
+ % endfor
+ decl => panic!("unsupported property declaration: {}", decl.name()),
+ }
+ }
+
+ pub fn matches(&self, name: &str) -> bool {
+ let name_lower = name.as_slice().to_ascii_lower();
+ match (self, name_lower.as_slice()) {
+ % for property in LONGHANDS:
+ % if property.derived_from is None:
+ (&PropertyDeclaration::${property.camel_case}Declaration(..), "${property.name}") => true,
+ % endif
+ % endfor
+ _ => false,
+ }
+ }
+
pub fn parse(name: &str, value: &[ComponentValue],
result_list: &mut Vec<PropertyDeclaration>,
base_url: &Url,
@@ -2425,6 +2674,12 @@ impl PropertyDeclaration {
}
}
+impl Show for PropertyDeclaration {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}: {}", self.name(), self.value())
+ }
+}
+
pub mod style_structs {
use super::longhands;
@@ -2786,7 +3041,11 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock],
}
}
PropertyDeclaration::ColorDeclaration(ref value) => {
- context.color = get_specified!(get_color, color, value);
+ context.color = match *value {
+ DeclaredValue::SpecifiedValue(ref specified_value) => specified_value.parsed,
+ DeclaredValue::Initial => longhands::color::get_initial_value(),
+ DeclaredValue::Inherit => inherited_style.get_color().color.clone(),
+ };
}
PropertyDeclaration::DisplayDeclaration(ref value) => {
context.display = get_specified!(get_box, display, value);
@@ -2964,6 +3223,30 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues {
result
}
+pub fn is_supported_property(property: &str) -> bool {
+ match property {
+ % for property in SHORTHANDS:
+ "${property.name}" => true,
+ % endfor
+ % for property in LONGHANDS:
+ "${property.name}" => true,
+ % endfor
+ _ => false,
+ }
+}
+
+pub fn longhands_from_shorthand(shorthand: &str) -> Option<Vec<String>> {
+ match shorthand {
+ % for property in SHORTHANDS:
+ "${property.name}" => Some(vec!(
+ % for sub in property.sub_properties:
+ "${sub.name}".to_string(),
+ % endfor
+ )),
+ % endfor
+ _ => None,
+ }
+}
// Only re-export the types for computed values.
pub mod computed_values {
diff --git a/tests/content/test_interfaces.html b/tests/content/test_interfaces.html
index 2989fb83957..7949221adb0 100644
--- a/tests/content/test_interfaces.html
+++ b/tests/content/test_interfaces.html
@@ -55,6 +55,7 @@ var interfaceNamesInGlobalScope = [
"Blob",
"CanvasRenderingContext2D",
"CharacterData",
+ "CSSStyleDeclaration",
"DOMRect",
"Comment",
"Console",
@@ -69,7 +70,7 @@ var interfaceNamesInGlobalScope = [
"DOMTokenList",
"DOMStringMap",
"Element",
- "ErrorEvent"
+ "ErrorEvent",
"Event",
"EventTarget",
"File",
diff --git a/tests/html/cssprops.js b/tests/html/cssprops.js
new file mode 100644
index 00000000000..f96bd513709
--- /dev/null
+++ b/tests/html/cssprops.js
@@ -0,0 +1,534 @@
+function run_tests(properties) {
+ for (var property in Object.keys(properties)) {
+ var name = Object.keys(properties)[property];
+ var generator = create_value_generator(properties[name]);
+ var prop = properties[name].property || name;
+ while (run_test(name, generator, prop)) {
+ }
+ }
+}
+
+function generate_inline_style(name, value) {
+ if (value) {
+ return {'declaration': name + ": " + value,
+ 'value': value,
+ 'result': value};
+ }
+ return null;
+}
+
+function all_values(values) {
+ var results = [];
+ for (var i = 0; i < values.length; i++) {
+ var value = values[i];
+ if (typeof value == "function") {
+ var f = value();
+ var result;
+ while ((result = f()) != null) {
+ if (typeof result == "object" && 'serialized' in result) {
+ results.push(result.serialized); //XXXjdm push actual and expect serialized
+ } else {
+ results.push(result);
+ }
+ }
+ } else if (typeof value == "string") {
+ results.push(value);
+ } else if (value instanceof Array) {
+ var subresults = [];
+ for (var j = 0; j < value.length; j++) {
+ var subresult = all_values(value[j], true);
+ if (!(subresult instanceof Array)) {
+ subresult = [subresult];
+ }
+ subresults.push(subresult);
+ }
+ if (subresults.length > 1) {
+ function choose_slices(vecs) {
+ if (vecs.length == 1) {
+ return vecs[0].map(function(v) { return [v]; });
+ }
+ var slice_results = [];
+ var rest = choose_slices(vecs.slice(1, vecs.length));
+ for (var a = 0; a < vecs[0].length; a++) {
+ for (var b = 0; b < rest.length; b++) {
+ slice_results.push([vecs[0][a]].concat(rest[b]));
+ }
+ }
+ return slice_results;
+ }
+
+ subresults = choose_slices(subresults).map(function (a) { return a.join(' ') });
+ }
+ for (var j = 0; j < subresults.length; j++) {
+ results = results.concat(subresults[j]);
+ }
+ } else if (value instanceof Object && 'serialized' in value) {
+ results.push(value.serialized); //XXXjdm push actual and expect serialized
+ } else if (typeof value == "number") {
+ results.push(value.toString());
+ } else {
+ throw "unexpected value type: " + typeof(value);
+ }
+ }
+ return results;
+}
+
+function create_value_generator(property) {
+ var results = all_values(property.values);
+ return iterable(results);
+}
+
+function to_idl(property) {
+ return property.replace(/-\w/g, function(x){return x.toUpperCase()}).split('-').join('');
+}
+
+function run_test(property, generator, prop) {
+ var elem = document.createElement('div');
+ document.getElementById('parent').appendChild(elem);
+ var style = generate_inline_style(property, generator());
+ if (style && to_idl(prop) in elem.style) {
+ elem.setAttribute('style', style.declaration);
+ is(elem.style[to_idl(prop)], style.result, property + ' raw inline style declaration');
+ elem.setAttribute('style', '');
+ elem.style[to_idl(prop)] = style.value;
+ is(elem.style[to_idl(prop)], style.result, property + ' style property');
+ }
+ document.getElementById('parent').removeChild(elem);
+ return style != null;
+}
+
+function iterable(values) {
+ var i = 0;
+ return function() {
+ if (i < values.length) {
+ return values[i++];
+ }
+ return null;
+ }
+}
+
+function color() {
+ var colors = ['black', 'red', 'rgb(50, 75, 100)', 'rgba(5, 7, 10, 0.9)'];
+ return iterable(colors);
+}
+
+function percentage() {
+ var values = ["5%", {actual: ".5%", serialized: "0.5%"}];
+ return iterable(values);
+}
+
+function length() {
+ var values = ["1px", {actual: ".1em", serialized: "0.1em"}];
+ return iterable(values);
+}
+
+function degree() {
+ var values = ["87deg"];
+ return iterable(values);
+}
+
+function uri() {
+ var values = ["url(\"http://localhost/\")",
+ {actual: "url(http://localhost/)",
+ serialized: "url(\"http://localhost/\")"}];
+ return iterable(values);
+}
+
+function border_style() {
+ var values = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge',
+ 'inset', 'outset'];
+ return iterable(values);
+}
+
+function integer() {
+ var values = ['0', '101', '-51'];
+ return iterable(values);
+}
+
+function shape() {
+ var values = ['rect(1em, auto, 0.5px, 2000em)'];
+ return iterable(values);
+}
+
+function string() {
+ var values = ['"string"', {actual: "'string'", serialized: '"string"'}];
+ return iterable(values);
+}
+
+function counter() {
+ var values = ['counter(par-num)', 'counter(par-num, upper-roman)'];
+ return iterable(values);
+}
+
+function attr() {
+ var values = ['attr(foo-bar)', 'attr(foo_bar)'];
+ return iterable(values);
+}
+
+function family_name() {
+ var values = ['Gill,', '"Lucida" Grande,', 'Red/Black,'];
+ return iterable(values);
+}
+
+function generic_family() {
+ var values = ['serif', 'sans-serif'];
+ return iterable(values);
+}
+
+function absolute_size() {
+ var values = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
+ return iterable(values);
+}
+
+function relative_size() {
+ var values = ['larger', 'smaller'];
+ return iterable(values);
+}
+
+function number() {
+ var values = ['0', {'actual': '-0', serialized: '0'}, '1000', '-5123', '0.9', '-0.09'];
+ return iterable(values);
+}
+
+var properties = {
+ 'background-attachment': {
+ 'values': ['scroll', 'fixed', 'inherit'],
+ 'initial': 'scroll',
+ },
+ 'background-color': {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'transparent',
+ },
+ 'background-image': {
+ 'values': [uri, 'none', 'inherit'],
+ 'initial': 'none',
+ },
+ 'background-position': {
+ 'values': [[[percentage, length, 'left', 'center', 'right'],
+ [percentage, length, 'top', 'center', 'bottom']],
+ [['left', 'center', 'right'],
+ ['top', 'center', 'bottom']],
+ 'inherit'],
+ 'initial': '0% 0%',
+ },
+ 'background-repeat': {
+ 'values': ['repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'inherit'],
+ 'initial': 'repeat',
+ },
+ //background
+ 'border-collapse': {
+ 'values': ['collapse', 'separate', 'inherit'],
+ 'initial': 'separate',
+ },
+ //border-color
+ 'border-spacing': {
+ 'values': [length, 'inherit'],
+ 'initial': '0',
+ },
+ //border-style
+ //border-top, border-right, border-bottom, border-left
+ 'border-top-color': {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ },
+ 'border-right-color': {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ },
+ 'border-bottom-color': {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ },
+ 'border-left-color': {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ },
+ 'border-top-style': {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ },
+ 'border-right-style': {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ },
+ 'border-bottom-style': {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ },
+ 'border-left-style': {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ },
+ 'border-top-width': {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ },
+ 'border-right-width': {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ },
+ 'border-bottom-width': {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ },
+ 'border-left-width': {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ },
+ //border-width
+ //border
+ 'bottom': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'caption-side': {
+ 'values': ['top', 'bottom', 'inherit'],
+ 'initial': 'top',
+ },
+ 'clear': {
+ 'values': ['none', 'left', 'right', 'both', 'inherit'],
+ 'initial': 'none',
+ },
+ 'clip': {
+ 'values': [shape, 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'color': {
+ 'values': [color, 'inherit'],
+ 'initial': 'black', //FIXME depends on user agent
+ },
+ 'content': {
+ 'values': ['normal', 'none', string, uri, counter, attr, 'inherit'], //FIXME
+ 'initial': 'normal',
+ },
+ //counter-increment
+ //counter-reset
+ 'cursor': {
+ 'values': [/*uri,*/ 'auto', 'crosshair', 'default', 'pointer', 'move', 'e-resize', 'ne-resize',
+ 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize', 'text',
+ 'wait', 'help', 'progress', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'direction': {
+ 'values': ['ltr', 'rtl', 'inherit'],
+ 'initial': 'ltr',
+ },
+ 'display': {
+ 'values': ['inline', 'block', 'list-item', 'inline-block', 'table', 'inline-table',
+ 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row',
+ 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none',
+ 'inherit'],
+ 'initial': 'inline',
+ },
+ 'empty-cells': {
+ 'values': ['show', 'hide', 'inherit'],
+ 'initial': 'show',
+ },
+ 'float': {
+ 'values': ['left', 'right', 'none', 'inherit'],
+ 'initial': 'none',
+ 'property': 'cssFloat',
+ },
+ 'font-family': {
+ 'values': [[family_name, generic_family], 'inherit'],
+ 'initial': 'sans-serif', //FIXME depends on user agent
+ },
+ 'font-size': {
+ 'values': [absolute_size, relative_size, length, percentage, 'inherit'],
+ 'initial': 'medium',
+ },
+ 'font-style': {
+ 'values': ['normal', 'italic', 'oblique', 'inherit'],
+ 'initial': 'normal',
+ },
+ 'font-variant': {
+ 'values': ['normal', 'small-caps', 'inherit'],
+ 'initial': 'normal',
+ },
+ 'font-weight': {
+ 'values': ['normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 300, 400, 500, 600,
+ 700, 800, 900, 'inherit'],
+ 'initial': 'normal',
+ },
+ //font
+ 'height': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'left': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'letter-spacing': {
+ 'values': ['normal', length, 'inherit'],
+ 'initial': 'normal',
+ },
+ 'line-height': {
+ 'values': ['normal', number, length, percentage, 'inherit'],
+ 'initial': 'normal',
+ },
+ 'list-style-image': {
+ 'values': [uri, 'none', 'inherit'],
+ 'initial': 'none',
+ },
+ 'list-style-position': {
+ 'values': ['inside', 'outside', 'inherit'],
+ 'initial': 'outside',
+ },
+ 'list-style-type': {
+ 'values': ['disc', 'circle', 'square', 'decimal', 'decimal-leading-zero', 'lower-roman',
+ 'upper-roman', 'lower-greek', 'lower-latin', 'upper-latin', 'armenian', 'georgian',
+ 'lower-alpha', 'upper-alpha', 'none', 'inherit'],
+ 'initial': 'disc',
+ },
+ //list-style
+ 'margin-right': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ },
+ 'margin-left': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ },
+ 'margin-top': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ },
+ 'margin-bottom': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ },
+ //margin
+ 'max-height': {
+ 'values': [length, percentage, 'none', 'inherit'],
+ 'initial': 'none',
+ },
+ 'max-width': {
+ 'values': [length, percentage, 'none', 'inherit'],
+ 'initial': 'none',
+ },
+ 'min-height': {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ },
+ 'min-width': {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ },
+ 'orphans': {
+ 'values': [integer, 'inherit'],
+ 'initial': 2,
+ },
+ 'outline-color': {
+ 'values': [color, 'invert', 'inherit'],
+ 'initial': 'invert',
+ },
+ 'outline-style': {
+ 'values': [border_style, 'inherit'],
+ 'initial': 'none',
+ },
+ 'outline-width': {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ },
+ //outline
+ 'overflow': {
+ 'values': ['visible', 'hidden', 'scroll', 'auto', 'inherit'],
+ 'initial': 'visible',
+ },
+ 'padding-top': {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ },
+ 'padding-right': {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ },
+ 'padding-bottom': {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ },
+ 'padding-left': {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ },
+ //padding
+ 'page-break-after': {
+ 'values': ['auto', 'always', 'avoid', 'left', 'right', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'page-break-before': {
+ 'values': ['auto', 'always', 'avoid', 'left', 'right', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'page-break-inside': {
+ 'values': ['avoid', 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'position': {
+ 'values': ['static', 'relative', 'absolute', 'fixed', 'inherit'],
+ 'initial': 'static',
+ },
+ //FIXME quotes
+ 'right': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'table-layout': {
+ 'values': ['auto', 'fixed', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'text-align': {
+ 'values': ['left', 'right', 'center', 'justify', 'inherit'],
+ 'initial': null,
+ },
+ 'text-decoration': {
+ 'values': ['none', 'underline', 'overline', 'line-through', 'blink', 'inherit'],
+ 'initial': 'none',
+ },
+ 'text-indent': {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ },
+ 'text-transform': {
+ 'values': ['capitalize', 'uppercase', 'lowercase', 'none', 'inherit'],
+ 'initial': 'none',
+ },
+ 'top': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'unicode-bidi': {
+ 'values': ['normal', 'embed', 'bidi-override', 'inherit'],
+ 'initial': 'normal',
+ },
+ 'vertical-align': {
+ 'values': ['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom',
+ percentage, length, 'inherit'],
+ 'initial': 'baseline',
+ },
+ 'visibility': {
+ 'values': ['visible', 'hidden', 'collapse', 'inherit'],
+ 'initial': 'visible',
+ },
+ 'white-space': {
+ 'values': ['normal', 'pre', 'nowrap', 'pre-wrap', 'pre-line', 'inherit'],
+ 'initial': 'normal',
+ },
+ 'widows': {
+ 'values': [integer, 'inherit'],
+ 'initial': 2,
+ },
+ 'width': {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ },
+ 'word-spacing': {
+ 'values': ['normal', length, 'inherit'],
+ 'initial': 'normal',
+ },
+ 'z-index': {
+ 'values': ['auto', integer, 'inherit'],
+ 'initial': 'auto',
+ },
+};
diff --git a/tests/html/test_htmlelement_style.html b/tests/html/test_htmlelement_style.html
new file mode 100644
index 00000000000..778043e8494
--- /dev/null
+++ b/tests/html/test_htmlelement_style.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<script src="../content/harness.js"></script>
+<script src="cssprops.js"></script>
+</head>
+<body>
+<div id="parent"></div>
+<script>
+run_tests(properties);
+</script>
+</body>
+</html>
diff --git a/tests/html/test_style.html b/tests/html/test_style.html
new file mode 100644
index 00000000000..edf5012b304
--- /dev/null
+++ b/tests/html/test_style.html
@@ -0,0 +1,12 @@
+<div id="test" style="display: block; background-color: black; background-position: top left; font-style: italic">test text!</div>
+<script>
+var id = document.getElementById('test');
+alert(id.style.display);
+id.style.display = 'none';
+alert(id.style.display);
+alert(id.style.fontStyle + ' ' + id.style['font-style']);
+id.style.background = "black";
+alert(document.getElementById('test').style.background);
+alert(document.getElementById('test').style.backgroundColor);
+alert(document.getElementById('test').style.backgroundPosition);
+</script>