diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index d9fe4ed33274..54cfec836d38 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -1311,6 +1311,28 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue, // we don't have a shorthand that can express. Bail. break; } + case eCSSProperty__webkit_text_stroke: { + const nsCSSValue* strokeWidth = + data->ValueFor(eCSSProperty__webkit_text_stroke_width); + const nsCSSValue* strokeColor = + data->ValueFor(eCSSProperty__webkit_text_stroke_color); + bool isDefaultColor = strokeColor->GetUnit() == eCSSUnit_EnumColor && + strokeColor->GetIntValue() == NS_COLOR_CURRENTCOLOR; + + if (strokeWidth->GetUnit() != eCSSUnit_Integer || + strokeWidth->GetIntValue() != 0 || isDefaultColor) { + AppendValueToString(eCSSProperty__webkit_text_stroke_width, + aValue, aSerialization); + if (!isDefaultColor) { + aValue.Append(char16_t(' ')); + } + } + if (!isDefaultColor) { + AppendValueToString(eCSSProperty__webkit_text_stroke_color, + aValue, aSerialization); + } + break; + } case eCSSProperty_all: // If we got here, then we didn't have all "inherit" or "initial" or // "unset" values for all of the longhand property components of 'all'. diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index 96c8d771c0f9..e7501a5f850b 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -3635,6 +3635,14 @@ StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty, break; } + case eCSSProperty__webkit_text_stroke_color: { + auto styleText = static_cast(styleStruct); + SetCurrentOrActualColor(styleText->mWebkitTextStrokeColorForeground, + styleText->mWebkitTextStrokeColor, + aComputedValue); + break; + } + case eCSSProperty_border_spacing: { const nsStyleTableBorder *styleTableBorder = static_cast(styleStruct); diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 046a1ca875c4..a6bf5d4ba4c0 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -1086,6 +1086,7 @@ protected: bool ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSProperty aPropID); bool ParseScrollSnapDestination(nsCSSValue& aValue); bool ParseScrollSnapCoordinate(nsCSSValue& aValue); + bool ParseWebkitTextStroke(); /** * Parses a variable value from a custom property declaration. @@ -10803,6 +10804,37 @@ CSSParserImpl::ParseWebkitGradient(nsCSSValue& aValue) return true; } +bool +CSSParserImpl::ParseWebkitTextStroke() +{ + static const nsCSSProperty kWebkitTextStrokeIDs[] = { + eCSSProperty__webkit_text_stroke_width, + eCSSProperty__webkit_text_stroke_color + }; + + const size_t numProps = ArrayLength(kWebkitTextStrokeIDs); + nsCSSValue values[numProps]; + + int32_t found = ParseChoice(values, kWebkitTextStrokeIDs, numProps); + if (found < 1) { + return false; + } + + if (!(found & 1)) { // Provide default -webkit-text-stroke-width + values[0].SetFloatValue(0, eCSSUnit_Pixel); + } + + if (!(found & 2)) { // Provide default -webkit-text-stroke-color + values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor); + } + + for (size_t index = 0; index < numProps; ++index) { + AppendValue(kWebkitTextStrokeIDs[index], values[index]); + } + + return true; +} + int32_t CSSParserImpl::ParseChoice(nsCSSValue aValues[], const nsCSSProperty aPropIDs[], int32_t aNumIDs) @@ -11576,6 +11608,8 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) case eCSSProperty_mask_size: return ParseImageLayerSize(eCSSProperty_mask_size); #endif + case eCSSProperty__webkit_text_stroke: + return ParseWebkitTextStroke(); case eCSSProperty_all: return ParseAll(); default: diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index baf1a466dfa7..8d36656be53f 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -4051,6 +4051,38 @@ CSS_PROP_TEXT( nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) +CSS_PROP_SHORTHAND( + -webkit-text-stroke, + _webkit_text_stroke, + WebkitTextStroke, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.prefixes.webkit") +CSS_PROP_TEXT( + -webkit-text-stroke-color, + _webkit_text_stroke_color, + WebkitTextStrokeColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "layout.css.prefixes.webkit", + VARIANT_HC, + nullptr, + offsetof(nsStyleText, mWebkitTextStrokeColor), + eStyleAnimType_Custom) +CSS_PROP_TEXT( + -webkit-text-stroke-width, + _webkit_text_stroke_width, + WebkitTextStrokeWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.prefixes.webkit", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) CSS_PROP_TEXT( text-transform, text_transform, diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index df11fe0ae1d1..53329292d595 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -2919,6 +2919,12 @@ static const nsCSSProperty gTextEmphasisSubpropTable[] = { eCSSProperty_UNKNOWN }; +static const nsCSSProperty gWebkitTextStrokeSubpropTable[] = { + eCSSProperty__webkit_text_stroke_width, + eCSSProperty__webkit_text_stroke_color, + eCSSProperty_UNKNOWN +}; + static const nsCSSProperty gTransitionSubpropTable[] = { eCSSProperty_transition_property, eCSSProperty_transition_duration, diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 62a686b80b34..d5e15218dd8c 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -3927,6 +3927,22 @@ nsComputedDOMStyle::DoGetWebkitTextFillColor() return val.forget(); } +already_AddRefed +nsComputedDOMStyle::DoGetWebkitTextStrokeColor() +{ + RefPtr val = new nsROCSSPrimitiveValue; + SetToRGBAColor(val, mStyleContext->GetTextStrokeColor()); + return val.forget(); +} + +already_AddRefed +nsComputedDOMStyle::DoGetWebkitTextStrokeWidth() +{ + RefPtr val = new nsROCSSPrimitiveValue; + val->SetAppUnits(StyleText()->mWebkitTextStrokeWidth.GetCoordValue()); + return val.forget(); +} + already_AddRefed nsComputedDOMStyle::DoGetPointerEvents() { diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index f12bfc528079..0bbd55d9fbeb 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -426,6 +426,8 @@ private: already_AddRefed DoGetTabSize(); already_AddRefed DoGetTextSizeAdjust(); already_AddRefed DoGetWebkitTextFillColor(); + already_AddRefed DoGetWebkitTextStrokeColor(); + already_AddRefed DoGetWebkitTextStrokeWidth(); /* Visibility properties */ already_AddRefed DoGetColorAdjust(); @@ -721,4 +723,3 @@ NS_NewComputedDOMStyle(mozilla::dom::Element* aElement, nsComputedDOMStyle::eAll); #endif /* nsComputedDOMStyle_h__ */ - diff --git a/layout/style/nsComputedDOMStylePropertyList.h b/layout/style/nsComputedDOMStylePropertyList.h index e2b8b73ffa5b..f26c8339373a 100644 --- a/layout/style/nsComputedDOMStylePropertyList.h +++ b/layout/style/nsComputedDOMStylePropertyList.h @@ -303,6 +303,9 @@ COMPUTED_STYLE_PROP(_moz_window_shadow, WindowShadow) \* ********************************** */ COMPUTED_STYLE_PROP(_webkit_text_fill_color, WebkitTextFillColor) +//// COMPUTED_STYLE_PROP(webkit-text-stroke, WebkitTextStroke) +COMPUTED_STYLE_PROP(_webkit_text_stroke_color, WebkitTextStrokeColor) +COMPUTED_STYLE_PROP(_webkit_text_stroke_width, WebkitTextStrokeWidth) /* ***************************** *\ * Implementations of SVG styles * diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 8fbb2c788475..67cc88ab626f 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -4717,6 +4717,47 @@ nsRuleNode::ComputeTextData(void* aStartStruct, text->mWebkitTextFillColor, conditions); } + // -webkit-text-stroke-color: color, string, inherit, initial + const nsCSSValue* webkitTextStrokeColorValue = + aRuleData->ValueForWebkitTextStrokeColor(); + if (webkitTextStrokeColorValue->GetUnit() == eCSSUnit_Null) { + // We don't want to change anything in this case. + } else if (webkitTextStrokeColorValue->GetUnit() == eCSSUnit_Inherit || + webkitTextStrokeColorValue->GetUnit() == eCSSUnit_Unset) { + conditions.SetUncacheable(); + text->mWebkitTextStrokeColorForeground = + parentText->mWebkitTextStrokeColorForeground; + text->mWebkitTextStrokeColor = parentText->mWebkitTextStrokeColor; + } else if ((webkitTextStrokeColorValue->GetUnit() == eCSSUnit_EnumColor && + webkitTextStrokeColorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) || + webkitTextStrokeColorValue->GetUnit() == eCSSUnit_Initial) { + text->mWebkitTextStrokeColorForeground = true; + text->mWebkitTextStrokeColor = mPresContext->DefaultColor(); + } else { + text->mWebkitTextStrokeColorForeground = false; + SetColor(*webkitTextStrokeColorValue, 0, mPresContext, aContext, + text->mWebkitTextStrokeColor, conditions); + } + + // -webkit-text-stroke-width: length, inherit, initial, enum + const nsCSSValue* + webkitTextStrokeWidthValue = aRuleData->ValueForWebkitTextStrokeWidth(); + if (webkitTextStrokeWidthValue->GetUnit() == eCSSUnit_Enumerated) { + NS_ASSERTION(webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || + webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || + webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, + "Unexpected enum value"); + text->mWebkitTextStrokeWidth.SetCoordValue( + mPresContext->GetBorderWidthTable()[webkitTextStrokeWidthValue->GetIntValue()]); + } else { + SetCoord(*webkitTextStrokeWidthValue, text->mWebkitTextStrokeWidth, + parentText->mWebkitTextStrokeWidth, + SETCOORD_LH | SETCOORD_CALC_LENGTH_ONLY | + SETCOORD_CALC_CLAMP_NONNEGATIVE | + SETCOORD_INITIAL_ZERO | SETCOORD_UNSET_INHERIT, + aContext, mPresContext, conditions); + } + // -moz-control-character-visibility: enum, inherit, initial SetDiscrete(*aRuleData->ValueForControlCharacterVisibility(), text->mControlCharacterVisibility, diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index e9faf04b4d87..9fe339c9d315 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -1134,7 +1134,10 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, thisVisText->mTextEmphasisColor != otherVisText->mTextEmphasisColor || thisVisText->mWebkitTextFillColorForeground != otherVisText->mWebkitTextFillColorForeground || - thisVisText->mWebkitTextFillColor != otherVisText->mWebkitTextFillColor) { + thisVisText->mWebkitTextFillColor != otherVisText->mWebkitTextFillColor || + thisVisText->mWebkitTextStrokeColorForeground != + otherVisText->mWebkitTextStrokeColorForeground || + thisVisText->mWebkitTextStrokeColor != otherVisText->mWebkitTextStrokeColor) { change = true; } } @@ -1354,6 +1357,7 @@ nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty) aProperty == eCSSProperty_text_decoration_color || aProperty == eCSSProperty_text_emphasis_color || aProperty == eCSSProperty__webkit_text_fill_color || + aProperty == eCSSProperty__webkit_text_stroke_color || aProperty == eCSSProperty_fill || aProperty == eCSSProperty_stroke, "we need to add to nsStyleContext::CalcStyleDifference"); diff --git a/layout/style/nsStyleContext.h b/layout/style/nsStyleContext.h index 1ee933b4bf4d..1190bbcaa952 100644 --- a/layout/style/nsStyleContext.h +++ b/layout/style/nsStyleContext.h @@ -188,6 +188,16 @@ public: ? StyleColor()->mColor : StyleText()->mWebkitTextFillColor; } + /** + * Get the color that should be used to stroke text: either + * the current foreground color, or a separately-specified text stroke color. + */ + nscolor GetTextStrokeColor() { + const nsStyleText* textStyle = StyleText(); + return textStyle->mWebkitTextStrokeColorForeground + ? StyleColor()->mColor : textStyle->mWebkitTextStrokeColor; + } + // Does this style context or any of its ancestors have text // decoration lines? // Differs from nsStyleTextReset::HasTextDecorationLines, which tests diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index c39b0fc4c01c..021f4c69b027 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -3573,6 +3573,7 @@ nsStyleText::nsStyleText(StyleStructContext aContext) mTextAlignLastTrue = false; mTextEmphasisColorForeground = true; mWebkitTextFillColorForeground = true; + mWebkitTextStrokeColorForeground = true; mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE; mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL; mWordBreak = NS_STYLE_WORDBREAK_NORMAL; @@ -3591,12 +3592,14 @@ nsStyleText::nsStyleText(StyleStructContext aContext) NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT; mTextEmphasisColor = aContext.DefaultColor(); mWebkitTextFillColor = aContext.DefaultColor(); + mWebkitTextStrokeColor = aContext.DefaultColor(); mControlCharacterVisibility = nsCSSParser::ControlCharVisibilityDefault(); mWordSpacing.SetCoordValue(0); mLetterSpacing.SetNormalValue(); mLineHeight.SetNormalValue(); mTextIndent.SetCoordValue(0); + mWebkitTextStrokeWidth.SetCoordValue(0); mTextShadow = nullptr; mTabSize = NS_STYLE_TABSIZE_INITIAL; @@ -3609,6 +3612,7 @@ nsStyleText::nsStyleText(const nsStyleText& aSource) mTextAlignLastTrue(false), mTextEmphasisColorForeground(aSource.mTextEmphasisColorForeground), mWebkitTextFillColorForeground(aSource.mWebkitTextFillColorForeground), + mWebkitTextStrokeColorForeground(aSource.mWebkitTextStrokeColorForeground), mTextTransform(aSource.mTextTransform), mWhiteSpace(aSource.mWhiteSpace), mWordBreak(aSource.mWordBreak), @@ -3625,10 +3629,12 @@ nsStyleText::nsStyleText(const nsStyleText& aSource) mTabSize(aSource.mTabSize), mTextEmphasisColor(aSource.mTextEmphasisColor), mWebkitTextFillColor(aSource.mWebkitTextFillColor), + mWebkitTextStrokeColor(aSource.mWebkitTextStrokeColor), mWordSpacing(aSource.mWordSpacing), mLetterSpacing(aSource.mLetterSpacing), mLineHeight(aSource.mLineHeight), mTextIndent(aSource.mTextIndent), + mWebkitTextStrokeWidth(aSource.mWebkitTextStrokeWidth), mTextShadow(aSource.mTextShadow), mTextEmphasisStyleString(aSource.mTextEmphasisStyleString) { @@ -3692,7 +3698,8 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const if (!AreShadowArraysEqual(mTextShadow, aOther.mTextShadow) || mTextEmphasisStyle != aOther.mTextEmphasisStyle || - mTextEmphasisStyleString != aOther.mTextEmphasisStyleString) { + mTextEmphasisStyleString != aOther.mTextEmphasisStyleString || + mWebkitTextStrokeWidth != aOther.mWebkitTextStrokeWidth) { hint |= nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint | nsChangeHint_RepaintFrame; @@ -3709,7 +3716,9 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const if (mTextEmphasisColorForeground != aOther.mTextEmphasisColorForeground || mTextEmphasisColor != aOther.mTextEmphasisColor || mWebkitTextFillColorForeground != aOther.mWebkitTextFillColorForeground || - mWebkitTextFillColor != aOther.mWebkitTextFillColor) { + mWebkitTextFillColor != aOther.mWebkitTextFillColor || + mWebkitTextStrokeColorForeground != aOther.mWebkitTextStrokeColorForeground || + mWebkitTextStrokeColor != aOther.mWebkitTextStrokeColor) { NS_UpdateHint(hint, nsChangeHint_SchedulePaint); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 58ba39b37a48..351d8bde143f 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2022,6 +2022,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText bool mTextAlignLastTrue : 1; // [inherited] see nsStyleConsts.h bool mTextEmphasisColorForeground : 1;// [inherited] whether text-emphasis-color is currentColor bool mWebkitTextFillColorForeground : 1; // [inherited] whether -webkit-text-fill-color is currentColor + bool mWebkitTextStrokeColorForeground : 1; // [inherited] whether -webkit-text-stroke-color is currentColor uint8_t mTextTransform; // [inherited] see nsStyleConsts.h uint8_t mWhiteSpace; // [inherited] see nsStyleConsts.h uint8_t mWordBreak; // [inherited] see nsStyleConsts.h @@ -2038,11 +2039,13 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText int32_t mTabSize; // [inherited] see nsStyleConsts.h nscolor mTextEmphasisColor; // [inherited] nscolor mWebkitTextFillColor; // [inherited] + nscolor mWebkitTextStrokeColor; // [inherited] nsStyleCoord mWordSpacing; // [inherited] coord, percent, calc nsStyleCoord mLetterSpacing; // [inherited] coord, normal nsStyleCoord mLineHeight; // [inherited] coord, factor, normal nsStyleCoord mTextIndent; // [inherited] coord, percent, calc + nsStyleCoord mWebkitTextStrokeWidth; // [inherited] coord RefPtr mTextShadow; // [inherited] nullptr in case of a zero-length diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 5236b28544d6..c19c0f285897 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -7040,6 +7040,33 @@ if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) { other_values: [ "red", "rgba(255,255,255,0.5)", "transparent" ], invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000", "ff00ff", "rgb(255,xxx,255)" ] }; + gCSSProperties["-webkit-text-stroke"] = { + domProp: "webkitTextStroke", + inherited: true, + type: CSS_TYPE_TRUE_SHORTHAND, + prerequisites: { "color": "black" }, + subproperties: [ "-webkit-text-stroke-width", "-webkit-text-stroke-color" ], + initial_values: [ "0 currentColor", "currentColor 0px", "0", "currentColor", "0px black" ], + other_values: [ "thin black", "#f00 medium", "thick rgba(0,0,255,0.5)", "calc(4px - 8px) green", "2px", "green 0", "currentColor 4em", "currentColor calc(5px - 1px)" ], + invalid_values: [ "-3px black", "calc(50%+ 2px) #000", "30% #f00" ] + }; + gCSSProperties["-webkit-text-stroke-color"] = { + domProp: "webkitTextStrokeColor", + inherited: true, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor", "black", "#000", "#000000", "rgb(0,0,0)" ], + other_values: [ "red", "rgba(255,255,255,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#0000", "#00000", "#0000000", "#00000000", "#000000000", "000000", "ff00ff", "rgb(255,xxx,255)" ] + }; + gCSSProperties["-webkit-text-stroke-width"] = { + domProp: "webkitTextStrokeWidth", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0px", "0em", "0ex", "calc(0pt)", "calc(4px - 8px)" ], + other_values: [ "thin", "medium", "thick", "17px", "0.2em", "calc(3*25px + 5em)", "calc(5px - 1px)" ], + invalid_values: [ "5%", "1px calc(nonsense)", "1px red", "-0.1px", "-3px", "30%" ] + }, gCSSProperties["-webkit-text-size-adjust"] = { domProp: "webkitTextSizeAdjust", inherited: true, diff --git a/layout/style/test/test_transitions_per_property.html b/layout/style/test/test_transitions_per_property.html index e1ebfd42641f..831f42e21881 100644 --- a/layout/style/test/test_transitions_per_property.html +++ b/layout/style/test/test_transitions_per_property.html @@ -272,7 +272,8 @@ var supported_properties = { test_length_clamped, test_percent_clamped ], "word-spacing": [ test_length_transition, test_length_unclamped ], "z-index": [ test_integer_transition, test_pos_integer_or_auto_transition ], - "-webkit-text-fill-color": [ test_color_transition ] + "-webkit-text-fill-color": [ test_color_transition ], + "-webkit-text-stroke-color": [ test_color_transition ] }; if (SupportsMaskShorthand()) {