diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-12-10 01:16:26 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-10 01:16:26 -0800 |
commit | 6dd4b4822fa788694153ee61a04dd9a5dfb748ec (patch) | |
tree | 7067984403c44dc3b0d1f95a6cf1616e8a4e8965 | |
parent | b9a8ccd775c3192e3810a1730b1d0bc2b5c9dfb6 (diff) | |
parent | 9856c617dfa1487ada2868b5b0520d86817e83fc (diff) | |
download | servo-6dd4b4822fa788694153ee61a04dd9a5dfb748ec.tar.gz servo-6dd4b4822fa788694153ee61a04dd9a5dfb748ec.zip |
Auto merge of #14535 - servo:property-id, r=mbrubeck
Introduce a PropertyId enum and use it instead of strings of property names
<!-- Please describe your changes on the following line: -->
* `LonghandId` and `ShorthandId` are C-like enums
* `Atom` is used for the name of custom properties.
* `PropertyDeclarationId` is the identifier for `PropertyDeclaration`,
after parsing and shorthand expansion. (Longhand or custom property.)
* `PropertyId` represents any CSS property, e.g. in CSSOM.
(Longhand, shorthand, or custom.)
CC @upsuper
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).
<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because _____
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14535)
<!-- Reviewable:end -->
27 files changed, 602 insertions, 460 deletions
diff --git a/Cargo.lock b/Cargo.lock index 679a7b7fe2d..8a79b8eda44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2610,6 +2610,8 @@ dependencies = [ "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "quickersort 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2636,6 +2638,7 @@ dependencies = [ "cssparser 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index 2ac84aa2f4e..1ddf07db553 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -32,21 +32,6 @@ number dir -bottom -top -left -right -width -height -margin-bottom -margin-top -margin-left -margin-right -padding-bottom -padding-top -padding-left -padding-right - DOMContentLoaded select input diff --git a/components/layout/query.rs b/components/layout/query.rs index 765cd2bdab4..81d0ce5e320 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -24,7 +24,6 @@ use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElemen use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; use sequential; -use servo_atoms::Atom; use std::cmp::{min, max}; use std::ops::Deref; use std::sync::{Arc, Mutex}; @@ -32,8 +31,8 @@ use style::computed_values; use style::context::StyleContext; use style::dom::TElement; use style::logical_geometry::{WritingMode, BlockFlowDirection, InlineBaseDirection}; +use style::properties::{style_structs, PropertyId, PropertyDeclarationId, LonghandId}; use style::properties::longhands::{display, position}; -use style::properties::style_structs; use style::selector_parser::PseudoElement; use style::stylist::Stylist; use style_traits::ToCss; @@ -75,7 +74,7 @@ pub struct LayoutThreadData { pub scroll_area_response: Rect<i32>, /// A queued response for the resolved style property of an element. - pub resolved_style_response: Option<String>, + pub resolved_style_response: String, /// A queued response for the offset parent/rect of a node. pub offset_parent_response: OffsetParentResponse, @@ -628,8 +627,8 @@ pub fn process_node_scroll_area_request< N: LayoutNode>(requested_node: N, layou pub fn process_resolved_style_request<'a, N, C>(requested_node: N, style_context: &'a C, pseudo: &Option<PseudoElement>, - property: &Atom, - layout_root: &mut Flow) -> Option<String> + property: &PropertyId, + layout_root: &mut Flow) -> String where N: LayoutNode, C: StyleContext<'a> { @@ -667,13 +666,25 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, // The pseudo doesn't exist, return nothing. Chrome seems to query // the element itself in this case, Firefox uses the resolved value. // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29006 - return None; + return String::new(); } Some(layout_el) => layout_el }; let style = &*layout_el.resolved_style(); + let longhand_id = match *property { + PropertyId::Longhand(id) => id, + + // Firefox returns blank strings for the computed value of shorthands, + // so this should be web-compatible. + PropertyId::Shorthand(_) => return String::new(), + + PropertyId::Custom(ref name) => { + return style.computed_value_to_string(PropertyDeclarationId::Custom(name)) + } + }; + // Clear any temporarily-resolved data to maintain our invariants. See the comment // at the top of this function. display_none_root.map(|r| clear_descendant_data(r, &|e| e.as_node().clear_data())); @@ -697,7 +708,7 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, layout_el: <N::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteThreadSafeLayoutElement, layout_root: &mut Flow, requested_node: N, - property: &Atom) -> Option<String> { + longhand_id: LonghandId) -> String { let maybe_data = layout_el.borrow_layout_data(); let position = maybe_data.map_or(Point2D::zero(), |data| { match (*data).flow_construction_result { @@ -708,13 +719,13 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, _ => Point2D::zero() } }); - let property = match *property { - atom!("bottom") => PositionProperty::Bottom, - atom!("top") => PositionProperty::Top, - atom!("left") => PositionProperty::Left, - atom!("right") => PositionProperty::Right, - atom!("width") => PositionProperty::Width, - atom!("height") => PositionProperty::Height, + let property = match longhand_id { + LonghandId::Bottom => PositionProperty::Bottom, + LonghandId::Top => PositionProperty::Top, + LonghandId::Left => PositionProperty::Left, + LonghandId::Right => PositionProperty::Right, + LonghandId::Width => PositionProperty::Width, + LonghandId::Height => PositionProperty::Height, _ => unreachable!() }; let mut iterator = @@ -723,27 +734,25 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, position); sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); - iterator.result.map(|r| r.to_css_string()) + iterator.result.map(|r| r.to_css_string()).unwrap_or(String::new()) } // TODO: we will return neither the computed nor used value for margin and padding. - // Firefox returns blank strings for the computed value of shorthands, - // so this should be web-compatible. - match *property { - atom!("margin-bottom") | atom!("margin-top") | - atom!("margin-left") | atom!("margin-right") | - atom!("padding-bottom") | atom!("padding-top") | - atom!("padding-left") | atom!("padding-right") + match longhand_id { + LonghandId::MarginBottom | LonghandId::MarginTop | + LonghandId::MarginLeft | LonghandId::MarginRight | + LonghandId::PaddingBottom | LonghandId::PaddingTop | + LonghandId::PaddingLeft | LonghandId::PaddingRight if applies && style.get_box().display != display::computed_value::T::none => { - let (margin_padding, side) = match *property { - atom!("margin-bottom") => (MarginPadding::Margin, Side::Bottom), - atom!("margin-top") => (MarginPadding::Margin, Side::Top), - atom!("margin-left") => (MarginPadding::Margin, Side::Left), - atom!("margin-right") => (MarginPadding::Margin, Side::Right), - atom!("padding-bottom") => (MarginPadding::Padding, Side::Bottom), - atom!("padding-top") => (MarginPadding::Padding, Side::Top), - atom!("padding-left") => (MarginPadding::Padding, Side::Left), - atom!("padding-right") => (MarginPadding::Padding, Side::Right), + let (margin_padding, side) = match longhand_id { + LonghandId::MarginBottom => (MarginPadding::Margin, Side::Bottom), + LonghandId::MarginTop => (MarginPadding::Margin, Side::Top), + LonghandId::MarginLeft => (MarginPadding::Margin, Side::Left), + LonghandId::MarginRight => (MarginPadding::Margin, Side::Right), + LonghandId::PaddingBottom => (MarginPadding::Padding, Side::Bottom), + LonghandId::PaddingTop => (MarginPadding::Padding, Side::Top), + LonghandId::PaddingLeft => (MarginPadding::Padding, Side::Left), + LonghandId::PaddingRight => (MarginPadding::Padding, Side::Right), _ => unreachable!() }; let mut iterator = @@ -753,23 +762,22 @@ pub fn process_resolved_style_request<'a, N, C>(requested_node: N, style.writing_mode); sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator); - iterator.result.map(|r| r.to_css_string()) + iterator.result.map(|r| r.to_css_string()).unwrap_or(String::new()) }, - atom!("bottom") | atom!("top") | atom!("right") | - atom!("left") + LonghandId::Bottom | LonghandId::Top | LonghandId::Right | LonghandId::Left if applies && positioned && style.get_box().display != display::computed_value::T::none => { - used_value_for_position_property(layout_el, layout_root, requested_node, property) + used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id) } - atom!("width") | atom!("height") + LonghandId::Width | LonghandId::Height if applies && style.get_box().display != display::computed_value::T::none => { - used_value_for_position_property(layout_el, layout_root, requested_node, property) + used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id) } // FIXME: implement used value computation for line-height - ref property => { - style.computed_value_to_string(&*property).ok() + _ => { + style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)) } } } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 934a2e3a092..9994eb54bdc 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -466,7 +466,7 @@ impl LayoutThread { scroll_root_id_response: None, scroll_area_response: Rect::zero(), overflow_response: NodeOverflowResponse(None), - resolved_style_response: None, + resolved_style_response: String::new(), offset_parent_response: OffsetParentResponse::empty(), margin_style_response: MarginStyleResponse::empty(), stacking_context_scroll_offsets: HashMap::new(), @@ -1018,7 +1018,7 @@ impl LayoutThread { rw_data.scroll_root_id_response = None; }, ReflowQueryType::ResolvedStyleQuery(_, _, _) => { - rw_data.resolved_style_response = None; + rw_data.resolved_style_response = String::new(); }, ReflowQueryType::OffsetParentQuery(_) => { rw_data.offset_parent_response = OffsetParentResponse::empty(); diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index d7984939e28..896c2af5b24 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -286,6 +286,12 @@ impl Into<Vec<u8>> for DOMString { } } +impl<'a> Into<Cow<'a, str>> for DOMString { + fn into(self) -> Cow<'a, str> { + self.0.into() + } +} + impl Extend<char> for DOMString { fn extend<I>(&mut self, iterable: I) where I: IntoIterator<Item=char> { self.0.extend(iterable) diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index c6280597b17..36d85ec0322 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -12,12 +12,11 @@ use dom::element::Element; use dom::node::{Node, NodeDamage, window_from_node}; use dom::window::Window; use parking_lot::RwLock; -use servo_atoms::Atom; use std::ascii::AsciiExt; use std::sync::Arc; use style::parser::ParserContextExtraData; -use style::properties::{Shorthand, Importance, PropertyDeclarationBlock}; -use style::properties::{is_supported_property, parse_one_declaration, parse_style_attribute}; +use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId}; +use style::properties::{parse_one_declaration, parse_style_attribute}; use style::selector_parser::PseudoElement; use style_traits::ToCss; @@ -37,13 +36,13 @@ pub enum CSSModificationAccess { } macro_rules! css_properties( - ( $([$getter:ident, $setter:ident, $cssprop:expr]),* ) => ( + ( $([$getter:ident, $setter:ident, $id:expr],)* ) => ( $( fn $getter(&self) -> DOMString { - self.GetPropertyValue(DOMString::from($cssprop)) + self.get_property_value($id) } fn $setter(&self, value: DOMString) -> ErrorResult { - self.SetPropertyValue(DOMString::from($cssprop), value) + self.set_property($id, value, DOMString::new()) } )* ); @@ -74,41 +73,21 @@ impl CSSStyleDeclaration { CSSStyleDeclarationBinding::Wrap) } - fn get_computed_style(&self, property: &Atom) -> Option<DOMString> { + fn get_computed_style(&self, property: PropertyId) -> DOMString { let node = self.owner.upcast::<Node>(); if !node.is_in_doc() { // TODO: Node should be matched against the style rules of this window. // Firefox is currently the only browser to implement this. - return None; + return DOMString::new(); } let addr = node.to_trusted_node_address(); window_from_node(&*self.owner).resolved_style_query(addr, self.pseudo.clone(), property) } -} - -impl CSSStyleDeclarationMethods for CSSStyleDeclaration { - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length - fn Length(&self) -> u32 { - let elem = self.owner.upcast::<Element>(); - let len = match *elem.style_attribute().borrow() { - Some(ref lock) => lock.read().declarations.len(), - None => 0, - }; - len as u32 - } - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item - fn Item(&self, index: u32) -> DOMString { - self.IndexedGetter(index).unwrap_or_default() - } - - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue - fn GetPropertyValue(&self, mut property: DOMString) -> DOMString { + fn get_property_value(&self, id: PropertyId) -> DOMString { if self.readonly { // Readonly style declarations are used for getComputedStyle. - property.make_ascii_lowercase(); - let property = Atom::from(property); - return self.get_computed_style(&property).unwrap_or(DOMString::new()); + return self.get_computed_style(id); } let style_attribute = self.owner.style_attribute().borrow(); @@ -120,44 +99,16 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { }; let mut string = String::new(); - style_attribute.property_value_to_css(&property, &mut string).unwrap(); + style_attribute.property_value_to_css(&id, &mut string).unwrap(); DOMString::from(string) } - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority - fn GetPropertyPriority(&self, property: DOMString) -> DOMString { - let style_attribute = self.owner.style_attribute().borrow(); - let style_attribute = if let Some(ref lock) = *style_attribute { - lock.read() - } else { - // No style attribute is like an empty style attribute: no matching declaration. - return DOMString::new() - }; - - if style_attribute.property_priority(&property).important() { - DOMString::from("important") - } else { - // Step 4 - DOMString::new() - } - } - - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty - fn SetProperty(&self, - property: DOMString, - value: DOMString, - priority: DOMString) - -> ErrorResult { + fn set_property(&self, id: PropertyId, value: DOMString, priority: DOMString) -> ErrorResult { // Step 1 if self.readonly { return Err(Error::NoModificationAllowed); } - // Step 3 - if !is_supported_property(&property) { - return Ok(()); - } - let mut style_attribute = self.owner.style_attribute().borrow_mut(); if value.is_empty() { @@ -171,7 +122,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { return Ok(()) }; - style_attribute.remove_property(&property); + style_attribute.remove_property(&id); empty = style_attribute.declarations.is_empty() } if empty { @@ -188,7 +139,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // Step 6 let window = window_from_node(&*self.owner); let declarations = - parse_one_declaration(&property, &value, &window.get_url(), window.css_error_reporter(), + parse_one_declaration(id, &value, &window.get_url(), window.css_error_reporter(), ParserContextExtraData::default()); // Step 7 @@ -228,6 +179,75 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { node.dirty(NodeDamage::NodeStyleDamaged); Ok(()) } +} + +impl CSSStyleDeclarationMethods for CSSStyleDeclaration { + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length + fn Length(&self) -> u32 { + let elem = self.owner.upcast::<Element>(); + let len = match *elem.style_attribute().borrow() { + Some(ref lock) => lock.read().declarations.len(), + None => 0, + }; + len as u32 + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item + fn Item(&self, index: u32) -> DOMString { + self.IndexedGetter(index).unwrap_or_default() + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue + fn GetPropertyValue(&self, property: DOMString) -> DOMString { + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property + return DOMString::new() + }; + self.get_property_value(id) + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority + fn GetPropertyPriority(&self, property: DOMString) -> DOMString { + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property + return DOMString::new() + }; + + let style_attribute = self.owner.style_attribute().borrow(); + let style_attribute = if let Some(ref lock) = *style_attribute { + lock.read() + } else { + // No style attribute is like an empty style attribute: no matching declaration. + return DOMString::new() + }; + + if style_attribute.property_priority(&id).important() { + DOMString::from("important") + } else { + // Step 4 + DOMString::new() + } + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty + fn SetProperty(&self, + property: DOMString, + value: DOMString, + priority: DOMString) + -> ErrorResult { + // Step 3 + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property + return Ok(()) + }; + self.set_property(id, value, priority) + } // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult { @@ -237,9 +257,12 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { } // Step 2 & 3 - if !is_supported_property(&property) { - return Ok(()); - } + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property + return Ok(()) + }; // Step 4 let importance = match &*priority { @@ -253,10 +276,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { let mut style_attribute = lock.write(); // Step 5 & 6 - match Shorthand::from_name(&property) { - Some(shorthand) => style_attribute.set_importance(shorthand.longhands(), importance), - None => style_attribute.set_importance(&[&*property], importance), - } + style_attribute.set_importance(&id, importance); self.owner.set_style_attr(style_attribute.to_css_string()); let node = self.owner.upcast::<Node>(); @@ -277,6 +297,13 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { return Err(Error::NoModificationAllowed); } + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property, cannot be there to remove. + return Ok(DOMString::new()) + }; + let mut style_attribute = self.owner.style_attribute().borrow_mut(); let mut string = String::new(); let empty; @@ -289,10 +316,10 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { }; // Step 3 - style_attribute.property_value_to_css(&property, &mut string).unwrap(); + style_attribute.property_value_to_css(&id, &mut string).unwrap(); // Step 4 & 5 - style_attribute.remove_property(&property); + style_attribute.remove_property(&id); self.owner.set_style_attr(style_attribute.to_css_string()); empty = style_attribute.declarations.is_empty() } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 282979dc7bf..db6490b41fd 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -92,6 +92,7 @@ use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use style::context::ReflowGoal; use style::error_reporting::ParseErrorReporter; use style::media_queries; +use style::properties::PropertyId; use style::properties::longhands::overflow_x; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; @@ -1295,16 +1296,16 @@ impl Window { } pub fn resolved_style_query(&self, - element: TrustedNodeAddress, - pseudo: Option<PseudoElement>, - property: &Atom) -> Option<DOMString> { + element: TrustedNodeAddress, + pseudo: Option<PseudoElement>, + property: PropertyId) -> DOMString { if !self.reflow(ReflowGoal::ForScriptQuery, - ReflowQueryType::ResolvedStyleQuery(element, pseudo, property.clone()), + ReflowQueryType::ResolvedStyleQuery(element, pseudo, property), ReflowReason::Query) { - return None; + return DOMString::new(); } let ResolvedStyleResponse(resolved) = self.layout_rpc.resolved_style(); - resolved.map(DOMString::from) + DOMString::from(resolved) } pub fn offset_parent_query(&self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>) { diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index 581adbb45bc..3d0e00c46cc 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -14,11 +14,11 @@ use profile_traits::mem::ReportsChan; use rpc::LayoutRPC; use script_traits::{ConstellationControlMsg, LayoutControlMsg}; use script_traits::{LayoutMsg as ConstellationMsg, StackingContextScrollState, WindowSizeData}; -use servo_atoms::Atom; use servo_url::ServoUrl; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender}; use style::context::ReflowGoal; +use style::properties::PropertyId; use style::selector_parser::PseudoElement; use style::stylesheets::Stylesheet; @@ -97,7 +97,7 @@ pub enum ReflowQueryType { NodeScrollRootIdQuery(TrustedNodeAddress), NodeGeometryQuery(TrustedNodeAddress), NodeScrollGeometryQuery(TrustedNodeAddress), - ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom), + ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, PropertyId), OffsetParentQuery(TrustedNodeAddress), MarginStyleQuery(TrustedNodeAddress), } diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs index 6e19b3f3bcf..7bc58dbf334 100644 --- a/components/script_layout_interface/rpc.rs +++ b/components/script_layout_interface/rpc.rs @@ -57,7 +57,7 @@ pub struct HitTestResponse { pub node_address: Option<UntrustedNodeAddress>, } -pub struct ResolvedStyleResponse(pub Option<String>); +pub struct ResolvedStyleResponse(pub String); #[derive(Clone)] pub struct OffsetParentResponse { diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 5a1b854ca4d..95778b61c88 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -40,6 +40,7 @@ num-traits = "0.1.32" ordered-float = "0.2.2" owning_ref = "0.2.2" parking_lot = "0.3.3" +phf = "0.7.20" quickersort = "2.0.0" rand = "0.3" rayon = "0.5" @@ -64,4 +65,5 @@ version = "1.0" kernel32-sys = "0.2" [build-dependencies] +phf_codegen = "0.7.20" walkdir = "0.1" diff --git a/components/style/build.rs b/components/style/build.rs index b8d46348b94..8d68fe45ce6 100644 --- a/components/style/build.rs +++ b/components/style/build.rs @@ -3,8 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ extern crate walkdir; +extern crate phf_codegen; use std::env; +use std::fs::File; +use std::io::{BufWriter, BufReader, BufRead, Write}; use std::path::Path; use std::process::{Command, exit}; use walkdir::WalkDir; @@ -61,4 +64,21 @@ fn main() { if !status.success() { exit(1) } + + let path = Path::new(&env::var("OUT_DIR").unwrap()).join("static_ids.rs"); + let static_ids = Path::new(&env::var("OUT_DIR").unwrap()).join("static_ids.txt"); + let mut file = BufWriter::new(File::create(&path).unwrap()); + let static_ids = BufReader::new(File::open(&static_ids).unwrap()); + + write!(&mut file, "static STATIC_IDS: ::phf::Map<&'static str, StaticId> = ").unwrap(); + let mut map = phf_codegen::Map::new(); + for result in static_ids.lines() { + let line = result.unwrap(); + let mut split = line.split('\t'); + let key = split.next().unwrap().to_owned(); + let value = split.next().unwrap(); + map.entry(key, value); + } + map.build(&mut file).unwrap(); + write!(&mut file, ";\n").unwrap(); } diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs index a8798250699..5165b21f5d4 100644 --- a/components/style/gecko_string_cache/mod.rs +++ b/components/style/gecko_string_cache/mod.rs @@ -9,10 +9,9 @@ use gecko_bindings::bindings::Gecko_Atomize; use gecko_bindings::bindings::Gecko_ReleaseAtom; use gecko_bindings::structs::nsIAtom; use heapsize::HeapSizeOf; -use std::ascii::AsciiExt; use std::borrow::{Cow, Borrow}; use std::char::{self, DecodeUtf16}; -use std::fmt; +use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; use std::iter::Cloned; use std::mem; @@ -112,14 +111,6 @@ impl WeakAtom { } #[inline] - pub fn eq_str_ignore_ascii_case(&self, s: &str) -> bool { - self.chars().map(|r| match r { - Ok(c) => c.to_ascii_lowercase() as u32, - Err(e) => e.unpaired_surrogate() as u32, - }).eq(s.chars().map(|c| c.to_ascii_lowercase() as u32)) - } - - #[inline] pub fn to_string(&self) -> String { String::from_utf16(self.as_slice()).unwrap() } @@ -154,7 +145,7 @@ impl fmt::Debug for WeakAtom { impl fmt::Display for WeakAtom { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { for c in self.chars() { - try!(write!(w, "{}", c.unwrap_or(char::REPLACEMENT_CHARACTER))) + try!(w.write_char(c.unwrap_or(char::REPLACEMENT_CHARACTER))) } Ok(()) } diff --git a/components/style/keyframes.rs b/components/style/keyframes.rs index 474e4c8b539..f458ae45f8e 100644 --- a/components/style/keyframes.rs +++ b/components/style/keyframes.rs @@ -6,7 +6,7 @@ use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser}; use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule}; use parking_lot::RwLock; use parser::{ParserContext, ParserContextExtraData, log_css_error}; -use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; +use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId}; use properties::PropertyDeclarationParseResult; use properties::animated_properties::TransitionProperty; use std::fmt; @@ -343,8 +343,9 @@ impl<'a, 'b> DeclarationParser for KeyframeDeclarationParser<'a, 'b> { type Declaration = Vec<PropertyDeclaration>; fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<Vec<PropertyDeclaration>, ()> { + let id = try!(PropertyId::parse(name.into())); let mut results = Vec::new(); - match PropertyDeclaration::parse(name, self.context, input, &mut results, true) { + match PropertyDeclaration::parse(id, self.context, input, &mut results, true) { PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {} _ => return Err(()) } diff --git a/components/style/lib.rs b/components/style/lib.rs index 0955a651105..50725a52b00 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -38,7 +38,7 @@ //#![deny(unsafe_code)] #![allow(unused_unsafe)] -#![recursion_limit = "500"] // For match_ignore_ascii_case in PropertyDeclaration::parse +#![recursion_limit = "500"] // For define_css_keyword_enum! in -moz-appearance extern crate app_units; #[allow(unused_extern_crates)] @@ -71,6 +71,7 @@ extern crate num_traits; extern crate ordered_float; extern crate owning_ref; extern crate parking_lot; +extern crate phf; extern crate quickersort; extern crate rayon; extern crate rustc_serialize; diff --git a/components/style/properties/build.py b/components/style/properties/build.py index 4e61f456541..c20cac95cb4 100644 --- a/components/style/properties/build.py +++ b/components/style/properties/build.py @@ -33,6 +33,7 @@ def main(): rust = render(template, product=product, data=properties, __file__=template) if output == "style-crate": write(os.environ["OUT_DIR"], "properties.rs", rust) + write(os.environ["OUT_DIR"], "static_ids.txt", static_ids(properties)) if product == "gecko": template = os.path.join(BASE, "gecko.mako.rs") rust = render(template, data=properties) @@ -69,6 +70,15 @@ def write(directory, filename, content): open(os.path.join(directory, filename), "wb").write(content) +def static_ids(properties): + return '\n'.join( + "%s\tStaticId::%s(%sId::%s)" % (p.name, kind, kind, p.camel_case) + for kind, props in [("Longhand", properties.longhands), + ("Shorthand", properties.shorthands)] + for p in props + ) + + def write_html(properties): properties = dict( (p.name, { diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index adabb526797..de2d274280b 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -7,7 +7,6 @@ use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter}; use error_reporting::ParseErrorReporter; use parser::{ParserContext, ParserContextExtraData, log_css_error}; use servo_url::ServoUrl; -use std::ascii::AsciiExt; use std::boxed::Box as StdBox; use std::fmt; use style_traits::ToCss; @@ -65,90 +64,94 @@ impl PropertyDeclarationBlock { self.declarations.len() > self.important_count as usize } - pub fn get(&self, property_name: &str) -> Option< &(PropertyDeclaration, Importance)> { - self.declarations.iter().find(|&&(ref decl, _)| decl.matches(property_name)) + pub fn get(&self, property: PropertyDeclarationId) -> Option< &(PropertyDeclaration, Importance)> { + self.declarations.iter().find(|&&(ref decl, _)| decl.id() == property) } /// Find the value of the given property in this block and serialize it /// /// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue - pub fn property_value_to_css<W>(&self, property_name: &str, dest: &mut W) -> fmt::Result + pub fn property_value_to_css<W>(&self, property: &PropertyId, dest: &mut W) -> fmt::Result where W: fmt::Write { - // Step 1 - let property = property_name.to_ascii_lowercase(); + // Step 1: done when parsing a string to PropertyId // Step 2 - if let Some(shorthand) = Shorthand::from_name(&property) { - // Step 2.1 - let mut list = Vec::new(); - let mut important_count = 0; - - // Step 2.2 - for longhand in shorthand.longhands() { - // Step 2.2.1 - let declaration = self.get(longhand); - - // Step 2.2.2 & 2.2.3 - match declaration { - Some(&(ref declaration, importance)) => { - list.push(declaration); - if importance.important() { - important_count += 1; - } - }, - None => return Ok(()), + match property.as_shorthand() { + Ok(shorthand) => { + // Step 2.1 + let mut list = Vec::new(); + let mut important_count = 0; + + // Step 2.2 + for &longhand in shorthand.longhands() { + // Step 2.2.1 + let declaration = self.get(PropertyDeclarationId::Longhand(longhand)); + + // Step 2.2.2 & 2.2.3 + match declaration { + Some(&(ref declaration, importance)) => { + list.push(declaration); + if importance.important() { + important_count += 1; + } + }, + None => return Ok(()), + } } - } - // Step 3.3.2.4 - // If there is one or more longhand with important, and one or more - // without important, we don't serialize it as a shorthand. - if important_count > 0 && important_count != list.len() { - return Ok(()); - } - - // Step 2.3 - // We don't print !important when serializing individual properties, - // so we treat this as a normal-importance property - let importance = Importance::Normal; - let appendable_value = shorthand.get_shorthand_appendable_value(list).unwrap(); - return append_declaration_value(dest, appendable_value, importance) - } + // Step 3.3.2.4 + // If there is one or more longhand with important, and one or more + // without important, we don't serialize it as a shorthand. + if important_count > 0 && important_count != list.len() { + return Ok(()); + } - if let Some(&(ref value, _importance)) = self.get(property_name) { - // Step 3 - value.to_css(dest) - } else { - // Step 4 - Ok(()) + // Step 2.3 + // We don't print !important when serializing individual properties, + // so we treat this as a normal-importance property + let importance = Importance::Normal; + let appendable_value = shorthand.get_shorthand_appendable_value(list).unwrap(); + append_declaration_value(dest, appendable_value, importance) + } + Err(longhand_or_custom) => { + if let Some(&(ref value, _importance)) = self.get(longhand_or_custom) { + // Step 3 + value.to_css(dest) + } else { + // Step 4 + Ok(()) + } + } } } /// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority - pub fn property_priority(&self, property_name: &str) -> Importance { - // Step 1 - let property = property_name.to_ascii_lowercase(); + pub fn property_priority(&self, property: &PropertyId) -> Importance { + // Step 1: done when parsing a string to PropertyId // Step 2 - if let Some(shorthand) = Shorthand::from_name(&property) { - // Step 2.1 & 2.2 & 2.3 - if shorthand.longhands().iter().all(|l| { - self.get(l).map_or(false, |&(_, importance)| importance.important()) - }) { - Importance::Important - } else { - Importance::Normal + match property.as_shorthand() { + Ok(shorthand) => { + // Step 2.1 & 2.2 & 2.3 + if shorthand.longhands().iter().all(|&l| { + self.get(PropertyDeclarationId::Longhand(l)) + .map_or(false, |&(_, importance)| importance.important()) + }) { + Importance::Important + } else { + Importance::Normal + } + } + Err(longhand_or_custom) => { + // Step 3 + self.get(longhand_or_custom).map_or(Importance::Normal, |&(_, importance)| importance) } - } else { - // Step 3 - self.get(&property).map_or(Importance::Normal, |&(_, importance)| importance) } } - pub fn set_parsed_declaration(&mut self, declaration: PropertyDeclaration, - importance: Importance) { + pub fn set_parsed_declaration(&mut self, declaration: PropertyDeclaration, importance: Importance) { for slot in &mut *self.declarations { - if slot.0.name() == declaration.name() { + if slot.0.id() == declaration.id() { match (slot.1, importance) { (Importance::Normal, Importance::Important) => { self.important_count += 1; @@ -169,9 +172,9 @@ impl PropertyDeclarationBlock { } } - pub fn set_importance(&mut self, property_names: &[&str], new_importance: Importance) { + pub fn set_importance(&mut self, property: &PropertyId, new_importance: Importance) { for &mut (ref declaration, ref mut importance) in &mut self.declarations { - if property_names.iter().any(|p| declaration.matches(p)) { + if declaration.id().is_or_is_longhand_of(property) { match (*importance, new_importance) { (Importance::Normal, Importance::Important) => { self.important_count += 1; @@ -187,44 +190,34 @@ impl PropertyDeclarationBlock { } /// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty - pub fn remove_property(&mut self, property_name: &str) { - // Step 2 - let property = property_name.to_ascii_lowercase(); - - match Shorthand::from_name(&property) { - // Step 4 - Some(shorthand) => self.remove_longhands(shorthand.longhands()), - // Step 5 - None => self.remove_longhands(&[&*property]), - } - } - - fn remove_longhands(&mut self, names: &[&str]) { + pub fn remove_property(&mut self, property: &PropertyId) { let important_count = &mut self.important_count; self.declarations.retain(|&(ref declaration, importance)| { - let retain = !names.iter().any(|n| declaration.matches(n)); - if !retain && importance.important() { + let remove = declaration.id().is_or_is_longhand_of(property); + if remove && importance.important() { *important_count -= 1 } - retain + !remove }) } /// Take a declaration block known to contain a single property and serialize it. - pub fn single_value_to_css<W>(&self, property_name: &str, dest: &mut W) -> fmt::Result + pub fn single_value_to_css<W>(&self, property: &PropertyId, dest: &mut W) -> fmt::Result where W: fmt::Write { - match self.declarations.len() { - 0 => Err(fmt::Error), - 1 if self.declarations[0].0.name().eq_str_ignore_ascii_case(property_name) => { - self.declarations[0].0.to_css(dest) + match property.as_shorthand() { + Err(_longhand_or_custom) => { + if self.declarations.len() == 1 { + self.declarations[0].0.to_css(dest) + } else { + Err(fmt::Error) + } } - _ => { + Ok(shorthand) => { // we use this function because a closure won't be `Clone` fn get_declaration(dec: &(PropertyDeclaration, Importance)) -> &PropertyDeclaration { &dec.0 } - let shorthand = try!(Shorthand::from_name(property_name).ok_or(fmt::Error)); if !self.declarations.iter().all(|decl| decl.0.shorthands().contains(&shorthand)) { return Err(fmt::Error) } @@ -254,7 +247,7 @@ impl ToCss for PropertyDeclarationBlock { // Step 3 for &(ref declaration, importance) in &*self.declarations { // Step 3.1 - let property = declaration.name(); + let property = declaration.id(); // Step 3.2 if already_serialized.contains(&property) { @@ -266,11 +259,11 @@ impl ToCss for PropertyDeclarationBlock { if !shorthands.is_empty() { // Step 3.3.1 let mut longhands = self.declarations.iter() - .filter(|d| !already_serialized.contains(&d.0.name())) + .filter(|d| !already_serialized.contains(&d.0.id())) .collect::<Vec<_>>(); // Step 3.3.2 - for shorthand in shorthands { + for &shorthand in shorthands { let properties = shorthand.longhands(); // Substep 2 & 3 @@ -278,8 +271,7 @@ impl ToCss for PropertyDeclarationBlock { let mut important_count = 0; for &&(ref longhand, longhand_importance) in longhands.iter() { - let longhand_name = longhand.name(); - if properties.iter().any(|p| &longhand_name == *p) { + if longhand.id().is_longhand_of(shorthand) { current_longhands.push(longhand); if longhand_importance.important() { important_count += 1; @@ -325,7 +317,7 @@ impl ToCss for PropertyDeclarationBlock { for current_longhand in current_longhands { // Substep 9 - already_serialized.push(current_longhand.name()); + already_serialized.push(current_longhand.id()); let index_to_remove = longhands.iter().position(|l| l.0 == *current_longhand); if let Some(index) = index_to_remove { // Substep 10 @@ -348,9 +340,9 @@ impl ToCss for PropertyDeclarationBlock { // "error: unable to infer enough type information about `_`; // type annotations or generic parameter binding required [E0282]" // Use the same type as earlier call to reuse generated code. - try!(append_serialization::<W, Cloned<slice::Iter< &PropertyDeclaration>>>( + try!(append_serialization::<W, Cloned<slice::Iter< &PropertyDeclaration>>, _>( dest, - &property.to_string(), + &property, AppendableValue::Declaration(declaration), importance, &mut is_first_serialization)); @@ -367,7 +359,7 @@ impl ToCss for PropertyDeclarationBlock { pub enum AppendableValue<'a, I> where I: Iterator<Item=&'a PropertyDeclaration> { Declaration(&'a PropertyDeclaration), - DeclarationsForShorthand(Shorthand, I), + DeclarationsForShorthand(ShorthandId, I), Css(&'a str) } @@ -407,13 +399,15 @@ pub fn append_declaration_value<'a, W, I> Ok(()) } -pub fn append_serialization<'a, W, I>(dest: &mut W, - property_name: &str, +pub fn append_serialization<'a, W, I, N>(dest: &mut W, + property_name: &N, appendable_value: AppendableValue<'a, I>, importance: Importance, is_first_serialization: &mut bool) -> fmt::Result - where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { + where W: fmt::Write, + I: Iterator<Item=&'a PropertyDeclaration>, + N: ToCss { try!(handle_first_serialization(dest, is_first_serialization)); // Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal @@ -422,7 +416,8 @@ pub fn append_serialization<'a, W, I>(dest: &mut W, return append_declaration_value(dest, appendable_value, importance); } - try!(write!(dest, "{}:", property_name)); + try!(property_name.to_css(dest)); + try!(dest.write_char(':')); // for normal parsed values, add a space between key: and value match &appendable_value { @@ -451,7 +446,7 @@ pub fn parse_style_attribute(input: &str, parse_property_declaration_list(&context, &mut Parser::new(input)) } -pub fn parse_one_declaration(name: &str, +pub fn parse_one_declaration(id: PropertyId, input: &str, base_url: &ServoUrl, error_reporter: StdBox<ParseErrorReporter + Send>, @@ -459,7 +454,7 @@ pub fn parse_one_declaration(name: &str, -> Result<Vec<PropertyDeclaration>, ()> { let context = ParserContext::new_with_extra_data(Origin::Author, base_url, error_reporter, extra_data); let mut results = vec![]; - match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results, false) { + match PropertyDeclaration::parse(id, &context, &mut Parser::new(input), &mut results, false) { PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results), _ => Err(()) } @@ -482,9 +477,10 @@ impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> { fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(Vec<PropertyDeclaration>, Importance), ()> { + let id = try!(PropertyId::parse(name.into())); let mut results = vec![]; try!(input.parse_until_before(Delimiter::Bang, |input| { - match PropertyDeclaration::parse(name, self.context, input, &mut results, false) { + match PropertyDeclaration::parse(id, self.context, input, &mut results, false) { PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(()), _ => Err(()) } diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 5d58fd6db5c..6225c5b63e6 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -68,7 +68,7 @@ pub mod single_value { use cssparser::Parser; use parser::{Parse, ParserContext, ParserContextExtraData}; - use properties::{CSSWideKeyword, DeclaredValue, Shorthand}; + use properties::{CSSWideKeyword, DeclaredValue, ShorthandId}; use values::computed::{Context, ToComputedValue}; use values::{computed, specified}; ${caller.body()} @@ -182,7 +182,7 @@ % if not property.derived_from: use cssparser::Parser; use parser::{Parse, ParserContext, ParserContextExtraData}; - use properties::{CSSWideKeyword, DeclaredValue, Shorthand}; + use properties::{CSSWideKeyword, DeclaredValue, ShorthandId}; % endif use values::{Auto, Either, None_, Normal}; use cascade_info::CascadeInfo; @@ -380,7 +380,7 @@ #[allow(unused_imports)] use cssparser::Parser; use parser::ParserContext; - use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand}; + use properties::{longhands, PropertyDeclaration, DeclaredValue, ShorthandId}; use std::fmt; use style_traits::ToCss; @@ -500,7 +500,7 @@ css: css.clone().into_owned(), first_token_type: first_token_type, base_url: context.base_url.clone(), - from_shorthand: Some(Shorthand::${shorthand.camel_case}), + from_shorthand: Some(ShorthandId::${shorthand.camel_case}), } )); % endfor diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 46688434651..21897405c88 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -10,13 +10,12 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> -use std::ascii::AsciiExt; +use std::borrow::Cow; use std::boxed::Box as StdBox; use std::collections::HashSet; use std::fmt::{self, Write}; use std::sync::Arc; -use Atom; use app_units::Au; #[cfg(feature = "servo")] use cssparser::{Color as CSSParserColor, RGBA}; use cssparser::{Parser, TokenSerializationType}; @@ -25,6 +24,7 @@ use error_reporting::ParseErrorReporter; use euclid::size::Size2D; use computed_values; use font_metrics::FontMetricsProvider; +#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID; #[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide}; use logical_geometry::WritingMode; use parser::{Parse, ParserContext, ParserContextExtraData}; @@ -40,6 +40,12 @@ use rule_tree::StrongRuleNode; use self::property_bit_field::PropertyBitField; pub use self::declaration_block::*; +#[cfg(feature = "gecko")] +#[macro_export] +macro_rules! property_name { + ($s: tt) => { atom!($s) } +} + <%! from data import Method, Keyword, to_rust_ident import os.path @@ -242,7 +248,7 @@ mod property_bit_field { css: &String, first_token_type: TokenSerializationType, base_url: &ServoUrl, - from_shorthand: Option<Shorthand>, + from_shorthand: Option<ShorthandId>, custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>, f: F, error_reporter: &mut StdBox<ParseErrorReporter + Send>, @@ -265,7 +271,7 @@ mod property_bit_field { } % for shorthand in data.shorthands: % if property in shorthand.sub_properties: - Some(Shorthand::${shorthand.camel_case}) => { + Some(ShorthandId::${shorthand.camel_case}) => { shorthands::${shorthand.ident}::parse_value(&context, input) .map(|result| match result.${property.ident} { Some(value) => DeclaredValue::Value(value), @@ -378,41 +384,56 @@ impl Parse for CSSWideKeyword { #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub enum Shorthand { - % for property in data.shorthands: - ${property.camel_case}, +pub enum LonghandId { + % for i, property in enumerate(data.longhands): + ${property.camel_case} = ${i}, % endfor } -impl Shorthand { - pub fn from_name(name: &str) -> Option<Shorthand> { - match_ignore_ascii_case! { name, - % for property in data.shorthands: - "${property.name}" => Some(Shorthand::${property.camel_case}), +impl LonghandId { + pub fn name(&self) -> &'static str { + match *self { + % for property in data.longhands: + LonghandId::${property.camel_case} => "${property.name}", % endfor - _ => None } } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum ShorthandId { + % for property in data.shorthands: + ${property.camel_case}, + % endfor +} + +impl ToCss for ShorthandId { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + dest.write_str(self.name()) + } +} +impl ShorthandId { pub fn name(&self) -> &'static str { match *self { % for property in data.shorthands: - Shorthand::${property.camel_case} => "${property.name}", + ShorthandId::${property.camel_case} => "${property.name}", % endfor } } - pub fn longhands(&self) -> &'static [&'static str] { + pub fn longhands(&self) -> &'static [LonghandId] { % for property in data.shorthands: - static ${property.ident.upper()}: &'static [&'static str] = &[ + static ${property.ident.upper()}: &'static [LonghandId] = &[ % for sub in property.sub_properties: - "${sub.name}", + LonghandId::${sub.camel_case}, % endfor ]; % endfor match *self { % for property in data.shorthands: - Shorthand::${property.camel_case} => ${property.ident.upper()}, + ShorthandId::${property.camel_case} => ${property.ident.upper()}, % endfor } } @@ -421,7 +442,7 @@ impl Shorthand { where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { match *self { % for property in data.shorthands: - Shorthand::${property.camel_case} => { + ShorthandId::${property.camel_case} => { match shorthands::${property.ident}::LonghandsToSerialize::from_iter(declarations) { Ok(longhands) => longhands.to_css(dest), Err(_) => Err(fmt::Error) @@ -443,11 +464,9 @@ impl Shorthand { match self.get_shorthand_appendable_value(declarations) { None => Ok(false), Some(appendable_value) => { - let property_name = self.name(); - append_serialization( dest, - property_name, + &self, appendable_value, importance, is_first_serialization @@ -496,7 +515,7 @@ pub enum DeclaredValue<T> { css: String, first_token_type: TokenSerializationType, base_url: ServoUrl, - from_shorthand: Option<Shorthand>, + from_shorthand: Option<ShorthandId>, }, Initial, Inherit, @@ -535,6 +554,133 @@ impl<T: ToCss> ToCss for DeclaredValue<T> { #[derive(PartialEq, Clone)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum PropertyDeclarationId<'a> { + Longhand(LonghandId), + Custom(&'a ::custom_properties::Name), +} + +impl<'a> ToCss for PropertyDeclarationId<'a> { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()), + PropertyDeclarationId::Custom(name) => write!(dest, "--{}", name), + } + } +} + +impl<'a> PropertyDeclarationId<'a> { + pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool { + match *self { + PropertyDeclarationId::Longhand(id) => { + match *other { + PropertyId::Longhand(other_id) => id == other_id, + PropertyId::Shorthand(shorthand) => shorthand.longhands().contains(&id), + PropertyId::Custom(_) => false, + } + } + PropertyDeclarationId::Custom(name) => { + matches!(*other, PropertyId::Custom(ref other_name) if name == other_name) + } + } + } + + pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool { + match *self { + PropertyDeclarationId::Longhand(ref id) => shorthand.longhands().contains(id), + _ => false, + } + } +} + +#[derive(Eq, PartialEq, Clone)] +pub enum PropertyId { + Longhand(LonghandId), + Shorthand(ShorthandId), + Custom(::custom_properties::Name), +} + +impl fmt::Debug for PropertyId { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + self.to_css(formatter) + } +} + +impl ToCss for PropertyId { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + PropertyId::Longhand(id) => dest.write_str(id.name()), + PropertyId::Shorthand(id) => dest.write_str(id.name()), + PropertyId::Custom(ref name) => write!(dest, "--{}", name), + } + } +} + +// FIXME(https://github.com/rust-lang/rust/issues/33156): remove this enum and use PropertyId +// when stable Rust allows destructors in statics. +enum StaticId { + Longhand(LonghandId), + Shorthand(ShorthandId), +} +include!(concat!(env!("OUT_DIR"), "/static_ids.rs")); + +impl PropertyId { + /// Returns Err(()) for unknown non-custom properties + pub fn parse(s: Cow<str>) -> Result<Self, ()> { + if let Ok(name) = ::custom_properties::parse_name(&s) { + return Ok(PropertyId::Custom(::custom_properties::Name::from(name))) + } + + let lower_case = ::str::cow_into_ascii_lowercase(s); + match STATIC_IDS.get(&*lower_case) { + Some(&StaticId::Longhand(id)) => Ok(PropertyId::Longhand(id)), + Some(&StaticId::Shorthand(id)) => Ok(PropertyId::Shorthand(id)), + None => Err(()), + } + } + + #[cfg(feature = "gecko")] + #[allow(non_upper_case_globals)] + pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> { + use gecko_bindings::structs::*; + <% + def to_nscsspropertyid(ident): + if ident == "word_wrap": + return "nsCSSPropertyID_eCSSPropertyAlias_WordWrap" + + if ident == "float": + ident = "float_" + elif "outline_radius" in ident: + ident = ident.replace("right", "Right").replace("left", "Left") + elif ident.startswith("_moz_"): + ident = ident[len("_moz_"):] + return "nsCSSPropertyID::eCSSProperty_" + ident + %> + match id { + % for property in data.longhands: + ${to_nscsspropertyid(property.ident)} => { + Ok(PropertyId::Longhand(LonghandId::${property.camel_case})) + } + % endfor + % for property in data.shorthands: + ${to_nscsspropertyid(property.ident)} => { + Ok(PropertyId::Shorthand(ShorthandId::${property.camel_case})) + } + % endfor + _ => Err(()) + } + } + + pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId> { + match *self { + PropertyId::Shorthand(id) => Ok(id), + PropertyId::Longhand(id) => Err(PropertyDeclarationId::Longhand(id)), + PropertyId::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)), + } + } +} + +#[derive(PartialEq, Clone)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum PropertyDeclaration { % for property in data.longhands: ${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>), @@ -566,51 +712,10 @@ pub enum PropertyDeclarationParseResult { ValidOrIgnoredDeclaration, } -#[derive(Eq, PartialEq, Clone)] -pub enum PropertyDeclarationName { - Longhand(&'static str), - Custom(::custom_properties::Name), - Internal -} - -impl PropertyDeclarationName { - pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool { - match *self { - PropertyDeclarationName::Longhand(s) => s.eq_ignore_ascii_case(other), - PropertyDeclarationName::Custom(ref n) => n.eq_str_ignore_ascii_case(other), - PropertyDeclarationName::Internal => false - } - } -} - -impl PartialEq<str> for PropertyDeclarationName { - fn eq(&self, other: &str) -> bool { - match *self { - PropertyDeclarationName::Longhand(n) => n == other, - PropertyDeclarationName::Custom(ref n) => { - n.with_str(|s| ::custom_properties::parse_name(other) == Ok(s)) - } - PropertyDeclarationName::Internal => false, - } - } -} - -impl fmt::Display for PropertyDeclarationName { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - PropertyDeclarationName::Longhand(n) => f.write_str(n), - PropertyDeclarationName::Custom(ref n) => { - try!(f.write_str("--")); - n.with_str(|s| f.write_str(s)) - } - PropertyDeclarationName::Internal => Ok(()), - } - } -} - impl fmt::Debug for PropertyDeclaration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}: ", self.name())); + try!(self.id().to_css(f)); + try!(f.write_str(": ")); match *self { % for property in data.longhands: % if not property.derived_from: @@ -643,45 +748,20 @@ impl ToCss for PropertyDeclaration { } impl PropertyDeclaration { - pub fn name(&self) -> PropertyDeclarationName { + pub fn id(&self) -> PropertyDeclarationId { match *self { % for property in data.longhands: - PropertyDeclaration::${property.camel_case}(..) => - % if not property.derived_from: - PropertyDeclarationName::Longhand("${property.name}"), - % else: - PropertyDeclarationName::Internal, - % endif + PropertyDeclaration::${property.camel_case}(..) => { + PropertyDeclarationId::Longhand(LonghandId::${property.camel_case}) + } % endfor PropertyDeclaration::Custom(ref name, _) => { - PropertyDeclarationName::Custom(name.clone()) + PropertyDeclarationId::Custom(name) } } } - #[inline] - pub fn discriminant_value(&self) -> usize { - match *self { - % for i, property in enumerate(data.longhands): - PropertyDeclaration::${property.camel_case}(..) => ${i}, - % endfor - PropertyDeclaration::Custom(..) => ${len(data.longhands)} - } - } - - pub fn value(&self) -> String { - let mut value = String::new(); - if let Err(_) = self.to_css(&mut value) { - panic!("unsupported property declaration: {}", self.name()); - } - - value - } - - /// If this is a pending-substitution value from the given shorthand, return that value - // Extra space here because < seems to be removed by Mako when immediately followed by &. - // ↓ - pub fn with_variables_from_shorthand(&self, shorthand: Shorthand) -> Option< &str> { + pub fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> { match *self { % for property in data.longhands: PropertyDeclaration::${property.camel_case}(ref value) => match *value { @@ -727,49 +807,34 @@ impl PropertyDeclaration { } } - pub fn matches(&self, name: &str) -> bool { - match *self { - % for property in data.longhands: - PropertyDeclaration::${property.camel_case}(..) => - % if not property.derived_from: - name.eq_ignore_ascii_case("${property.name}"), - % else: - false, - % endif - % endfor - PropertyDeclaration::Custom(ref declaration_name, _) => { - declaration_name.with_str(|s| ::custom_properties::parse_name(name) == Ok(s)) - } - } - } - /// The `in_keyframe_block` parameter controls this: /// /// https://drafts.csswg.org/css-animations/#keyframes /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property /// > except those defined in this specification, /// > but does accept the `animation-play-state` property and interprets it specially. - pub fn parse(name: &str, context: &ParserContext, input: &mut Parser, + pub fn parse(id: PropertyId, context: &ParserContext, input: &mut Parser, result_list: &mut Vec<PropertyDeclaration>, in_keyframe_block: bool) -> PropertyDeclarationParseResult { - if let Ok(name) = ::custom_properties::parse_name(name) { - let value = match input.try(|i| CSSWideKeyword::parse(context, i)) { - Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited - Ok(CSSWideKeyword::InheritKeyword) => DeclaredValue::Inherit, - Ok(CSSWideKeyword::InitialKeyword) => DeclaredValue::Initial, - Err(()) => match ::custom_properties::SpecifiedValue::parse(context, input) { - Ok(value) => DeclaredValue::Value(value), - Err(()) => return PropertyDeclarationParseResult::InvalidValue, - } - }; - result_list.push(PropertyDeclaration::Custom(Atom::from(name), value)); - return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration; - } - match_ignore_ascii_case! { name, + match id { + PropertyId::Custom(name) => { + let value = match input.try(|i| CSSWideKeyword::parse(context, i)) { + Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited + Ok(CSSWideKeyword::InheritKeyword) => DeclaredValue::Inherit, + Ok(CSSWideKeyword::InitialKeyword) => DeclaredValue::Initial, + Err(()) => match ::custom_properties::SpecifiedValue::parse(context, input) { + Ok(value) => DeclaredValue::Value(value), + Err(()) => return PropertyDeclarationParseResult::InvalidValue, + } + }; + result_list.push(PropertyDeclaration::Custom(name, value)); + return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration; + } + PropertyId::Longhand(id) => match id { % for property in data.longhands: - % if not property.derived_from: - "${property.name}" => { + LonghandId::${property.camel_case} => { + % if not property.derived_from: % if not property.allowed_in_keyframe_block: if in_keyframe_block { return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock @@ -793,13 +858,15 @@ impl PropertyDeclaration { }, Err(()) => PropertyDeclarationParseResult::InvalidValue, } - }, - % else: - "${property.name}" => PropertyDeclarationParseResult::UnknownProperty, - % endif + % else: + PropertyDeclarationParseResult::UnknownProperty + % endif + } % endfor + }, + PropertyId::Shorthand(id) => match id { % for shorthand in data.shorthands: - "${shorthand.name}" => { + ShorthandId::${shorthand.camel_case} => { % if not shorthand.allowed_in_keyframe_block: if in_keyframe_block { return PropertyDeclarationParseResult::AnimationPropertyInKeyframeBlock @@ -846,19 +913,13 @@ impl PropertyDeclaration { Err(()) => PropertyDeclarationParseResult::InvalidValue, } } - }, - % endfor - - _ => { - if cfg!(all(debug_assertions, feature = "gecko")) && !name.starts_with('-') { - println!("stylo: Unimplemented property setter: {}", name); } - PropertyDeclarationParseResult::UnknownProperty + % endfor } } } - pub fn shorthands(&self) -> &'static [Shorthand] { + pub fn shorthands(&self) -> &'static [ShorthandId] { // first generate longhand to shorthands lookup map <% longhand_to_shorthand_map = {} @@ -875,9 +936,9 @@ impl PropertyDeclaration { // based on lookup results for each longhand, create result arrays % for property in data.longhands: - static ${property.ident.upper()}: &'static [Shorthand] = &[ + static ${property.ident.upper()}: &'static [ShorthandId] = &[ % for shorthand in longhand_to_shorthand_map.get(property.ident, []): - Shorthand::${shorthand}, + ShorthandId::${shorthand}, % endfor ]; % endfor @@ -1321,18 +1382,21 @@ impl ComputedValues { false } - pub fn computed_value_to_string(&self, name: &str) -> Result<String, ()> { - match name { + pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String { + match property { % for style_struct in data.active_style_structs(): % for longhand in style_struct.longhands: - "${longhand.name}" => Ok(self.${style_struct.ident}.${longhand.ident}.to_css_string()), + PropertyDeclarationId::Longhand(LonghandId::${longhand.camel_case}) => { + self.${style_struct.ident}.${longhand.ident}.to_css_string() + } % endfor % endfor - _ => { - let name = try!(::custom_properties::parse_name(name)); - let map = try!(self.custom_properties.as_ref().ok_or(())); - let value = try!(map.get(&Atom::from(name)).ok_or(())); - Ok(value.to_css_string()) + PropertyDeclarationId::Custom(name) => { + self.custom_properties + .as_ref() + .and_then(|map| map.get(name)) + .map(|value| value.to_css_string()) + .unwrap_or(String::new()) } } } @@ -1576,9 +1640,11 @@ pub fn apply_declarations<'a, F, I>(viewport_size: Size2D<Au>, ComputedValues::do_cascade_property(|cascade_property| { % for category_to_cascade_now in ["early", "other"]: for declaration in iter_declarations() { - if let PropertyDeclaration::Custom(..) = *declaration { - continue - } + let longhand_id = match declaration.id() { + PropertyDeclarationId::Longhand(id) => id, + PropertyDeclarationId::Custom(..) => continue, + }; + // The computed value of some properties depends on the // (sometimes computed) value of *other* properties. // @@ -1610,7 +1676,7 @@ pub fn apply_declarations<'a, F, I>(viewport_size: Size2D<Au>, continue } - let discriminant = declaration.discriminant_value(); + let discriminant = longhand_id as usize; (cascade_property[discriminant])(declaration, inherited_style, &mut context, @@ -1948,31 +2014,21 @@ pub fn modify_style_for_inline_absolute_hypothetical_fragment(style: &mut Arc<Co } } -// FIXME: https://github.com/w3c/csswg-drafts/issues/580 -pub fn is_supported_property(property: &str) -> bool { - match_ignore_ascii_case! { property, - % for property in data.shorthands + data.longhands: - "${property.name}" => true, - % endfor - _ => property.starts_with("--") - } -} - #[macro_export] macro_rules! css_properties_accessors { ($macro_name: ident) => { $macro_name! { - % for property in data.shorthands + data.longhands: - % if not property.derived_from and not property.internal: - % if '-' in property.name: - [${property.ident.capitalize()}, Set${property.ident.capitalize()}, "${property.name}"], - % endif - % if property != data.longhands[-1]: - [${property.camel_case}, Set${property.camel_case}, "${property.name}"], - % else: - [${property.camel_case}, Set${property.camel_case}, "${property.name}"] + % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]: + % for property in props: + % if not property.derived_from and not property.internal: + % if '-' in property.name: + [${property.ident.capitalize()}, Set${property.ident.capitalize()}, + PropertyId::${kind}(${kind}Id::${property.camel_case})], + % endif + [${property.camel_case}, Set${property.camel_case}, + PropertyId::${kind}(${kind}Id::${property.camel_case})], % endif - % endif + % endfor % endfor } } diff --git a/components/style/properties/shorthand/serialize.mako.rs b/components/style/properties/shorthand/serialize.mako.rs index e061796b80d..3dbf39a8ac3 100644 --- a/components/style/properties/shorthand/serialize.mako.rs +++ b/components/style/properties/shorthand/serialize.mako.rs @@ -2,7 +2,7 @@ * 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 properties::{AppendableValue, DeclaredValue, PropertyDeclaration, Shorthand}; +use properties::{AppendableValue, DeclaredValue, PropertyDeclaration, ShorthandId}; use style_traits::ToCss; use values::specified::{BorderStyle, CSSColor}; use std::fmt; @@ -88,7 +88,7 @@ fn serialize_directional_border<W, I>(dest: &mut W, pub fn is_overflow_shorthand<'a, I>(appendable_value: &AppendableValue<'a, I>) -> bool where I: Iterator<Item=&'a PropertyDeclaration> { if let AppendableValue::DeclarationsForShorthand(shorthand, _) = *appendable_value { - if let Shorthand::Overflow = shorthand { + if let ShorthandId::Overflow = shorthand { return true; } } diff --git a/components/style/str.rs b/components/style/str.rs index 73518625b6e..be763892118 100644 --- a/components/style/str.rs +++ b/components/style/str.rs @@ -3,6 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use num_traits::ToPrimitive; +use std::ascii::AsciiExt; +use std::borrow::Cow; use std::convert::AsRef; use std::iter::{Filter, Peekable}; use std::str::Split; @@ -125,3 +127,13 @@ pub fn str_join<I, T>(strs: I, join: &str) -> String acc }) } + +/// Like AsciiExt::to_ascii_lowercase, but avoids allocating when the input is already lower-case. +pub fn cow_into_ascii_lowercase<'a, S: Into<Cow<'a, str>>>(s: S) -> Cow<'a, str> { + let mut cow = s.into(); + match cow.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { + Some(first_uppercase) => cow.to_mut()[first_uppercase..].make_ascii_lowercase(), + None => {} + } + cow +} diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 1fa492ec6b3..3e6bcdbf446 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -49,7 +49,7 @@ use style::gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI}; use style::parallel; use style::parser::{ParserContext, ParserContextExtraData}; use style::properties::{CascadeFlags, ComputedValues, Importance, PropertyDeclaration}; -use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock}; +use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock, PropertyId}; use style::properties::{apply_declarations, parse_one_declaration}; use style::restyle_hints::RestyleHint; use style::selector_parser::PseudoElementCascadeType; @@ -534,6 +534,11 @@ pub extern "C" fn Servo_ParseProperty(property: *const nsACString, value: *const principal: *mut ThreadSafePrincipalHolder) -> RawServoDeclarationBlockStrong { let name = unsafe { property.as_ref().unwrap().as_str_unchecked() }; + let id = if let Ok(id) = PropertyId::parse(name.into()) { + id + } else { + return RawServoDeclarationBlockStrong::null() + }; let value = unsafe { value.as_ref().unwrap().as_str_unchecked() }; let base_str = unsafe { base_url.as_ref().unwrap().as_str_unchecked() }; let base_url = ServoUrl::parse(base_str).unwrap(); @@ -548,7 +553,7 @@ pub extern "C" fn Servo_ParseProperty(property: *const nsACString, value: *const extra_data); let mut results = vec![]; - match PropertyDeclaration::parse(name, &context, &mut Parser::new(value), + match PropertyDeclaration::parse(id, &context, &mut Parser::new(value), &mut results, false) { PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}, _ => return RawServoDeclarationBlockStrong::null(), @@ -611,7 +616,7 @@ pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue( buffer: *mut nsAString) { let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations); - let property = get_property_name_from_atom(property, is_custom); + let property = get_property_id_from_atom(property, is_custom); let mut string = String::new(); let rv = declarations.read().single_value_to_css(&property, &mut string); debug_assert!(rv.is_ok()); @@ -631,23 +636,21 @@ pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(declarations: RawServoDe let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations); if let Some(&(ref decl, _)) = declarations.read().declarations.get(index as usize) { let result = unsafe { result.as_mut().unwrap() }; - write!(result, "{}", decl.name()).unwrap(); + decl.id().to_css(result).unwrap(); true } else { false } } -// FIXME Methods of PropertyDeclarationBlock should take atoms directly. -// This function is just a temporary workaround before that finishes. -fn get_property_name_from_atom(atom: *mut nsIAtom, is_custom: bool) -> String { +fn get_property_id_from_atom(atom: *mut nsIAtom, is_custom: bool) -> PropertyId { let atom = Atom::from(atom); if !is_custom { - atom.to_string() + // FIXME: can we do this mapping without going through a UTF-8 string? + // Maybe even from nsCSSPropertyID directly? + PropertyId::parse(atom.to_string().into()).expect("got unknown property name from Gecko") } else { - let mut result = String::with_capacity(atom.len() as usize + 2); - write!(result, "--{}", atom).unwrap(); - result + PropertyId::Custom(atom) } } @@ -656,7 +659,7 @@ pub extern "C" fn Servo_DeclarationBlock_GetPropertyValue(declarations: RawServo property: *mut nsIAtom, is_custom: bool, value: *mut nsAString) { let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations); - let property = get_property_name_from_atom(property, is_custom); + let property = get_property_id_from_atom(property, is_custom); declarations.read().property_value_to_css(&property, unsafe { value.as_mut().unwrap() }).unwrap(); } @@ -664,7 +667,7 @@ pub extern "C" fn Servo_DeclarationBlock_GetPropertyValue(declarations: RawServo pub extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(declarations: RawServoDeclarationBlockBorrowed, property: *mut nsIAtom, is_custom: bool) -> bool { let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations); - let property = get_property_name_from_atom(property, is_custom); + let property = get_property_id_from_atom(property, is_custom); declarations.read().property_priority(&property).important() } @@ -672,12 +675,12 @@ pub extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(declarations: Ra pub extern "C" fn Servo_DeclarationBlock_SetProperty(declarations: RawServoDeclarationBlockBorrowed, property: *mut nsIAtom, is_custom: bool, value: *mut nsACString, is_important: bool) -> bool { - let property = get_property_name_from_atom(property, is_custom); + let property = get_property_id_from_atom(property, is_custom); let value = unsafe { value.as_ref().unwrap().as_str_unchecked() }; // FIXME Needs real URL and ParserContextExtraData. let base_url = &*DUMMY_BASE_URL; let extra_data = ParserContextExtraData::default(); - if let Ok(decls) = parse_one_declaration(&property, value, &base_url, + if let Ok(decls) = parse_one_declaration(property, value, &base_url, Box::new(StdoutErrorReporter), extra_data) { let mut declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations).write(); let importance = if is_important { Importance::Important } else { Importance::Normal }; @@ -694,19 +697,24 @@ pub extern "C" fn Servo_DeclarationBlock_SetProperty(declarations: RawServoDecla pub extern "C" fn Servo_DeclarationBlock_RemoveProperty(declarations: RawServoDeclarationBlockBorrowed, property: *mut nsIAtom, is_custom: bool) { let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations); - let property = get_property_name_from_atom(property, is_custom); + let property = get_property_id_from_atom(property, is_custom); declarations.write().remove_property(&property); } #[no_mangle] pub extern "C" fn Servo_CSSSupports(property: *const nsACString, value: *const nsACString) -> bool { let property = unsafe { property.as_ref().unwrap().as_str_unchecked() }; + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + return false + }; let value = unsafe { value.as_ref().unwrap().as_str_unchecked() }; let base_url = &*DUMMY_BASE_URL; let extra_data = ParserContextExtraData::default(); - match parse_one_declaration(&property, &value, &base_url, Box::new(StdoutErrorReporter), extra_data) { + match parse_one_declaration(id, &value, &base_url, Box::new(StdoutErrorReporter), extra_data) { Ok(decls) => !decls.is_empty(), Err(()) => false, } diff --git a/tests/unit/style/Cargo.toml b/tests/unit/style/Cargo.toml index c2a668caa4c..a7fe7971cf7 100644 --- a/tests/unit/style/Cargo.toml +++ b/tests/unit/style/Cargo.toml @@ -16,6 +16,7 @@ testing = ["style/testing"] app_units = "0.3" cssparser = {version = "0.7", features = ["heap_size"]} euclid = "0.10.1" +matches = "0.1" owning_ref = "0.2.2" parking_lot = "0.3" rustc-serialize = "0.3" diff --git a/tests/unit/style/lib.rs b/tests/unit/style/lib.rs index 1ca7414bf68..95cd087ccbf 100644 --- a/tests/unit/style/lib.rs +++ b/tests/unit/style/lib.rs @@ -10,6 +10,7 @@ extern crate app_units; extern crate cssparser; extern crate euclid; #[macro_use] extern crate html5ever_atoms; +#[macro_use] #[allow(unused_extern_crates)] extern crate matches; extern crate owning_ref; extern crate parking_lot; extern crate rustc_serialize; diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index 6780716e876..c27fe00dc61 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -4,7 +4,7 @@ pub use std::sync::Arc; pub use style::computed_values::display::T::inline_block; -pub use style::properties::{DeclaredValue, PropertyDeclaration, PropertyDeclarationBlock, Importance}; +pub use style::properties::{DeclaredValue, PropertyDeclaration, PropertyDeclarationBlock, Importance, PropertyId}; pub use style::values::specified::{BorderStyle, BorderWidth, CSSColor, Length}; pub use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent}; pub use style::properties::longhands::outline_color::computed_value::T as ComputedColor; @@ -1027,7 +1027,8 @@ mod shorthand_serialization { let mut s = String::new(); - let x = block.single_value_to_css("scroll-snap-type", &mut s); + let id = PropertyId::parse("scroll-snap-type".into()).unwrap(); + let x = block.single_value_to_css(&id, &mut s); assert_eq!(x.is_ok(), true); assert_eq!(s, ""); @@ -1049,7 +1050,8 @@ mod shorthand_serialization { let mut s = String::new(); - let x = block.single_value_to_css("scroll-snap-type", &mut s); + let id = PropertyId::parse("scroll-snap-type".into()).unwrap(); + let x = block.single_value_to_css(&id, &mut s); assert_eq!(x.is_ok(), true); assert_eq!(s, "mandatory"); diff --git a/tests/unit/style/str.rs b/tests/unit/style/str.rs index dafbd8fb7da..45d747d4da8 100644 --- a/tests/unit/style/str.rs +++ b/tests/unit/style/str.rs @@ -2,7 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use style::str::{split_html_space_chars, str_join}; +use std::borrow::Cow; +use style::str::{split_html_space_chars, str_join, cow_into_ascii_lowercase}; #[test] pub fn split_html_space_chars_whitespace() { @@ -33,3 +34,13 @@ pub fn test_str_join_many() { let expected = "-alpha--beta-gamma-"; assert_eq!(actual, expected); } + +#[test] +pub fn test_cow_into_ascii_lowercase() { + assert!(matches!(cow_into_ascii_lowercase("abc.d"), Cow::Borrowed("abc.d"))); + let string = String::from("abc.d"); + assert!(matches!(cow_into_ascii_lowercase(string), Cow::Owned(ref s) if s == "abc.d")); + assert!(matches!(cow_into_ascii_lowercase("Abc.d"), Cow::Owned(ref s) if s == "abc.d")); + assert!(matches!(cow_into_ascii_lowercase("aBC.D"), Cow::Owned(ref s) if s == "abc.d")); + assert!(matches!(cow_into_ascii_lowercase("abc.D"), Cow::Owned(ref s) if s == "abc.d")); +} diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 476d2d4ecd4..a5a5be5a7d1 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -16,8 +16,8 @@ use std::sync::atomic::AtomicBool; use style::error_reporting::ParseErrorReporter; use style::keyframes::{Keyframe, KeyframeSelector, KeyframePercentage}; use style::parser::ParserContextExtraData; -use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands}; use style::properties::Importance; +use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands}; use style::properties::longhands::animation_play_state; use style::stylesheets::{Origin, Namespaces}; use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule}; diff --git a/tests/unit/style/stylist.rs b/tests/unit/style/stylist.rs index 786ce7863f4..98c5b68a2ef 100644 --- a/tests/unit/style/stylist.rs +++ b/tests/unit/style/stylist.rs @@ -101,7 +101,7 @@ fn test_insert() { let rules_list = get_mock_rules(&[".intro.foo", "#top"]); let mut selector_map = SelectorMap::new(); selector_map.insert(rules_list[1][0].clone()); - assert_eq!(1, selector_map.id_hash.get(&atom!("top")).unwrap()[0].source_order); + assert_eq!(1, selector_map.id_hash.get(&Atom::from("top")).unwrap()[0].source_order); selector_map.insert(rules_list[0][0].clone()); assert_eq!(0, selector_map.class_hash.get(&Atom::from("intro")).unwrap()[0].source_order); assert!(selector_map.class_hash.get(&Atom::from("foo")).is_none()); |