diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs new file mode 100644 index 000000000000..99cb65c4807d --- /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, +} + +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) -> 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) -> CSSStyleDeclaration { + CSSStyleDeclaration { + reflector_: Reflector::new(), + owner: JS::from_rooted(owner), + } + } + + pub fn new(global: JSRef, owner: JSRef) -> Temporary { + reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner), + global::Window(global), + CSSStyleDeclarationBinding::Wrap) + } +} + +trait PrivateCSSStyleDeclarationHelpers { + fn get_declaration(self, property: &Atom) -> Option; +} + +impl<'a> PrivateCSSStyleDeclarationHelpers for JSRef<'a, CSSStyleDeclaration> { + fn get_declaration(self, property: &Atom) -> Option { + let owner = self.owner.root(); + let element: JSRef = 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 = 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 = 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 = 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 = 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 = 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 da1ff1d408f3..00f481dcd324 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>; fn summarize(self) -> Vec; 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; } 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 { + 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 f8a8ea7ce5cd..8fb0f2c4be53 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, } 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, document: JSRef) -> 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 { + 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 000000000000..8ef419df6a54 --- /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 000000000000..bf7a7b92b9e4 --- /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 359ef11f0a77..39c7699900d2 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 26f773f6006c..b62b137a38e8 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 6675fefd07be..8f7bbda8385f 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 357609cc5346..e668b1203ad7 100644 --- a/components/style/legacy.rs +++ b/components/style/legacy.rs @@ -6,6 +6,7 @@ //! ``, 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 6d2567920284..fec6c2d5b611 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 69b3576f4103..9dcd65d6fc5a 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, + } + impl CSSColor { + pub fn parse(component_value: &ComponentValue) -> Result { + 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, + } + 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); + 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 { @@ -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 { @@ -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 { @@ -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 { @@ -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, } + 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, } + 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 { 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 { @@ -488,6 +644,12 @@ pub mod computed { // TODO, as needed: viewport size, etc. } + #[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 { @@ -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, } + 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, } + 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 f82c821a8bb3..8b7abd23ecfa 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 | | | 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)), } } @@ -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 /// | | 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), } + 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; } #[deriving(Clone)] - pub type SpecifiedValue = Option; + pub type SpecifiedValue = common_specified::CSSImage; #[inline] pub fn get_initial_value() -> computed_value::T { None @@ -763,13 +852,13 @@ pub mod longhands { -> Result { 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: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, ()> { - 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; + /*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 { @@ -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; @@ -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; @@ -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 { // depending on whether the property is inherited. } +impl DeclaredValue { + pub fn specified_value(&self) -> Option { + 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, 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> { + 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 2989fb839575..7949221adb0a 100644 --- a/tests/content/test_interfaces.html +++ b/tests/content/test_interfaces.html @@ -55,6 +55,7 @@ "Blob", "CanvasRenderingContext2D", "CharacterData", + "CSSStyleDeclaration", "DOMRect", "Comment", "Console", @@ -69,7 +70,7 @@ "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 000000000000..f96bd5137091 --- /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 000000000000..778043e8494a --- /dev/null +++ b/tests/html/test_htmlelement_style.html @@ -0,0 +1,12 @@ + + + + + + +
+ + + diff --git a/tests/html/test_style.html b/tests/html/test_style.html new file mode 100644 index 000000000000..edf5012b3042 --- /dev/null +++ b/tests/html/test_style.html @@ -0,0 +1,12 @@ +
test text!
+