Bug 1289007 - part1: parse and compute initial-letter property. draft
authorJeremy Chen <jeremychen@mozilla.com>
Thu, 28 Jul 2016 15:23:36 +0800
changeset 393680 13e450ca5d19eb48490f1a574245a084339e60a1
parent 393679 72f31eee3cce0f854747a8284472bb93e0e44b0b
child 526635 c1246f2c43ef4b51102a80f33562e412c4a4f90b
push id24373
push userjichen@mozilla.com
push dateThu, 28 Jul 2016 07:24:05 +0000
bugs1289007
milestone50.0a1
Bug 1289007 - part1: parse and compute initial-letter property. MozReview-Commit-ID: E0eXolZ93oJ
layout/inspector/inDOMUtils.cpp
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsComputedDOMStylePropertyList.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
modules/libpref/init/all.js
testing/profiles/prefs_general.js
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -839,16 +839,17 @@ PropertySupportsVariant(nsCSSProperty aP
         supported = VARIANT_URL;
         break;
 
       case eCSSProperty_grid_column_start:
       case eCSSProperty_grid_column_end:
       case eCSSProperty_grid_row_start:
       case eCSSProperty_grid_row_end:
       case eCSSProperty_font_weight:
+      case eCSSProperty_initial_letter:
         supported = VARIANT_NUMBER;
         break;
 
       default:
         supported = 0;
         break;
     }
 
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -999,16 +999,18 @@ protected:
   bool ParseGridShorthandAutoProps();
   bool ParseGridLine(nsCSSValue& aValue);
   bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID);
   bool ParseGridColumnRow(nsCSSProperty aStartPropID,
                           nsCSSProperty aEndPropID);
   bool ParseGridArea();
   bool ParseGridGap();
 
+  bool ParseInitialLetter();
+
   // parsing 'align/justify-items/self' from the css-align spec
   bool ParseAlignJustifyPosition(nsCSSValue& aResult,
                                  const KTableEntry aTable[]);
   bool ParseJustifyItems();
   bool ParseAlignItems();
   bool ParseAlignJustifySelf(nsCSSProperty aPropID);
   // parsing 'align/justify-content' from the css-align spec
   bool ParseAlignJustifyContent(nsCSSProperty aPropID);
@@ -1240,16 +1242,26 @@ protected:
   {
     return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr);
   }
   bool ParseNonNegativeNumber(nsCSSValue& aValue)
   {
     return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER, nullptr);
   }
 
+  // Helpers for some common ParseSingleTokenOneOrLargerVariant calls.
+  bool ParseOneOrLargerInteger(nsCSSValue& aValue)
+  {
+    return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_INTEGER, nullptr);
+  }
+  bool ParseOneOrLargerNumber(nsCSSValue& aValue)
+  {
+    return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_NUMBER, nullptr);
+  }
+
   // http://dev.w3.org/csswg/css-values/#custom-idents
   // Parse an identifier that is none of:
   // * a CSS-wide keyword
   // * "default"
   // * a keyword in |aExcludedKeywords|
   // * a keyword in |aPropertyKTable|
   //
   // |aExcludedKeywords| is an array of nsCSSKeyword
@@ -9774,16 +9786,43 @@ CSSParserImpl::ParseGridGap()
     return false;
   }
   AppendValue(eCSSProperty_grid_row_gap, first);
   AppendValue(eCSSProperty_grid_column_gap,
               result == CSSParseResult::NotFound ? first : second);
   return true;
 }
 
