source: webkit/trunk/Source/WebCore/css/StyleProperties.cpp

Last change on this file was 295476, checked in by [email protected], 3 years ago

Delay system font shorthand resolution until after parsing
https://bugs.webkit.org/show_bug.cgi?id=241454

Reviewed by Antti Koivisto.

This is the fifth piece of https://bugs.webkit.org/show_bug.cgi?id=237817, and is the main crux
of the fix to that bug. When content says something like "font: caption" or "font: -apple-system-body"
we have to map that to CSS properties like font-size and font-weight, so inheritance works properly
across different elements. On iOS, system settings can affect this mapping. Before this patch, we
were performing this mapping inside the parser, which is wrong because we'll never re-parse things
in response to a change in the environment. So, if the page is live, and then the user changes a setting
in system preferences, we won't re-parse, which means the page won't update to accomodate the new setting
the user changed.

This patch changes the parser to not do this mapping, but instead just to emit CSSValues which directly
and simply represent the value that was present in the CSS source itself. So, if the content says
"font: caption" we'll create CSSPrimitiveValues which just hold "caption" and use that for all the longhands
of the font property. Then, we do the mapping when the values are applied, inside StyleBuilder. StyleBuilder
is re-run in response to environment changes, so this piece is necessary for system settings to immediately
take effect without a reload of the page.

This change is web-exposed, because the contents of CSSValues are exposed to webpages via inspecting the
CSSStyleSheet in JavaScript. So, the change is a little scary, but I think it's the only way to have the
right thing happen with system settings.

This patch also deletes the now-unused system font shorthand cache, which was reimplemented in
https://github.com/WebKit/WebKit/commit/10cdfcb983187328f4229d5812a0da2a4210e4ef.

This patch isn't sufficient to make system settings fully work - there are some follow-up patches which
are still necessary on top of this:

  1. Have font creation code actually interrogate system settings, and react accordingly, to create fonts

with the appropriate size/weight

  1. Make sure the right things get invalidated, so we don't get erroneous cache hits when system settings

change

  1. Make sure the right events are being delivered to the right places, and triggering the right invalidation,

in response to system settings being changed.

  • LayoutTests/fast/text/font-shorthand-resolution-expected.txt: Added.
  • LayoutTests/fast/text/font-shorthand-resolution.html: Added.
  • Source/WebCore/css/CSSProperties.json:
  • Source/WebCore/css/parser/CSSPropertyParser.cpp:

(WebCore::CSSPropertyParser::consumeSystemFont):
(WebCore::CSSPropertyParser::parseShorthand):

  • Source/WebCore/css/parser/CSSPropertyParserHelpers.h:

(WebCore::CSSPropertyParserHelpers::isSystemFontShorthand):
(WebCore::CSSPropertyParserHelpers::lowerFontShorthand):

  • Source/WebCore/platform/graphics/SystemFontDatabase.h:
  • Source/WebCore/rendering/RenderTheme.h:
  • Source/WebCore/rendering/RenderThemeCocoa.h:
  • Source/WebCore/rendering/RenderThemeCocoa.mm:

(WebCore::RenderThemeCocoa::systemFont const): Deleted.

  • Source/WebCore/rendering/RenderThemeGtk.cpp:

(WebCore::RenderThemeGtk::systemFont const): Deleted.

  • Source/WebCore/rendering/RenderThemePlayStation.cpp:

(WebCore::RenderThemePlayStation::systemFont const): Deleted.

  • Source/WebCore/rendering/RenderThemeWin.cpp:

(WebCore::RenderThemeWin::systemFont const): Deleted.

  • Source/WebCore/style/StyleBuilderConverter.h:

(WebCore::Style::BuilderConverter::convertFontWeight):
(WebCore::Style::BuilderConverter::convertFontVariantCaps):
(WebCore::Style::BuilderConverter::convertLineHeight):

  • Source/WebCore/style/StyleBuilderCustom.h:

(WebCore::Style::BuilderCustom::applyValueFontFamily):
(WebCore::Style::BuilderCustom::applyValueFontStyle):
(WebCore::Style::BuilderCustom::applyValueFontSize):

  • Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::contentSizeCategoryDidChange):

  • Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm:

(WebKit::WebProcess::accessibilityPreferencesDidChange):

Canonical link: https://commits.webkit.org/251481@main

  • Property svn:eol-style set to native
