aboutsummaryrefslogtreecommitdiffstats
path: root/components
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 /components
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.
Diffstat (limited to 'components')
-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
12 files changed, 1145 insertions, 46 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 {