2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 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.
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.
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.
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.
24 #include "core/css/StylePropertySerializer.h"
26 #include "core/CSSValueKeywords.h"
27 #include "core/StylePropertyShorthand.h"
28 #include "core/css/RuntimeCSSEnabled.h"
29 #include "wtf/BitArray.h"
30 #include "wtf/text/StringBuilder.h"
34 static bool isInitialOrInherit(const String& value)
36 DEFINE_STATIC_LOCAL(String, initial, ("initial"));
37 DEFINE_STATIC_LOCAL(String, inherit, ("inherit"));
38 return value.length() == 7 && (value == initial || value == inherit);
41 StylePropertySerializer::StylePropertySerializer(const StylePropertySet& properties)
42 : m_propertySet(properties)
46 String StylePropertySerializer::getPropertyText(CSSPropertyID propertyID, const String& value, bool isImportant, bool isNotFirstDecl) const
51 result.append(getPropertyName(propertyID));
52 result.appendLiteral(": ");
55 result.appendLiteral(" !important");
57 return result.toString();
60 String StylePropertySerializer::asText() const
64 BitArray<numCSSProperties> shorthandPropertyUsed;
65 BitArray<numCSSProperties> shorthandPropertyAppeared;
67 unsigned size = m_propertySet.propertyCount();
68 unsigned numDecls = 0;
69 for (unsigned n = 0; n < size; ++n) {
70 StylePropertySet::PropertyReference property = m_propertySet.propertyAt(n);
71 CSSPropertyID propertyID = property.id();
72 // Only enabled or internal properties should be part of the style.
73 ASSERT(RuntimeCSSEnabled::isCSSPropertyEnabled(propertyID) || isInternalProperty(propertyID));
74 CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
75 CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
79 case CSSPropertyBackgroundAttachment:
80 case CSSPropertyBackgroundClip:
81 case CSSPropertyBackgroundColor:
82 case CSSPropertyBackgroundImage:
83 case CSSPropertyBackgroundOrigin:
84 case CSSPropertyBackgroundPositionX:
85 case CSSPropertyBackgroundPositionY:
86 case CSSPropertyBackgroundSize:
87 case CSSPropertyBackgroundRepeatX:
88 case CSSPropertyBackgroundRepeatY:
89 shorthandPropertyAppeared.set(CSSPropertyBackground - firstCSSProperty);
91 case CSSPropertyContent:
92 if (property.value()->isValueList())
93 value = toCSSValueList(property.value())->customCSSText(AlwaysQuoteCSSString);
95 case CSSPropertyBorderTopWidth:
96 case CSSPropertyBorderRightWidth:
97 case CSSPropertyBorderBottomWidth:
98 case CSSPropertyBorderLeftWidth:
99 if (!borderFallbackShorthandProperty)
100 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
101 case CSSPropertyBorderTopStyle:
102 case CSSPropertyBorderRightStyle:
103 case CSSPropertyBorderBottomStyle:
104 case CSSPropertyBorderLeftStyle:
105 if (!borderFallbackShorthandProperty)
106 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
107 case CSSPropertyBorderTopColor:
108 case CSSPropertyBorderRightColor:
109 case CSSPropertyBorderBottomColor:
110 case CSSPropertyBorderLeftColor:
111 if (!borderFallbackShorthandProperty)
112 borderFallbackShorthandProperty = CSSPropertyBorderColor;
114 // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
115 if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
116 value = borderPropertyValue(ReturnNullOnUncommonValues);
118 shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
120 shorthandPropertyID = CSSPropertyBorder;
121 } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
122 shorthandPropertyID = CSSPropertyBorder;
123 if (!shorthandPropertyID)
124 shorthandPropertyID = borderFallbackShorthandProperty;
126 case CSSPropertyWebkitBorderHorizontalSpacing:
127 case CSSPropertyWebkitBorderVerticalSpacing:
128 shorthandPropertyID = CSSPropertyBorderSpacing;
130 case CSSPropertyFontFamily:
131 case CSSPropertyLineHeight:
132 case CSSPropertyFontSize:
133 case CSSPropertyFontStretch:
134 case CSSPropertyFontStyle:
135 case CSSPropertyFontVariant:
136 case CSSPropertyFontWeight:
137 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
139 case CSSPropertyListStyleType:
140 case CSSPropertyListStylePosition:
141 case CSSPropertyListStyleImage:
142 shorthandPropertyID = CSSPropertyListStyle;
144 case CSSPropertyMarginTop:
145 case CSSPropertyMarginRight:
146 case CSSPropertyMarginBottom:
147 case CSSPropertyMarginLeft:
148 shorthandPropertyID = CSSPropertyMargin;
150 case CSSPropertyOutlineWidth:
151 case CSSPropertyOutlineStyle:
152 case CSSPropertyOutlineColor:
153 shorthandPropertyID = CSSPropertyOutline;
155 case CSSPropertyOverflowX:
156 case CSSPropertyOverflowY:
157 shorthandPropertyID = CSSPropertyOverflow;
159 case CSSPropertyPaddingTop:
160 case CSSPropertyPaddingRight:
161 case CSSPropertyPaddingBottom:
162 case CSSPropertyPaddingLeft:
163 shorthandPropertyID = CSSPropertyPadding;
165 case CSSPropertyTransitionProperty:
166 case CSSPropertyTransitionDuration:
167 case CSSPropertyTransitionTimingFunction:
168 case CSSPropertyTransitionDelay:
169 shorthandPropertyID = CSSPropertyTransition;
171 case CSSPropertyWebkitAnimationName:
172 case CSSPropertyWebkitAnimationDuration:
173 case CSSPropertyWebkitAnimationTimingFunction:
174 case CSSPropertyWebkitAnimationDelay:
175 case CSSPropertyWebkitAnimationIterationCount:
176 case CSSPropertyWebkitAnimationDirection:
177 case CSSPropertyWebkitAnimationFillMode:
178 shorthandPropertyID = CSSPropertyWebkitAnimation;
180 case CSSPropertyFlexDirection:
181 case CSSPropertyFlexWrap:
182 shorthandPropertyID = CSSPropertyFlexFlow;
184 case CSSPropertyFlexBasis:
185 case CSSPropertyFlexGrow:
186 case CSSPropertyFlexShrink:
187 shorthandPropertyID = CSSPropertyFlex;
189 case CSSPropertyWebkitMaskPositionX:
190 case CSSPropertyWebkitMaskPositionY:
191 case CSSPropertyWebkitMaskRepeatX:
192 case CSSPropertyWebkitMaskRepeatY:
193 case CSSPropertyWebkitMaskImage:
194 case CSSPropertyWebkitMaskRepeat:
195 case CSSPropertyWebkitMaskPosition:
196 case CSSPropertyWebkitMaskClip:
197 case CSSPropertyWebkitMaskOrigin:
198 shorthandPropertyID = CSSPropertyWebkitMask;
200 case CSSPropertyWebkitTransformOriginX:
201 case CSSPropertyWebkitTransformOriginY:
202 case CSSPropertyWebkitTransformOriginZ:
203 shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
205 case CSSPropertyWebkitTransitionProperty:
206 case CSSPropertyWebkitTransitionDuration:
207 case CSSPropertyWebkitTransitionTimingFunction:
208 case CSSPropertyWebkitTransitionDelay:
209 shorthandPropertyID = CSSPropertyWebkitTransition;
215 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
216 if (shorthandPropertyID) {
217 if (shorthandPropertyUsed.get(shortPropertyIndex))
219 if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
220 value = m_propertySet.getPropertyValue(shorthandPropertyID);
221 shorthandPropertyAppeared.set(shortPropertyIndex);
224 if (!value.isNull()) {
225 if (shorthandPropertyID) {
226 propertyID = shorthandPropertyID;
227 shorthandPropertyUsed.set(shortPropertyIndex);
230 value = property.value()->cssText();
232 if (value == "initial" && !CSSPropertyMetadata::isInheritedProperty(propertyID))
235 result.append(getPropertyText(propertyID, value, property.isImportant(), numDecls++));
238 if (shorthandPropertyAppeared.get(CSSPropertyBackground - firstCSSProperty))
239 appendBackgroundPropertyAsText(result, numDecls);
241 ASSERT(!numDecls ^ !result.isEmpty());
242 return result.toString();
245 String StylePropertySerializer::getPropertyValue(CSSPropertyID propertyID) const
247 // Shorthand and 4-values properties
248 switch (propertyID) {
249 case CSSPropertyAnimation:
250 return getLayeredShorthandValue(animationShorthand());
251 case CSSPropertyBorderSpacing:
252 return borderSpacingValue(borderSpacingShorthand());
253 case CSSPropertyBackgroundPosition:
254 return getLayeredShorthandValue(backgroundPositionShorthand());
255 case CSSPropertyBackgroundRepeat:
256 return backgroundRepeatPropertyValue();
257 case CSSPropertyBackground:
258 return getLayeredShorthandValue(backgroundShorthand());
259 case CSSPropertyBorder:
260 return borderPropertyValue(OmitUncommonValues);
261 case CSSPropertyBorderTop:
262 return getShorthandValue(borderTopShorthand());
263 case CSSPropertyBorderRight:
264 return getShorthandValue(borderRightShorthand());
265 case CSSPropertyBorderBottom:
266 return getShorthandValue(borderBottomShorthand());
267 case CSSPropertyBorderLeft:
268 return getShorthandValue(borderLeftShorthand());
269 case CSSPropertyOutline:
270 return getShorthandValue(outlineShorthand());
271 case CSSPropertyBorderColor:
272 return get4Values(borderColorShorthand());
273 case CSSPropertyBorderWidth:
274 return get4Values(borderWidthShorthand());
275 case CSSPropertyBorderStyle:
276 return get4Values(borderStyleShorthand());
277 case CSSPropertyWebkitColumnRule:
278 return getShorthandValue(webkitColumnRuleShorthand());
279 case CSSPropertyWebkitColumns:
280 return getShorthandValue(webkitColumnsShorthand());
281 case CSSPropertyFlex:
282 return getShorthandValue(flexShorthand());
283 case CSSPropertyFlexFlow:
284 return getShorthandValue(flexFlowShorthand());
285 case CSSPropertyGridColumn:
286 return getShorthandValue(gridColumnShorthand());
287 case CSSPropertyGridRow:
288 return getShorthandValue(gridRowShorthand());
289 case CSSPropertyGridArea:
290 return getShorthandValue(gridAreaShorthand());
291 case CSSPropertyFont:
293 case CSSPropertyMargin:
294 return get4Values(marginShorthand());
295 case CSSPropertyWebkitMarginCollapse:
296 return getShorthandValue(webkitMarginCollapseShorthand());
297 case CSSPropertyOverflow:
298 return getCommonValue(overflowShorthand());
299 case CSSPropertyPadding:
300 return get4Values(paddingShorthand());
301 case CSSPropertyTransition:
302 return getLayeredShorthandValue(transitionShorthand());
303 case CSSPropertyListStyle:
304 return getShorthandValue(listStyleShorthand());
305 case CSSPropertyWebkitMaskPosition:
306 return getLayeredShorthandValue(webkitMaskPositionShorthand());
307 case CSSPropertyWebkitMaskRepeat:
308 return getLayeredShorthandValue(webkitMaskRepeatShorthand());
309 case CSSPropertyWebkitMask:
310 return getLayeredShorthandValue(webkitMaskShorthand());
311 case CSSPropertyWebkitTextEmphasis:
312 return getShorthandValue(webkitTextEmphasisShorthand());
313 case CSSPropertyWebkitTextStroke:
314 return getShorthandValue(webkitTextStrokeShorthand());
315 case CSSPropertyTransformOrigin:
316 case CSSPropertyWebkitTransformOrigin:
317 return getShorthandValue(webkitTransformOriginShorthand());
318 case CSSPropertyWebkitTransition:
319 return getLayeredShorthandValue(webkitTransitionShorthand());
320 case CSSPropertyWebkitAnimation:
321 return getLayeredShorthandValue(webkitAnimationShorthand());
322 case CSSPropertyMarker: {
323 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyMarkerStart);
325 return value->cssText();
328 case CSSPropertyBorderRadius:
329 return get4Values(borderRadiusShorthand());
335 String StylePropertySerializer::borderSpacingValue(const StylePropertyShorthand& shorthand) const
337 RefPtrWillBeRawPtr<CSSValue> horizontalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[0]);
338 RefPtrWillBeRawPtr<CSSValue> verticalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[1]);
340 // While standard border-spacing property does not allow specifying border-spacing-vertical without
341 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
342 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
343 if (!horizontalValue || !verticalValue)
346 String horizontalValueCSSText = horizontalValue->cssText();
347 String verticalValueCSSText = verticalValue->cssText();
348 if (horizontalValueCSSText == verticalValueCSSText)
349 return horizontalValueCSSText;
350 return horizontalValueCSSText + ' ' + verticalValueCSSText;
353 void StylePropertySerializer::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
355 int foundPropertyIndex = m_propertySet.findPropertyIndex(propertyID);
356 if (foundPropertyIndex == -1)
357 return; // All longhands must have at least implicit values if "font" is specified.
359 if (m_propertySet.propertyAt(foundPropertyIndex).isImplicit()) {
360 commonValue = String();
365 switch (propertyID) {
366 case CSSPropertyFontStyle:
368 case CSSPropertyFontFamily:
369 case CSSPropertyFontStretch:
370 case CSSPropertyFontVariant:
371 case CSSPropertyFontWeight:
374 case CSSPropertyLineHeight:
378 ASSERT_NOT_REACHED();
381 if (prefix && !result.isEmpty())
382 result.append(prefix);
383 String value = m_propertySet.propertyAt(foundPropertyIndex).value()->cssText();
384 result.append(value);
385 if (!commonValue.isNull() && commonValue != value)
386 commonValue = String();
389 String StylePropertySerializer::fontValue() const
391 int fontSizePropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontSize);
392 int fontFamilyPropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontFamily);
393 if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
394 return emptyString();
396 StylePropertySet::PropertyReference fontSizeProperty = m_propertySet.propertyAt(fontSizePropertyIndex);
397 StylePropertySet::PropertyReference fontFamilyProperty = m_propertySet.propertyAt(fontFamilyPropertyIndex);
398 if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
399 return emptyString();
401 String commonValue = fontSizeProperty.value()->cssText();
402 StringBuilder result;
403 appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
404 appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result, commonValue);
405 appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
406 appendFontLonghandValueIfExplicit(CSSPropertyFontStretch, result, commonValue);
407 if (!result.isEmpty())
409 result.append(fontSizeProperty.value()->cssText());
410 appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
411 if (!result.isEmpty())
413 result.append(fontFamilyProperty.value()->cssText());
414 if (isInitialOrInherit(commonValue))
416 return result.toString();
419 String StylePropertySerializer::get4Values(const StylePropertyShorthand& shorthand) const
421 // Assume the properties are in the usual order top, right, bottom, left.
422 int topValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[0]);
423 int rightValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[1]);
424 int bottomValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[2]);
425 int leftValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[3]);
427 if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
430 StylePropertySet::PropertyReference top = m_propertySet.propertyAt(topValueIndex);
431 StylePropertySet::PropertyReference right = m_propertySet.propertyAt(rightValueIndex);
432 StylePropertySet::PropertyReference bottom = m_propertySet.propertyAt(bottomValueIndex);
433 StylePropertySet::PropertyReference left = m_propertySet.propertyAt(leftValueIndex);
435 // All 4 properties must be specified.
436 if (!top.value() || !right.value() || !bottom.value() || !left.value())
439 if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
440 return getValueName(CSSValueInherit);
442 if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
443 if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
444 // All components are "initial" and "top" is not implicit.
445 return getValueName(CSSValueInitial);
449 if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
452 bool showLeft = !right.value()->equals(*left.value());
453 bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
454 bool showRight = !top.value()->equals(*right.value()) || showBottom;
456 StringBuilder result;
457 result.append(top.value()->cssText());
460 result.append(right.value()->cssText());
464 result.append(bottom.value()->cssText());
468 result.append(left.value()->cssText());
470 return result.toString();
473 String StylePropertySerializer::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
475 StringBuilder result;
477 const unsigned size = shorthand.length();
478 // Begin by collecting the properties into an array.
479 WillBeHeapVector<RefPtrWillBeMember<CSSValue> > values(size);
480 size_t numLayers = 0;
482 for (unsigned i = 0; i < size; ++i) {
483 values[i] = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
485 if (values[i]->isBaseValueList()) {
486 CSSValueList* valueList = toCSSValueList(values[i].get());
487 numLayers = std::max(valueList->length(), numLayers);
489 numLayers = std::max<size_t>(1U, numLayers);
495 bool commonValueInitialized = false;
497 // Now stitch the properties together. Implicit initial values are flagged as such and
498 // can safely be omitted.
499 for (size_t i = 0; i < numLayers; i++) {
500 StringBuilder layerResult;
501 bool useRepeatXShorthand = false;
502 bool useRepeatYShorthand = false;
503 bool useSingleWordShorthand = false;
504 bool foundPositionYCSSProperty = false;
505 for (unsigned j = 0; j < size; j++) {
506 RefPtrWillBeRawPtr<CSSValue> value = nullptr;
508 if (values[j]->isBaseValueList()) {
509 value = toCSSValueList(values[j].get())->itemWithBoundsCheck(i);
513 // Color only belongs in the last layer.
514 if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
515 if (i != numLayers - 1)
518 // Other singletons only belong in the first layer.
524 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
525 // then it was written with only one value. Here we figure out which value that was so we can
526 // report back correctly.
527 if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))
528 || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))) {
530 // BUG 49055: make sure the value was not reset in the layer check just above.
531 if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
532 || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
533 RefPtrWillBeRawPtr<CSSValue> yValue = nullptr;
534 RefPtrWillBeRawPtr<CSSValue> nextValue = values[j + 1];
535 if (nextValue->isValueList())
536 yValue = toCSSValueList(nextValue.get())->item(i);
540 // background-repeat-x(y) or mask-repeat-x(y) may be like this : "initial, repeat". We can omit the implicit initial values
541 // before starting to compare their values.
542 if (value->isImplicitInitialValue() || yValue->isImplicitInitialValue())
545 // FIXME: At some point we need to fix this code to avoid returning an invalid shorthand,
546 // since some longhand combinations are not serializable into a single shorthand.
547 if (!value->isPrimitiveValue() || !yValue->isPrimitiveValue())
550 CSSValueID xId = toCSSPrimitiveValue(value.get())->getValueID();
551 CSSValueID yId = toCSSPrimitiveValue(yValue.get())->getValueID();
553 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
554 useRepeatXShorthand = true;
556 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
557 useRepeatYShorthand = true;
561 useSingleWordShorthand = true;
568 if (value && !value->isImplicitInitialValue()) {
569 if (!layerResult.isEmpty())
570 layerResult.append(' ');
571 if (foundPositionYCSSProperty
572 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
573 layerResult.appendLiteral("/ ");
574 if (!foundPositionYCSSProperty
575 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
578 if (useRepeatXShorthand) {
579 useRepeatXShorthand = false;
580 layerResult.append(getValueName(CSSValueRepeatX));
581 } else if (useRepeatYShorthand) {
582 useRepeatYShorthand = false;
583 layerResult.append(getValueName(CSSValueRepeatY));
585 if (useSingleWordShorthand)
586 useSingleWordShorthand = false;
587 valueText = value->cssText();
588 layerResult.append(valueText);
591 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
592 || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
593 foundPositionYCSSProperty = true;
595 // background-position is a special case: if only the first offset is specified,
596 // the second one defaults to "center", not the same value.
597 if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
598 commonValue = String();
602 if (!commonValueInitialized) {
603 commonValue = valueText;
604 commonValueInitialized = true;
605 } else if (!commonValue.isNull() && commonValue != valueText)
606 commonValue = String();
609 if (!layerResult.isEmpty()) {
610 if (!result.isEmpty())
611 result.appendLiteral(", ");
612 result.append(layerResult);
616 if (isInitialOrInherit(commonValue))
619 if (result.isEmpty())
621 return result.toString();
624 String StylePropertySerializer::getShorthandValue(const StylePropertyShorthand& shorthand) const
627 StringBuilder result;
628 for (unsigned i = 0; i < shorthand.length(); ++i) {
629 if (!m_propertySet.isPropertyImplicit(shorthand.properties()[i])) {
630 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
633 String valueText = value->cssText();
635 commonValue = valueText;
636 else if (!commonValue.isNull() && commonValue != valueText)
637 commonValue = String();
638 if (value->isInitialValue())
640 if (!result.isEmpty())
642 result.append(valueText);
644 commonValue = String();
646 if (isInitialOrInherit(commonValue))
648 if (result.isEmpty())
650 return result.toString();
653 // only returns a non-null value if all properties have the same, non-null value
654 String StylePropertySerializer::getCommonValue(const StylePropertyShorthand& shorthand) const
657 bool lastPropertyWasImportant = false;
658 for (unsigned i = 0; i < shorthand.length(); ++i) {
659 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
660 // FIXME: CSSInitialValue::cssText should generate the right value.
663 String text = value->cssText();
668 else if (res != text)
671 bool currentPropertyIsImportant = m_propertySet.propertyIsImportant(shorthand.properties()[i]);
672 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
674 lastPropertyWasImportant = currentPropertyIsImportant;
679 String StylePropertySerializer::borderPropertyValue(CommonValueMode valueMode) const
681 const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
683 StringBuilder result;
684 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
685 String value = getCommonValue(properties[i]);
686 if (value.isNull()) {
687 if (valueMode == ReturnNullOnUncommonValues)
689 ASSERT(valueMode == OmitUncommonValues);
694 else if (!commonValue.isNull() && commonValue != value)
695 commonValue = String();
696 if (value == "initial")
698 if (!result.isEmpty())
700 result.append(value);
702 if (isInitialOrInherit(commonValue))
704 return result.isEmpty() ? String() : result.toString();
707 static void appendBackgroundRepeatValue(StringBuilder& builder, const CSSValue& repeatXCSSValue, const CSSValue& repeatYCSSValue)
709 // FIXME: Ensure initial values do not appear in CSS_VALUE_LISTS.
710 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue, initialRepeatValue, (CSSPrimitiveValue::create(CSSValueRepeat)));
711 const CSSPrimitiveValue& repeatX = repeatXCSSValue.isInitialValue() ? *initialRepeatValue : toCSSPrimitiveValue(repeatXCSSValue);
712 const CSSPrimitiveValue& repeatY = repeatYCSSValue.isInitialValue() ? *initialRepeatValue : toCSSPrimitiveValue(repeatYCSSValue);
713 CSSValueID repeatXValueId = repeatX.getValueID();
714 CSSValueID repeatYValueId = repeatY.getValueID();
715 if (repeatXValueId == repeatYValueId) {
716 builder.append(repeatX.cssText());
717 } else if (repeatXValueId == CSSValueNoRepeat && repeatYValueId == CSSValueRepeat) {
718 builder.append("repeat-y");
719 } else if (repeatXValueId == CSSValueRepeat && repeatYValueId == CSSValueNoRepeat) {
720 builder.append("repeat-x");
722 builder.append(repeatX.cssText());
724 builder.append(repeatY.cssText());
728 String StylePropertySerializer::backgroundRepeatPropertyValue() const
730 RefPtrWillBeRawPtr<CSSValue> repeatX = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundRepeatX);
731 RefPtrWillBeRawPtr<CSSValue> repeatY = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundRepeatY);
732 if (!repeatX || !repeatY)
734 if (m_propertySet.propertyIsImportant(CSSPropertyBackgroundRepeatX) != m_propertySet.propertyIsImportant(CSSPropertyBackgroundRepeatY))
736 if (repeatX->cssValueType() == repeatY->cssValueType()
737 && (repeatX->cssValueType() == CSSValue::CSS_INITIAL || repeatX->cssValueType() == CSSValue::CSS_INHERIT)) {
738 return repeatX->cssText();
741 RefPtrWillBeRawPtr<CSSValueList> repeatXList;
742 if (repeatX->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
743 repeatXList = CSSValueList::createCommaSeparated();
744 repeatXList->append(repeatX);
745 } else if (repeatX->cssValueType() == CSSValue::CSS_VALUE_LIST) {
746 repeatXList = toCSSValueList(repeatX.get());
751 RefPtrWillBeRawPtr<CSSValueList> repeatYList;
752 if (repeatY->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
753 repeatYList = CSSValueList::createCommaSeparated();
754 repeatYList->append(repeatY);
755 } else if (repeatY->cssValueType() == CSSValue::CSS_VALUE_LIST) {
756 repeatYList = toCSSValueList(repeatY.get());
761 size_t shorthandLength = lowestCommonMultiple(repeatXList->length(), repeatYList->length());
762 StringBuilder builder;
763 for (size_t i = 0; i < shorthandLength; ++i) {
765 builder.append(", ");
766 appendBackgroundRepeatValue(builder,
767 *repeatXList->item(i % repeatXList->length()),
768 *repeatYList->item(i % repeatYList->length()));
770 return builder.toString();
773 void StylePropertySerializer::appendBackgroundPropertyAsText(StringBuilder& result, unsigned& numDecls) const
775 if (isPropertyShorthandAvailable(backgroundShorthand())) {
776 String backgroundValue = getPropertyValue(CSSPropertyBackground);
777 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundImage);
778 result.append(getPropertyText(CSSPropertyBackground, backgroundValue, isImportant, numDecls++));
781 if (shorthandHasOnlyInitialOrInheritedValue(backgroundShorthand())) {
782 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundImage);
783 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundImage);
784 result.append(getPropertyText(CSSPropertyBackground, value->cssText(), isImportant, numDecls++));
788 // backgroundShorthandProperty without layered shorhand properties
789 const CSSPropertyID backgroundPropertyIds[] = {
790 CSSPropertyBackgroundImage,
791 CSSPropertyBackgroundAttachment,
792 CSSPropertyBackgroundColor,
793 CSSPropertyBackgroundSize,
794 CSSPropertyBackgroundOrigin,
795 CSSPropertyBackgroundClip
798 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(backgroundPropertyIds); ++i) {
799 CSSPropertyID propertyID = backgroundPropertyIds[i];
800 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(propertyID);
803 result.append(getPropertyText(propertyID, value->cssText(), m_propertySet.propertyIsImportant(propertyID), numDecls++));
806 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
807 // It is required because background-position-x/y are non-standard properties and WebKit generated output
808 // would not work in Firefox (<rdar://problem/5143183>)
809 // It would be a better solution if background-position was CSS_PAIR.
810 if (shorthandHasOnlyInitialOrInheritedValue(backgroundPositionShorthand())) {
811 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundPositionX);
812 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionX);
813 result.append(getPropertyText(CSSPropertyBackgroundPosition, value->cssText(), isImportant, numDecls++));
814 } else if (isPropertyShorthandAvailable(backgroundPositionShorthand())) {
815 String positionValue = m_propertySet.getPropertyValue(CSSPropertyBackgroundPosition);
816 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionX);
817 if (!positionValue.isNull())
818 result.append(getPropertyText(CSSPropertyBackgroundPosition, positionValue, isImportant, numDecls++));
820 // should check background-position-x or background-position-y.
821 if (RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundPositionX)) {
822 if (!value->isImplicitInitialValue()) {
823 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionX);
824 result.append(getPropertyText(CSSPropertyBackgroundPositionX, value->cssText(), isImportant, numDecls++));
827 if (RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundPositionY)) {
828 if (!value->isImplicitInitialValue()) {
829 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionY);
830 result.append(getPropertyText(CSSPropertyBackgroundPositionY, value->cssText(), isImportant, numDecls++));
835 String repeatValue = m_propertySet.getPropertyValue(CSSPropertyBackgroundRepeat);
836 if (!repeatValue.isNull())
837 result.append(getPropertyText(CSSPropertyBackgroundRepeat, repeatValue, m_propertySet.propertyIsImportant(CSSPropertyBackgroundRepeatX), numDecls++));
840 bool StylePropertySerializer::isPropertyShorthandAvailable(const StylePropertyShorthand& shorthand) const
842 ASSERT(shorthand.length() > 0);
844 bool isImportant = m_propertySet.propertyIsImportant(shorthand.properties()[0]);
845 for (unsigned i = 0; i < shorthand.length(); ++i) {
846 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
847 if (!value || (value->isInitialValue() && !value->isImplicitInitialValue()) || value->isInheritedValue())
849 if (isImportant != m_propertySet.propertyIsImportant(shorthand.properties()[i]))
855 bool StylePropertySerializer::shorthandHasOnlyInitialOrInheritedValue(const StylePropertyShorthand& shorthand) const
857 ASSERT(shorthand.length() > 0);
858 bool isImportant = m_propertySet.propertyIsImportant(shorthand.properties()[0]);
859 bool isInitialValue = true;
860 bool isInheritedValue = true;
861 for (unsigned i = 0; i < shorthand.length(); ++i) {
862 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
865 if (!value->isInitialValue())
866 isInitialValue = false;
867 if (!value->isInheritedValue())
868 isInheritedValue = false;
869 if (isImportant != m_propertySet.propertyIsImportant(shorthand.properties()[i]))
872 return isInitialValue || isInheritedValue;