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 CSSPropertyFontStyle:
134 case CSSPropertyFontVariant:
135 case CSSPropertyFontWeight:
136 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
138 case CSSPropertyListStyleType:
139 case CSSPropertyListStylePosition:
140 case CSSPropertyListStyleImage:
141 shorthandPropertyID = CSSPropertyListStyle;
143 case CSSPropertyMarginTop:
144 case CSSPropertyMarginRight:
145 case CSSPropertyMarginBottom:
146 case CSSPropertyMarginLeft:
147 shorthandPropertyID = CSSPropertyMargin;
149 case CSSPropertyOutlineWidth:
150 case CSSPropertyOutlineStyle:
151 case CSSPropertyOutlineColor:
152 shorthandPropertyID = CSSPropertyOutline;
154 case CSSPropertyOverflowX:
155 case CSSPropertyOverflowY:
156 shorthandPropertyID = CSSPropertyOverflow;
158 case CSSPropertyPaddingTop:
159 case CSSPropertyPaddingRight:
160 case CSSPropertyPaddingBottom:
161 case CSSPropertyPaddingLeft:
162 shorthandPropertyID = CSSPropertyPadding;
164 case CSSPropertyTransitionProperty:
165 case CSSPropertyTransitionDuration:
166 case CSSPropertyTransitionTimingFunction:
167 case CSSPropertyTransitionDelay:
168 shorthandPropertyID = CSSPropertyTransition;
170 case CSSPropertyWebkitAnimationName:
171 case CSSPropertyWebkitAnimationDuration:
172 case CSSPropertyWebkitAnimationTimingFunction:
173 case CSSPropertyWebkitAnimationDelay:
174 case CSSPropertyWebkitAnimationIterationCount:
175 case CSSPropertyWebkitAnimationDirection:
176 case CSSPropertyWebkitAnimationFillMode:
177 shorthandPropertyID = CSSPropertyWebkitAnimation;
179 case CSSPropertyFlexDirection:
180 case CSSPropertyFlexWrap:
181 shorthandPropertyID = CSSPropertyFlexFlow;
183 case CSSPropertyFlexBasis:
184 case CSSPropertyFlexGrow:
185 case CSSPropertyFlexShrink:
186 shorthandPropertyID = CSSPropertyFlex;
188 case CSSPropertyWebkitMaskPositionX:
189 case CSSPropertyWebkitMaskPositionY:
190 case CSSPropertyWebkitMaskRepeatX:
191 case CSSPropertyWebkitMaskRepeatY:
192 case CSSPropertyWebkitMaskImage:
193 case CSSPropertyWebkitMaskRepeat:
194 case CSSPropertyWebkitMaskPosition:
195 case CSSPropertyWebkitMaskClip:
196 case CSSPropertyWebkitMaskOrigin:
197 shorthandPropertyID = CSSPropertyWebkitMask;
199 case CSSPropertyWebkitTransformOriginX:
200 case CSSPropertyWebkitTransformOriginY:
201 case CSSPropertyWebkitTransformOriginZ:
202 shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
204 case CSSPropertyWebkitTransitionProperty:
205 case CSSPropertyWebkitTransitionDuration:
206 case CSSPropertyWebkitTransitionTimingFunction:
207 case CSSPropertyWebkitTransitionDelay:
208 shorthandPropertyID = CSSPropertyWebkitTransition;
214 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
215 if (shorthandPropertyID) {
216 if (shorthandPropertyUsed.get(shortPropertyIndex))
218 if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
219 value = m_propertySet.getPropertyValue(shorthandPropertyID);
220 shorthandPropertyAppeared.set(shortPropertyIndex);
223 if (!value.isNull()) {
224 if (shorthandPropertyID) {
225 propertyID = shorthandPropertyID;
226 shorthandPropertyUsed.set(shortPropertyIndex);
229 value = property.value()->cssText();
231 if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
234 result.append(getPropertyText(propertyID, value, property.isImportant(), numDecls++));
237 if (shorthandPropertyAppeared.get(CSSPropertyBackground - firstCSSProperty))
238 appendBackgroundPropertyAsText(result, numDecls);
240 ASSERT(!numDecls ^ !result.isEmpty());
241 return result.toString();
244 String StylePropertySerializer::getPropertyValue(CSSPropertyID propertyID) const
246 // Shorthand and 4-values properties
247 switch (propertyID) {
248 case CSSPropertyAnimation:
249 return getLayeredShorthandValue(animationShorthand());
250 case CSSPropertyBorderSpacing:
251 return borderSpacingValue(borderSpacingShorthand());
252 case CSSPropertyBackgroundPosition:
253 return getLayeredShorthandValue(backgroundPositionShorthand());
254 case CSSPropertyBackgroundRepeat:
255 return backgroundRepeatPropertyValue();
256 case CSSPropertyBackground:
257 return getLayeredShorthandValue(backgroundShorthand());
258 case CSSPropertyBorder:
259 return borderPropertyValue(OmitUncommonValues);
260 case CSSPropertyBorderTop:
261 return getShorthandValue(borderTopShorthand());
262 case CSSPropertyBorderRight:
263 return getShorthandValue(borderRightShorthand());
264 case CSSPropertyBorderBottom:
265 return getShorthandValue(borderBottomShorthand());
266 case CSSPropertyBorderLeft:
267 return getShorthandValue(borderLeftShorthand());
268 case CSSPropertyOutline:
269 return getShorthandValue(outlineShorthand());
270 case CSSPropertyBorderColor:
271 return get4Values(borderColorShorthand());
272 case CSSPropertyBorderWidth:
273 return get4Values(borderWidthShorthand());
274 case CSSPropertyBorderStyle:
275 return get4Values(borderStyleShorthand());
276 case CSSPropertyWebkitColumnRule:
277 return getShorthandValue(webkitColumnRuleShorthand());
278 case CSSPropertyWebkitColumns:
279 return getShorthandValue(webkitColumnsShorthand());
280 case CSSPropertyFlex:
281 return getShorthandValue(flexShorthand());
282 case CSSPropertyFlexFlow:
283 return getShorthandValue(flexFlowShorthand());
284 case CSSPropertyGridColumn:
285 return getShorthandValue(gridColumnShorthand());
286 case CSSPropertyGridRow:
287 return getShorthandValue(gridRowShorthand());
288 case CSSPropertyGridArea:
289 return getShorthandValue(gridAreaShorthand());
290 case CSSPropertyFont:
292 case CSSPropertyMargin:
293 return get4Values(marginShorthand());
294 case CSSPropertyWebkitMarginCollapse:
295 return getShorthandValue(webkitMarginCollapseShorthand());
296 case CSSPropertyOverflow:
297 return getCommonValue(overflowShorthand());
298 case CSSPropertyPadding:
299 return get4Values(paddingShorthand());
300 case CSSPropertyTransition:
301 return getLayeredShorthandValue(transitionShorthand());
302 case CSSPropertyListStyle:
303 return getShorthandValue(listStyleShorthand());
304 case CSSPropertyWebkitMaskPosition:
305 return getLayeredShorthandValue(webkitMaskPositionShorthand());
306 case CSSPropertyWebkitMaskRepeat:
307 return getLayeredShorthandValue(webkitMaskRepeatShorthand());
308 case CSSPropertyWebkitMask:
309 return getLayeredShorthandValue(webkitMaskShorthand());
310 case CSSPropertyWebkitTextEmphasis:
311 return getShorthandValue(webkitTextEmphasisShorthand());
312 case CSSPropertyWebkitTextStroke:
313 return getShorthandValue(webkitTextStrokeShorthand());
314 case CSSPropertyTransformOrigin:
315 case CSSPropertyWebkitTransformOrigin:
316 return getShorthandValue(webkitTransformOriginShorthand());
317 case CSSPropertyWebkitTransition:
318 return getLayeredShorthandValue(webkitTransitionShorthand());
319 case CSSPropertyWebkitAnimation:
320 return getLayeredShorthandValue(webkitAnimationShorthand());
321 case CSSPropertyMarker: {
322 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyMarkerStart);
324 return value->cssText();
327 case CSSPropertyBorderRadius:
328 return get4Values(borderRadiusShorthand());
334 String StylePropertySerializer::borderSpacingValue(const StylePropertyShorthand& shorthand) const
336 RefPtrWillBeRawPtr<CSSValue> horizontalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[0]);
337 RefPtrWillBeRawPtr<CSSValue> verticalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[1]);
339 // While standard border-spacing property does not allow specifying border-spacing-vertical without
340 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
341 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
342 if (!horizontalValue || !verticalValue)
345 String horizontalValueCSSText = horizontalValue->cssText();
346 String verticalValueCSSText = verticalValue->cssText();
347 if (horizontalValueCSSText == verticalValueCSSText)
348 return horizontalValueCSSText;
349 return horizontalValueCSSText + ' ' + verticalValueCSSText;
352 void StylePropertySerializer::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
354 int foundPropertyIndex = m_propertySet.findPropertyIndex(propertyID);
355 if (foundPropertyIndex == -1)
356 return; // All longhands must have at least implicit values if "font" is specified.
358 if (m_propertySet.propertyAt(foundPropertyIndex).isImplicit()) {
359 commonValue = String();
364 switch (propertyID) {
365 case CSSPropertyFontStyle:
367 case CSSPropertyFontFamily:
368 case CSSPropertyFontVariant:
369 case CSSPropertyFontWeight:
372 case CSSPropertyLineHeight:
376 ASSERT_NOT_REACHED();
379 if (prefix && !result.isEmpty())
380 result.append(prefix);
381 String value = m_propertySet.propertyAt(foundPropertyIndex).value()->cssText();
382 result.append(value);
383 if (!commonValue.isNull() && commonValue != value)
384 commonValue = String();
387 String StylePropertySerializer::fontValue() const
389 int fontSizePropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontSize);
390 int fontFamilyPropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontFamily);
391 if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
392 return emptyString();
394 StylePropertySet::PropertyReference fontSizeProperty = m_propertySet.propertyAt(fontSizePropertyIndex);
395 StylePropertySet::PropertyReference fontFamilyProperty = m_propertySet.propertyAt(fontFamilyPropertyIndex);
396 if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
397 return emptyString();
399 String commonValue = fontSizeProperty.value()->cssText();
400 StringBuilder result;
401 appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
402 appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result, commonValue);
403 appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
404 if (!result.isEmpty())
406 result.append(fontSizeProperty.value()->cssText());
407 appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
408 if (!result.isEmpty())
410 result.append(fontFamilyProperty.value()->cssText());
411 if (isInitialOrInherit(commonValue))
413 return result.toString();
416 String StylePropertySerializer::get4Values(const StylePropertyShorthand& shorthand) const
418 // Assume the properties are in the usual order top, right, bottom, left.
419 int topValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[0]);
420 int rightValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[1]);
421 int bottomValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[2]);
422 int leftValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[3]);
424 if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
427 StylePropertySet::PropertyReference top = m_propertySet.propertyAt(topValueIndex);
428 StylePropertySet::PropertyReference right = m_propertySet.propertyAt(rightValueIndex);
429 StylePropertySet::PropertyReference bottom = m_propertySet.propertyAt(bottomValueIndex);
430 StylePropertySet::PropertyReference left = m_propertySet.propertyAt(leftValueIndex);
432 // All 4 properties must be specified.
433 if (!top.value() || !right.value() || !bottom.value() || !left.value())
436 if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
437 return getValueName(CSSValueInherit);
439 if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
440 if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
441 // All components are "initial" and "top" is not implicit.
442 return getValueName(CSSValueInitial);
446 if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
449 bool showLeft = !right.value()->equals(*left.value());
450 bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
451 bool showRight = !top.value()->equals(*right.value()) || showBottom;
453 StringBuilder result;
454 result.append(top.value()->cssText());
457 result.append(right.value()->cssText());
461 result.append(bottom.value()->cssText());
465 result.append(left.value()->cssText());
467 return result.toString();
470 String StylePropertySerializer::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
472 StringBuilder result;
474 const unsigned size = shorthand.length();
475 // Begin by collecting the properties into an array.
476 WillBeHeapVector<RefPtrWillBeMember<CSSValue> > values(size);
477 size_t numLayers = 0;
479 for (unsigned i = 0; i < size; ++i) {
480 values[i] = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
482 if (values[i]->isBaseValueList()) {
483 CSSValueList* valueList = toCSSValueList(values[i].get());
484 numLayers = std::max(valueList->length(), numLayers);
486 numLayers = std::max<size_t>(1U, numLayers);
492 bool commonValueInitialized = false;
494 // Now stitch the properties together. Implicit initial values are flagged as such and
495 // can safely be omitted.
496 for (size_t i = 0; i < numLayers; i++) {
497 StringBuilder layerResult;
498 bool useRepeatXShorthand = false;
499 bool useRepeatYShorthand = false;
500 bool useSingleWordShorthand = false;
501 bool foundPositionYCSSProperty = false;
502 for (unsigned j = 0; j < size; j++) {
503 RefPtrWillBeRawPtr<CSSValue> value = nullptr;
505 if (values[j]->isBaseValueList())
506 value = toCSSValueList(values[j].get())->item(i);
510 // Color only belongs in the last layer.
511 if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
512 if (i != numLayers - 1)
515 // Other singletons only belong in the first layer.
521 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
522 // then it was written with only one value. Here we figure out which value that was so we can
523 // report back correctly.
524 if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))
525 || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))) {
527 // BUG 49055: make sure the value was not reset in the layer check just above.
528 if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
529 || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
530 RefPtrWillBeRawPtr<CSSValue> yValue = nullptr;
531 RefPtrWillBeRawPtr<CSSValue> nextValue = values[j + 1];
532 if (nextValue->isValueList())
533 yValue = toCSSValueList(nextValue.get())->itemWithoutBoundsCheck(i);
537 // background-repeat-x(y) or mask-repeat-x(y) may be like this : "initial, repeat". We can omit the implicit initial values
538 // before starting to compare their values.
539 if (value->isImplicitInitialValue() || yValue->isImplicitInitialValue())
542 // FIXME: At some point we need to fix this code to avoid returning an invalid shorthand,
543 // since some longhand combinations are not serializable into a single shorthand.
544 if (!value->isPrimitiveValue() || !yValue->isPrimitiveValue())
547 CSSValueID xId = toCSSPrimitiveValue(value.get())->getValueID();
548 CSSValueID yId = toCSSPrimitiveValue(yValue.get())->getValueID();
550 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
551 useRepeatXShorthand = true;
553 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
554 useRepeatYShorthand = true;
558 useSingleWordShorthand = true;
565 if (value && !value->isImplicitInitialValue()) {
566 if (!layerResult.isEmpty())
567 layerResult.append(' ');
568 if (foundPositionYCSSProperty
569 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
570 layerResult.appendLiteral("/ ");
571 if (!foundPositionYCSSProperty
572 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
575 if (useRepeatXShorthand) {
576 useRepeatXShorthand = false;
577 layerResult.append(getValueName(CSSValueRepeatX));
578 } else if (useRepeatYShorthand) {
579 useRepeatYShorthand = false;
580 layerResult.append(getValueName(CSSValueRepeatY));
582 if (useSingleWordShorthand)
583 useSingleWordShorthand = false;
584 valueText = value->cssText();
585 layerResult.append(valueText);
588 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
589 || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
590 foundPositionYCSSProperty = true;
592 // background-position is a special case: if only the first offset is specified,
593 // the second one defaults to "center", not the same value.
594 if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
595 commonValue = String();
599 if (!commonValueInitialized) {
600 commonValue = valueText;
601 commonValueInitialized = true;
602 } else if (!commonValue.isNull() && commonValue != valueText)
603 commonValue = String();
606 if (!layerResult.isEmpty()) {
607 if (!result.isEmpty())
608 result.appendLiteral(", ");
609 result.append(layerResult);
613 if (isInitialOrInherit(commonValue))
616 if (result.isEmpty())
618 return result.toString();
621 String StylePropertySerializer::getShorthandValue(const StylePropertyShorthand& shorthand) const
624 StringBuilder result;
625 for (unsigned i = 0; i < shorthand.length(); ++i) {
626 if (!m_propertySet.isPropertyImplicit(shorthand.properties()[i])) {
627 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
630 String valueText = value->cssText();
632 commonValue = valueText;
633 else if (!commonValue.isNull() && commonValue != valueText)
634 commonValue = String();
635 if (value->isInitialValue())
637 if (!result.isEmpty())
639 result.append(valueText);
641 commonValue = String();
643 if (isInitialOrInherit(commonValue))
645 if (result.isEmpty())
647 return result.toString();
650 // only returns a non-null value if all properties have the same, non-null value
651 String StylePropertySerializer::getCommonValue(const StylePropertyShorthand& shorthand) const
654 bool lastPropertyWasImportant = false;
655 for (unsigned i = 0; i < shorthand.length(); ++i) {
656 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
657 // FIXME: CSSInitialValue::cssText should generate the right value.
660 String text = value->cssText();
665 else if (res != text)
668 bool currentPropertyIsImportant = m_propertySet.propertyIsImportant(shorthand.properties()[i]);
669 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
671 lastPropertyWasImportant = currentPropertyIsImportant;
676 String StylePropertySerializer::borderPropertyValue(CommonValueMode valueMode) const
678 const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
680 StringBuilder result;
681 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
682 String value = getCommonValue(properties[i]);
683 if (value.isNull()) {
684 if (valueMode == ReturnNullOnUncommonValues)
686 ASSERT(valueMode == OmitUncommonValues);
691 else if (!commonValue.isNull() && commonValue != value)
692 commonValue = String();
693 if (value == "initial")
695 if (!result.isEmpty())
697 result.append(value);
699 if (isInitialOrInherit(commonValue))
701 return result.isEmpty() ? String() : result.toString();
704 static void appendBackgroundRepeatValue(StringBuilder& builder, const CSSValue& repeatXCSSValue, const CSSValue& repeatYCSSValue)
706 // FIXME: Ensure initial values do not appear in CSS_VALUE_LISTS.
707 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue, initialRepeatValue, (CSSPrimitiveValue::create(CSSValueRepeat)));
708 const CSSPrimitiveValue& repeatX = repeatXCSSValue.isInitialValue() ? *initialRepeatValue : toCSSPrimitiveValue(repeatXCSSValue);
709 const CSSPrimitiveValue& repeatY = repeatYCSSValue.isInitialValue() ? *initialRepeatValue : toCSSPrimitiveValue(repeatYCSSValue);
710 CSSValueID repeatXValueId = repeatX.getValueID();
711 CSSValueID repeatYValueId = repeatY.getValueID();
712 if (repeatXValueId == repeatYValueId) {
713 builder.append(repeatX.cssText());
714 } else if (repeatXValueId == CSSValueNoRepeat && repeatYValueId == CSSValueRepeat) {
715 builder.append("repeat-y");
716 } else if (repeatXValueId == CSSValueRepeat && repeatYValueId == CSSValueNoRepeat) {
717 builder.append("repeat-x");
719 builder.append(repeatX.cssText());
721 builder.append(repeatY.cssText());
725 String StylePropertySerializer::backgroundRepeatPropertyValue() const
727 RefPtrWillBeRawPtr<CSSValue> repeatX = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundRepeatX);
728 RefPtrWillBeRawPtr<CSSValue> repeatY = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundRepeatY);
729 if (!repeatX || !repeatY)
731 if (m_propertySet.propertyIsImportant(CSSPropertyBackgroundRepeatX) != m_propertySet.propertyIsImportant(CSSPropertyBackgroundRepeatY))
733 if (repeatX->cssValueType() == repeatY->cssValueType()
734 && (repeatX->cssValueType() == CSSValue::CSS_INITIAL || repeatX->cssValueType() == CSSValue::CSS_INHERIT)) {
735 return repeatX->cssText();
738 RefPtrWillBeRawPtr<CSSValueList> repeatXList;
739 if (repeatX->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
740 repeatXList = CSSValueList::createCommaSeparated();
741 repeatXList->append(repeatX);
742 } else if (repeatX->cssValueType() == CSSValue::CSS_VALUE_LIST) {
743 repeatXList = toCSSValueList(repeatX.get());
748 RefPtrWillBeRawPtr<CSSValueList> repeatYList;
749 if (repeatY->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
750 repeatYList = CSSValueList::createCommaSeparated();
751 repeatYList->append(repeatY);
752 } else if (repeatY->cssValueType() == CSSValue::CSS_VALUE_LIST) {
753 repeatYList = toCSSValueList(repeatY.get());
758 size_t shorthandLength = lowestCommonMultiple(repeatXList->length(), repeatYList->length());
759 StringBuilder builder;
760 for (size_t i = 0; i < shorthandLength; ++i) {
762 builder.append(", ");
763 appendBackgroundRepeatValue(builder,
764 *repeatXList->item(i % repeatXList->length()),
765 *repeatYList->item(i % repeatYList->length()));
767 return builder.toString();
770 void StylePropertySerializer::appendBackgroundPropertyAsText(StringBuilder& result, unsigned& numDecls) const
772 if (isPropertyShorthandAvailable(backgroundShorthand())) {
773 String backgroundValue = getPropertyValue(CSSPropertyBackground);
774 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundImage);
775 result.append(getPropertyText(CSSPropertyBackground, backgroundValue, isImportant, numDecls++));
778 if (shorthandHasOnlyInitialOrInheritedValue(backgroundShorthand())) {
779 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundImage);
780 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundImage);
781 result.append(getPropertyText(CSSPropertyBackground, value->cssText(), isImportant, numDecls++));
785 // backgroundShorthandProperty without layered shorhand properties
786 const CSSPropertyID backgroundPropertyIds[] = {
787 CSSPropertyBackgroundImage,
788 CSSPropertyBackgroundAttachment,
789 CSSPropertyBackgroundColor,
790 CSSPropertyBackgroundSize,
791 CSSPropertyBackgroundOrigin,
792 CSSPropertyBackgroundClip
795 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(backgroundPropertyIds); ++i) {
796 CSSPropertyID propertyID = backgroundPropertyIds[i];
797 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(propertyID);
800 result.append(getPropertyText(propertyID, value->cssText(), m_propertySet.propertyIsImportant(propertyID), numDecls++));
803 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
804 // It is required because background-position-x/y are non-standard properties and WebKit generated output
805 // would not work in Firefox (<rdar://problem/5143183>)
806 // It would be a better solution if background-position was CSS_PAIR.
807 if (shorthandHasOnlyInitialOrInheritedValue(backgroundPositionShorthand())) {
808 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundPositionX);
809 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionX);
810 result.append(getPropertyText(CSSPropertyBackgroundPosition, value->cssText(), isImportant, numDecls++));
811 } else if (isPropertyShorthandAvailable(backgroundPositionShorthand())) {
812 String positionValue = m_propertySet.getPropertyValue(CSSPropertyBackgroundPosition);
813 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionX);
814 if (!positionValue.isNull())
815 result.append(getPropertyText(CSSPropertyBackgroundPosition, positionValue, isImportant, numDecls++));
817 // should check background-position-x or background-position-y.
818 if (RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundPositionX)) {
819 if (!value->isImplicitInitialValue()) {
820 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionX);
821 result.append(getPropertyText(CSSPropertyBackgroundPositionX, value->cssText(), isImportant, numDecls++));
824 if (RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyBackgroundPositionY)) {
825 if (!value->isImplicitInitialValue()) {
826 bool isImportant = m_propertySet.propertyIsImportant(CSSPropertyBackgroundPositionY);
827 result.append(getPropertyText(CSSPropertyBackgroundPositionY, value->cssText(), isImportant, numDecls++));
832 String repeatValue = m_propertySet.getPropertyValue(CSSPropertyBackgroundRepeat);
833 if (!repeatValue.isNull())
834 result.append(getPropertyText(CSSPropertyBackgroundRepeat, repeatValue, m_propertySet.propertyIsImportant(CSSPropertyBackgroundRepeatX), numDecls++));
837 bool StylePropertySerializer::isPropertyShorthandAvailable(const StylePropertyShorthand& shorthand) const
839 ASSERT(shorthand.length() > 0);
841 bool isImportant = m_propertySet.propertyIsImportant(shorthand.properties()[0]);
842 for (unsigned i = 0; i < shorthand.length(); ++i) {
843 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
844 if (!value || (value->isInitialValue() && !value->isImplicitInitialValue()) || value->isInheritedValue())
846 if (isImportant != m_propertySet.propertyIsImportant(shorthand.properties()[i]))
852 bool StylePropertySerializer::shorthandHasOnlyInitialOrInheritedValue(const StylePropertyShorthand& shorthand) const
854 ASSERT(shorthand.length() > 0);
855 bool isImportant = m_propertySet.propertyIsImportant(shorthand.properties()[0]);
856 bool isInitialValue = true;
857 bool isInheritedValue = true;
858 for (unsigned i = 0; i < shorthand.length(); ++i) {
859 RefPtrWillBeRawPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
862 if (!value->isInitialValue())
863 isInitialValue = false;
864 if (!value->isInheritedValue())
865 isInheritedValue = false;
866 if (isImportant != m_propertySet.propertyIsImportant(shorthand.properties()[i]))
869 return isInitialValue || isInheritedValue;