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 |
|
---|
54 | namespace WebCore {
|
---|
55 |
|
---|
56 | DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(StyleProperties);
|
---|
57 | DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(ImmutableStyleProperties);
|
---|
58 | DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(MutableStyleProperties);
|
---|
59 |
|
---|
60 | static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count)
|
---|
61 | {
|
---|
62 | return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(StylePropertyMetadata) * count + sizeof(PackedPtr<const CSSValue>) * count;
|
---|
63 | }
|
---|
64 |
|
---|
65 | static bool isCSSWideValueKeyword(StringView value)
|
---|
66 | {
|
---|
67 | return value == "initial"_s || value == "inherit"_s || value == "unset"_s || value == "revert"_s;
|
---|
68 | }
|
---|
69 |
|
---|
70 | static 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 |
|
---|
75 | static 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 |
|
---|
80 | static bool isValueID(const RefPtr<CSSValue>& value, CSSValueID id)
|
---|
81 | {
|
---|
82 | return value && isValueID(*value, id);
|
---|
83 | }
|
---|
84 |
|
---|
85 | Ref<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 |
|
---|
91 | Ref<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 |
|
---|
99 | MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode)
|
---|
100 | : StyleProperties(cssParserMode, MutablePropertiesType)
|
---|
101 | {
|
---|
102 | }
|
---|
103 |
|
---|
104 | MutableStyleProperties::MutableStyleProperties(Vector<CSSProperty>&& properties)
|
---|
105 | : StyleProperties(HTMLStandardMode, MutablePropertiesType)
|
---|
106 | , m_propertyVector(WTFMove(properties))
|
---|
107 | {
|
---|
108 | }
|
---|
109 |
|
---|
110 | MutableStyleProperties::~MutableStyleProperties() = default;
|
---|
111 |
|
---|
112 | ImmutableStyleProperties::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 |
|
---|
125 | ImmutableStyleProperties::~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 |
|
---|
132 | MutableStyleProperties::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 |
|
---|
146 | bool 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 |
|
---|
177 | String 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 |
|
---|
385 | std::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 |
|
---|
395 | CSSValueID StyleProperties::propertyAsValueID(CSSPropertyID property) const
|
---|
396 | {
|
---|
397 | auto cssValue = getPropertyCSSValue(property);
|
---|
398 | return is<CSSPrimitiveValue>(cssValue) ? downcast<CSSPrimitiveValue>(*cssValue).valueID() : CSSValueInvalid;
|
---|
399 | }
|
---|
400 |
|
---|
401 | String 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 |
|
---|
409 | String 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 |
|
---|
427 | void 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 |
|
---|
468 | std::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 |
|
---|
544 | String 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 |
|
---|
577 | String 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 |
|
---|
664 | String 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 |
|
---|
675 | String 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 |
|
---|
690 | String 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 |
|
---|
728 | String 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 |
|
---|
784 | String 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 |
|
---|
946 | String 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 |
|
---|
1014 | String 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 |
|
---|
1027 | String StyleProperties::getGridShorthandValue(const StylePropertyShorthand& shorthand) const
|
---|
1028 | {
|
---|
1029 | return getShorthandValue(shorthand, " / ");
|
---|
1030 | }
|
---|
1031 |
|
---|
1032 | String 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.
|
---|
1062 | String 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 |
|
---|
1086 | String 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 |
|
---|
1094 | String 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 |
|
---|
1162 | String 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 |
|
---|
1186 | String 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 |
|
---|
1212 | RefPtr<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 |
|
---|
1220 | RefPtr<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 |
|
---|
1228 | bool 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 |
|
---|
1237 | bool 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 |
|
---|
1263 | bool 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 |
|
---|
1282 | bool 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 |
|
---|
1299 | bool 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 |
|
---|
1307 | String 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 |
|
---|
1315 | bool 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 |
|
---|
1323 | bool 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 |
|
---|
1340 | bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important)
|
---|
1341 | {
|
---|
1342 | CSSParserContext parserContext(cssParserMode());
|
---|
1343 | return setProperty(propertyID, value, important, parserContext);
|
---|
1344 | }
|
---|
1345 |
|
---|
1346 | bool 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 |
|
---|
1370 | void 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 |
|
---|
1384 | bool 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 |
|
---|
1401 | bool 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 |
|
---|
1430 | bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
|
---|
1431 | {
|
---|
1432 | return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
|
---|
1433 | }
|
---|
1434 |
|
---|
1435 | bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
|
---|
1436 | {
|
---|
1437 | return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
|
---|
1438 | }
|
---|
1439 |
|
---|
1440 | bool 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 |
|
---|
1454 | bool 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 |
|
---|
1466 | bool 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 |
|
---|
1476 | String StyleProperties::asText() const
|
---|
1477 | {
|
---|
1478 | return asTextInternal().toString();
|
---|
1479 | }
|
---|
1480 |
|
---|
1481 | AtomString StyleProperties::asTextAtom() const
|
---|
1482 | {
|
---|
1483 | return asTextInternal().toAtomString();
|
---|
1484 | }
|
---|
1485 |
|
---|
1486 | StringBuilder 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 |
|
---|
1842 | bool StyleProperties::hasCSSOMWrapper() const
|
---|
1843 | {
|
---|
1844 | return is<MutableStyleProperties>(*this) && downcast<MutableStyleProperties>(*this).m_cssomWrapper;
|
---|
1845 | }
|
---|
1846 |
|
---|
1847 | void 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 |
|
---|
1854 | bool 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.
|
---|
1866 | static 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 |
|
---|
1889 | void MutableStyleProperties::clear()
|
---|
1890 | {
|
---|
1891 | m_propertyVector.clear();
|
---|
1892 | }
|
---|
1893 |
|
---|
1894 | const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
|
---|
1895 |
|
---|
1896 | Ref<MutableStyleProperties> StyleProperties::copyBlockProperties() const
|
---|
1897 | {
|
---|
1898 | return copyPropertiesInSet(blockProperties, numBlockProperties);
|
---|
1899 | }
|
---|
1900 |
|
---|
1901 | void MutableStyleProperties::removeBlockProperties()
|
---|
1902 | {
|
---|
1903 | removePropertiesInSet(blockProperties, numBlockProperties);
|
---|
1904 | }
|
---|
1905 |
|
---|
1906 | bool 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 |
|
---|
1921 | int 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 |
|
---|
1934 | int 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 |
|
---|
1947 | int 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 |
|
---|
1963 | int 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 |
|
---|
1978 | CSSProperty* 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 |
|
---|
1986 | CSSProperty* 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 |
|
---|
1994 | bool 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 |
|
---|
2002 | Ref<MutableStyleProperties> StyleProperties::mutableCopy() const
|
---|
2003 | {
|
---|
2004 | return adoptRef(*new MutableStyleProperties(*this));
|
---|
2005 | }
|
---|
2006 |
|
---|
2007 | Ref<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 |
|
---|
2019 | PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration()
|
---|
2020 | {
|
---|
2021 | return m_cssomWrapper.get();
|
---|
2022 | }
|
---|
2023 |
|
---|
2024 | CSSStyleDeclaration& 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 |
|
---|
2035 | CSSStyleDeclaration& 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 |
|
---|
2045 | unsigned 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.
|
---|
2052 | struct SameSizeAsStyleProperties : public RefCounted<SameSizeAsStyleProperties> {
|
---|
2053 | unsigned bitfield;
|
---|
2054 | };
|
---|
2055 | static_assert(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), "style property set should stay small");
|
---|
2056 |
|
---|
2057 | #ifndef NDEBUG
|
---|
2058 | void StyleProperties::showStyle()
|
---|
2059 | {
|
---|
2060 | fprintf(stderr, "%s\n", asText().ascii().data());
|
---|
2061 | }
|
---|
2062 | #endif
|
---|
2063 |
|
---|
2064 | Ref<MutableStyleProperties> MutableStyleProperties::create(CSSParserMode cssParserMode)
|
---|
2065 | {
|
---|
2066 | return adoptRef(*new MutableStyleProperties(cssParserMode));
|
---|
2067 | }
|
---|
2068 |
|
---|
2069 | Ref<MutableStyleProperties> MutableStyleProperties::create(Vector<CSSProperty>&& properties)
|
---|
2070 | {
|
---|
2071 | return adoptRef(*new MutableStyleProperties(WTFMove(properties)));
|
---|
2072 | }
|
---|
2073 |
|
---|
2074 | String StyleProperties::PropertyReference::cssName() const
|
---|
2075 | {
|
---|
2076 | if (id() == CSSPropertyCustom)
|
---|
2077 | return downcast<CSSCustomPropertyValue>(*value()).name();
|
---|
2078 | return getPropertyNameString(id());
|
---|
2079 | }
|
---|
2080 |
|
---|
2081 | String StyleProperties::PropertyReference::cssText() const
|
---|
2082 | {
|
---|
2083 | return makeString(cssName(), ": ", m_value->cssText(), isImportant() ? " !important" : "", ';');
|
---|
2084 | }
|
---|
2085 |
|
---|
2086 | } // namespace WebCore
|
---|