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