File size: 83.3 KB
Line 
1/*
2 * (C) 1999-2003 Lars Knoll ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
4 * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
5 * Copyright (C) 2013 Intel Corporation. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24#include "StyleProperties.h"
25
26#include "CSSBorderImageWidthValue.h"
27#include "CSSComputedStyleDeclaration.h"
28#include "CSSCustomPropertyValue.h"
29#include "CSSGridLineNamesValue.h"
30#include "CSSGridTemplateAreasValue.h"
31#include "CSSOffsetRotateValue.h"
32#include "CSSParser.h"
33#include "CSSPendingSubstitutionValue.h"
34#include "CSSPropertyParser.h"
35#include "CSSTokenizer.h"
36#include "CSSValueKeywords.h"
37#include "CSSValueList.h"
38#include "CSSValuePool.h"
39#include "Color.h"
40#include "Document.h"
41#include "PropertySetCSSStyleDeclaration.h"
42#include "Rect.h"
43#include "StylePropertyShorthand.h"
44#include "StylePropertyShorthandFunctions.h"
45#include "StyleSheetContents.h"
46#include <bitset>
47#include <wtf/text/StringBuilder.h>
48
49#ifndef NDEBUG
50#include <stdio.h>
51#include <wtf/text/CString.h>
52#endif
53
54namespace WebCore {
55
56DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(StyleProperties);
57DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(ImmutableStyleProperties);
58DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(MutableStyleProperties);
59
60static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count)
61{
62 return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(StylePropertyMetadata) * count + sizeof(PackedPtr<const CSSValue>) * count;
63}
64
65static bool isCSSWideValueKeyword(StringView value)
66{
67 return value == "initial"_s || value == "inherit"_s || value == "unset"_s || value == "revert"_s;
68}
69
70static bool isNoneValue(const RefPtr<CSSValue>& value)
71{
72 return value && value->isPrimitiveValue() && downcast<CSSPrimitiveValue>(value.get())->isValueID() && downcast<CSSPrimitiveValue>(value.get())->valueID() == CSSValueNone;
73}
74
75static bool isValueID(const Ref<CSSValue>& value, CSSValueID id)
76{
77 return value->isPrimitiveValue() && downcast<CSSPrimitiveValue>(value.get()).isValueID() && downcast<CSSPrimitiveValue>(value.get()).valueID() == id;
78}
79
80static bool isValueID(const RefPtr<CSSValue>& value, CSSValueID id)
81{
82 return value && isValueID(*value, id);
83}
84
85Ref<ImmutableStyleProperties> ImmutableStyleProperties::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
86{
87 void* slot = ImmutableStylePropertiesMalloc::malloc(sizeForImmutableStylePropertiesWithPropertyCount(count));
88 return adoptRef(*new (NotNull, slot) ImmutableStyleProperties(properties, count, cssParserMode));
89}
90
91Ref<ImmutableStyleProperties> StyleProperties::immutableCopyIfNeeded() const
92{
93 if (is<ImmutableStyleProperties>(*this))
94 return downcast<ImmutableStyleProperties>(const_cast<StyleProperties&>(*this));
95 const MutableStyleProperties& mutableThis = downcast<MutableStyleProperties>(*this);
96 return ImmutableStyleProperties::create(mutableThis.m_propertyVector.data(), mutableThis.m_propertyVector.size(), cssParserMode());
97}
98
99MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode)
100 : StyleProperties(cssParserMode, MutablePropertiesType)
101{
102}
103
104MutableStyleProperties::MutableStyleProperties(Vector<CSSProperty>&& properties)
105 : StyleProperties(HTMLStandardMode, MutablePropertiesType)
106 , m_propertyVector(WTFMove(properties))
107{
108}
109
110MutableStyleProperties::~MutableStyleProperties() = default;
111
112ImmutableStyleProperties::ImmutableStyleProperties(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
113 : StyleProperties(cssParserMode, length)
114{
115 StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(this->metadataArray());
116 PackedPtr<CSSValue>* valueArray = bitwise_cast<PackedPtr<CSSValue>*>(this->valueArray());
117 for (unsigned i = 0; i < length; ++i) {
118 metadataArray[i] = properties[i].metadata();
119 auto* value = properties[i].value();
120 valueArray[i] = value;
121 value->ref();
122 }
123}
124
125ImmutableStyleProperties::~ImmutableStyleProperties()
126{
127 PackedPtr<CSSValue>* valueArray = bitwise_cast<PackedPtr<CSSValue>*>(this->valueArray());
128 for (unsigned i = 0; i < m_arraySize; ++i)
129 valueArray[i]->deref();
130}
131
132MutableStyleProperties::MutableStyleProperties(const StyleProperties& other)
133 : StyleProperties(other.cssParserMode(), MutablePropertiesType)
134{
135 if (is<MutableStyleProperties>(other))
136 m_propertyVector = downcast<MutableStyleProperties>(other).m_propertyVector;
137 else {
138 const auto& immutableOther = downcast<ImmutableStyleProperties>(other);
139 unsigned propertyCount = immutableOther.propertyCount();
140 m_propertyVector.reserveInitialCapacity(propertyCount);
141 for (unsigned i = 0; i < propertyCount; ++i)
142 m_propertyVector.uncheckedAppend(immutableOther.propertyAt(i).toCSSProperty());
143 }
144}
145
146bool StyleProperties::shorthandHasVariableReference(CSSPropertyID propertyID, String& shorthandValue) const
147{
148 auto shorthand = shorthandForProperty(propertyID);
149 if (shorthand.length()) {
150 size_t numSetFromShorthand = 0;
151 // Checks for shorthand property if any of its longhand properties have set to a variable
152 // or are all pending substitution
153 for (size_t i = 0; i < shorthand.length(); i++) {
154 auto cssPropertyValue = getPropertyCSSValue(shorthand.properties()[i]);
155
156 auto hasBeenSetFromLonghand = is<CSSVariableReferenceValue>(cssPropertyValue);
157 auto hasBeenSetFromShorthand = is<CSSPendingSubstitutionValue>(cssPropertyValue);
158 auto hasNotBeenSetFromRequestedShorthand = hasBeenSetFromShorthand && downcast<CSSPendingSubstitutionValue>(*cssPropertyValue).shorthandPropertyId() != propertyID;
159
160 // Request for shorthand value should return empty string if any longhand values have been
161 // set to a variable or if they were set to a variable by a different shorthand.
162 if (hasBeenSetFromLonghand || hasNotBeenSetFromRequestedShorthand)
163 return true;
164 if (hasBeenSetFromShorthand)
165 numSetFromShorthand += 1;
166 }
167 if (numSetFromShorthand) {
168 if (numSetFromShorthand != shorthand.length())
169 return true;
170 shorthandValue = downcast<CSSPendingSubstitutionValue>(* getPropertyCSSValue(shorthand.properties()[0])).shorthandValue().cssText();
171 return true;
172 }
173 }
174 return false;
175}
176
177String StyleProperties::getPropertyValue(CSSPropertyID propertyID, Document* document) const
178{
179 if (auto value = getPropertyCSSValue(propertyID)) {
180 switch (propertyID) {
181 case CSSPropertyFillOpacity:
182 case CSSPropertyFloodOpacity:
183 case CSSPropertyOpacity:
184 case CSSPropertyStopOpacity:
185 case CSSPropertyStrokeOpacity:
186 // Opacity percentage values serialize as a fraction in the range 0-1, not "%".
187 if (is<CSSPrimitiveValue>(*value) && downcast<CSSPrimitiveValue>(*value).isPercentage())
188 return makeString(downcast<CSSPrimitiveValue>(*value).doubleValue() / 100);
189 FALLTHROUGH;
190 default:
191 return value->cssText(document);
192 }
193 }
194
195 {
196 auto shorthandValue = String();
197 if (shorthandHasVariableReference(propertyID, shorthandValue))
198 return shorthandValue;
199 }
200
201 // Shorthand and 4-values properties
202 switch (propertyID) {
203 case CSSPropertyAll:
204 return getCommonValue(allShorthand());
205 case CSSPropertyAnimation:
206 return getLayeredShorthandValue(animationShorthand());
207 case CSSPropertyBorderSpacing:
208 return borderSpacingValue(borderSpacingShorthand());
209 case CSSPropertyBackgroundPosition:
210 return getLayeredShorthandValue(backgroundPositionShorthand());
211 case CSSPropertyBackgroundRepeat:
212 return getLayeredShorthandValue(backgroundRepeatShorthand());
213 case CSSPropertyBackground:
214 return getLayeredShorthandValue(backgroundShorthand());
215 case CSSPropertyBorder:
216 return borderPropertyValue(borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand());
217 case CSSPropertyBorderTop:
218 return getShorthandValue(borderTopShorthand());
219 case CSSPropertyBorderRight:
220 return getShorthandValue(borderRightShorthand());
221 case CSSPropertyBorderBottom:
222 return getShorthandValue(borderBottomShorthand());
223 case CSSPropertyBorderLeft:
224 return getShorthandValue(borderLeftShorthand());
225 case CSSPropertyBorderBlock:
226 return borderPropertyValue(borderBlockWidthShorthand(), borderBlockStyleShorthand(), borderBlockColorShorthand());
227 case CSSPropertyBorderBlockColor:
228 return get2Values(borderBlockColorShorthand());
229 case CSSPropertyBorderBlockStyle:
230 return get2Values(borderBlockStyleShorthand());
231 case CSSPropertyBorderBlockWidth:
232 return get2Values(borderBlockWidthShorthand());
233 case CSSPropertyBorderBlockStart:
234 return getShorthandValue(borderBlockStartShorthand());
235 case CSSPropertyBorderBlockEnd:
236 return getShorthandValue(borderBlockEndShorthand());
237 case CSSPropertyBorderImage:
238 case CSSPropertyWebkitBorderImage:
239 return borderImagePropertyValue(propertyID);
240 case CSSPropertyBorderInline:
241 return borderPropertyValue(borderInlineWidthShorthand(), borderInlineStyleShorthand(), borderInlineColorShorthand());
242 case CSSPropertyBorderInlineColor:
243 return get2Values(borderInlineColorShorthand());
244 case CSSPropertyBorderInlineStyle:
245 return get2Values(borderInlineStyleShorthand());
246 case CSSPropertyBorderInlineWidth:
247 return get2Values(borderInlineWidthShorthand());
248 case CSSPropertyBorderInlineStart:
249 return getShorthandValue(borderInlineStartShorthand());
250 case CSSPropertyBorderInlineEnd:
251 return getShorthandValue(borderInlineEndShorthand());
252 case CSSPropertyOffset:
253 return offsetValue();
254 case CSSPropertyOutline:
255 return getShorthandValue(outlineShorthand());
256 case CSSPropertyBorderColor:
257 return get4Values(borderColorShorthand());
258 case CSSPropertyBorderWidth:
259 return get4Values(borderWidthShorthand());
260 case CSSPropertyBorderStyle:
261 return get4Values(borderStyleShorthand());
262 case CSSPropertyColumnRule:
263 return getShorthandValue(columnRuleShorthand());
264 case CSSPropertyColumns:
265 return getShorthandValue(columnsShorthand());
266 case CSSPropertyContainer:
267 if (auto type = getPropertyCSSValue(CSSPropertyContainerType)) {
268 if (isNoneValue(type)) {
269 if (auto name = getPropertyCSSValue(CSSPropertyContainerName))
270 return name->cssText();
271 return { };
272 }
273 }
274 return getShorthandValue(containerShorthand(), " / ");
275 case CSSPropertyFlex:
276 return getShorthandValue(flexShorthand());
277 case CSSPropertyFlexFlow:
278 return getShorthandValue(flexFlowShorthand());
279 case CSSPropertyGridArea:
280 return getGridShorthandValue(gridAreaShorthand());
281 case CSSPropertyGridTemplate:
282 return getGridTemplateValue();
283 case CSSPropertyGrid:
284 return getGridValue();
285 case CSSPropertyGridColumn:
286 return getGridShorthandValue(gridColumnShorthand());
287 case CSSPropertyGridRow:
288 return getGridShorthandValue(gridRowShorthand());
289 case CSSPropertyPageBreakAfter:
290 return pageBreakPropertyValue(pageBreakAfterShorthand());
291 case CSSPropertyPageBreakBefore:
292 return pageBreakPropertyValue(pageBreakBeforeShorthand());
293 case CSSPropertyPageBreakInside:
294 return pageBreakPropertyValue(pageBreakInsideShorthand());
295 case CSSPropertyPlaceContent:
296 return getAlignmentShorthandValue(placeContentShorthand());
297 case CSSPropertyPlaceItems:
298 return getAlignmentShorthandValue(placeItemsShorthand());
299 case CSSPropertyPlaceSelf:
300 return getAlignmentShorthandValue(placeSelfShorthand());
301 case CSSPropertyFont:
302 return fontValue();
303 case CSSPropertyFontVariant:
304 return fontVariantValue();
305 case CSSPropertyTextDecoration:
306 if (auto line = getPropertyCSSValue(CSSPropertyTextDecorationLine))
307 return line->cssText();
308 return String();
309 case CSSPropertyWebkitTextDecoration:
310 return getShorthandValue(webkitTextDecorationShorthand());
311 case CSSPropertyTextDecorationSkip:
312 return textDecorationSkipValue();
313 case CSSPropertyInset:
314 return get4Values(insetShorthand());
315 case CSSPropertyInsetBlock:
316 return get2Values(insetBlockShorthand());
317 case CSSPropertyInsetInline:
318 return get2Values(insetInlineShorthand());
319 case CSSPropertyMargin:
320 return get4Values(marginShorthand());
321 case CSSPropertyMarginBlock:
322 return get2Values(marginBlockShorthand());
323 case CSSPropertyMarginInline:
324 return get2Values(marginInlineShorthand());
325 case CSSPropertyOverflow:
326 return get2Values(overflowShorthand());
327 case CSSPropertyOverscrollBehavior:
328 return get2Values(overscrollBehaviorShorthand());
329 case CSSPropertyPadding:
330 return get4Values(paddingShorthand());
331 case CSSPropertyPaddingBlock:
332 return get2Values(paddingBlockShorthand());
333 case CSSPropertyPaddingInline:
334 return get2Values(paddingInlineShorthand());
335 case CSSPropertyTransition:
336 return getLayeredShorthandValue(transitionShorthand());
337 case CSSPropertyListStyle:
338 return getShorthandValue(listStyleShorthand());
339 case CSSPropertyMaskPosition:
340 return getLayeredShorthandValue(maskPositionShorthand());
341 case CSSPropertyWebkitMaskPosition:
342 return getLayeredShorthandValue(webkitMaskPositionShorthand());
343 case CSSPropertyMaskRepeat:
344 return getLayeredShorthandValue(maskRepeatShorthand());
345 case CSSPropertyMask:
346 case CSSPropertyWebkitMask:
347 return getLayeredShorthandValue(shorthandForProperty(propertyID));
348 case CSSPropertyTextEmphasis:
349 return getShorthandValue(textEmphasisShorthand());
350 case CSSPropertyWebkitTextStroke:
351 return getShorthandValue(webkitTextStrokeShorthand());
352 case CSSPropertyPerspectiveOrigin:
353 return getShorthandValue(perspectiveOriginShorthand());
354 case CSSPropertyTransformOrigin:
355 return getShorthandValue(transformOriginShorthand());
356 case CSSPropertyMarker:
357 if (auto value = getPropertyCSSValue(CSSPropertyMarkerStart))
358 return value->cssText();
359 return String();
360 case CSSPropertyBorderRadius:
361 return get4Values(borderRadiusShorthand());
362 case CSSPropertyGap:
363 return get2Values(gapShorthand());
364 case CSSPropertyScrollMargin:
365 return get4Values(scrollMarginShorthand());
366 case CSSPropertyScrollMarginBlock:
367 return get2Values(scrollMarginBlockShorthand());
368 case CSSPropertyScrollMarginInline:
369 return get2Values(scrollMarginInlineShorthand());
370 case CSSPropertyScrollPadding:
371 return get4Values(scrollPaddingShorthand());
372 case CSSPropertyScrollPaddingBlock:
373 return get2Values(scrollPaddingBlockShorthand());
374 case CSSPropertyScrollPaddingInline:
375 return get2Values(scrollPaddingInlineShorthand());
376 case CSSPropertyWebkitTextOrientation:
377 return getPropertyValue(CSSPropertyTextOrientation);
378 case CSSPropertyContainIntrinsicSize:
379 return get2Values(containIntrinsicSizeShorthand());
380 default:
381 return String();
382 }
383}
384
385std::optional<Color> StyleProperties::propertyAsColor(CSSPropertyID property) const
386{
387 auto colorValue = getPropertyCSSValue(property);
388 if (!is<CSSPrimitiveValue>(colorValue))
389 return std::nullopt;
390
391 auto& primitiveColor = downcast<CSSPrimitiveValue>(*colorValue);
392 return primitiveColor.isRGBColor() ? primitiveColor.color() : CSSParser::parseColorWithoutContext(colorValue->cssText());
393}
394
395CSSValueID StyleProperties::propertyAsValueID(CSSPropertyID property) const
396{
397 auto cssValue = getPropertyCSSValue(property);
398 return is<CSSPrimitiveValue>(cssValue) ? downcast<CSSPrimitiveValue>(*cssValue).valueID() : CSSValueInvalid;
399}
400
401String StyleProperties::getCustomPropertyValue(const String& propertyName) const
402{
403 RefPtr<CSSValue> value = getCustomPropertyCSSValue(propertyName);
404 if (value)
405 return value->cssText();
406 return String();
407}
408
409String StyleProperties::borderSpacingValue(const StylePropertyShorthand& shorthand) const
410{
411 auto horizontalValue = getPropertyCSSValue(shorthand.properties()[0]);
412 auto verticalValue = getPropertyCSSValue(shorthand.properties()[1]);
413
414 // While standard border-spacing property does not allow specifying border-spacing-vertical without
415 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
416 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
417 if (!horizontalValue || !verticalValue)
418 return String();
419
420 String horizontalValueCSSText = horizontalValue->cssText();
421 String verticalValueCSSText = verticalValue->cssText();
422 if (horizontalValueCSSText == verticalValueCSSText)
423 return horizontalValueCSSText;
424 return horizontalValueCSSText + ' ' + verticalValueCSSText;
425}
426
427void StyleProperties::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
428{
429 int foundPropertyIndex = findPropertyIndex(propertyID);
430 if (foundPropertyIndex == -1)
431 return; // All longhands must have at least implicit values if "font" is specified.
432
433 if (propertyAt(foundPropertyIndex).isImplicit()) {
434 commonValue = String();
435 return;
436 }
437
438 char prefix = '\0';
439 switch (propertyID) {
440 case CSSPropertyFontStyle:
441 break; // No prefix.
442 case CSSPropertyFontFamily:
443 case CSSPropertyFontVariantAlternates:
444 case CSSPropertyFontVariantCaps:
445 case CSSPropertyFontVariantLigatures:
446 case CSSPropertyFontVariantNumeric:
447 case CSSPropertyFontVariantPosition:
448 case CSSPropertyFontVariantEastAsian:
449 case CSSPropertyFontWeight:
450 case CSSPropertyFontStretch:
451 prefix = ' ';
452 break;
453 case CSSPropertyLineHeight:
454 prefix = '/';
455 break;
456 default:
457 ASSERT_NOT_REACHED();
458 }
459
460 if (prefix && !result.isEmpty())
461 result.append(prefix);
462 String value = propertyAt(foundPropertyIndex).value()->cssText();
463 result.append(value);
464 if (!commonValue.isNull() && commonValue != value)
465 commonValue = String();
466}
467
468std::optional<CSSValueID> StyleProperties::isSingleFontShorthand() const
469{
470 // Intentionally don't check font-stretch here, because it isn't set by the font shorthand in CSSPropertyParser::consumeSystemFont().
471
472 auto sizePropertyIndex = findPropertyIndex(CSSPropertyFontSize);
473 auto familyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily);
474 auto stylePropertyIndex = findPropertyIndex(CSSPropertyFontStyle);
475 auto variantCapsPropertyIndex = findPropertyIndex(CSSPropertyFontVariantCaps);
476 auto weightPropertyIndex = findPropertyIndex(CSSPropertyFontWeight);
477 auto lineHeightPropertyIndex = findPropertyIndex(CSSPropertyLineHeight);
478
479 if (sizePropertyIndex == -1
480 || familyPropertyIndex == -1
481 || stylePropertyIndex == -1
482 || variantCapsPropertyIndex == -1
483 || weightPropertyIndex == -1
484 || lineHeightPropertyIndex == -1)
485 return std::nullopt;
486
487 auto sizeProperty = propertyAt(sizePropertyIndex);
488 auto familyProperty = propertyAt(familyPropertyIndex);
489 auto styleProperty = propertyAt(stylePropertyIndex);
490 auto variantCapsProperty = propertyAt(variantCapsPropertyIndex);
491 auto weightProperty = propertyAt(weightPropertyIndex);
492 auto lineHeightProperty = propertyAt(lineHeightPropertyIndex);
493
494 if (sizeProperty.isImplicit()
495 || familyProperty.isImplicit()
496 || styleProperty.isImplicit()
497 || variantCapsProperty.isImplicit()
498 || weightProperty.isImplicit()
499 || lineHeightProperty.isImplicit())
500 return std::nullopt;
501
502 auto* sizeValue = sizeProperty.value();
503 auto* familyValue = familyProperty.value();
504 auto* styleValue = styleProperty.value();
505 auto* variantCapsValue = variantCapsProperty.value();
506 auto* weightValue = weightProperty.value();
507 auto* lineHeightValue = lineHeightProperty.value();
508
509 if (!is<CSSPrimitiveValue>(sizeValue)
510 || !is<CSSPrimitiveValue>(familyValue)
511 || !is<CSSPrimitiveValue>(styleValue)
512 || !is<CSSPrimitiveValue>(variantCapsValue)
513 || !is<CSSPrimitiveValue>(weightValue)
514 || !is<CSSPrimitiveValue>(lineHeightValue))
515 return std::nullopt;
516
517 auto& sizePrimitiveValue = downcast<CSSPrimitiveValue>(*sizeValue);
518 auto& familyPrimitiveValue = downcast<CSSPrimitiveValue>(*familyValue);
519 auto& stylePrimitiveValue = downcast<CSSPrimitiveValue>(*styleValue);
520 auto& variantCapsPrimitiveValue = downcast<CSSPrimitiveValue>(*variantCapsValue);
521 auto& weightPrimitiveValue = downcast<CSSPrimitiveValue>(*weightValue);
522 auto& lineHeightPrimitiveValue = downcast<CSSPrimitiveValue>(*lineHeightValue);
523
524 auto sizeValueID = sizePrimitiveValue.valueID();
525 auto familyValueID = familyPrimitiveValue.valueID();
526 auto styleValueID = stylePrimitiveValue.valueID();
527 auto variantCapsValueID = variantCapsPrimitiveValue.valueID();
528 auto weightValueID = weightPrimitiveValue.valueID();
529 auto lineHeightValueID = lineHeightPrimitiveValue.valueID();
530
531 if (sizeValueID != familyValueID
532 || sizeValueID != styleValueID
533 || sizeValueID != variantCapsValueID
534 || sizeValueID != weightValueID
535 || sizeValueID != lineHeightValueID)
536 return std::nullopt;
537
538 if (sizeValueID == CSSValueInvalid)
539 return std::nullopt;
540
541 return sizeValueID;
542}
543
544String StyleProperties::fontValue() const
545{
546 int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize);
547 int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily);
548 if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
549 return emptyString();
550
551 PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex);
552 PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex);
553 if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
554 return emptyString();
555
556 if (auto shorthand = isSingleFontShorthand())
557 return getValueNameAtomString(shorthand.value());
558
559 String commonValue = fontSizeProperty.value()->cssText();
560 StringBuilder result;
561 appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
562 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantCaps, result, commonValue);
563 appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
564 appendFontLonghandValueIfExplicit(CSSPropertyFontStretch, result, commonValue);
565 if (!result.isEmpty())
566 result.append(' ');
567 result.append(fontSizeProperty.value()->cssText());
568 appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
569 if (!result.isEmpty())
570 result.append(' ');
571 result.append(fontFamilyProperty.value()->cssText());
572 if (isCSSWideValueKeyword(commonValue))
573 return commonValue;
574 return result.toString();
575}
576
577String StyleProperties::offsetValue() const
578{
579 StringBuilder result;
580
581 auto offsetPositionIndex = findPropertyIndex(CSSPropertyOffsetPosition);
582 auto offsetPathIndex = findPropertyIndex(CSSPropertyOffsetPath);
583
584 // Either offset-position and offset-path must be specified.
585 if (offsetPositionIndex == -1 && offsetPathIndex == -1)
586 return String();
587
588 if (offsetPositionIndex != -1) {
589 auto offsetPosition = propertyAt(offsetPositionIndex);
590 if (!offsetPosition.isImplicit()) {
591 if (!offsetPosition.value())
592 return String();
593
594 result.append(offsetPosition.value()->cssText());
595 }
596 }
597
598 if (offsetPathIndex != -1) {
599 auto offsetPath = propertyAt(offsetPathIndex);
600 if (!offsetPath.isImplicit()) {
601 if (!offsetPath.value())
602 return String();
603
604 if (!result.isEmpty())
605 result.append(' ');
606 result.append(offsetPath.value()->cssText());
607 }
608 }
609
610 // At this point, result is not empty because either offset-position or offset-path
611 // must be present.
612
613 auto offsetDistanceIndex = findPropertyIndex(CSSPropertyOffsetDistance);
614 if (offsetDistanceIndex != -1) {
615 auto offsetDistance = propertyAt(offsetDistanceIndex);
616 if (!offsetDistance.isImplicit()) {
617 auto offsetDistanceValue = offsetDistance.value();
618 if (!offsetDistanceValue || !is<CSSPrimitiveValue>(offsetDistanceValue))
619 return String();
620 // Only include offset-distance if the distance is non-zero.
621 // isZero() returns std::nullopt if offsetDistanceValue is a calculated value, in which case
622 // we use value_or() to override to false.
623 if (!(downcast<CSSPrimitiveValue>(offsetDistanceValue)->isZero().value_or(false))) {
624 result.append(' ');
625 result.append(downcast<CSSPrimitiveValue>(offsetDistanceValue)->cssText());
626 }
627 }
628 }
629
630 auto offsetRotateIndex = findPropertyIndex(CSSPropertyOffsetRotate);
631 if (offsetRotateIndex != -1) {
632 auto offsetRotate = propertyAt(offsetRotateIndex);
633 if (!offsetRotate.isImplicit()) {
634 auto offsetRotateValue = offsetRotate.value();
635 if (!offsetRotateValue || !is<CSSOffsetRotateValue>(offsetRotateValue))
636 return String();
637
638 if (!(downcast<CSSOffsetRotateValue>(offsetRotateValue)->isInitialValue())) {
639 result.append(' ');
640 result.append(downcast<CSSOffsetRotateValue>(offsetRotateValue)->cssText());
641 }
642 }
643 }
644
645 auto offsetAnchorIndex = findPropertyIndex(CSSPropertyOffsetAnchor);
646 if (offsetAnchorIndex != -1) {
647 auto offsetAnchor = propertyAt(offsetAnchorIndex);
648 if (!offsetAnchor.isImplicit()) {
649 auto offsetAnchorValue = offsetAnchor.value();
650 if (!offsetAnchorValue)
651 return String();
652
653 if (!is<CSSPrimitiveValue>(offsetAnchorValue) || !downcast<CSSPrimitiveValue>(offsetAnchorValue)->isValueID()
654 || downcast<CSSPrimitiveValue>(offsetAnchorValue)->valueID() != CSSValueAuto) {
655 result.append(" / ");
656 result.append(offsetAnchorValue->cssText());
657 }
658 }
659 }
660
661 return result.toString();
662}
663
664String StyleProperties::textDecorationSkipValue() const
665{
666 int textDecorationSkipInkPropertyIndex = findPropertyIndex(CSSPropertyTextDecorationSkipInk);
667 if (textDecorationSkipInkPropertyIndex == -1)
668 return emptyString();
669 PropertyReference textDecorationSkipInkProperty = propertyAt(textDecorationSkipInkPropertyIndex);
670 if (textDecorationSkipInkProperty.isImplicit())
671 return emptyString();
672 return textDecorationSkipInkProperty.value()->cssText();
673}
674
675String StyleProperties::fontVariantValue() const
676{
677 String commonValue;
678 StringBuilder result;
679 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantLigatures, result, commonValue);
680 if (isCSSWideValueKeyword(result.toString()))
681 return result.toString();
682 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantAlternates, result, commonValue);
683 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantCaps, result, commonValue);
684 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantEastAsian, result, commonValue);
685 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantNumeric, result, commonValue);
686 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantPosition, result, commonValue);
687 return result.toString();
688}
689
690String StyleProperties::get2Values(const StylePropertyShorthand& shorthand) const
691{
692 // Assume the properties are in the usual order start, end.
693 int startValueIndex = findPropertyIndex(shorthand.properties()[0]);
694 int endValueIndex = findPropertyIndex(shorthand.properties()[1]);
695
696 if (startValueIndex == -1 || endValueIndex == -1)
697 return { };
698
699 auto start = propertyAt(startValueIndex);
700 auto end = propertyAt(endValueIndex);
701
702 // All 2 properties must be specified.
703 if (!start.value() || !end.value())
704 return { };
705
706 // Important flags must be the same
707 if (start.isImportant() != end.isImportant())
708 return { };
709
710 if (start.isInherited() && end.isInherited())
711 return getValueName(CSSValueInherit);
712
713 if (start.value()->isInitialValue() || end.value()->isInitialValue()) {
714 if (start.value()->isInitialValue() && end.value()->isInitialValue() && !start.isImplicit())
715 return getValueName(CSSValueInitial);
716 return { };
717 }
718
719 StringBuilder result;
720 result.append(start.value()->cssText());
721 if (!start.value()->equals(*end.value())) {
722 result.append(' ');
723 result.append(end.value()->cssText());
724 }
725 return result.toString();
726}
727
728String StyleProperties::get4Values(const StylePropertyShorthand& shorthand) const
729{
730 // Assume the properties are in the usual order top, right, bottom, left.
731 int topValueIndex = findPropertyIndex(shorthand.properties()[0]);
732 int rightValueIndex = findPropertyIndex(shorthand.properties()[1]);
733 int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]);
734 int leftValueIndex = findPropertyIndex(shorthand.properties()[3]);
735
736 if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
737 return String();
738
739 PropertyReference top = propertyAt(topValueIndex);
740 PropertyReference right = propertyAt(rightValueIndex);
741 PropertyReference bottom = propertyAt(bottomValueIndex);
742 PropertyReference left = propertyAt(leftValueIndex);
743
744 // All 4 properties must be specified.
745 if (!top.value() || !right.value() || !bottom.value() || !left.value())
746 return String();
747
748 // Important flags must be the same
749 if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
750 return String();
751
752 if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
753 return getValueName(CSSValueInherit);
754
755 if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
756 if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
757 // All components are "initial" and "top" is not implicit.
758 return getValueName(CSSValueInitial);
759 }
760 return String();
761 }
762
763 bool showLeft = !right.value()->equals(*left.value());
764 bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
765 bool showRight = !top.value()->equals(*right.value()) || showBottom;
766
767 StringBuilder result;
768 result.append(top.value()->cssText());
769 if (showRight) {
770 result.append(' ');
771 result.append(right.value()->cssText());
772 }
773 if (showBottom) {
774 result.append(' ');
775 result.append(bottom.value()->cssText());
776 }
777 if (showLeft) {
778 result.append(' ');
779 result.append(left.value()->cssText());
780 }
781 return result.toString();
782}
783
784String StyleProperties::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
785{
786 StringBuilder result;
787
788 const unsigned size = shorthand.length();
789 Vector<RefPtr<CSSValue>> values(size);
790 size_t numLayers = 0;
791
792 for (unsigned i = 0; i < size; ++i) {
793 values[i] = getPropertyCSSValue(shorthand.properties()[i]);
794 if (!values[i]) {
795 // We don't have all longhand properties defined as required for the shorthand
796 // property and thus should not serialize to a shorthand value. See spec at
797 // https://www.w3.org/TR/cssom-1/#serialize-a-css-declaration-block
798 return String();
799 }
800 if (values[i]->isBaseValueList())
801 numLayers = std::max(downcast<CSSValueList>(*values[i]).length(), numLayers);
802 else
803 numLayers = std::max<size_t>(1U, numLayers);
804 }
805
806 String commonValue;
807
808 // Now stitch the properties together.
809 // Implicit initial values are flagged as such and can safely be omitted.
810 for (size_t i = 0; i < numLayers; i++) {
811 StringBuilder layerResult;
812 bool useRepeatXShorthand = false;
813 bool useRepeatYShorthand = false;
814 bool useSingleWordShorthand = false;
815 bool foundPositionYCSSProperty = false;
816 for (unsigned j = 0; j < size; j++) {
817 auto property = shorthand.properties()[j];
818
819 auto value = values[j];
820 if (value) {
821 if (value->isBaseValueList())
822 value = downcast<CSSValueList>(*value).item(i);
823 else {
824 // Color only belongs in the last layer.
825 if (property == CSSPropertyBackgroundColor) {
826 if (i != numLayers - 1)
827 value = nullptr;
828 } else if (i) // Other singletons only belong in the first layer.
829 value = nullptr;
830 }
831 }
832
833 // We need to report background-repeat as it was written in the CSS.
834 // If the property is implicit, then it was written with only one value. Here we figure out which value that was so we can report back correctly.
835 if (value && j < size - 1 && (property == CSSPropertyBackgroundRepeatX || property == CSSPropertyMaskRepeatX) && isPropertyImplicit(property)) {
836 // Make sure the value was not reset in the layer check just above.
837 auto nextProperty = shorthand.properties()[j + 1];
838 if (nextProperty == CSSPropertyBackgroundRepeatY || nextProperty == CSSPropertyMaskRepeatY) {
839 if (auto yValue = values[j + 1]) {
840 if (is<CSSValueList>(*yValue))
841 yValue = downcast<CSSValueList>(*yValue).itemWithoutBoundsCheck(i);
842 if (!is<CSSPrimitiveValue>(*value) || !is<CSSPrimitiveValue>(*yValue))
843 continue;
844
845 auto xId = downcast<CSSPrimitiveValue>(*value).valueID();
846 auto yId = downcast<CSSPrimitiveValue>(*yValue).valueID();
847 if (xId != yId) {
848 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
849 useRepeatXShorthand = true;
850 ++j;
851 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
852 useRepeatYShorthand = true;
853 continue;
854 }
855 } else {
856 useSingleWordShorthand = true;
857 ++j;
858 }
859 }
860 }
861 }
862
863 auto canOmitValue = [&]() {
864 if (shorthand.id() == CSSPropertyMask) {
865 if (property == CSSPropertyMaskClip) {
866 // If the mask-clip value is the same as the value for mask-origin (the previous value),
867 // then we can skip serializing it, as one value sets both properties.
868 ASSERT(j > 0);
869 ASSERT(shorthand.properties()[j - 1] == CSSPropertyMaskOrigin);
870 auto originValue = values[j - 1];
871 if (is<CSSValueList>(*originValue))
872 originValue = downcast<CSSValueList>(*originValue).itemWithoutBoundsCheck(i);
873 if (!is<CSSPrimitiveValue>(*value) || !is<CSSPrimitiveValue>(*originValue))
874 return false;
875
876 auto maskId = downcast<CSSPrimitiveValue>(*value).valueID();
877 auto originId = downcast<CSSPrimitiveValue>(*originValue).valueID();
878 return maskId == originId && (!isCSSWideValueKeyword(StringView { getValueName(maskId) }) || value->isImplicitInitialValue());
879 }
880 if (property == CSSPropertyMaskOrigin) {
881 // We can skip serializing mask-origin if it's the initial value, but only if we're also going to skip serializing
882 // the mask-clip as well (otherwise the single value for mask-clip would be assumed to be setting the value for both).
883 ASSERT(j + 1 < size);
884 ASSERT(shorthand.properties()[j + 1] == CSSPropertyMaskClip);
885 auto clipValue = values[j + 1];
886 if (is<CSSValueList>(*clipValue))
887 clipValue = downcast<CSSValueList>(*clipValue).itemWithoutBoundsCheck(i);
888 return value->isImplicitInitialValue() && clipValue->isImplicitInitialValue();
889 }
890 }
891
892 return value->isImplicitInitialValue();
893 };
894
895 String valueText;
896 if (value && !canOmitValue()) {
897 if (!layerResult.isEmpty())
898 layerResult.append(' ');
899
900 if (property == CSSPropertyBackgroundSize || property == CSSPropertyMaskSize) {
901 if (!foundPositionYCSSProperty)
902 continue;
903 layerResult.append("/ ");
904 }
905
906 if (useRepeatXShorthand) {
907 useRepeatXShorthand = false;
908 layerResult.append(getValueName(CSSValueRepeatX));
909 } else if (useRepeatYShorthand) {
910 useRepeatYShorthand = false;
911 layerResult.append(getValueName(CSSValueRepeatY));
912 } else if (shorthand.id() == CSSPropertyMask && property == CSSPropertyMaskOrigin && value->isImplicitInitialValue()) {
913 // If we're about to write the value for mask-origin, but it's an implicit initial value that's just a placeholder
914 // for a 'real' mask-clip value, then write the actual value not 'initial'.
915 layerResult.append(getValueName(CSSValueBorderBox));
916 } else {
917 if (useSingleWordShorthand)
918 useSingleWordShorthand = false;
919 valueText = value->cssText();
920 layerResult.append(valueText);
921 }
922
923 if (property == CSSPropertyBackgroundPositionY || property == CSSPropertyWebkitMaskPositionY)
924 foundPositionYCSSProperty = true;
925 }
926
927 if (commonValue.isNull())
928 commonValue = valueText;
929 else if (commonValue != valueText)
930 commonValue = emptyString(); // Could use value here other than a CSS-wide value keyword or the null string.
931 }
932
933 if (shorthand.id() == CSSPropertyMask && layerResult.isEmpty())
934 layerResult.append(getValueName(CSSValueNone));
935
936 if (!layerResult.isEmpty())
937 result.append(result.isEmpty() ? "" : ", ", layerResult.toString());
938 }
939
940 if (isCSSWideValueKeyword(commonValue))
941 return commonValue;
942
943 return result.isEmpty() ? String() : result.toString();
944}
945
946String StyleProperties::getGridTemplateValue() const
947{
948 StringBuilder result;
949 int areasIndex = findPropertyIndex(CSSPropertyGridTemplateAreas);
950 if (areasIndex == -1)
951 return String();
952 auto rows = getPropertyCSSValue(CSSPropertyGridTemplateRows);
953 if (!rows)
954 return String();
955 auto columns = getPropertyCSSValue(CSSPropertyGridTemplateColumns);
956 if (!columns)
957 return String();
958
959 auto areas = propertyAt(areasIndex);
960 if (!is<CSSGridTemplateAreasValue>(areas.value())) {
961 String rowsText = rows->cssText();
962 result.append(rowsText);
963
964 String columnsText = columns->cssText();
965 // If the values are identical, and either a css wide keyword
966 // or 'none', then we can just output it once.
967 if (columnsText != rowsText || (!isCSSWideValueKeyword(columnsText) && !isValueID(columns, CSSValueNone))) {
968 result.append(" / ");
969 result.append(columnsText);
970 }
971 return result.toString();
972 }
973 // We only want to try serializing the interleaved areas/templates
974 // format if it was set from this shorthand, since that automatically
975 // excludes values that can't be represented in this format (subgrid,
976 // and the repeat() function).
977 if (!areas.toCSSProperty().isSetFromShorthand())
978 return String();
979
980 ASSERT(is<CSSValueList>(rows));
981 ASSERT(is<CSSGridTemplateAreasValue>(areas.value()));
982 auto& areasValue = downcast<CSSGridTemplateAreasValue>(*areas.value());
983 bool first = true;
984 unsigned row = 0;
985 for (auto& currentValue : downcast<CSSValueList>(*rows)) {
986 if (!first)
987 result.append(" ");
988 first = false;
989
990 if (is<CSSGridLineNamesValue>(currentValue))
991 result.append(currentValue->cssText());
992 else {
993 result.append("\"");
994 result.append(areasValue.stringForRow(row));
995 result.append("\"");
996
997 if (!isValueID(currentValue, CSSValueAuto)) {
998 result.append(" ");
999 result.append(currentValue->cssText());
1000 }
1001 row++;
1002 }
1003 }
1004
1005 String columnsText = columns->cssText();
1006 if (!isNoneValue(columns)) {
1007 result.append(" / ");
1008 result.append(columnsText);
1009 }
1010
1011 return result.toString();
1012}
1013
1014String StyleProperties::getGridValue() const
1015{
1016 // If none of the implicit track properties have been set, then
1017 // this shorthand can be represented using the grid-template shorthand
1018 // format.
1019 if (isValueID(getPropertyCSSValue(CSSPropertyGridAutoColumns), CSSValueAuto)
1020 && isValueID(getPropertyCSSValue(CSSPropertyGridAutoRows), CSSValueAuto)
1021 && isValueID(getPropertyCSSValue(CSSPropertyGridAutoFlow), CSSValueRow)) {
1022 return getGridTemplateValue();
1023 }
1024 return getGridShorthandValue(gridShorthand());
1025}
1026
1027String StyleProperties::getGridShorthandValue(const StylePropertyShorthand& shorthand) const
1028{
1029 return getShorthandValue(shorthand, " / ");
1030}
1031
1032String StyleProperties::getShorthandValue(const StylePropertyShorthand& shorthand, const char* separator) const
1033{
1034 String commonValue;
1035 StringBuilder result;
1036 for (unsigned i = 0; i < shorthand.length(); ++i) {
1037 if (!isPropertyImplicit(shorthand.properties()[i])) {
1038 auto value = getPropertyCSSValue(shorthand.properties()[i]);
1039 if (!value)
1040 return String();
1041 String valueText = value->cssText();
1042 if (!i)
1043 commonValue = valueText;
1044 else if (!commonValue.isNull() && commonValue != valueText)
1045 commonValue = String();
1046 if (value->isInitialValue())
1047 continue;
1048 if (!result.isEmpty())
1049 result.append(separator);
1050 result.append(valueText);
1051 } else
1052 commonValue = String();
1053 }
1054 if (isCSSWideValueKeyword(commonValue))
1055 return commonValue;
1056 if (result.isEmpty())
1057 return String();
1058 return result.toString();
1059}
1060
1061// Returns a non-null value if all properties have the same value.
1062String StyleProperties::getCommonValue(const StylePropertyShorthand& shorthand) const
1063{
1064 String result;
1065 bool lastPropertyWasImportant = false;
1066 for (unsigned i = 0; i < shorthand.length(); ++i) {
1067 auto value = getPropertyCSSValue(shorthand.properties()[i]);
1068 if (!value)
1069 return String();
1070 // FIXME: CSSInitialValue::cssText should generate the right value.
1071 String text = value->cssText();
1072 if (text.isNull())
1073 return String();
1074 if (result.isNull())
1075 result = text;
1076 else if (result != text)
1077 return String();
1078 bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
1079 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
1080 return String();
1081 lastPropertyWasImportant = currentPropertyIsImportant;
1082 }
1083 return result;
1084}
1085
1086String StyleProperties::getAlignmentShorthandValue(const StylePropertyShorthand& shorthand) const
1087{
1088 String value = getCommonValue(shorthand);
1089 if (value.isNull() || value.isEmpty())
1090 return getShorthandValue(shorthand);
1091 return value;
1092}
1093
1094String StyleProperties::borderImagePropertyValue(CSSPropertyID propertyID) const
1095{
1096 const StylePropertyShorthand& shorthand = borderImageShorthand();
1097 StringBuilder result;
1098 bool lastPropertyWasImportant = false;
1099 bool omittedSlice = false;
1100 bool omittedWidth = false;
1101 String commonWideValueText;
1102 auto separator = "";
1103 for (unsigned i = 0; i < shorthand.length(); ++i) {
1104 // All longhands should have the same importance.
1105 auto longhand = shorthand.properties()[i];
1106 bool currentPropertyIsImportant = propertyIsImportant(longhand);
1107 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
1108 return String();
1109 lastPropertyWasImportant = currentPropertyIsImportant;
1110
1111 // All longhands should be present.
1112 auto value = getPropertyCSSValue(longhand);
1113 if (!value)
1114 return String();
1115
1116 // Omit implicit initial values. However, border-image-width and border-image-outset require border-image-slice.
1117 if (value->isInitialValue() && isPropertyImplicit(longhand)) {
1118 if (longhand == CSSPropertyBorderImageSlice)
1119 omittedSlice = true;
1120 else if (longhand == CSSPropertyBorderImageWidth)
1121 omittedWidth = true;
1122 continue;
1123 }
1124 if (omittedSlice && (longhand == CSSPropertyBorderImageWidth || longhand == CSSPropertyBorderImageOutset))
1125 return String();
1126
1127 // -webkit-border-image has a legacy behavior that makes fixed border slices also set the border widths.
1128 if (is<CSSBorderImageWidthValue>(value.get())) {
1129 auto* borderImageWidth = downcast<CSSBorderImageWidthValue>(value.get());
1130 Quad* widths = borderImageWidth->widths();
1131 bool overridesBorderWidths = propertyID == CSSPropertyWebkitBorderImage && widths && (widths->top()->isLength() || widths->right()->isLength() || widths->bottom()->isLength() || widths->left()->isLength());
1132 if (overridesBorderWidths != borderImageWidth->m_overridesBorderWidths)
1133 return String();
1134 value = borderImageWidth->m_widths;
1135 }
1136
1137 // If a longhand is set to a css-wide keyword, the others should be the same.
1138 String valueText = value->cssText();
1139 if (isCSSWideValueKeyword(valueText)) {
1140 if (!i)
1141 commonWideValueText = valueText;
1142 else if (commonWideValueText != valueText)
1143 return String();
1144 continue;
1145 }
1146 if (!commonWideValueText.isNull())
1147 return String();
1148
1149 // Append separator and text.
1150 if (longhand == CSSPropertyBorderImageWidth)
1151 separator = " / ";
1152 else if (longhand == CSSPropertyBorderImageOutset)
1153 separator = omittedWidth ? " / / " : " / ";
1154 result.append(separator, valueText);
1155 separator = " ";
1156 }
1157 if (!commonWideValueText.isNull())
1158 return commonWideValueText;
1159 return result.toString();
1160}
1161
1162String StyleProperties::borderPropertyValue(const StylePropertyShorthand& width, const StylePropertyShorthand& style, const StylePropertyShorthand& color) const
1163{
1164 const StylePropertyShorthand properties[3] = { width, style, color };
1165 String commonValue;
1166 StringBuilder result;
1167 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
1168 String value = getCommonValue(properties[i]);
1169 if (value.isNull())
1170 return String();
1171 if (!i)
1172 commonValue = value;
1173 else if (commonValue != value)
1174 commonValue = String();
1175 if (value == "initial"_s)
1176 continue;
1177 if (!result.isEmpty())
1178 result.append(' ');
1179 result.append(value);
1180 }
1181 if (isCSSWideValueKeyword(commonValue))
1182 return commonValue;
1183 return result.toString();
1184}
1185
1186String StyleProperties::pageBreakPropertyValue(const StylePropertyShorthand& shorthand) const
1187{
1188 auto value = getPropertyCSSValue(shorthand.properties()[0]);
1189 if (!value)
1190 return String();
1191 // FIXME: Remove this isCSSWideKeyword check after we do this consistently for all shorthands in getPropertyValue.
1192 if (value->isCSSWideKeyword())
1193 return value->cssText();
1194
1195 if (!is<CSSPrimitiveValue>(*value))
1196 return String();
1197
1198 CSSValueID valueId = downcast<CSSPrimitiveValue>(*value).valueID();
1199 switch (valueId) {
1200 case CSSValuePage:
1201 return "always"_s;
1202 case CSSValueAuto:
1203 case CSSValueAvoid:
1204 case CSSValueLeft:
1205 case CSSValueRight:
1206 return value->cssText();
1207 default:
1208 return String();
1209 }
1210}
1211
1212RefPtr<CSSValue> StyleProperties::getPropertyCSSValue(CSSPropertyID propertyID) const
1213{
1214 int foundPropertyIndex = findPropertyIndex(propertyID);
1215 if (foundPropertyIndex == -1)
1216 return nullptr;
1217 return propertyAt(foundPropertyIndex).value();
1218}
1219
1220RefPtr<CSSValue> StyleProperties::getCustomPropertyCSSValue(const String& propertyName) const
1221{
1222 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
1223 if (foundPropertyIndex == -1)
1224 return nullptr;
1225 return propertyAt(foundPropertyIndex).value();
1226}
1227
1228bool MutableStyleProperties::removeShorthandProperty(CSSPropertyID propertyID)
1229{
1230 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
1231 if (!shorthand.length())
1232 return false;
1233
1234 return removePropertiesInSet(shorthand.properties(), shorthand.length());
1235}
1236
1237bool MutableStyleProperties::removeProperty(CSSPropertyID propertyID, String* returnText)
1238{
1239 if (removeShorthandProperty(propertyID)) {
1240 // FIXME: Return an equivalent shorthand when possible.
1241 if (returnText)
1242 *returnText = emptyString();
1243 return true;
1244 }
1245
1246 int foundPropertyIndex = findPropertyIndex(propertyID);
1247 if (foundPropertyIndex == -1) {
1248 if (returnText)
1249 *returnText = emptyString();
1250 return false;
1251 }
1252
1253 if (returnText)
1254 *returnText = propertyAt(foundPropertyIndex).value()->cssText();
1255
1256 // A more efficient removal strategy would involve marking entries as empty
1257 // and sweeping them when the vector grows too big.
1258 m_propertyVector.remove(foundPropertyIndex);
1259
1260 return true;
1261}
1262
1263bool MutableStyleProperties::removeCustomProperty(const String& propertyName, String* returnText)
1264{
1265 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
1266 if (foundPropertyIndex == -1) {
1267 if (returnText)
1268 *returnText = emptyString();
1269 return false;
1270 }
1271
1272 if (returnText)
1273 *returnText = propertyAt(foundPropertyIndex).value()->cssText();
1274
1275 // A more efficient removal strategy would involve marking entries as empty
1276 // and sweeping them when the vector grows too big.
1277 m_propertyVector.remove(foundPropertyIndex);
1278
1279 return true;
1280}
1281
1282bool StyleProperties::propertyIsImportant(CSSPropertyID propertyID) const
1283{
1284 int foundPropertyIndex = findPropertyIndex(propertyID);
1285 if (foundPropertyIndex != -1)
1286 return propertyAt(foundPropertyIndex).isImportant();
1287
1288 auto shorthand = shorthandForProperty(propertyID);
1289 if (!shorthand.length())
1290 return false;
1291
1292 for (auto longhand : shorthand) {
1293 if (!propertyIsImportant(longhand))
1294 return false;
1295 }
1296 return true;
1297}
1298
1299bool StyleProperties::customPropertyIsImportant(const String& propertyName) const
1300{
1301 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
1302 if (foundPropertyIndex != -1)
1303 return propertyAt(foundPropertyIndex).isImportant();
1304 return false;
1305}
1306
1307String StyleProperties::getPropertyShorthand(CSSPropertyID propertyID) const
1308{
1309 int foundPropertyIndex = findPropertyIndex(propertyID);
1310 if (foundPropertyIndex == -1)
1311 return String();
1312 return getPropertyNameString(propertyAt(foundPropertyIndex).shorthandID());
1313}
1314
1315bool StyleProperties::isPropertyImplicit(CSSPropertyID propertyID) const
1316{
1317 int foundPropertyIndex = findPropertyIndex(propertyID);
1318 if (foundPropertyIndex == -1)
1319 return false;
1320 return propertyAt(foundPropertyIndex).isImplicit();
1321}
1322
1323bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important, CSSParserContext parserContext)
1324{
1325 if (!isEnabledCSSProperty(propertyID))
1326 return false;
1327
1328 // Setting the value to an empty string just removes the property in both IE and Gecko.
1329 // Setting it to null seems to produce less consistent results, but we treat it just the same.
1330 if (value.isEmpty())
1331 return removeProperty(propertyID);
1332
1333 parserContext.mode = cssParserMode();
1334
1335 // When replacing an existing property value, this moves the property to the end of the list.
1336 // Firefox preserves the position, and MSIE moves the property to the beginning.
1337 return CSSParser::parseValue(*this, propertyID, value, important, parserContext) == CSSParser::ParseResult::Changed;
1338}
1339
1340bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important)
1341{
1342 CSSParserContext parserContext(cssParserMode());
1343 return setProperty(propertyID, value, important, parserContext);
1344}
1345
1346bool MutableStyleProperties::setCustomProperty(const Document* document, const String& propertyName, const String& value, bool important, CSSParserContext parserContext)
1347{
1348 // Setting the value to an empty string just removes the property in both IE and Gecko.
1349 // Setting it to null seems to produce less consistent results, but we treat it just the same.
1350 if (value.isEmpty())
1351 return removeCustomProperty(propertyName);
1352
1353 parserContext.mode = cssParserMode();
1354
1355 String syntax = "*"_s;
1356 auto* registered = document ? document->getCSSRegisteredCustomPropertySet().get(propertyName) : nullptr;
1357
1358 if (registered)
1359 syntax = registered->syntax;
1360
1361 CSSTokenizer tokenizer(value);
1362 if (!CSSPropertyParser::canParseTypedCustomPropertyValue(syntax, tokenizer.tokenRange(), parserContext))
1363 return false;
1364
1365 // When replacing an existing property value, this moves the property to the end of the list.
1366 // Firefox preserves the position, and MSIE moves the property to the beginning.
1367 return CSSParser::parseCustomPropertyValue(*this, AtomString { propertyName }, value, important, parserContext) == CSSParser::ParseResult::Changed;
1368}
1369
1370void MutableStyleProperties::setProperty(CSSPropertyID propertyID, RefPtr<CSSValue>&& value, bool important)
1371{
1372 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
1373 if (!shorthand.length()) {
1374 setProperty(CSSProperty(propertyID, WTFMove(value), important));
1375 return;
1376 }
1377
1378 removePropertiesInSet(shorthand.properties(), shorthand.length());
1379
1380 for (auto longhand : shorthand)
1381 m_propertyVector.append(CSSProperty(longhand, value.copyRef(), important));
1382}
1383
1384bool MutableStyleProperties::canUpdateInPlace(const CSSProperty& property, CSSProperty* toReplace) const
1385{
1386 // If the property is in a logical property group, we can't just update the value in-place,
1387 // because afterwards there might be another property of the same group but different mapping logic.
1388 // In that case the latter might override the former, so setProperty would have no effect.
1389 CSSPropertyID id = property.id();
1390 if (CSSProperty::isInLogicalPropertyGroup(id)) {
1391 ASSERT(toReplace >= m_propertyVector.begin());
1392 ASSERT(toReplace < m_propertyVector.end());
1393 for (CSSProperty* it = toReplace + 1; it != m_propertyVector.end(); ++it) {
1394 if (CSSProperty::areInSameLogicalPropertyGroupWithDifferentMappingLogic(id, it->id()))
1395 return false;
1396 }
1397 }
1398 return true;
1399}
1400
1401bool MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot)
1402{
1403 if (!removeShorthandProperty(property.id())) {
1404 CSSProperty* toReplace = slot;
1405 if (!slot) {
1406 if (property.id() == CSSPropertyCustom) {
1407 if (property.value())
1408 toReplace = findCustomCSSPropertyWithName(downcast<CSSCustomPropertyValue>(*property.value()).name());
1409 } else
1410 toReplace = findCSSPropertyWithID(property.id());
1411 }
1412
1413 if (toReplace) {
1414 if (canUpdateInPlace(property, toReplace)) {
1415 if (*toReplace == property)
1416 return false;
1417
1418 *toReplace = property;
1419 return true;
1420 }
1421 m_propertyVector.remove(toReplace - m_propertyVector.begin());
1422 toReplace = nullptr;
1423 }
1424 }
1425
1426 m_propertyVector.append(property);
1427 return true;
1428}
1429
1430bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
1431{
1432 return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
1433}
1434
1435bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
1436{
1437 return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
1438}
1439
1440bool MutableStyleProperties::parseDeclaration(const String& styleDeclaration, CSSParserContext context)
1441{
1442 auto oldProperties = WTFMove(m_propertyVector);
1443 m_propertyVector.clear();
1444
1445 context.mode = cssParserMode();
1446
1447 CSSParser parser(context);
1448 parser.parseDeclaration(*this, styleDeclaration);
1449
1450 // We could do better. Just changing property order does not require style invalidation.
1451 return oldProperties != m_propertyVector;
1452}
1453
1454bool MutableStyleProperties::addParsedProperties(const ParsedPropertyVector& properties)
1455{
1456 bool anyChanged = false;
1457 m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
1458 for (const auto& property : properties) {
1459 if (addParsedProperty(property))
1460 anyChanged = true;
1461 }
1462
1463 return anyChanged;
1464}
1465
1466bool MutableStyleProperties::addParsedProperty(const CSSProperty& property)
1467{
1468 if (property.id() == CSSPropertyCustom) {
1469 if ((property.value() && !customPropertyIsImportant(downcast<CSSCustomPropertyValue>(*property.value()).name())) || property.isImportant())
1470 return setProperty(property);
1471 return false;
1472 }
1473 return setProperty(property);
1474}
1475
1476String StyleProperties::asText() const
1477{
1478 return asTextInternal().toString();
1479}
1480
1481AtomString StyleProperties::asTextAtom() const
1482{
1483 return asTextInternal().toAtomString();
1484}
1485
1486StringBuilder StyleProperties::asTextInternal() const
1487{
1488 StringBuilder result;
1489
1490 int positionXPropertyIndex = -1;
1491 int positionYPropertyIndex = -1;
1492 int repeatXPropertyIndex = -1;
1493 int repeatYPropertyIndex = -1;
1494
1495 std::bitset<numCSSProperties> shorthandPropertyUsed;
1496 std::bitset<numCSSProperties> shorthandPropertyAppeared;
1497
1498 unsigned size = propertyCount();
1499 unsigned numDecls = 0;
1500 for (unsigned n = 0; n < size; ++n) {
1501 PropertyReference property = propertyAt(n);
1502 CSSPropertyID propertyID = property.id();
1503 CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
1504 CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
1505 CSSPropertyID borderBlockFallbackShorthandProperty = CSSPropertyInvalid;
1506 CSSPropertyID borderInlineFallbackShorthandProperty = CSSPropertyInvalid;
1507 String value;
1508 auto serializeBorderShorthand = [&] (const CSSPropertyID borderProperty, const CSSPropertyID fallbackProperty) {
1509 // FIXME: Deal with cases where only some of border sides are specified.
1510 ASSERT(borderProperty - firstCSSProperty < static_cast<CSSPropertyID>(shorthandPropertyAppeared.size()));
1511 if (!shorthandPropertyAppeared[borderProperty - firstCSSProperty] && isEnabledCSSProperty(borderProperty)) {
1512 value = getPropertyValue(borderProperty);
1513 if (value.isNull())
1514 shorthandPropertyAppeared.set(borderProperty - firstCSSProperty);
1515 else
1516 shorthandPropertyID = borderProperty;
1517 } else if (shorthandPropertyUsed[borderProperty - firstCSSProperty])
1518 shorthandPropertyID = borderProperty;
1519 if (!shorthandPropertyID)
1520 shorthandPropertyID = fallbackProperty;
1521 };
1522
1523 if (is<CSSPendingSubstitutionValue>(property.value())) {
1524 auto& substitutionValue = downcast<CSSPendingSubstitutionValue>(*property.value());
1525 shorthandPropertyID = substitutionValue.shorthandPropertyId();
1526 value = substitutionValue.shorthandValue().cssText();
1527 } else {
1528 switch (propertyID) {
1529 case CSSPropertyAnimationName:
1530 case CSSPropertyAnimationDuration:
1531 case CSSPropertyAnimationTimingFunction:
1532 case CSSPropertyAnimationDelay:
1533 case CSSPropertyAnimationIterationCount:
1534 case CSSPropertyAnimationDirection:
1535 case CSSPropertyAnimationFillMode:
1536 case CSSPropertyAnimationPlayState:
1537 shorthandPropertyID = CSSPropertyAnimation;
1538 break;
1539 case CSSPropertyBackgroundPositionX:
1540 positionXPropertyIndex = n;
1541 continue;
1542 case CSSPropertyBackgroundPositionY:
1543 positionYPropertyIndex = n;
1544 continue;
1545 case CSSPropertyBackgroundRepeatX:
1546 repeatXPropertyIndex = n;
1547 continue;
1548 case CSSPropertyBackgroundRepeatY:
1549 repeatYPropertyIndex = n;
1550 continue;
1551 case CSSPropertyBorderTopWidth:
1552 case CSSPropertyBorderRightWidth:
1553 case CSSPropertyBorderBottomWidth:
1554 case CSSPropertyBorderLeftWidth:
1555 if (!borderFallbackShorthandProperty)
1556 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
1557 FALLTHROUGH;
1558 case CSSPropertyBorderTopStyle:
1559 case CSSPropertyBorderRightStyle:
1560 case CSSPropertyBorderBottomStyle:
1561 case CSSPropertyBorderLeftStyle:
1562 if (!borderFallbackShorthandProperty)
1563 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
1564 FALLTHROUGH;
1565 case CSSPropertyBorderTopColor:
1566 case CSSPropertyBorderRightColor:
1567 case CSSPropertyBorderBottomColor:
1568 case CSSPropertyBorderLeftColor:
1569 if (!borderFallbackShorthandProperty)
1570 borderFallbackShorthandProperty = CSSPropertyBorderColor;
1571 serializeBorderShorthand(CSSPropertyBorder, borderFallbackShorthandProperty);
1572 break;
1573 case CSSPropertyBorderBlockStartWidth:
1574 case CSSPropertyBorderBlockEndWidth:
1575 if (!borderBlockFallbackShorthandProperty)
1576 borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockWidth;
1577 FALLTHROUGH;
1578 case CSSPropertyBorderBlockStartStyle:
1579 case CSSPropertyBorderBlockEndStyle:
1580 if (!borderBlockFallbackShorthandProperty)
1581 borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockStyle;
1582 FALLTHROUGH;
1583 case CSSPropertyBorderBlockStartColor:
1584 case CSSPropertyBorderBlockEndColor:
1585 if (!borderBlockFallbackShorthandProperty)
1586 borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockColor;
1587 serializeBorderShorthand(CSSPropertyBorderBlock, borderBlockFallbackShorthandProperty);
1588 break;
1589 case CSSPropertyBorderInlineStartWidth:
1590 case CSSPropertyBorderInlineEndWidth:
1591 if (!borderInlineFallbackShorthandProperty)
1592 borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineWidth;
1593 FALLTHROUGH;
1594 case CSSPropertyBorderInlineStartStyle:
1595 case CSSPropertyBorderInlineEndStyle:
1596 if (!borderInlineFallbackShorthandProperty)
1597 borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineStyle;
1598 FALLTHROUGH;
1599 case CSSPropertyBorderTopLeftRadius:
1600 case CSSPropertyBorderTopRightRadius:
1601 case CSSPropertyBorderBottomRightRadius:
1602 case CSSPropertyBorderBottomLeftRadius:
1603 shorthandPropertyID = CSSPropertyBorderRadius;
1604 break;
1605 case CSSPropertyBorderInlineStartColor:
1606 case CSSPropertyBorderInlineEndColor:
1607 if (!borderInlineFallbackShorthandProperty)
1608 borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineColor;
1609 serializeBorderShorthand(CSSPropertyBorderInline, borderInlineFallbackShorthandProperty);
1610 break;
1611 case CSSPropertyWebkitBorderHorizontalSpacing:
1612 case CSSPropertyWebkitBorderVerticalSpacing:
1613 shorthandPropertyID = CSSPropertyBorderSpacing;
1614 break;
1615 case CSSPropertyBorderImageSource:
1616 case CSSPropertyBorderImageSlice:
1617 case CSSPropertyBorderImageWidth:
1618 case CSSPropertyBorderImageOutset:
1619 case CSSPropertyBorderImageRepeat:
1620 serializeBorderShorthand(CSSPropertyBorderImage, CSSPropertyWebkitBorderImage);
1621 break;
1622 case CSSPropertyFontFamily:
1623 case CSSPropertyLineHeight:
1624 case CSSPropertyFontSize:
1625 case CSSPropertyFontStyle:
1626 case CSSPropertyFontVariantCaps:
1627 case CSSPropertyFontWeight:
1628 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
1629 break;
1630 case CSSPropertyTop:
1631 case CSSPropertyRight:
1632 case CSSPropertyBottom:
1633 case CSSPropertyLeft:
1634 shorthandPropertyID = CSSPropertyInset;
1635 break;
1636 case CSSPropertyInsetBlockStart:
1637 case CSSPropertyInsetBlockEnd:
1638 shorthandPropertyID = CSSPropertyInsetBlock;
1639 break;
1640 case CSSPropertyInsetInlineStart:
1641 case CSSPropertyInsetInlineEnd:
1642 shorthandPropertyID = CSSPropertyInsetInline;
1643 break;
1644 case CSSPropertyListStyleType:
1645 case CSSPropertyListStylePosition:
1646 case CSSPropertyListStyleImage:
1647 shorthandPropertyID = CSSPropertyListStyle;
1648 break;
1649 case CSSPropertyMarginTop:
1650 case CSSPropertyMarginRight:
1651 case CSSPropertyMarginBottom:
1652 case CSSPropertyMarginLeft:
1653 shorthandPropertyID = CSSPropertyMargin;
1654 break;
1655 case CSSPropertyMarginBlockStart:
1656 case CSSPropertyMarginBlockEnd:
1657 shorthandPropertyID = CSSPropertyMarginBlock;
1658 break;
1659 case CSSPropertyMarginInlineStart:
1660 case CSSPropertyMarginInlineEnd:
1661 shorthandPropertyID = CSSPropertyMarginInline;
1662 break;
1663 case CSSPropertyOutlineWidth:
1664 case CSSPropertyOutlineStyle:
1665 case CSSPropertyOutlineColor:
1666 shorthandPropertyID = CSSPropertyOutline;
1667 break;
1668 case CSSPropertyOverflowX:
1669 case CSSPropertyOverflowY:
1670 shorthandPropertyID = CSSPropertyOverflow;
1671 break;
1672 case CSSPropertyOverscrollBehaviorX:
1673 case CSSPropertyOverscrollBehaviorY:
1674 shorthandPropertyID = CSSPropertyOverscrollBehavior;
1675 break;
1676 case CSSPropertyPaddingTop:
1677 case CSSPropertyPaddingRight:
1678 case CSSPropertyPaddingBottom:
1679 case CSSPropertyPaddingLeft:
1680 shorthandPropertyID = CSSPropertyPadding;
1681 break;
1682 case CSSPropertyPaddingBlockStart:
1683 case CSSPropertyPaddingBlockEnd:
1684 shorthandPropertyID = CSSPropertyPaddingBlock;
1685 break;
1686 case CSSPropertyPaddingInlineStart:
1687 case CSSPropertyPaddingInlineEnd:
1688 shorthandPropertyID = CSSPropertyPaddingInline;
1689 break;
1690 case CSSPropertyScrollMarginTop:
1691 case CSSPropertyScrollMarginRight:
1692 case CSSPropertyScrollMarginBottom:
1693 case CSSPropertyScrollMarginLeft:
1694 shorthandPropertyID = CSSPropertyScrollMargin;
1695 break;
1696 case CSSPropertyScrollMarginBlockStart:
1697 case CSSPropertyScrollMarginBlockEnd:
1698 shorthandPropertyID = CSSPropertyScrollMarginBlock;
1699 break;
1700 case CSSPropertyScrollMarginInlineStart:
1701 case CSSPropertyScrollMarginInlineEnd:
1702 shorthandPropertyID = CSSPropertyScrollMarginInline;
1703 break;
1704 case CSSPropertyScrollPaddingTop:
1705 case CSSPropertyScrollPaddingRight:
1706 case CSSPropertyScrollPaddingBottom:
1707 case CSSPropertyScrollPaddingLeft:
1708 shorthandPropertyID = CSSPropertyScrollPadding;
1709 break;
1710 case CSSPropertyScrollPaddingBlockStart:
1711 case CSSPropertyScrollPaddingBlockEnd:
1712 shorthandPropertyID = CSSPropertyScrollPaddingBlock;
1713 break;
1714 case CSSPropertyScrollPaddingInlineStart:
1715 case CSSPropertyScrollPaddingInlineEnd:
1716 shorthandPropertyID = CSSPropertyScrollPaddingInline;
1717 break;
1718 case CSSPropertyTextDecorationLine:
1719 shorthandPropertyID = CSSPropertyTextDecoration;
1720 break;
1721 case CSSPropertyTransitionProperty:
1722 case CSSPropertyTransitionDuration:
1723 case CSSPropertyTransitionTimingFunction:
1724 case CSSPropertyTransitionDelay:
1725 shorthandPropertyID = CSSPropertyTransition;
1726 break;
1727 case CSSPropertyFlexDirection:
1728 case CSSPropertyFlexWrap:
1729 shorthandPropertyID = CSSPropertyFlexFlow;
1730 break;
1731 case CSSPropertyFlexBasis:
1732 case CSSPropertyFlexGrow:
1733 case CSSPropertyFlexShrink:
1734 shorthandPropertyID = CSSPropertyFlex;
1735 break;
1736 case CSSPropertyWebkitMaskPositionX:
1737 case CSSPropertyWebkitMaskPositionY:
1738 case CSSPropertyMaskRepeatX:
1739 case CSSPropertyMaskRepeatY:
1740 case CSSPropertyMaskImage:
1741 case CSSPropertyMaskRepeat:
1742 case CSSPropertyMaskPosition:
1743 case CSSPropertyMaskClip:
1744 case CSSPropertyMaskOrigin:
1745 shorthandPropertyID = CSSPropertyMask;
1746 break;
1747 case CSSPropertyWebkitMaskClip:
1748 case CSSPropertyWebkitMaskPosition:
1749 // TODO: A lot of the above properties can be both prefixed and unprefixed?
1750 shorthandPropertyID = CSSPropertyWebkitMask;
1751 break;
1752 case CSSPropertyPerspectiveOriginX:
1753 case CSSPropertyPerspectiveOriginY:
1754 shorthandPropertyID = CSSPropertyPerspectiveOrigin;
1755 break;
1756 case CSSPropertyTransformOriginX:
1757 case CSSPropertyTransformOriginY:
1758 case CSSPropertyTransformOriginZ:
1759 shorthandPropertyID = CSSPropertyTransformOrigin;
1760 break;
1761 case CSSPropertyContainIntrinsicHeight:
1762 case CSSPropertyContainIntrinsicWidth:
1763 shorthandPropertyID = CSSPropertyContainIntrinsicSize;
1764 break;
1765 default:
1766 break;
1767 }
1768 }
1769
1770 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
1771 if (shorthandPropertyID && isEnabledCSSProperty(shorthandPropertyID)) {
1772 ASSERT(shortPropertyIndex < shorthandPropertyUsed.size());
1773 if (shorthandPropertyUsed[shortPropertyIndex])
1774 continue;
1775 if (!shorthandPropertyAppeared[shortPropertyIndex] && value.isNull())
1776 value = getPropertyValue(shorthandPropertyID);
1777 shorthandPropertyAppeared.set(shortPropertyIndex);
1778 }
1779
1780 if (!value.isNull()) {
1781 propertyID = shorthandPropertyID;
1782 shorthandPropertyUsed.set(shortPropertyIndex);
1783 } else
1784 value = property.value()->cssText();
1785
1786 if (propertyID != CSSPropertyCustom && value == "initial"_s && !CSSProperty::isInheritedProperty(propertyID))
1787 continue;
1788
1789 if (numDecls++)
1790 result.append(' ');
1791
1792 if (propertyID == CSSPropertyCustom)
1793 result.append(downcast<CSSCustomPropertyValue>(*property.value()).name());
1794 else
1795 result.append(getPropertyName(propertyID));
1796
1797 result.append(": ", value, property.isImportant() ? " !important" : "", ';');
1798 }
1799
1800 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position/repeat in output.
1801 // In 2007 we decided this was required because background-position/repeat-x/y are non-standard properties and WebKit generated output would not work in Firefox (<rdar://problem/5143183>).
1802 auto appendPositionOrProperty = [&] (int xIndex, int yIndex, const char* name, const StylePropertyShorthand& shorthand) {
1803 if (xIndex != -1 && yIndex != -1 && propertyAt(xIndex).isImportant() == propertyAt(yIndex).isImportant()) {
1804 String value;
1805 auto xProperty = propertyAt(xIndex);
1806 auto yProperty = propertyAt(yIndex);
1807 if (xProperty.value()->isValueList() || yProperty.value()->isValueList())
1808 value = getLayeredShorthandValue(shorthand);
1809 else {
1810 auto x = xProperty.value()->cssText();
1811 auto y = yProperty.value()->cssText();
1812 if (x == y && isCSSWideValueKeyword(x))
1813 value = x;
1814 else
1815 value = makeString(x, ' ', y);
1816 }
1817 if (value != "initial"_s) {
1818 result.append(numDecls ? " " : "", name, ": ", value, xProperty.isImportant() ? " !important" : "", ';');
1819 ++numDecls;
1820 }
1821 } else {
1822 if (xIndex != -1) {
1823 if (numDecls++)
1824 result.append(' ');
1825 result.append(propertyAt(xIndex).cssText());
1826 }
1827 if (yIndex != -1) {
1828 if (numDecls++)
1829 result.append(' ');
1830 result.append(propertyAt(yIndex).cssText());
1831 }
1832 }
1833 };
1834
1835 appendPositionOrProperty(positionXPropertyIndex, positionYPropertyIndex, "background-position", backgroundPositionShorthand());
1836 appendPositionOrProperty(repeatXPropertyIndex, repeatYPropertyIndex, "background-repeat", backgroundRepeatShorthand());
1837
1838 ASSERT(!numDecls ^ !result.isEmpty());
1839 return result;
1840}
1841
1842bool StyleProperties::hasCSSOMWrapper() const
1843{
1844 return is<MutableStyleProperties>(*this) && downcast<MutableStyleProperties>(*this).m_cssomWrapper;
1845}
1846
1847void MutableStyleProperties::mergeAndOverrideOnConflict(const StyleProperties& other)
1848{
1849 unsigned size = other.propertyCount();
1850 for (unsigned i = 0; i < size; ++i)
1851 addParsedProperty(other.propertyAt(i).toCSSProperty());
1852}
1853
1854bool StyleProperties::traverseSubresources(const Function<bool(const CachedResource&)>& handler) const
1855{
1856 unsigned size = propertyCount();
1857 for (unsigned i = 0; i < size; ++i) {
1858 if (propertyAt(i).value()->traverseSubresources(handler))
1859 return true;
1860 }
1861 return false;
1862}
1863
1864// This is the list of properties we want to copy in the copyBlockProperties() function.
1865// It is the list of CSS properties that apply specially to block-level elements.
1866static const CSSPropertyID blockProperties[] = {
1867 CSSPropertyOrphans,
1868 CSSPropertyOverflow, // This can be also be applied to replaced elements
1869 CSSPropertyColumnCount,
1870 CSSPropertyColumnGap,
1871 CSSPropertyRowGap,
1872 CSSPropertyColumnRuleColor,
1873 CSSPropertyColumnRuleStyle,
1874 CSSPropertyColumnRuleWidth,
1875 CSSPropertyWebkitColumnBreakBefore,
1876 CSSPropertyWebkitColumnBreakAfter,
1877 CSSPropertyWebkitColumnBreakInside,
1878 CSSPropertyColumnWidth,
1879 CSSPropertyPageBreakAfter,
1880 CSSPropertyPageBreakBefore,
1881 CSSPropertyPageBreakInside,
1882 CSSPropertyTextAlign,
1883 CSSPropertyTextAlignLast,
1884 CSSPropertyTextJustify,
1885 CSSPropertyTextIndent,
1886 CSSPropertyWidows
1887};
1888
1889void MutableStyleProperties::clear()
1890{
1891 m_propertyVector.clear();
1892}
1893
1894const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
1895
1896Ref<MutableStyleProperties> StyleProperties::copyBlockProperties() const
1897{
1898 return copyPropertiesInSet(blockProperties, numBlockProperties);
1899}
1900
1901void MutableStyleProperties::removeBlockProperties()
1902{
1903 removePropertiesInSet(blockProperties, numBlockProperties);
1904}
1905
1906bool MutableStyleProperties::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1907{
1908 if (m_propertyVector.isEmpty())
1909 return false;
1910
1911 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1912 HashSet<CSSPropertyID> toRemove;
1913 for (unsigned i = 0; i < length; ++i)
1914 toRemove.add(set[i]);
1915
1916 return m_propertyVector.removeAllMatching([&toRemove] (const CSSProperty& property) {
1917 return toRemove.contains(property.id());
1918 }) > 0;
1919}
1920
1921int ImmutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1922{
1923 // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1924 // the compiler converting it to an int multiple times in the loop.
1925 uint16_t id = static_cast<uint16_t>(propertyID);
1926 for (int n = m_arraySize - 1 ; n >= 0; --n) {
1927 if (metadataArray()[n].m_propertyID == id)
1928 return n;
1929 }
1930
1931 return -1;
1932}
1933
1934int MutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1935{
1936 // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1937 // the compiler converting it to an int multiple times in the loop.
1938 uint16_t id = static_cast<uint16_t>(propertyID);
1939 for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1940 if (m_propertyVector.at(n).metadata().m_propertyID == id)
1941 return n;
1942 }
1943
1944 return -1;
1945}
1946
1947int ImmutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1948{
1949 for (int n = m_arraySize - 1 ; n >= 0; --n) {
1950 if (metadataArray()[n].m_propertyID == CSSPropertyCustom) {
1951 // We found a custom property. See if the name matches.
1952 auto* value = valueArray()[n].get();
1953 if (!value)
1954 continue;
1955 if (downcast<CSSCustomPropertyValue>(*value).name() == propertyName)
1956 return n;
1957 }
1958 }
1959
1960 return -1;
1961}
1962
1963int MutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1964{
1965 for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1966 if (m_propertyVector.at(n).metadata().m_propertyID == CSSPropertyCustom) {
1967 // We found a custom property. See if the name matches.
1968 if (!m_propertyVector.at(n).value())
1969 continue;
1970 if (downcast<CSSCustomPropertyValue>(*m_propertyVector.at(n).value()).name() == propertyName)
1971 return n;
1972 }
1973 }
1974
1975 return -1;
1976}
1977
1978CSSProperty* MutableStyleProperties::findCSSPropertyWithID(CSSPropertyID propertyID)
1979{
1980 int foundPropertyIndex = findPropertyIndex(propertyID);
1981 if (foundPropertyIndex == -1)
1982 return 0;
1983 return &m_propertyVector.at(foundPropertyIndex);
1984}
1985
1986CSSProperty* MutableStyleProperties::findCustomCSSPropertyWithName(const String& propertyName)
1987{
1988 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
1989 if (foundPropertyIndex == -1)
1990 return 0;
1991 return &m_propertyVector.at(foundPropertyIndex);
1992}
1993
1994bool StyleProperties::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
1995{
1996 int foundPropertyIndex = findPropertyIndex(propertyID);
1997 if (foundPropertyIndex == -1)
1998 return false;
1999 return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
2000}
2001
2002Ref<MutableStyleProperties> StyleProperties::mutableCopy() const
2003{
2004 return adoptRef(*new MutableStyleProperties(*this));
2005}
2006
2007Ref<MutableStyleProperties> StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
2008{
2009 Vector<CSSProperty> list;
2010 list.reserveInitialCapacity(length);
2011 for (unsigned i = 0; i < length; ++i) {
2012 if (auto value = getPropertyCSSValue(set[i]))
2013 list.uncheckedAppend(CSSProperty(set[i], WTFMove(value), false));
2014 }
2015 list.shrinkToFit();
2016 return MutableStyleProperties::create(WTFMove(list));
2017}
2018
2019PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration()
2020{
2021 return m_cssomWrapper.get();
2022}
2023
2024CSSStyleDeclaration& MutableStyleProperties::ensureCSSStyleDeclaration()
2025{
2026 if (m_cssomWrapper) {
2027 ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule());
2028 ASSERT(!m_cssomWrapper->parentElement());
2029 return *m_cssomWrapper;
2030 }
2031 m_cssomWrapper = makeUnique<PropertySetCSSStyleDeclaration>(*this);
2032 return *m_cssomWrapper;
2033}
2034
2035CSSStyleDeclaration& MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement& parentElement)
2036{
2037 if (m_cssomWrapper) {
2038 ASSERT(m_cssomWrapper->parentElement() == &parentElement);
2039 return *m_cssomWrapper;
2040 }
2041 m_cssomWrapper = makeUnique<InlineCSSStyleDeclaration>(*this, parentElement);
2042 return *m_cssomWrapper;
2043}
2044
2045unsigned StyleProperties::averageSizeInBytes()
2046{
2047 // Please update this if the storage scheme changes so that this longer reflects the actual size.
2048 return sizeForImmutableStylePropertiesWithPropertyCount(4);
2049}
2050
2051// See the function above if you need to update this.
2052struct SameSizeAsStyleProperties : public RefCounted<SameSizeAsStyleProperties> {
2053 unsigned bitfield;
2054};
2055static_assert(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), "style property set should stay small");
2056
2057#ifndef NDEBUG
2058void StyleProperties::showStyle()
2059{
2060 fprintf(stderr, "%s\n", asText().ascii().data());
2061}
2062#endif
2063
2064Ref<MutableStyleProperties> MutableStyleProperties::create(CSSParserMode cssParserMode)
2065{
2066 return adoptRef(*new MutableStyleProperties(cssParserMode));
2067}
2068
2069Ref<MutableStyleProperties> MutableStyleProperties::create(Vector<CSSProperty>&& properties)
2070{
2071 return adoptRef(*new MutableStyleProperties(WTFMove(properties)));
2072}
2073
2074String StyleProperties::PropertyReference::cssName() const
2075{
2076 if (id() == CSSPropertyCustom)
2077 return downcast<CSSCustomPropertyValue>(*value()).name();
2078 return getPropertyNameString(id());
2079}
2080
2081String StyleProperties::PropertyReference::cssText() const
2082{
2083 return makeString(cssName(), ": ", m_value->cssText(), isImportant() ? " !important" : "", ';');
2084}
2085
2086} // namespace WebCore
Note: See TracBrowser for help on using the repository browser.