From 34ba00e6d97973257f61adf6ecbb6a2205b88664 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 19 Jan 2017 15:56:53 -0800 Subject: [PATCH] Basic handling framework for presentation attributes in Stylo, with handling for font-size and color This introduces a basic framework for servo's style system to be able to query the style of presentation attributes which it can then insert into the cascade. It uses that framework to implement the size and color attributes on . There are a number of improvements that can be done on top of this: - Implement all other properties - Abstractify the ruledata parameter of the mappers using templates or virtual dispatch so that it can be a Servo decl block instead - Implement aforementiond abstraction over Servo decl blocks (this obsoletes the code in the first item above, so it might just be better to skip that and directly do this) - Replace uses of nsHTMLStyleSheet with an abstract base class containing common elements between Servo and Gecko I'd prefer for these to be done in separate steps. --- components/script/dom/htmlfontelement.rs | 21 ++----- components/style/gecko/wrapper.rs | 16 ++++-- components/style/gecko_bindings/bindings.rs | 13 ++++- .../gecko_bindings/sugar/ns_css_value.rs | 24 +++++++- components/style/values/specified/length.rs | 19 +++++++ ports/geckolib/glue.rs | 57 ++++++++++++++++++- 6 files changed, 126 insertions(+), 24 deletions(-) diff --git a/components/script/dom/htmlfontelement.rs b/components/script/dom/htmlfontelement.rs index 0eca01a47f9..80a0266595c 100644 --- a/components/script/dom/htmlfontelement.rs +++ b/components/script/dom/htmlfontelement.rs @@ -123,7 +123,7 @@ impl HTMLFontElementLayoutHelpers for LayoutJS { } /// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size -pub fn parse_legacy_font_size(mut input: &str) -> Option<&'static str> { +fn parse_length(mut input: &str) -> Option { // Steps 1 & 2 are not relevant // Step 3 @@ -153,8 +153,8 @@ pub fn parse_legacy_font_size(mut input: &str) -> Option<&'static str> { // Steps 6, 7, 8 let mut value = match read_numbers(input_chars) { - (Some(v), _) => v, - (None, _) => return None, + (Some(v), _) if v >= 0 => v, + _ => return None, }; // Step 9 @@ -165,18 +165,5 @@ pub fn parse_legacy_font_size(mut input: &str) -> Option<&'static str> { } // Steps 10, 11, 12 - Some(match value { - n if n >= 7 => "xxx-large", - 6 => "xx-large", - 5 => "x-large", - 4 => "large", - 3 => "medium", - 2 => "small", - n if n <= 1 => "x-small", - _ => unreachable!(), - }) -} - -fn parse_length(value: &str) -> Option { - parse_legacy_font_size(&value).and_then(|parsed| specified::Length::from_str(&parsed)) + Some(specified::Length::from_font_size_int(value as u8)) } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index b73db50a50b..872fc06486c 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -30,7 +30,8 @@ use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink, Gecko use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags}; use gecko_bindings::bindings::Gecko_ClassOrClassList; use gecko_bindings::bindings::Gecko_GetAnimationRule; -use gecko_bindings::bindings::Gecko_GetServoDeclarationBlock; +use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock; +use gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock; use gecko_bindings::bindings::Gecko_GetStyleContext; use gecko_bindings::structs; use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode}; @@ -42,6 +43,7 @@ use parking_lot::RwLock; use parser::ParserContextExtraData; use properties::{ComputedValues, parse_style_attribute}; use properties::PropertyDeclarationBlock; +use rule_tree::CascadeLevel as ServoCascadeLevel; use selector_parser::{ElementExt, Snapshot}; use selectors::Element; use selectors::parser::{AttrSelector, NamespaceConstraint}; @@ -385,7 +387,7 @@ impl<'le> TElement for GeckoElement<'le> { } fn style_attribute(&self) -> Option<&Arc>> { - let declarations = unsafe { Gecko_GetServoDeclarationBlock(self.0) }; + let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0) }; declarations.map(|s| s.as_arc_opt()).unwrap_or(None) } @@ -483,10 +485,16 @@ impl<'le> PartialEq for GeckoElement<'le> { } impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> { - fn synthesize_presentational_hints_for_legacy_attributes(&self, _hints: &mut V) + fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) where V: Push, { - // FIXME(bholley) - Need to implement this. + let declarations = unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0) }; + let declarations = declarations.and_then(|s| s.as_arc_opt()); + if let Some(decl) = declarations { + hints.push( + ApplicableDeclarationBlock::from_declarations(Clone::clone(decl), ServoCascadeLevel::PresHints) + ); + } } } diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index eafce93ea25..b16d0a442b3 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -499,7 +499,11 @@ extern "C" { -> u32; } extern "C" { - pub fn Gecko_GetServoDeclarationBlock(element: RawGeckoElementBorrowed) + pub fn Gecko_GetStyleAttrDeclarationBlock(element: RawGeckoElementBorrowed) + -> RawServoDeclarationBlockStrongBorrowedOrNull; +} +extern "C" { + pub fn Gecko_GetHTMLPresentationAttrDeclarationBlock(element: RawGeckoElementBorrowed) -> RawServoDeclarationBlockStrongBorrowedOrNull; } extern "C" { @@ -1391,6 +1395,13 @@ extern "C" { property: nsCSSPropertyID); } +extern "C" { + pub fn Servo_DeclarationBlock_AddPresValue(declarations: + RawServoDeclarationBlockBorrowed, + property: nsCSSPropertyID, + css_value: + nsCSSValueBorrowedMut); +} extern "C" { pub fn Servo_CSSSupports2(name: *const nsACString_internal, value: *const nsACString_internal) -> bool; diff --git a/components/style/gecko_bindings/sugar/ns_css_value.rs b/components/style/gecko_bindings/sugar/ns_css_value.rs index 3f125b13311..0eb9dc5c90c 100644 --- a/components/style/gecko_bindings/sugar/ns_css_value.rs +++ b/components/style/gecko_bindings/sugar/ns_css_value.rs @@ -12,7 +12,7 @@ use gecko_bindings::bindings::Gecko_CSSValue_GetPercentage; use gecko_bindings::bindings::Gecko_CSSValue_SetAbsoluteLength; use gecko_bindings::bindings::Gecko_CSSValue_SetCalc; use gecko_bindings::bindings::Gecko_CSSValue_SetPercentage; -use gecko_bindings::structs::{nsCSSValue, nsCSSUnit, nsCSSValue_Array}; +use gecko_bindings::structs::{nsCSSValue, nsCSSUnit, nsCSSValue_Array, nscolor}; use std::mem; use std::ops::Index; use std::slice; @@ -34,6 +34,28 @@ impl nsCSSValue { unsafe { *self.mValue.mInt.as_ref() } } + /// Checks if it is an integer and returns it if so + pub fn integer(&self) -> Option { + if self.mUnit == nsCSSUnit::eCSSUnit_Integer || + self.mUnit == nsCSSUnit::eCSSUnit_Enumerated || + self.mUnit == nsCSSUnit::eCSSUnit_EnumColor { + Some(unsafe { *self.mValue.mInt.as_ref() }) + } else { + None + } + } + + /// Checks if it is an RGBA color, returning it if so + /// Only use it with colors set by SetColorValue(), + /// which always sets RGBA colors + pub fn color_value(&self) -> Option { + if self.mUnit == nsCSSUnit::eCSSUnit_RGBAColor { + Some(unsafe { *self.mValue.mColor.as_ref() }) + } else { + None + } + } + /// Returns this nsCSSValue value as a floating point value, unchecked in /// release builds. pub fn float_unchecked(&self) -> f32 { diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index 9cc284fd774..351c692cc9a 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -293,6 +293,20 @@ impl NoCalcLength { }) } + /// https://drafts.csswg.org/css-fonts-3/#font-size-prop + pub fn from_font_size_int(i: u8) -> Self { + let au = match i { + 0 | 1 => Au::from_px(FONT_MEDIUM_PX) * 3 / 4, + 2 => Au::from_px(FONT_MEDIUM_PX) * 8 / 9, + 3 => Au::from_px(FONT_MEDIUM_PX), + 4 => Au::from_px(FONT_MEDIUM_PX) * 6 / 5, + 5 => Au::from_px(FONT_MEDIUM_PX) * 3 / 2, + 6 => Au::from_px(FONT_MEDIUM_PX) * 2, + _ => Au::from_px(FONT_MEDIUM_PX) * 3, + }; + NoCalcLength::Absolute(au) + } + /// Parse a given absolute or relative dimension. pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result { match_ignore_ascii_case! { unit, @@ -429,6 +443,11 @@ impl Length { NoCalcLength::parse_dimension(value, unit).map(Length::NoCalc) } + /// https://drafts.csswg.org/css-fonts-3/#font-size-prop + pub fn from_font_size_int(i: u8) -> Self { + Length::NoCalc(NoCalcLength::from_font_size_int(i)) + } + #[inline] fn parse_internal(input: &mut Parser, context: AllowedNumericType) -> Result { match try!(input.next()) { diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 5de30623403..e075843a114 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -34,7 +34,7 @@ use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSet use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed}; use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong}; use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong}; -use style::gecko_bindings::bindings::{nsACString, nsAString}; +use style::gecko_bindings::bindings::{nsACString, nsCSSValueBorrowedMut, nsAString}; use style::gecko_bindings::bindings::Gecko_AnimationAppendKeyframe; use style::gecko_bindings::bindings::RawGeckoAnimationValueListBorrowedMut; use style::gecko_bindings::bindings::RawGeckoElementBorrowed; @@ -946,6 +946,61 @@ pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(declarations: RawSer remove_property(declarations, get_property_id_from_nscsspropertyid!(property, ())) } +#[no_mangle] +pub extern "C" fn Servo_DeclarationBlock_AddPresValue(declarations: RawServoDeclarationBlockBorrowed, + property: nsCSSPropertyID, + css_value: nsCSSValueBorrowedMut) { + use style::gecko::values::convert_nscolor_to_rgba; + use style::properties::{DeclaredValue, LonghandId, PropertyDeclaration, PropertyId, longhands}; + use style::values::specified; + + let declarations = RwLock::::as_arc(&declarations); + let prop = PropertyId::from_nscsspropertyid(property); + + let long = match prop { + Ok(PropertyId::Longhand(long)) => long, + _ => { + error!("stylo: unknown presentation property with id {:?}", property); + return + } + }; + let decl = match long { + LonghandId::FontSize => { + if let Some(int) = css_value.integer() { + PropertyDeclaration::FontSize(DeclaredValue::Value( + longhands::font_size::SpecifiedValue( + specified::LengthOrPercentage::Length( + specified::NoCalcLength::from_font_size_int(int as u8) + ) + ) + )) + } else { + error!("stylo: got unexpected non-integer value for font-size presentation attribute"); + return + } + } + LonghandId::Color => { + if let Some(color) = css_value.color_value() { + PropertyDeclaration::Color(DeclaredValue::Value( + specified::CSSRGBA { + parsed: convert_nscolor_to_rgba(color), + authored: None + } + )) + } else { + error!("stylo: got unexpected non-integer value for color presentation attribute"); + return + } + } + _ => { + error!("stylo: cannot handle longhand {:?} from presentation attribute", long); + return + } + }; + declarations.write().declarations.push((decl, Importance::Normal)); + +} + #[no_mangle] pub extern "C" fn Servo_CSSSupports2(property: *const nsACString, value: *const nsACString) -> bool { let property = unsafe { property.as_ref().unwrap().as_str_unchecked() };