1/*
2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
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 "CSSComputedStyleDeclaration.h"
27#include "CSSCustomPropertyValue.h"
28#include "CSSDeferredParser.h"
29#include "CSSParser.h"
30#include "CSSPendingSubstitutionValue.h"
31#include "CSSPropertyParser.h"
32#include "CSSTokenizer.h"
33#include "CSSValueKeywords.h"
34#include "CSSValueList.h"
35#include "CSSValuePool.h"
36#include "Color.h"
37#include "Document.h"
38#include "PropertySetCSSStyleDeclaration.h"
39#include "StylePropertyShorthand.h"
40#include "StylePropertyShorthandFunctions.h"
41#include "StyleSheetContents.h"
42#include <bitset>
43#include <wtf/text/StringBuilder.h>
44
45#ifndef NDEBUG
46#include <stdio.h>
47#include <wtf/text/CString.h>
48#endif
49
50namespace WebCore {
51
52static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count)
53{
54 return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
55}
56
57static bool isInitialOrInherit(const String& value)
58{
59 return value.length() == 7 && (value == "initial" || value == "inherit");
60}
61
62Ref<ImmutableStyleProperties> ImmutableStyleProperties::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
63{
64 void* slot = WTF::fastMalloc(sizeForImmutableStylePropertiesWithPropertyCount(count));
65 return adoptRef(*new (NotNull, slot) ImmutableStyleProperties(properties, count, cssParserMode));
66}
67
68Ref<ImmutableStyleProperties> StyleProperties::immutableCopyIfNeeded() const
69{
70 if (is<ImmutableStyleProperties>(*this))
71 return downcast<ImmutableStyleProperties>(const_cast<StyleProperties&>(*this));
72 const MutableStyleProperties& mutableThis = downcast<MutableStyleProperties>(*this);
73 return ImmutableStyleProperties::create(mutableThis.m_propertyVector.data(), mutableThis.m_propertyVector.size(), cssParserMode());
74}
75
76MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode)
77 : StyleProperties(cssParserMode, MutablePropertiesType)
78{
79}
80
81MutableStyleProperties::MutableStyleProperties(const CSSProperty* properties, unsigned length)
82 : StyleProperties(HTMLStandardMode, MutablePropertiesType)
83{
84 m_propertyVector.reserveInitialCapacity(length);
85 for (unsigned i = 0; i < length; ++i)
86 m_propertyVector.uncheckedAppend(properties[i]);
87}
88
89MutableStyleProperties::~MutableStyleProperties() = default;
90
91ImmutableStyleProperties::ImmutableStyleProperties(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
92 : StyleProperties(cssParserMode, length)
93{
94 StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(this->metadataArray());
95 CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
96 for (unsigned i = 0; i < length; ++i) {
97 metadataArray[i] = properties[i].metadata();
98 valueArray[i] = properties[i].value();
99 valueArray[i]->ref();
100 }
101}
102
103ImmutableStyleProperties::~ImmutableStyleProperties()
104{
105 CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
106 for (unsigned i = 0; i < m_arraySize; ++i)
107 valueArray[i]->deref();
108}
109
110MutableStyleProperties::MutableStyleProperties(const StyleProperties& other)
111 : StyleProperties(other.cssParserMode(), MutablePropertiesType)
112{
113 ASSERT(other.type() != DeferredPropertiesType);
114 if (is<MutableStyleProperties>(other))
115 m_propertyVector = downcast<MutableStyleProperties>(other).m_propertyVector;
116 else {
117 const auto& immutableOther = downcast<ImmutableStyleProperties>(other);
118 unsigned propertyCount = immutableOther.propertyCount();
119 m_propertyVector.reserveInitialCapacity(propertyCount);
120 for (unsigned i = 0; i < propertyCount; ++i)
121 m_propertyVector.uncheckedAppend(immutableOther.propertyAt(i).toCSSProperty());
122 }
123}
124
125String StyleProperties::getPropertyValue(CSSPropertyID propertyID) const
126{
127 RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
128 if (value)
129 return value->cssText();
130
131 const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
132 if (shorthand.length()) {
133 RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[0]);
134 if (!value || value->isPendingSubstitutionValue())
135 return String();
136 // FIXME: If all longhands are the same css-generic keyword(e.g. initial or inherit),
137 // then the shorthand should be serialized to that keyword.
138 // It seems to be needed to handle this in a single function commonly for all the shorthands,
139 // not in each of the shorthand serialization function.
140 // We could call that function here.
141 }
142
143 // Shorthand and 4-values properties
144 switch (propertyID) {
145 case CSSPropertyAll:
146 return getCommonValue(allShorthand());
147 case CSSPropertyAnimation:
148 return getLayeredShorthandValue(animationShorthand());
149 case CSSPropertyBorderSpacing:
150 return borderSpacingValue(borderSpacingShorthand());
151 case CSSPropertyBackgroundPosition:
152 return getLayeredShorthandValue(backgroundPositionShorthand());
153 case CSSPropertyBackgroundRepeat:
154 return getLayeredShorthandValue(backgroundRepeatShorthand());
155 case CSSPropertyBackground:
156 return getLayeredShorthandValue(backgroundShorthand());
157 case CSSPropertyBorder:
158 return borderPropertyValue(borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand());
159 case CSSPropertyBorderTop:
160 return getShorthandValue(borderTopShorthand());
161 case CSSPropertyBorderRight:
162 return getShorthandValue(borderRightShorthand());
163 case CSSPropertyBorderBottom:
164 return getShorthandValue(borderBottomShorthand());
165 case CSSPropertyBorderLeft:
166 return getShorthandValue(borderLeftShorthand());
167 case CSSPropertyBorderBlock:
168 return borderPropertyValue(borderBlockWidthShorthand(), borderBlockStyleShorthand(), borderBlockColorShorthand());
169 case CSSPropertyBorderBlockColor:
170 return get2Values(borderBlockColorShorthand());
171 case CSSPropertyBorderBlockStyle:
172 return get2Values(borderBlockStyleShorthand());
173 case CSSPropertyBorderBlockWidth:
174 return get2Values(borderBlockWidthShorthand());
175 case CSSPropertyBorderBlockStart:
176 return getShorthandValue(borderBlockStartShorthand());
177 case CSSPropertyBorderBlockEnd:
178 return getShorthandValue(borderBlockEndShorthand());
179 case CSSPropertyBorderInline:
180 return borderPropertyValue(borderInlineWidthShorthand(), borderInlineStyleShorthand(), borderInlineColorShorthand());
181 case CSSPropertyBorderInlineColor:
182 return get2Values(borderInlineColorShorthand());
183 case CSSPropertyBorderInlineStyle:
184 return get2Values(borderInlineStyleShorthand());
185 case CSSPropertyBorderInlineWidth:
186 return get2Values(borderInlineWidthShorthand());
187 case CSSPropertyBorderInlineStart:
188 return getShorthandValue(borderInlineStartShorthand());
189 case CSSPropertyBorderInlineEnd:
190 return getShorthandValue(borderInlineEndShorthand());
191 case CSSPropertyOutline:
192 return getShorthandValue(outlineShorthand());
193 case CSSPropertyBorderColor:
194 return get4Values(borderColorShorthand());
195 case CSSPropertyBorderWidth:
196 return get4Values(borderWidthShorthand());
197 case CSSPropertyBorderStyle:
198 return get4Values(borderStyleShorthand());
199 case CSSPropertyColumnRule:
200 return getShorthandValue(columnRuleShorthand());
201 case CSSPropertyColumns:
202 return getShorthandValue(columnsShorthand());
203 case CSSPropertyFlex:
204 return getShorthandValue(flexShorthand());
205 case CSSPropertyFlexFlow:
206 return getShorthandValue(flexFlowShorthand());
207 case CSSPropertyGridArea:
208 return getShorthandValue(gridAreaShorthand());
209 case CSSPropertyGridTemplate:
210 return getShorthandValue(gridTemplateShorthand());
211 case CSSPropertyGrid:
212 return getShorthandValue(gridShorthand());
213 case CSSPropertyGridColumn:
214 return getShorthandValue(gridColumnShorthand());
215 case CSSPropertyGridRow:
216 return getShorthandValue(gridRowShorthand());
217 case CSSPropertyPageBreakAfter:
218 return pageBreakPropertyValue(pageBreakAfterShorthand());
219 case CSSPropertyPageBreakBefore:
220 return pageBreakPropertyValue(pageBreakBeforeShorthand());
221 case CSSPropertyPageBreakInside:
222 return pageBreakPropertyValue(pageBreakInsideShorthand());
223 case CSSPropertyPlaceContent:
224 return getAlignmentShorthandValue(placeContentShorthand());
225 case CSSPropertyPlaceItems:
226 return getAlignmentShorthandValue(placeItemsShorthand());
227 case CSSPropertyPlaceSelf:
228 return getAlignmentShorthandValue(placeSelfShorthand());
229 case CSSPropertyFont:
230 return fontValue();
231 case CSSPropertyInset:
232 return get4Values(insetShorthand());
233 case CSSPropertyInsetBlock:
234 return get2Values(insetBlockShorthand());
235 case CSSPropertyInsetInline:
236 return get2Values(insetInlineShorthand());
237 case CSSPropertyMargin:
238 return get4Values(marginShorthand());
239 case CSSPropertyMarginBlock:
240 return get2Values(marginBlockShorthand());
241 case CSSPropertyMarginInline:
242 return get2Values(marginInlineShorthand());
243 case CSSPropertyWebkitMarginCollapse:
244 return getShorthandValue(webkitMarginCollapseShorthand());
245 case CSSPropertyOverflow:
246 return getCommonValue(overflowShorthand());
247 case CSSPropertyPadding:
248 return get4Values(paddingShorthand());
249 case CSSPropertyPaddingBlock:
250 return get2Values(paddingBlockShorthand());
251 case CSSPropertyPaddingInline:
252 return get2Values(paddingInlineShorthand());
253 case CSSPropertyTransition:
254 return getLayeredShorthandValue(transitionShorthand());
255 case CSSPropertyListStyle:
256 return getShorthandValue(listStyleShorthand());
257 case CSSPropertyWebkitMarquee:
258 return getShorthandValue(webkitMarqueeShorthand());
259 case CSSPropertyWebkitMaskPosition:
260 return getLayeredShorthandValue(webkitMaskPositionShorthand());
261 case CSSPropertyWebkitMaskRepeat:
262 return getLayeredShorthandValue(webkitMaskRepeatShorthand());
263 case CSSPropertyWebkitMask:
264 return getLayeredShorthandValue(webkitMaskShorthand());
265 case CSSPropertyWebkitTextEmphasis:
266 return getShorthandValue(webkitTextEmphasisShorthand());
267 case CSSPropertyWebkitTextStroke:
268 return getShorthandValue(webkitTextStrokeShorthand());
269 case CSSPropertyPerspectiveOrigin:
270 return getShorthandValue(perspectiveOriginShorthand());
271 case CSSPropertyTransformOrigin:
272 return getShorthandValue(transformOriginShorthand());
273 case CSSPropertyMarker: {
274 RefPtr<CSSValue> value = getPropertyCSSValueInternal(CSSPropertyMarkerStart);
275 if (value)
276 return value->cssText();
277 return String();
278 }
279 case CSSPropertyBorderRadius:
280 return get4Values(borderRadiusShorthand());
281#if ENABLE(CSS_SCROLL_SNAP)
282 case CSSPropertyScrollSnapMargin:
283 return get4Values(scrollSnapMarginShorthand());
284 case CSSPropertyScrollPadding:
285 return get4Values(scrollPaddingShorthand());
286#endif
287 default:
288 return String();
289 }
290}
291
292Optional<Color> StyleProperties::propertyAsColor(CSSPropertyID property) const
293{
294 auto colorValue = getPropertyCSSValue(property);
295 if (!is<CSSPrimitiveValue>(colorValue))
296 return WTF::nullopt;
297
298 auto& primitiveColor = downcast<CSSPrimitiveValue>(*colorValue);
299 return primitiveColor.isRGBColor() ? primitiveColor.color() : CSSParser::parseColor(colorValue->cssText());
300}
301
302CSSValueID StyleProperties::propertyAsValueID(CSSPropertyID property) const
303{
304 auto cssValue = getPropertyCSSValue(property);
305 return is<CSSPrimitiveValue>(cssValue) ? downcast<CSSPrimitiveValue>(*cssValue).valueID() : CSSValueInvalid;
306}
307
308String StyleProperties::getCustomPropertyValue(const String& propertyName) const
309{
310 RefPtr<CSSValue> value = getCustomPropertyCSSValue(propertyName);
311 if (value)
312 return value->cssText();
313 return String();
314}
315
316String StyleProperties::borderSpacingValue(const StylePropertyShorthand& shorthand) const
317{
318 RefPtr<CSSValue> horizontalValue = getPropertyCSSValueInternal(shorthand.properties()[0]);
319 RefPtr<CSSValue> verticalValue = getPropertyCSSValueInternal(shorthand.properties()[1]);
320
321 // While standard border-spacing property does not allow specifying border-spacing-vertical without
322 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
323 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
324 if (!horizontalValue || !verticalValue)
325 return String();
326
327 String horizontalValueCSSText = horizontalValue->cssText();
328 String verticalValueCSSText = verticalValue->cssText();
329 if (horizontalValueCSSText == verticalValueCSSText)
330 return horizontalValueCSSText;
331 return horizontalValueCSSText + ' ' + verticalValueCSSText;
332}
333
334void StyleProperties::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
335{
336 int foundPropertyIndex = findPropertyIndex(propertyID);
337 if (foundPropertyIndex == -1)
338 return; // All longhands must have at least implicit values if "font" is specified.
339
340 if (propertyAt(foundPropertyIndex).isImplicit()) {
341 commonValue = String();
342 return;
343 }
344
345 char prefix = '\0';
346 switch (propertyID) {
347 case CSSPropertyFontStyle:
348 break; // No prefix.
349 case CSSPropertyFontFamily:
350 case CSSPropertyFontVariantCaps:
351 case CSSPropertyFontWeight:
352 case CSSPropertyFontStretch:
353 prefix = ' ';
354 break;
355 case CSSPropertyLineHeight:
356 prefix = '/';
357 break;
358 default:
359 ASSERT_NOT_REACHED();
360 }
361
362 if (prefix && !result.isEmpty())
363 result.append(prefix);
364 String value = propertyAt(foundPropertyIndex).value()->cssText();
365 result.append(value);
366 if (!commonValue.isNull() && commonValue != value)
367 commonValue = String();
368}
369
370String StyleProperties::fontValue() const
371{
372 int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize);
373 int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily);
374 if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
375 return emptyString();
376
377 PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex);
378 PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex);
379 if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
380 return emptyString();
381
382 String commonValue = fontSizeProperty.value()->cssText();
383 StringBuilder result;
384 appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
385 appendFontLonghandValueIfExplicit(CSSPropertyFontVariantCaps, result, commonValue);
386 appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
387 appendFontLonghandValueIfExplicit(CSSPropertyFontStretch, result, commonValue);
388 if (!result.isEmpty())
389 result.append(' ');
390 result.append(fontSizeProperty.value()->cssText());
391 appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
392 if (!result.isEmpty())
393 result.append(' ');
394 result.append(fontFamilyProperty.value()->cssText());
395 if (isInitialOrInherit(commonValue))
396 return commonValue;
397 return result.toString();
398}
399
400String StyleProperties::get2Values(const StylePropertyShorthand& shorthand) const
401{
402 // Assume the properties are in the usual order start, end.
403 int startValueIndex = findPropertyIndex(shorthand.properties()[0]);
404 int endValueIndex = findPropertyIndex(shorthand.properties()[1]);
405
406 if (startValueIndex == -1 || endValueIndex == -1)
407 return { };
408
409 auto start = propertyAt(startValueIndex);
410 auto end = propertyAt(endValueIndex);
411
412 // All 2 properties must be specified.
413 if (!start.value() || !end.value())
414 return { };
415
416 // Important flags must be the same
417 if (start.isImportant() != end.isImportant())
418 return { };
419
420 if (start.isInherited() && end.isInherited())
421 return getValueName(CSSValueInherit);
422
423 if (start.value()->isInitialValue() || end.value()->isInitialValue()) {
424 if (start.value()->isInitialValue() && end.value()->isInitialValue() && !start.isImplicit())
425 return getValueName(CSSValueInitial);
426 return { };
427 }
428
429 StringBuilder result;
430 result.append(start.value()->cssText());
431 if (!start.value()->equals(*end.value())) {
432 result.append(' ');
433 result.append(end.value()->cssText());
434 }
435 return result.toString();
436}
437
438String StyleProperties::get4Values(const StylePropertyShorthand& shorthand) const
439{
440 // Assume the properties are in the usual order top, right, bottom, left.
441 int topValueIndex = findPropertyIndex(shorthand.properties()[0]);
442 int rightValueIndex = findPropertyIndex(shorthand.properties()[1]);
443 int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]);
444 int leftValueIndex = findPropertyIndex(shorthand.properties()[3]);
445
446 if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
447 return String();
448
449 PropertyReference top = propertyAt(topValueIndex);
450 PropertyReference right = propertyAt(rightValueIndex);
451 PropertyReference bottom = propertyAt(bottomValueIndex);
452 PropertyReference left = propertyAt(leftValueIndex);
453
454 // All 4 properties must be specified.
455 if (!top.value() || !right.value() || !bottom.value() || !left.value())
456 return String();
457
458 // Important flags must be the same
459 if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
460 return String();
461
462 if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
463 return getValueName(CSSValueInherit);
464
465 if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
466 if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
467 // All components are "initial" and "top" is not implicit.
468 return getValueName(CSSValueInitial);
469 }
470 return String();
471 }
472
473 bool showLeft = !right.value()->equals(*left.value());
474 bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
475 bool showRight = !top.value()->equals(*right.value()) || showBottom;
476
477 StringBuilder result;
478 result.append(top.value()->cssText());
479 if (showRight) {
480 result.append(' ');
481 result.append(right.value()->cssText());
482 }
483 if (showBottom) {
484 result.append(' ');
485 result.append(bottom.value()->cssText());
486 }
487 if (showLeft) {
488 result.append(' ');
489 result.append(left.value()->cssText());
490 }
491 return result.toString();
492}
493
494String StyleProperties::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
495{
496 StringBuilder result;
497
498 const unsigned size = shorthand.length();
499 // Begin by collecting the properties into an array.
500 Vector< RefPtr<CSSValue>> values(size);
501 size_t numLayers = 0;
502
503 for (unsigned i = 0; i < size; ++i) {
504 values[i] = getPropertyCSSValueInternal(shorthand.properties()[i]);
505 if (!values[i]) {
506 // We don't have all longhand properties defined as required for the shorthand
507 // property and thus should not serialize to a shorthand value. See spec at
508 // http://www.w3.org/TR/cssom-1/#serialize-a-css-declaration-block.
509 return String();
510 }
511 if (values[i]->isBaseValueList())
512 numLayers = std::max(downcast<CSSValueList>(*values[i]).length(), numLayers);
513 else
514 numLayers = std::max<size_t>(1U, numLayers);
515 }
516
517 String commonValue;
518 bool commonValueInitialized = false;
519
520 // Now stitch the properties together. Implicit initial values are flagged as such and
521 // can safely be omitted.
522 for (size_t i = 0; i < numLayers; i++) {
523 StringBuilder layerResult;
524 bool useRepeatXShorthand = false;
525 bool useRepeatYShorthand = false;
526 bool useSingleWordShorthand = false;
527 bool foundPositionYCSSProperty = false;
528 for (unsigned j = 0; j < size; j++) {
529 RefPtr<CSSValue> value;
530 if (values[j]) {
531 if (values[j]->isBaseValueList())
532 value = downcast<CSSValueList>(*values[j]).item(i);
533 else {
534 value = values[j];
535
536 // Color only belongs in the last layer.
537 if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
538 if (i != numLayers - 1)
539 value = nullptr;
540 } else if (i) // Other singletons only belong in the first layer.
541 value = nullptr;
542 }
543 }
544
545 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
546 // then it was written with only one value. Here we figure out which value that was so we can
547 // report back correctly.
548 if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j]))
549 || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && isPropertyImplicit(shorthand.properties()[j]))) {
550
551 // BUG 49055: make sure the value was not reset in the layer check just above.
552 if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
553 || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
554 RefPtr<CSSValue> yValue;
555 RefPtr<CSSValue> nextValue = values[j + 1];
556 if (nextValue) {
557 if (is<CSSValueList>(*nextValue))
558 yValue = downcast<CSSValueList>(*nextValue).itemWithoutBoundsCheck(i);
559 else
560 yValue = nextValue;
561
562 if (!is<CSSPrimitiveValue>(*value) || !is<CSSPrimitiveValue>(*yValue))
563 continue;
564
565 CSSValueID xId = downcast<CSSPrimitiveValue>(*value).valueID();
566 CSSValueID yId = downcast<CSSPrimitiveValue>(*yValue).valueID();
567 if (xId != yId) {
568 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
569 useRepeatXShorthand = true;
570 ++j;
571 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
572 useRepeatYShorthand = true;
573 continue;
574 }
575 } else {
576 useSingleWordShorthand = true;
577 ++j;
578 }
579 }
580 }
581 }
582
583 String valueText;
584 if (value && !value->isImplicitInitialValue()) {
585 if (!layerResult.isEmpty())
586 layerResult.append(' ');
587 if (foundPositionYCSSProperty
588 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
589 layerResult.appendLiteral("/ ");
590 if (!foundPositionYCSSProperty
591 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
592 continue;
593
594 if (useRepeatXShorthand) {
595 useRepeatXShorthand = false;
596 layerResult.append(getValueName(CSSValueRepeatX));
597 } else if (useRepeatYShorthand) {
598 useRepeatYShorthand = false;
599 layerResult.append(getValueName(CSSValueRepeatY));
600 } else {
601 if (useSingleWordShorthand)
602 useSingleWordShorthand = false;
603 valueText = value->cssText();
604 layerResult.append(valueText);
605 }
606
607 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
608 || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
609 foundPositionYCSSProperty = true;
610
611 // background-position is a special case: if only the first offset is specified,
612 // the second one defaults to "center", not the same value.
613 if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
614 commonValue = String();
615 }
616 }
617
618 if (!commonValueInitialized) {
619 commonValue = valueText;
620 commonValueInitialized = true;
621 } else if (!commonValue.isNull() && commonValue != valueText)
622 commonValue = String();
623 }
624
625 if (!layerResult.isEmpty()) {
626 if (!result.isEmpty())
627 result.appendLiteral(", ");
628 result.append(layerResult);
629 }
630 }
631
632 if (isInitialOrInherit(commonValue))
633 return commonValue;
634
635 if (result.isEmpty())
636 return String();
637 return result.toString();
638}
639
640String StyleProperties::getShorthandValue(const StylePropertyShorthand& shorthand) const
641{
642 String commonValue;
643 StringBuilder result;
644 for (unsigned i = 0; i < shorthand.length(); ++i) {
645 if (!isPropertyImplicit(shorthand.properties()[i])) {
646 RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[i]);
647 if (!value)
648 return String();
649 String valueText = value->cssText();
650 if (!i)
651 commonValue = valueText;
652 else if (!commonValue.isNull() && commonValue != valueText)
653 commonValue = String();
654 if (value->isInitialValue())
655 continue;
656 if (!result.isEmpty())
657 result.append(' ');
658 result.append(valueText);
659 } else
660 commonValue = String();
661 }
662 if (isInitialOrInherit(commonValue))
663 return commonValue;
664 if (result.isEmpty())
665 return String();
666 return result.toString();
667}
668
669// only returns a non-null value if all properties have the same, non-null value
670String StyleProperties::getCommonValue(const StylePropertyShorthand& shorthand) const
671{
672 String res;
673 bool lastPropertyWasImportant = false;
674 for (unsigned i = 0; i < shorthand.length(); ++i) {
675 RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[i]);
676 if (!value)
677 return String();
678 // FIXME: CSSInitialValue::cssText should generate the right value.
679 String text = value->cssText();
680 if (text.isNull())
681 return String();
682 if (res.isNull())
683 res = text;
684 else if (res != text)
685 return String();
686
687 bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
688 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
689 return String();
690 lastPropertyWasImportant = currentPropertyIsImportant;
691 }
692 return res;
693}
694
695String StyleProperties::getAlignmentShorthandValue(const StylePropertyShorthand& shorthand) const
696{
697 String value = getCommonValue(shorthand);
698 if (value.isNull() || value.isEmpty())
699 return getShorthandValue(shorthand);
700 return value;
701}
702
703String StyleProperties::borderPropertyValue(const StylePropertyShorthand& width, const StylePropertyShorthand& style, const StylePropertyShorthand& color) const
704{
705 const StylePropertyShorthand properties[3] = { width, style, color };
706 String commonValue;
707 StringBuilder result;
708 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
709 String value = getCommonValue(properties[i]);
710 if (value.isNull())
711 return String();
712 if (!i)
713 commonValue = value;
714 else if (commonValue != value)
715 commonValue = String();
716 if (value == "initial")
717 continue;
718 if (!result.isEmpty())
719 result.append(' ');
720 result.append(value);
721 }
722 if (isInitialOrInherit(commonValue))
723 return commonValue;
724 return result.toString();
725}
726
727String StyleProperties::pageBreakPropertyValue(const StylePropertyShorthand& shorthand) const
728{
729 RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[0]);
730 // FIXME: Remove this isGlobalKeyword check after we do this consistently for all shorthands in getPropertyValue.
731 if (value->isGlobalKeyword())
732 return value->cssText();
733 CSSValueID valueId = downcast<CSSPrimitiveValue>(*value).valueID();
734 switch (valueId) {
735 case CSSValuePage:
736 return "always"_s;
737 case CSSValueAuto:
738 case CSSValueAvoid:
739 case CSSValueLeft:
740 case CSSValueRight:
741 return value->cssText();
742 default:
743 return String();
744 }
745}
746
747RefPtr<CSSValue> StyleProperties::getPropertyCSSValue(CSSPropertyID propertyID) const
748{
749 return getPropertyCSSValueInternal(propertyID);
750}
751
752RefPtr<CSSValue> StyleProperties::getPropertyCSSValueInternal(CSSPropertyID propertyID) const
753{
754 int foundPropertyIndex = findPropertyIndex(propertyID);
755 if (foundPropertyIndex == -1)
756 return nullptr;
757 return propertyAt(foundPropertyIndex).value();
758}
759
760RefPtr<CSSValue> StyleProperties::getCustomPropertyCSSValue(const String& propertyName) const
761{
762 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
763 if (foundPropertyIndex == -1)
764 return nullptr;
765 return propertyAt(foundPropertyIndex).value();
766}
767
768bool MutableStyleProperties::removeShorthandProperty(CSSPropertyID propertyID)
769{
770 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
771 if (!shorthand.length())
772 return false;
773
774 return removePropertiesInSet(shorthand.properties(), shorthand.length());
775}
776
777bool MutableStyleProperties::removeProperty(CSSPropertyID propertyID, String* returnText)
778{
779 if (removeShorthandProperty(propertyID)) {
780 // FIXME: Return an equivalent shorthand when possible.
781 if (returnText)
782 *returnText = emptyString();
783 return true;
784 }
785
786 int foundPropertyIndex = findPropertyIndex(propertyID);
787 if (foundPropertyIndex == -1) {
788 if (returnText)
789 *returnText = emptyString();
790 return false;
791 }
792
793 if (returnText)
794 *returnText = propertyAt(foundPropertyIndex).value()->cssText();
795
796 // A more efficient removal strategy would involve marking entries as empty
797 // and sweeping them when the vector grows too big.
798 m_propertyVector.remove(foundPropertyIndex);
799
800 return true;
801}
802
803bool MutableStyleProperties::removeCustomProperty(const String& propertyName, String* returnText)
804{
805 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
806 if (foundPropertyIndex == -1) {
807 if (returnText)
808 *returnText = emptyString();
809 return false;
810 }
811
812 if (returnText)
813 *returnText = propertyAt(foundPropertyIndex).value()->cssText();
814
815 // A more efficient removal strategy would involve marking entries as empty
816 // and sweeping them when the vector grows too big.
817 m_propertyVector.remove(foundPropertyIndex);
818
819 return true;
820}
821
822bool StyleProperties::propertyIsImportant(CSSPropertyID propertyID) const
823{
824 int foundPropertyIndex = findPropertyIndex(propertyID);
825 if (foundPropertyIndex != -1)
826 return propertyAt(foundPropertyIndex).isImportant();
827
828 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
829 if (!shorthand.length())
830 return false;
831
832 for (unsigned i = 0; i < shorthand.length(); ++i) {
833 if (!propertyIsImportant(shorthand.properties()[i]))
834 return false;
835 }
836 return true;
837}
838
839bool StyleProperties::customPropertyIsImportant(const String& propertyName) const
840{
841 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
842 if (foundPropertyIndex != -1)
843 return propertyAt(foundPropertyIndex).isImportant();
844 return false;
845}
846
847String StyleProperties::getPropertyShorthand(CSSPropertyID propertyID) const
848{
849 int foundPropertyIndex = findPropertyIndex(propertyID);
850 if (foundPropertyIndex == -1)
851 return String();
852 return getPropertyNameString(propertyAt(foundPropertyIndex).shorthandID());
853}
854
855bool StyleProperties::isPropertyImplicit(CSSPropertyID propertyID) const
856{
857 int foundPropertyIndex = findPropertyIndex(propertyID);
858 if (foundPropertyIndex == -1)
859 return false;
860 return propertyAt(foundPropertyIndex).isImplicit();
861}
862
863bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important, CSSParserContext parserContext)
864{
865 if (!isEnabledCSSProperty(propertyID))
866 return false;
867
868 // Setting the value to an empty string just removes the property in both IE and Gecko.
869 // Setting it to null seems to produce less consistent results, but we treat it just the same.
870 if (value.isEmpty())
871 return removeProperty(propertyID);
872
873 parserContext.mode = cssParserMode();
874
875 // When replacing an existing property value, this moves the property to the end of the list.
876 // Firefox preserves the position, and MSIE moves the property to the beginning.
877 return CSSParser::parseValue(*this, propertyID, value, important, parserContext) == CSSParser::ParseResult::Changed;
878}
879
880bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important)
881{
882 CSSParserContext parserContext(cssParserMode());
883 return setProperty(propertyID, value, important, parserContext);
884}
885
886bool MutableStyleProperties::setCustomProperty(const Document* document, const String& propertyName, const String& value, bool important, CSSParserContext parserContext)
887{
888 // Setting the value to an empty string just removes the property in both IE and Gecko.
889 // Setting it to null seems to produce less consistent results, but we treat it just the same.
890 if (value.isEmpty())
891 return removeCustomProperty(propertyName);
892
893 parserContext.mode = cssParserMode();
894
895 String syntax = "*";
896 auto* registered = document ? document->getCSSRegisteredCustomPropertySet().get(propertyName) : nullptr;
897
898 if (registered)
899 syntax = registered->syntax;
900
901 CSSTokenizer tokenizer(value);
902 if (!CSSPropertyParser::canParseTypedCustomPropertyValue(syntax, tokenizer.tokenRange(), parserContext))
903 return false;
904
905 // When replacing an existing property value, this moves the property to the end of the list.
906 // Firefox preserves the position, and MSIE moves the property to the beginning.
907 return CSSParser::parseCustomPropertyValue(*this, propertyName, value, important, parserContext) == CSSParser::ParseResult::Changed;
908}
909
910void MutableStyleProperties::setProperty(CSSPropertyID propertyID, RefPtr<CSSValue>&& value, bool important)
911{
912 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
913 if (!shorthand.length()) {
914 setProperty(CSSProperty(propertyID, WTFMove(value), important));
915 return;
916 }
917
918 removePropertiesInSet(shorthand.properties(), shorthand.length());
919
920 for (unsigned i = 0; i < shorthand.length(); ++i)
921 m_propertyVector.append(CSSProperty(shorthand.properties()[i], value.copyRef(), important));
922}
923
924bool MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot)
925{
926 if (!removeShorthandProperty(property.id())) {
927 CSSProperty* toReplace = slot;
928 if (!slot) {
929 if (property.id() == CSSPropertyCustom) {
930 if (property.value())
931 toReplace = findCustomCSSPropertyWithName(downcast<CSSCustomPropertyValue>(*property.value()).name());
932 } else
933 toReplace = findCSSPropertyWithID(property.id());
934 }
935
936 if (toReplace) {
937 if (*toReplace == property)
938 return false;
939
940 *toReplace = property;
941 return true;
942 }
943 }
944
945 m_propertyVector.append(property);
946 return true;
947}
948
949bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
950{
951 return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
952}
953
954bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
955{
956 return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
957}
958
959bool MutableStyleProperties::parseDeclaration(const String& styleDeclaration, CSSParserContext context)
960{
961 auto oldProperties = WTFMove(m_propertyVector);
962 m_propertyVector.clear();
963
964 context.mode = cssParserMode();
965
966 CSSParser parser(context);
967 parser.parseDeclaration(*this, styleDeclaration);
968
969 // We could do better. Just changing property order does not require style invalidation.
970 return oldProperties != m_propertyVector;
971}
972
973bool MutableStyleProperties::addParsedProperties(const ParsedPropertyVector& properties)
974{
975 bool anyChanged = false;
976 m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
977 for (const auto& property : properties) {
978 if (addParsedProperty(property))
979 anyChanged = true;
980 }
981
982 return anyChanged;
983}
984
985bool MutableStyleProperties::addParsedProperty(const CSSProperty& property)
986{
987 if (property.id() == CSSPropertyCustom) {
988 if ((property.value() && !customPropertyIsImportant(downcast<CSSCustomPropertyValue>(*property.value()).name())) || property.isImportant())
989 return setProperty(property);
990 return false;
991 }
992 return setProperty(property);
993}
994
995String StyleProperties::asText() const
996{
997 StringBuilder result;
998
999 int positionXPropertyIndex = -1;
1000 int positionYPropertyIndex = -1;
1001 int repeatXPropertyIndex = -1;
1002 int repeatYPropertyIndex = -1;
1003
1004 std::bitset<numCSSProperties> shorthandPropertyUsed;
1005 std::bitset<numCSSProperties> shorthandPropertyAppeared;
1006
1007 unsigned size = propertyCount();
1008 unsigned numDecls = 0;
1009 for (unsigned n = 0; n < size; ++n) {
1010 PropertyReference property = propertyAt(n);
1011 CSSPropertyID propertyID = property.id();
1012 CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
1013 CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
1014 CSSPropertyID borderBlockFallbackShorthandProperty = CSSPropertyInvalid;
1015 CSSPropertyID borderInlineFallbackShorthandProperty = CSSPropertyInvalid;
1016 String value;
1017 auto serializeBorderShorthand = [&] (const CSSPropertyID borderProperty, const CSSPropertyID fallbackProperty) {
1018 // FIXME: Deal with cases where only some of border sides are specified.
1019 ASSERT(borderProperty - firstCSSProperty < static_cast<CSSPropertyID>(shorthandPropertyAppeared.size()));
1020 if (!shorthandPropertyAppeared[borderProperty - firstCSSProperty] && isEnabledCSSProperty(borderProperty)) {
1021 value = getPropertyValue(borderProperty);
1022 if (value.isNull())
1023 shorthandPropertyAppeared.set(borderProperty - firstCSSProperty);
1024 else
1025 shorthandPropertyID = borderProperty;
1026 } else if (shorthandPropertyUsed[borderProperty - firstCSSProperty])
1027 shorthandPropertyID = borderProperty;
1028 if (!shorthandPropertyID)
1029 shorthandPropertyID = fallbackProperty;
1030 };
1031
1032 if (property.value() && property.value()->isPendingSubstitutionValue()) {
1033 auto& substitutionValue = downcast<CSSPendingSubstitutionValue>(*property.value());
1034 shorthandPropertyID = substitutionValue.shorthandPropertyId();
1035 value = substitutionValue.shorthandValue()->cssText();
1036 } else {
1037 switch (propertyID) {
1038 case CSSPropertyAnimationName:
1039 case CSSPropertyAnimationDuration:
1040 case CSSPropertyAnimationTimingFunction:
1041 case CSSPropertyAnimationDelay:
1042 case CSSPropertyAnimationIterationCount:
1043 case CSSPropertyAnimationDirection:
1044 case CSSPropertyAnimationFillMode:
1045 case CSSPropertyAnimationPlayState:
1046 shorthandPropertyID = CSSPropertyAnimation;
1047 break;
1048 case CSSPropertyBackgroundPositionX:
1049 positionXPropertyIndex = n;
1050 continue;
1051 case CSSPropertyBackgroundPositionY:
1052 positionYPropertyIndex = n;
1053 continue;
1054 case CSSPropertyBackgroundRepeatX:
1055 repeatXPropertyIndex = n;
1056 continue;
1057 case CSSPropertyBackgroundRepeatY:
1058 repeatYPropertyIndex = n;
1059 continue;
1060 case CSSPropertyBorderTopWidth:
1061 case CSSPropertyBorderRightWidth:
1062 case CSSPropertyBorderBottomWidth:
1063 case CSSPropertyBorderLeftWidth:
1064 if (!borderFallbackShorthandProperty)
1065 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
1066 FALLTHROUGH;
1067 case CSSPropertyBorderTopStyle:
1068 case CSSPropertyBorderRightStyle:
1069 case CSSPropertyBorderBottomStyle:
1070 case CSSPropertyBorderLeftStyle:
1071 if (!borderFallbackShorthandProperty)
1072 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
1073 FALLTHROUGH;
1074 case CSSPropertyBorderTopColor:
1075 case CSSPropertyBorderRightColor:
1076 case CSSPropertyBorderBottomColor:
1077 case CSSPropertyBorderLeftColor:
1078 if (!borderFallbackShorthandProperty)
1079 borderFallbackShorthandProperty = CSSPropertyBorderColor;
1080 serializeBorderShorthand(CSSPropertyBorder, borderFallbackShorthandProperty);
1081 break;
1082 case CSSPropertyBorderBlockStartWidth:
1083 case CSSPropertyBorderBlockEndWidth:
1084 if (!borderBlockFallbackShorthandProperty)
1085 borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockWidth;
1086 FALLTHROUGH;
1087 case CSSPropertyBorderBlockStartStyle:
1088 case CSSPropertyBorderBlockEndStyle:
1089 if (!borderBlockFallbackShorthandProperty)
1090 borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockStyle;
1091 FALLTHROUGH;
1092 case CSSPropertyBorderBlockStartColor:
1093 case CSSPropertyBorderBlockEndColor:
1094 if (!borderBlockFallbackShorthandProperty)
1095 borderBlockFallbackShorthandProperty = CSSPropertyBorderBlockColor;
1096 serializeBorderShorthand(CSSPropertyBorderBlock, borderBlockFallbackShorthandProperty);
1097 break;
1098 case CSSPropertyBorderInlineStartWidth:
1099 case CSSPropertyBorderInlineEndWidth:
1100 if (!borderInlineFallbackShorthandProperty)
1101 borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineWidth;
1102 FALLTHROUGH;
1103 case CSSPropertyBorderInlineStartStyle:
1104 case CSSPropertyBorderInlineEndStyle:
1105 if (!borderInlineFallbackShorthandProperty)
1106 borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineStyle;
1107 FALLTHROUGH;
1108 case CSSPropertyBorderInlineStartColor:
1109 case CSSPropertyBorderInlineEndColor:
1110 if (!borderInlineFallbackShorthandProperty)
1111 borderInlineFallbackShorthandProperty = CSSPropertyBorderInlineColor;
1112 serializeBorderShorthand(CSSPropertyBorderInline, borderInlineFallbackShorthandProperty);
1113 break;
1114 case CSSPropertyWebkitBorderHorizontalSpacing:
1115 case CSSPropertyWebkitBorderVerticalSpacing:
1116 shorthandPropertyID = CSSPropertyBorderSpacing;
1117 break;
1118 case CSSPropertyFontFamily:
1119 case CSSPropertyLineHeight:
1120 case CSSPropertyFontSize:
1121 case CSSPropertyFontStyle:
1122 case CSSPropertyFontVariantCaps:
1123 case CSSPropertyFontWeight:
1124 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
1125 break;
1126 case CSSPropertyTop:
1127 case CSSPropertyRight:
1128 case CSSPropertyBottom:
1129 case CSSPropertyLeft:
1130 shorthandPropertyID = CSSPropertyInset;
1131 break;
1132 case CSSPropertyInsetBlockStart:
1133 case CSSPropertyInsetBlockEnd:
1134 shorthandPropertyID = CSSPropertyInsetBlock;
1135 break;
1136 case CSSPropertyInsetInlineStart:
1137 case CSSPropertyInsetInlineEnd:
1138 shorthandPropertyID = CSSPropertyInsetInline;
1139 break;
1140 case CSSPropertyListStyleType:
1141 case CSSPropertyListStylePosition:
1142 case CSSPropertyListStyleImage:
1143 shorthandPropertyID = CSSPropertyListStyle;
1144 break;
1145 case CSSPropertyMarginTop:
1146 case CSSPropertyMarginRight:
1147 case CSSPropertyMarginBottom:
1148 case CSSPropertyMarginLeft:
1149 shorthandPropertyID = CSSPropertyMargin;
1150 break;
1151 case CSSPropertyMarginBlockStart:
1152 case CSSPropertyMarginBlockEnd:
1153 shorthandPropertyID = CSSPropertyMarginBlock;
1154 break;
1155 case CSSPropertyMarginInlineStart:
1156 case CSSPropertyMarginInlineEnd:
1157 shorthandPropertyID = CSSPropertyMarginInline;
1158 break;
1159 case CSSPropertyOutlineWidth:
1160 case CSSPropertyOutlineStyle:
1161 case CSSPropertyOutlineColor:
1162 shorthandPropertyID = CSSPropertyOutline;
1163 break;
1164 case CSSPropertyOverflowX:
1165 case CSSPropertyOverflowY:
1166 shorthandPropertyID = CSSPropertyOverflow;
1167 break;
1168 case CSSPropertyPaddingTop:
1169 case CSSPropertyPaddingRight:
1170 case CSSPropertyPaddingBottom:
1171 case CSSPropertyPaddingLeft:
1172 shorthandPropertyID = CSSPropertyPadding;
1173 break;
1174 case CSSPropertyPaddingBlockStart:
1175 case CSSPropertyPaddingBlockEnd:
1176 shorthandPropertyID = CSSPropertyPaddingBlock;
1177 break;
1178 case CSSPropertyPaddingInlineStart:
1179 case CSSPropertyPaddingInlineEnd:
1180 shorthandPropertyID = CSSPropertyPaddingInline;
1181 break;
1182#if ENABLE(CSS_SCROLL_SNAP)
1183 case CSSPropertyScrollPaddingTop:
1184 case CSSPropertyScrollPaddingRight:
1185 case CSSPropertyScrollPaddingBottom:
1186 case CSSPropertyScrollPaddingLeft:
1187 shorthandPropertyID = CSSPropertyScrollPadding;
1188 break;
1189 case CSSPropertyScrollSnapMarginTop:
1190 case CSSPropertyScrollSnapMarginRight:
1191 case CSSPropertyScrollSnapMarginBottom:
1192 case CSSPropertyScrollSnapMarginLeft:
1193 shorthandPropertyID = CSSPropertyScrollSnapMargin;
1194 break;
1195#endif
1196 case CSSPropertyTransitionProperty:
1197 case CSSPropertyTransitionDuration:
1198 case CSSPropertyTransitionTimingFunction:
1199 case CSSPropertyTransitionDelay:
1200 shorthandPropertyID = CSSPropertyTransition;
1201 break;
1202 case CSSPropertyFlexDirection:
1203 case CSSPropertyFlexWrap:
1204 shorthandPropertyID = CSSPropertyFlexFlow;
1205 break;
1206 case CSSPropertyFlexBasis:
1207 case CSSPropertyFlexGrow:
1208 case CSSPropertyFlexShrink:
1209 shorthandPropertyID = CSSPropertyFlex;
1210 break;
1211 case CSSPropertyWebkitMaskPositionX:
1212 case CSSPropertyWebkitMaskPositionY:
1213 case CSSPropertyWebkitMaskRepeatX:
1214 case CSSPropertyWebkitMaskRepeatY:
1215 case CSSPropertyWebkitMaskImage:
1216 case CSSPropertyWebkitMaskRepeat:
1217 case CSSPropertyWebkitMaskPosition:
1218 case CSSPropertyWebkitMaskClip:
1219 case CSSPropertyWebkitMaskOrigin:
1220 shorthandPropertyID = CSSPropertyWebkitMask;
1221 break;
1222 case CSSPropertyPerspectiveOriginX:
1223 case CSSPropertyPerspectiveOriginY:
1224 shorthandPropertyID = CSSPropertyPerspectiveOrigin;
1225 break;
1226 case CSSPropertyTransformOriginX:
1227 case CSSPropertyTransformOriginY:
1228 case CSSPropertyTransformOriginZ:
1229 shorthandPropertyID = CSSPropertyTransformOrigin;
1230 break;
1231 default:
1232 break;
1233 }
1234 }
1235
1236 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
1237 if (shorthandPropertyID && isEnabledCSSProperty(shorthandPropertyID)) {
1238 ASSERT(shortPropertyIndex < shorthandPropertyUsed.size());
1239 if (shorthandPropertyUsed[shortPropertyIndex])
1240 continue;
1241 if (!shorthandPropertyAppeared[shortPropertyIndex] && value.isNull())
1242 value = getPropertyValue(shorthandPropertyID);
1243 shorthandPropertyAppeared.set(shortPropertyIndex);
1244 }
1245
1246 if (!value.isNull()) {
1247 propertyID = shorthandPropertyID;
1248 shorthandPropertyUsed.set(shortPropertyIndex);
1249 } else
1250 value = property.value()->cssText();
1251
1252 if (propertyID != CSSPropertyCustom && value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
1253 continue;
1254
1255 if (numDecls++)
1256 result.append(' ');
1257
1258 if (propertyID == CSSPropertyCustom)
1259 result.append(downcast<CSSCustomPropertyValue>(*property.value()).name());
1260 else
1261 result.append(getPropertyName(propertyID));
1262
1263 result.appendLiteral(": ");
1264 result.append(value);
1265 if (property.isImportant())
1266 result.appendLiteral(" !important");
1267 result.append(';');
1268 }
1269
1270 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
1271 // It is required because background-position-x/y are non-standard properties and WebKit generated output
1272 // would not work in Firefox (<rdar://problem/5143183>)
1273 // It would be a better solution if background-position was CSS_PAIR.
1274 if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
1275 PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
1276 PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
1277
1278 if (numDecls++)
1279 result.append(' ');
1280 result.appendLiteral("background-position: ");
1281 if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
1282 result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
1283 else {
1284 result.append(positionXProperty.value()->cssText());
1285 result.append(' ');
1286 result.append(positionYProperty.value()->cssText());
1287 }
1288 if (positionXProperty.isImportant())
1289 result.appendLiteral(" !important");
1290 result.append(';');
1291 } else {
1292 if (positionXPropertyIndex != -1) {
1293 if (numDecls++)
1294 result.append(' ');
1295 result.append(propertyAt(positionXPropertyIndex).cssText());
1296 }
1297 if (positionYPropertyIndex != -1) {
1298 if (numDecls++)
1299 result.append(' ');
1300 result.append(propertyAt(positionYPropertyIndex).cssText());
1301 }
1302 }
1303
1304 // FIXME: We need to do the same for background-repeat.
1305 if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
1306 PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
1307 PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
1308
1309 if (numDecls++)
1310 result.append(' ');
1311 result.appendLiteral("background-repeat: ");
1312 if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
1313 result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
1314 else {
1315 result.append(repeatXProperty.value()->cssText());
1316 result.append(' ');
1317 result.append(repeatYProperty.value()->cssText());
1318 }
1319 if (repeatXProperty.isImportant())
1320 result.appendLiteral(" !important");
1321 result.append(';');
1322 } else {
1323 if (repeatXPropertyIndex != -1) {
1324 if (numDecls++)
1325 result.append(' ');
1326 result.append(propertyAt(repeatXPropertyIndex).cssText());
1327 }
1328 if (repeatYPropertyIndex != -1) {
1329 if (numDecls++)
1330 result.append(' ');
1331 result.append(propertyAt(repeatYPropertyIndex).cssText());
1332 }
1333 }
1334
1335 ASSERT(!numDecls ^ !result.isEmpty());
1336 return result.toString();
1337}
1338
1339bool StyleProperties::hasCSSOMWrapper() const
1340{
1341 return is<MutableStyleProperties>(*this) && downcast<MutableStyleProperties>(*this).m_cssomWrapper;
1342}
1343
1344void MutableStyleProperties::mergeAndOverrideOnConflict(const StyleProperties& other)
1345{
1346 unsigned size = other.propertyCount();
1347 for (unsigned i = 0; i < size; ++i)
1348 addParsedProperty(other.propertyAt(i).toCSSProperty());
1349}
1350
1351bool StyleProperties::traverseSubresources(const WTF::Function<bool (const CachedResource&)>& handler) const
1352{
1353 unsigned size = propertyCount();
1354 for (unsigned i = 0; i < size; ++i) {
1355 if (propertyAt(i).value()->traverseSubresources(handler))
1356 return true;
1357 }
1358 return false;
1359}
1360
1361// This is the list of properties we want to copy in the copyBlockProperties() function.
1362// It is the list of CSS properties that apply specially to block-level elements.
1363static const CSSPropertyID blockProperties[] = {
1364 CSSPropertyOrphans,
1365 CSSPropertyOverflow, // This can be also be applied to replaced elements
1366 CSSPropertyWebkitAspectRatio,
1367 CSSPropertyColumnCount,
1368 CSSPropertyColumnGap,
1369 CSSPropertyRowGap,
1370 CSSPropertyColumnRuleColor,
1371 CSSPropertyColumnRuleStyle,
1372 CSSPropertyColumnRuleWidth,
1373 CSSPropertyWebkitColumnBreakBefore,
1374 CSSPropertyWebkitColumnBreakAfter,
1375 CSSPropertyWebkitColumnBreakInside,
1376 CSSPropertyColumnWidth,
1377 CSSPropertyPageBreakAfter,
1378 CSSPropertyPageBreakBefore,
1379 CSSPropertyPageBreakInside,
1380 CSSPropertyTextAlign,
1381#if ENABLE(CSS3_TEXT)
1382 CSSPropertyWebkitTextAlignLast,
1383 CSSPropertyWebkitTextJustify,
1384#endif // CSS3_TEXT
1385 CSSPropertyTextIndent,
1386 CSSPropertyWidows
1387};
1388
1389void MutableStyleProperties::clear()
1390{
1391 m_propertyVector.clear();
1392}
1393
1394const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
1395
1396Ref<MutableStyleProperties> StyleProperties::copyBlockProperties() const
1397{
1398 return copyPropertiesInSet(blockProperties, numBlockProperties);
1399}
1400
1401void MutableStyleProperties::removeBlockProperties()
1402{
1403 removePropertiesInSet(blockProperties, numBlockProperties);
1404}
1405
1406bool MutableStyleProperties::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1407{
1408 if (m_propertyVector.isEmpty())
1409 return false;
1410
1411 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1412 HashSet<CSSPropertyID> toRemove;
1413 for (unsigned i = 0; i < length; ++i)
1414 toRemove.add(set[i]);
1415
1416 return m_propertyVector.removeAllMatching([&toRemove] (const CSSProperty& property) {
1417 return toRemove.contains(property.id());
1418 }) > 0;
1419}
1420
1421int ImmutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1422{
1423 // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1424 // the compiler converting it to an int multiple times in the loop.
1425 uint16_t id = static_cast<uint16_t>(propertyID);
1426 for (int n = m_arraySize - 1 ; n >= 0; --n) {
1427 if (metadataArray()[n].m_propertyID == id)
1428 return n;
1429 }
1430
1431 return -1;
1432}
1433
1434int MutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1435{
1436 // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1437 // the compiler converting it to an int multiple times in the loop.
1438 uint16_t id = static_cast<uint16_t>(propertyID);
1439 for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1440 if (m_propertyVector.at(n).metadata().m_propertyID == id)
1441 return n;
1442 }
1443
1444 return -1;
1445}
1446
1447int ImmutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1448{
1449 // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1450 // the compiler converting it to an int multiple times in the loop.
1451 for (int n = m_arraySize - 1 ; n >= 0; --n) {
1452 if (metadataArray()[n].m_propertyID == CSSPropertyCustom) {
1453 // We found a custom property. See if the name matches.
1454 if (!valueArray()[n])
1455 continue;
1456 if (downcast<CSSCustomPropertyValue>(*valueArray()[n]).name() == propertyName)
1457 return n;
1458 }
1459 }
1460
1461 return -1;
1462}
1463
1464int MutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1465{
1466 // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1467 // the compiler converting it to an int multiple times in the loop.
1468 for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1469 if (m_propertyVector.at(n).metadata().m_propertyID == CSSPropertyCustom) {
1470 // We found a custom property. See if the name matches.
1471 if (!m_propertyVector.at(n).value())
1472 continue;
1473 if (downcast<CSSCustomPropertyValue>(*m_propertyVector.at(n).value()).name() == propertyName)
1474 return n;
1475 }
1476 }
1477
1478 return -1;
1479}
1480
1481CSSProperty* MutableStyleProperties::findCSSPropertyWithID(CSSPropertyID propertyID)
1482{
1483 int foundPropertyIndex = findPropertyIndex(propertyID);
1484 if (foundPropertyIndex == -1)
1485 return 0;
1486 return &m_propertyVector.at(foundPropertyIndex);
1487}
1488
1489CSSProperty* MutableStyleProperties::findCustomCSSPropertyWithName(const String& propertyName)
1490{
1491 int foundPropertyIndex = findCustomPropertyIndex(propertyName);
1492 if (foundPropertyIndex == -1)
1493 return 0;
1494 return &m_propertyVector.at(foundPropertyIndex);
1495}
1496
1497bool StyleProperties::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
1498{
1499 int foundPropertyIndex = findPropertyIndex(propertyID);
1500 if (foundPropertyIndex == -1)
1501 return false;
1502 return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
1503}
1504
1505Ref<MutableStyleProperties> StyleProperties::mutableCopy() const
1506{
1507 return adoptRef(*new MutableStyleProperties(*this));
1508}
1509
1510Ref<MutableStyleProperties> StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1511{
1512 Vector<CSSProperty, 256> list;
1513 list.reserveInitialCapacity(length);
1514 for (unsigned i = 0; i < length; ++i) {
1515 if (auto value = getPropertyCSSValueInternal(set[i]))
1516 list.uncheckedAppend(CSSProperty(set[i], WTFMove(value), false));
1517 }
1518 return MutableStyleProperties::create(list.data(), list.size());
1519}
1520
1521PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration()
1522{
1523 return m_cssomWrapper.get();
1524}
1525
1526CSSStyleDeclaration& MutableStyleProperties::ensureCSSStyleDeclaration()
1527{
1528 if (m_cssomWrapper) {
1529 ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule());
1530 ASSERT(!m_cssomWrapper->parentElement());
1531 return *m_cssomWrapper;
1532 }
1533 m_cssomWrapper = std::make_unique<PropertySetCSSStyleDeclaration>(*this);
1534 return *m_cssomWrapper;
1535}
1536
1537CSSStyleDeclaration& MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement& parentElement)
1538{
1539 if (m_cssomWrapper) {
1540 ASSERT(m_cssomWrapper->parentElement() == &parentElement);
1541 return *m_cssomWrapper;
1542 }
1543 m_cssomWrapper = std::make_unique<InlineCSSStyleDeclaration>(*this, parentElement);
1544 return *m_cssomWrapper;
1545}
1546
1547unsigned StyleProperties::averageSizeInBytes()
1548{
1549 // Please update this if the storage scheme changes so that this longer reflects the actual size.
1550 return sizeForImmutableStylePropertiesWithPropertyCount(4);
1551}
1552
1553// See the function above if you need to update this.
1554struct SameSizeAsStyleProperties : public RefCounted<SameSizeAsStyleProperties> {
1555 unsigned bitfield;
1556};
1557COMPILE_ASSERT(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), style_property_set_should_stay_small);
1558
1559#ifndef NDEBUG
1560void StyleProperties::showStyle()
1561{
1562 fprintf(stderr, "%s\n", asText().ascii().data());
1563}
1564#endif
1565
1566Ref<MutableStyleProperties> MutableStyleProperties::create(CSSParserMode cssParserMode)
1567{
1568 return adoptRef(*new MutableStyleProperties(cssParserMode));
1569}
1570
1571Ref<MutableStyleProperties> MutableStyleProperties::create(const CSSProperty* properties, unsigned count)
1572{
1573 return adoptRef(*new MutableStyleProperties(properties, count));
1574}
1575
1576String StyleProperties::PropertyReference::cssName() const
1577{
1578 if (id() == CSSPropertyCustom)
1579 return downcast<CSSCustomPropertyValue>(*value()).name();
1580 return getPropertyNameString(id());
1581}
1582
1583String StyleProperties::PropertyReference::cssText() const
1584{
1585 StringBuilder result;
1586 result.append(cssName());
1587 result.appendLiteral(": ");
1588 result.append(m_value->cssText());
1589 if (isImportant())
1590 result.appendLiteral(" !important");
1591 result.append(';');
1592 return result.toString();
1593}
1594
1595Ref<DeferredStyleProperties> DeferredStyleProperties::create(const CSSParserTokenRange& tokenRange, CSSDeferredParser& parser)
1596{
1597 return adoptRef(*new DeferredStyleProperties(tokenRange, parser));
1598}
1599
1600DeferredStyleProperties::DeferredStyleProperties(const CSSParserTokenRange& range, CSSDeferredParser& parser)
1601 : StylePropertiesBase(parser.mode(), DeferredPropertiesType)
1602 , m_parser(parser)
1603{
1604 size_t length = range.end() - range.begin();
1605 m_tokens.reserveCapacity(length);
1606 m_tokens.append(range.begin(), length);
1607}
1608
1609DeferredStyleProperties::~DeferredStyleProperties() = default;
1610
1611Ref<ImmutableStyleProperties> DeferredStyleProperties::parseDeferredProperties()
1612{
1613 return m_parser->parseDeclaration(m_tokens);
1614}
1615
1616} // namespace WebCore
1617