+// normal | [<number> <integer>?]
+bool
+CSSParserImpl::ParseInitialLetter()
+{
+  nsCSSValue value;
+  // 'inherit', 'initial', 'unset', 'none', and 'normal' must be alone
+  if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NORMAL,
+                               nullptr)) {
+    nsCSSValue first, second;
+    if (!ParseOneOrLargerNumber(first)) {
+      return false;
+    }
+
+    if (!ParseOneOrLargerInteger(second)) {
+      AppendValue(eCSSProperty_initial_letter, first);
+      return true;
+    } else {
+      RefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(2);
+      val->Item(0) = first;
+      val->Item(1) = second;
+      value.SetArrayValue(val, eCSSUnit_Array);
+    }
+  }
+  AppendValue(eCSSProperty_initial_letter, value);
+  return true;
+}
+
 // [ $aTable && <overflow-position>? ] ?
 // $aTable is for <content-position> or <self-position>
 bool
 CSSParserImpl::ParseAlignJustifyPosition(nsCSSValue& aResult,
                                          const KTableEntry aTable[])
 {
   nsCSSValue pos, overflowPos;
   int32_t value = 0;
@@ -11565,16 +11604,18 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_align_content:
   case eCSSProperty_justify_content:
     return ParseAlignJustifyContent(aPropID);
   case eCSSProperty_align_items:
     return ParseAlignItems();
   case eCSSProperty_align_self:
   case eCSSProperty_justify_self:
     return ParseAlignJustifySelf(aPropID);
+  case eCSSProperty_initial_letter:
+    return ParseInitialLetter();
   case eCSSProperty_justify_items:
     return ParseJustifyItems();
   case eCSSProperty_list_style:
     return ParseListStyle();
   case eCSSProperty_margin:
     return ParseMargin();
   case eCSSProperty_object_position:
     return ParseObjectPosition();
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2263,16 +2263,27 @@ CSS_PROP_TEXT(
     hyphens,
     Hyphens,
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     kHyphensKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+CSS_PROP_TEXTRESET(
+    initial-letter,
+    initial_letter,
+    InitialLetter,
+    CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_APPLIES_TO_FIRST_LETTER,
+    "layout.css.initial-letter.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
 CSS_PROP_VISIBILITY(
     image-orientation,
     image_orientation,
     ImageOrientation,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.image-orientation.enabled",
     0,
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -3539,16 +3539,35 @@ nsComputedDOMStyle::DoGetImageRegion()
     leftVal->SetAppUnits(list->mImageRegion.x);
     val->SetRect(domRect);
   }
 
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
+nsComputedDOMStyle::DoGetInitialLetter()
+{
+  const nsStyleTextReset* textReset = StyleTextReset();
+  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+  if (textReset->mInitialLetterSink == 0) {
+    val->SetIdent(eCSSKeyword_normal);
+    return val.forget();
+  } else {
+    RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
+    val->SetNumber(textReset->mInitialLetterSize);
+    valueList->AppendCSSValue(val.forget());
+    RefPtr<nsROCSSPrimitiveValue> second = new nsROCSSPrimitiveValue;
+    second->SetNumber(textReset->mInitialLetterSink);
+    valueList->AppendCSSValue(second.forget());
+    return valueList.forget();
+  }
+}
+
+already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetLineHeight()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
   nscoord lineHeight;
   if (GetLineHeightCoord(lineHeight)) {
     val->SetAppUnits(lineHeight);
   } else {
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -399,16 +399,17 @@ private:
 
   /* List properties */
   already_AddRefed<CSSValue> DoGetListStyleImage();
   already_AddRefed<CSSValue> DoGetListStylePosition();
   already_AddRefed<CSSValue> DoGetListStyleType();
   already_AddRefed<CSSValue> DoGetImageRegion();
 
   /* Text Properties */
+  already_AddRefed<CSSValue> DoGetInitialLetter();
   already_AddRefed<CSSValue> DoGetLineHeight();
   already_AddRefed<CSSValue> DoGetRubyAlign();
   already_AddRefed<CSSValue> DoGetRubyPosition();
   already_AddRefed<CSSValue> DoGetTextAlign();
   already_AddRefed<CSSValue> DoGetTextAlignLast();
   already_AddRefed<CSSValue> DoGetTextCombineUpright();
   already_AddRefed<CSSValue> DoGetTextDecoration();
   already_AddRefed<CSSValue> DoGetTextDecorationColor();
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -148,16 +148,17 @@ COMPUTED_STYLE_PROP(grid_row_gap,       
 COMPUTED_STYLE_PROP(grid_row_start,                GridRowStart)
 COMPUTED_STYLE_PROP(grid_template_areas,           GridTemplateAreas)
 COMPUTED_STYLE_PROP(grid_template_columns,         GridTemplateColumns)
 COMPUTED_STYLE_PROP(grid_template_rows,            GridTemplateRows)
 COMPUTED_STYLE_PROP(height,                        Height)
 COMPUTED_STYLE_PROP(hyphens,                       Hyphens)
 COMPUTED_STYLE_PROP(image_orientation,             ImageOrientation)
 COMPUTED_STYLE_PROP(ime_mode,                      IMEMode)
+COMPUTED_STYLE_PROP(initial_letter,                InitialLetter)
 COMPUTED_STYLE_PROP(isolation,                     Isolation)
 COMPUTED_STYLE_PROP(justify_content,               JustifyContent)
 COMPUTED_STYLE_PROP(justify_items,                 JustifyItems)
 COMPUTED_STYLE_PROP(justify_self,                  JustifySelf)
 COMPUTED_STYLE_PROP(left,                          Left)
 COMPUTED_STYLE_PROP(letter_spacing,                LetterSpacing)
 COMPUTED_STYLE_PROP(line_height,                   LineHeight)
 //// COMPUTED_STYLE_PROP(list_style,               ListStyle)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4972,16 +4972,46 @@ nsRuleNode::ComputeTextResetData(void* a
   }
 
   // unicode-bidi: enum, inherit, initial
   SetValue(*aRuleData->ValueForUnicodeBidi(), text->mUnicodeBidi, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentText->mUnicodeBidi,
            NS_STYLE_UNICODE_BIDI_NORMAL);
 
+  // initial-letter: normal, number, array(number, integer?), initial
+  const nsCSSValue* initialLetterValue = aRuleData->ValueForInitialLetter();
+  if (initialLetterValue->GetUnit() == eCSSUnit_Null) {
+    // We don't want to change anything in this case.
+  } else if (initialLetterValue->GetUnit() == eCSSUnit_Inherit) {
+    conditions.SetUncacheable();
+    text->mInitialLetterSink = parentText->mInitialLetterSink;
+    text->mInitialLetterSize = parentText->mInitialLetterSize;
+  } else if (initialLetterValue->GetUnit() == eCSSUnit_Initial ||
+             initialLetterValue->GetUnit() == eCSSUnit_Unset ||
+             initialLetterValue->GetUnit() == eCSSUnit_Normal) {
+    // Use invalid values in initial-letter property to mean normal. So we can
+    // determine whether it is normal by checking mInitialLetterSink == 0.
+    text->mInitialLetterSink = 0;
+    text->mInitialLetterSize = 0.0f;
+  } else if (initialLetterValue->GetUnit() == eCSSUnit_Array) {
+    const nsCSSValue& firstValue = initialLetterValue->GetArrayValue()->Item(0);
+    const nsCSSValue& secondValue = initialLetterValue->GetArrayValue()->Item(1);
+    MOZ_ASSERT(firstValue.GetUnit() == eCSSUnit_Number &&
+               secondValue.GetUnit() == eCSSUnit_Integer,
+               "unexpected value unit");
+    text->mInitialLetterSize = firstValue.GetFloatValue();
+    text->mInitialLetterSink = secondValue.GetIntValue();
+  } else if (initialLetterValue->GetUnit() == eCSSUnit_Number) {
+    text->mInitialLetterSize = initialLetterValue->GetFloatValue();
+    text->mInitialLetterSink = NSToCoordFloorClamped(text->mInitialLetterSize);
+  } else {
+    MOZ_ASSERT_UNREACHABLE("unknown unit for initial-letter");
+  }
+
   COMPUTE_END_RESET(TextReset, text)
 }
 
 const void*
 nsRuleNode::ComputeUserInterfaceData(void* aStartStruct,
                                      const nsRuleData* aRuleData,
                                      nsStyleContext* aContext,
                                      nsRuleNode* aHighestNode,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3571,16 +3571,18 @@ nsStyleContent::AllocateContents(uint32_
 
 // --------------------
 // nsStyleTextReset
 //
 
 nsStyleTextReset::nsStyleTextReset(StyleStructContext aContext)
   : mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE)
   , mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL)
+  , mInitialLetterSink(0)
+  , mInitialLetterSize(0.0f)
   , mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID | BORDER_COLOR_FOREGROUND)
   , mTextDecorationColor(NS_RGB(0, 0, 0))
 {
   MOZ_COUNT_CTOR(nsStyleTextReset);
 }
 
 nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource)
 {
@@ -3591,17 +3593,19 @@ nsStyleTextReset::nsStyleTextReset(const
 nsStyleTextReset::~nsStyleTextReset()
 {
   MOZ_COUNT_DTOR(nsStyleTextReset);
 }
 
 nsChangeHint
 nsStyleTextReset::CalcDifference(const nsStyleTextReset& aNewData) const
 {
-  if (mUnicodeBidi != aNewData.mUnicodeBidi) {
+  if (mUnicodeBidi != aNewData.mUnicodeBidi ||
+      mInitialLetterSink != aNewData.mInitialLetterSink ||
+      mInitialLetterSize != aNewData.mInitialLetterSize) {
     return NS_STYLE_HINT_REFLOW;
   }
 
   uint8_t lineStyle = GetDecorationStyle();
   uint8_t otherLineStyle = aNewData.GetDecorationStyle();
   if (mTextDecorationLine != aNewData.mTextDecorationLine ||
       lineStyle != otherLineStyle) {
     // Changes to our text-decoration line can impact our overflow area &
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1972,16 +1972,18 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
            nsChangeHint_ReflowChangesSizeOrPosition |
            nsChangeHint_ClearAncestorIntrinsics;
   }
 
   nsStyleTextOverflow mTextOverflow;    // [reset] enum, string
 
   uint8_t mTextDecorationLine;          // [reset] see nsStyleConsts.h
   uint8_t mUnicodeBidi;                 // [reset] see nsStyleConsts.h
+  nscoord mInitialLetterSink;           // [reset] 0 means normal
+  float mInitialLetterSize;             // [reset] 0.0f means normal
 protected:
   uint8_t mTextDecorationStyle;         // [reset] see nsStyleConsts.h
 
   nscolor mTextDecorationColor;         // [reset] the colors to use for a decoration lines, not used at currentColor
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText
 {
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -6656,16 +6656,27 @@ if (IsCSSPropertyPrefEnabled("layout.css
       "0deg from-image",
       "from-image 0deg",
       "flip from-image",
       "from-image flip",
     ]
   };
 }
 
+if (IsCSSPropertyPrefEnabled("layout.css.initial-letter.enabled")) {
+  gCSSProperties["initial-letter"] = {
+    domProp: "initialLetter",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [ "2", "2.5", "3.7 2", "4 3" ],
+    invalid_values: [ "-3", "3.7 -2", "25%", "16px", "1 0", "0", "0 1" ]
+  };
+}
+
 if (IsCSSPropertyPrefEnabled("layout.css.osx-font-smoothing.enabled")) {
   gCSSProperties["-moz-osx-font-smoothing"] = {
     domProp: "MozOsxFontSmoothing",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "auto" ],
     other_values: [ "grayscale" ],
     invalid_values: [ "none", "subpixel-antialiased", "antialiased" ]
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2408,16 +2408,19 @@ pref("layout.css.visited_links_enabled",
 pref("layout.css.dpi", -1);
 
 // Set the number of device pixels per CSS pixel. A value <= 0 means choose
 // automatically based on user settings for the platform (e.g., "UI scale factor"
 // on Mac). A positive value is used as-is. This effectively controls the size
 // of a CSS "px". This is only used for windows on the screen, not for printing.
 pref("layout.css.devPixelsPerPx", "-1.0");
 
+// Is support for CSS initial-letter property enabled?
+pref("layout.css.initial-letter.enabled", false);
+
 // Is support for CSS Masking features enabled?
 pref("layout.css.masking.enabled", true);
 
 // Is support for mix-blend-mode enabled?
 pref("layout.css.mix-blend-mode.enabled", true);
 
 // Is support for isolation enabled?
 pref("layout.css.isolation.enabled", true);
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -158,16 +158,19 @@ user_pref("layout.css.report_errors", tr
 
 // Enable CSS Grid for testing
 user_pref("layout.css.grid.enabled", true);
 user_pref("layout.css.grid-template-subgrid-value.enabled", true);
 
 // Enable CSS 'contain' for testing
 user_pref("layout.css.contain.enabled", true);
 
+// Enable CSS initial-letter for testing
+user_pref("layout.css.initial-letter.enabled", true);
+
 // Enable CSS object-fit & object-position for testing
 user_pref("layout.css.object-fit-and-position.enabled", true);
 
 // Enable webkit prefixed CSS features for testing
 user_pref("layout.css.prefixes.webkit", true);
 
 // Enable -webkit-{min|max}-device-pixel-ratio media queries for testing
 user_pref("layout.css.prefixes.device-pixel-ratio-webkit", true);