• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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.
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 "core/css/StylePropertySerializer.h"
25 
26 #include "CSSValueKeywords.h"
27 #include "StylePropertyShorthand.h"
28 #include "core/css/RuntimeCSSEnabled.h"
29 #include "wtf/BitArray.h"
30 #include "wtf/text/StringBuilder.h"
31 
32 using namespace std;
33 
34 namespace WebCore {
35 
isInitialOrInherit(const String & value)36 static bool isInitialOrInherit(const String& value)
37 {
38     DEFINE_STATIC_LOCAL(String, initial, ("initial"));
39     DEFINE_STATIC_LOCAL(String, inherit, ("inherit"));
40     return value.length() == 7 && (value == initial || value == inherit);
41 }
42 
StylePropertySerializer(const StylePropertySet & properties)43 StylePropertySerializer::StylePropertySerializer(const StylePropertySet& properties)
44     : m_propertySet(properties)
45 {
46 }
47 
asText() const48 String StylePropertySerializer::asText() const
49 {
50     StringBuilder result;
51 
52     int positionXPropertyIndex = -1;
53     int positionYPropertyIndex = -1;
54     int repeatXPropertyIndex = -1;
55     int repeatYPropertyIndex = -1;
56 
57     BitArray<numCSSProperties> shorthandPropertyUsed;
58     BitArray<numCSSProperties> shorthandPropertyAppeared;
59 
60     unsigned size = m_propertySet.propertyCount();
61     unsigned numDecls = 0;
62     for (unsigned n = 0; n < size; ++n) {
63         StylePropertySet::PropertyReference property = m_propertySet.propertyAt(n);
64         CSSPropertyID propertyID = property.id();
65         // Only enabled or internal properties should be part of the style.
66         ASSERT(RuntimeCSSEnabled::isCSSPropertyEnabled(propertyID) || isInternalProperty(propertyID));
67         CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
68         CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
69         String value;
70 
71         switch (propertyID) {
72         case CSSPropertyVariable:
73             if (numDecls++)
74                 result.append(' ');
75             result.append(property.cssText());
76             continue;
77         case CSSPropertyBackgroundPositionX:
78             positionXPropertyIndex = n;
79             continue;
80         case CSSPropertyBackgroundPositionY:
81             positionYPropertyIndex = n;
82             continue;
83         case CSSPropertyBackgroundRepeatX:
84             repeatXPropertyIndex = n;
85             continue;
86         case CSSPropertyBackgroundRepeatY:
87             repeatYPropertyIndex = n;
88             continue;
89         case CSSPropertyContent:
90             if (property.value()->isValueList())
91                 value = toCSSValueList(property.value())->customCSSText(AlwaysQuoteCSSString);
92             break;
93         case CSSPropertyBorderTopWidth:
94         case CSSPropertyBorderRightWidth:
95         case CSSPropertyBorderBottomWidth:
96         case CSSPropertyBorderLeftWidth:
97             if (!borderFallbackShorthandProperty)
98                 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
99         case CSSPropertyBorderTopStyle:
100         case CSSPropertyBorderRightStyle:
101         case CSSPropertyBorderBottomStyle:
102         case CSSPropertyBorderLeftStyle:
103             if (!borderFallbackShorthandProperty)
104                 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
105         case CSSPropertyBorderTopColor:
106         case CSSPropertyBorderRightColor:
107         case CSSPropertyBorderBottomColor:
108         case CSSPropertyBorderLeftColor:
109             if (!borderFallbackShorthandProperty)
110                 borderFallbackShorthandProperty = CSSPropertyBorderColor;
111 
112             // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
113             if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
114                 value = borderPropertyValue(ReturnNullOnUncommonValues);
115                 if (value.isNull())
116                     shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
117                 else
118                     shorthandPropertyID = CSSPropertyBorder;
119             } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
120                 shorthandPropertyID = CSSPropertyBorder;
121             if (!shorthandPropertyID)
122                 shorthandPropertyID = borderFallbackShorthandProperty;
123             break;
124         case CSSPropertyWebkitBorderHorizontalSpacing:
125         case CSSPropertyWebkitBorderVerticalSpacing:
126             shorthandPropertyID = CSSPropertyBorderSpacing;
127             break;
128         case CSSPropertyFontFamily:
129         case CSSPropertyLineHeight:
130         case CSSPropertyFontSize:
131         case CSSPropertyFontStyle:
132         case CSSPropertyFontVariant:
133         case CSSPropertyFontWeight:
134             // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
135             break;
136         case CSSPropertyListStyleType:
137         case CSSPropertyListStylePosition:
138         case CSSPropertyListStyleImage:
139             shorthandPropertyID = CSSPropertyListStyle;
140             break;
141         case CSSPropertyMarginTop:
142         case CSSPropertyMarginRight:
143         case CSSPropertyMarginBottom:
144         case CSSPropertyMarginLeft:
145             shorthandPropertyID = CSSPropertyMargin;
146             break;
147         case CSSPropertyOutlineWidth:
148         case CSSPropertyOutlineStyle:
149         case CSSPropertyOutlineColor:
150             shorthandPropertyID = CSSPropertyOutline;
151             break;
152         case CSSPropertyOverflowX:
153         case CSSPropertyOverflowY:
154             shorthandPropertyID = CSSPropertyOverflow;
155             break;
156         case CSSPropertyPaddingTop:
157         case CSSPropertyPaddingRight:
158         case CSSPropertyPaddingBottom:
159         case CSSPropertyPaddingLeft:
160             shorthandPropertyID = CSSPropertyPadding;
161             break;
162         case CSSPropertyTransitionProperty:
163         case CSSPropertyTransitionDuration:
164         case CSSPropertyTransitionTimingFunction:
165         case CSSPropertyTransitionDelay:
166             shorthandPropertyID = CSSPropertyTransition;
167             break;
168         case CSSPropertyWebkitAnimationName:
169         case CSSPropertyWebkitAnimationDuration:
170         case CSSPropertyWebkitAnimationTimingFunction:
171         case CSSPropertyWebkitAnimationDelay:
172         case CSSPropertyWebkitAnimationIterationCount:
173         case CSSPropertyWebkitAnimationDirection:
174         case CSSPropertyWebkitAnimationFillMode:
175             shorthandPropertyID = CSSPropertyWebkitAnimation;
176             break;
177         case CSSPropertyFlexDirection:
178         case CSSPropertyFlexWrap:
179             shorthandPropertyID = CSSPropertyFlexFlow;
180             break;
181         case CSSPropertyFlexBasis:
182         case CSSPropertyFlexGrow:
183         case CSSPropertyFlexShrink:
184             shorthandPropertyID = CSSPropertyFlex;
185             break;
186         case CSSPropertyWebkitMaskPositionX:
187         case CSSPropertyWebkitMaskPositionY:
188         case CSSPropertyWebkitMaskRepeatX:
189         case CSSPropertyWebkitMaskRepeatY:
190         case CSSPropertyWebkitMaskImage:
191         case CSSPropertyWebkitMaskRepeat:
192         case CSSPropertyWebkitMaskPosition:
193         case CSSPropertyWebkitMaskClip:
194         case CSSPropertyWebkitMaskOrigin:
195             shorthandPropertyID = CSSPropertyWebkitMask;
196             break;
197         case CSSPropertyWebkitTransformOriginX:
198         case CSSPropertyWebkitTransformOriginY:
199         case CSSPropertyWebkitTransformOriginZ:
200             shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
201             break;
202         case CSSPropertyWebkitTransitionProperty:
203         case CSSPropertyWebkitTransitionDuration:
204         case CSSPropertyWebkitTransitionTimingFunction:
205         case CSSPropertyWebkitTransitionDelay:
206             shorthandPropertyID = CSSPropertyWebkitTransition;
207             break;
208         default:
209             break;
210         }
211 
212         unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
213         if (shorthandPropertyID) {
214             if (shorthandPropertyUsed.get(shortPropertyIndex))
215                 continue;
216             if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
217                 value = m_propertySet.getPropertyValue(shorthandPropertyID);
218             shorthandPropertyAppeared.set(shortPropertyIndex);
219         }
220 
221         if (!value.isNull()) {
222             if (shorthandPropertyID) {
223                 propertyID = shorthandPropertyID;
224                 shorthandPropertyUsed.set(shortPropertyIndex);
225             }
226         } else
227             value = property.value()->cssText();
228 
229         if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
230             continue;
231 
232         if (numDecls++)
233             result.append(' ');
234         result.append(getPropertyName(propertyID));
235         result.appendLiteral(": ");
236         result.append(value);
237         if (property.isImportant())
238             result.appendLiteral(" !important");
239         result.append(';');
240     }
241 
242     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
243     // It is required because background-position-x/y are non-standard properties and WebKit generated output
244     // would not work in Firefox (<rdar://problem/5143183>)
245     // It would be a better solution if background-position was CSS_PAIR.
246     if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && m_propertySet.propertyAt(positionXPropertyIndex).isImportant() == m_propertySet.propertyAt(positionYPropertyIndex).isImportant()) {
247         StylePropertySet::PropertyReference positionXProperty = m_propertySet.propertyAt(positionXPropertyIndex);
248         StylePropertySet::PropertyReference positionYProperty = m_propertySet.propertyAt(positionYPropertyIndex);
249 
250         if (numDecls++)
251             result.append(' ');
252         result.appendLiteral("background-position: ");
253         if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
254             result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
255         else {
256             result.append(positionXProperty.value()->cssText());
257             result.append(' ');
258             result.append(positionYProperty.value()->cssText());
259         }
260         if (positionXProperty.isImportant())
261             result.appendLiteral(" !important");
262         result.append(';');
263     } else {
264         if (positionXPropertyIndex != -1) {
265             if (numDecls++)
266                 result.append(' ');
267             result.append(m_propertySet.propertyAt(positionXPropertyIndex).cssText());
268         }
269         if (positionYPropertyIndex != -1) {
270             if (numDecls++)
271                 result.append(' ');
272             result.append(m_propertySet.propertyAt(positionYPropertyIndex).cssText());
273         }
274     }
275 
276     // FIXME: We need to do the same for background-repeat.
277     if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && m_propertySet.propertyAt(repeatXPropertyIndex).isImportant() == m_propertySet.propertyAt(repeatYPropertyIndex).isImportant()) {
278         StylePropertySet::PropertyReference repeatXProperty = m_propertySet.propertyAt(repeatXPropertyIndex);
279         StylePropertySet::PropertyReference repeatYProperty = m_propertySet.propertyAt(repeatYPropertyIndex);
280 
281         if (numDecls++)
282             result.append(' ');
283         result.appendLiteral("background-repeat: ");
284         if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
285             result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
286         else {
287             result.append(repeatXProperty.value()->cssText());
288             result.append(' ');
289             result.append(repeatYProperty.value()->cssText());
290         }
291         if (repeatXProperty.isImportant())
292             result.appendLiteral(" !important");
293         result.append(';');
294     } else {
295         if (repeatXPropertyIndex != -1) {
296             if (numDecls++)
297                 result.append(' ');
298             result.append(m_propertySet.propertyAt(repeatXPropertyIndex).cssText());
299         }
300         if (repeatYPropertyIndex != -1) {
301             if (numDecls++)
302                 result.append(' ');
303             result.append(m_propertySet.propertyAt(repeatYPropertyIndex).cssText());
304         }
305     }
306 
307     ASSERT(!numDecls ^ !result.isEmpty());
308     return result.toString();
309 }
310 
getPropertyValue(CSSPropertyID propertyID) const311 String StylePropertySerializer::getPropertyValue(CSSPropertyID propertyID) const
312 {
313     // Shorthand and 4-values properties
314     switch (propertyID) {
315     case CSSPropertyAnimation:
316         return getLayeredShorthandValue(animationShorthand());
317     case CSSPropertyBorderSpacing:
318         return borderSpacingValue(borderSpacingShorthand());
319     case CSSPropertyBackgroundPosition:
320         return getLayeredShorthandValue(backgroundPositionShorthand());
321     case CSSPropertyBackgroundRepeat:
322         return getLayeredShorthandValue(backgroundRepeatShorthand());
323     case CSSPropertyBackground:
324         return getLayeredShorthandValue(backgroundShorthand());
325     case CSSPropertyBorder:
326         return borderPropertyValue(OmitUncommonValues);
327     case CSSPropertyBorderTop:
328         return getShorthandValue(borderTopShorthand());
329     case CSSPropertyBorderRight:
330         return getShorthandValue(borderRightShorthand());
331     case CSSPropertyBorderBottom:
332         return getShorthandValue(borderBottomShorthand());
333     case CSSPropertyBorderLeft:
334         return getShorthandValue(borderLeftShorthand());
335     case CSSPropertyOutline:
336         return getShorthandValue(outlineShorthand());
337     case CSSPropertyBorderColor:
338         return get4Values(borderColorShorthand());
339     case CSSPropertyBorderWidth:
340         return get4Values(borderWidthShorthand());
341     case CSSPropertyBorderStyle:
342         return get4Values(borderStyleShorthand());
343     case CSSPropertyWebkitColumnRule:
344         return getShorthandValue(webkitColumnRuleShorthand());
345     case CSSPropertyWebkitColumns:
346         return getShorthandValue(webkitColumnsShorthand());
347     case CSSPropertyFlex:
348         return getShorthandValue(flexShorthand());
349     case CSSPropertyFlexFlow:
350         return getShorthandValue(flexFlowShorthand());
351     case CSSPropertyGridColumn:
352         return getShorthandValue(gridColumnShorthand());
353     case CSSPropertyGridRow:
354         return getShorthandValue(gridRowShorthand());
355     case CSSPropertyGridArea:
356         return getShorthandValue(gridAreaShorthand());
357     case CSSPropertyFont:
358         return fontValue();
359     case CSSPropertyMargin:
360         return get4Values(marginShorthand());
361     case CSSPropertyWebkitMarginCollapse:
362         return getShorthandValue(webkitMarginCollapseShorthand());
363     case CSSPropertyOverflow:
364         return getCommonValue(overflowShorthand());
365     case CSSPropertyPadding:
366         return get4Values(paddingShorthand());
367     case CSSPropertyTransition:
368         return getLayeredShorthandValue(transitionShorthand());
369     case CSSPropertyListStyle:
370         return getShorthandValue(listStyleShorthand());
371     case CSSPropertyWebkitMaskPosition:
372         return getLayeredShorthandValue(webkitMaskPositionShorthand());
373     case CSSPropertyWebkitMaskRepeat:
374         return getLayeredShorthandValue(webkitMaskRepeatShorthand());
375     case CSSPropertyWebkitMask:
376         return getLayeredShorthandValue(webkitMaskShorthand());
377     case CSSPropertyWebkitTextEmphasis:
378         return getShorthandValue(webkitTextEmphasisShorthand());
379     case CSSPropertyWebkitTextStroke:
380         return getShorthandValue(webkitTextStrokeShorthand());
381     case CSSPropertyWebkitTransformOrigin:
382         return getShorthandValue(webkitTransformOriginShorthand());
383     case CSSPropertyWebkitTransition:
384         return getLayeredShorthandValue(webkitTransitionShorthand());
385     case CSSPropertyWebkitAnimation:
386         return getLayeredShorthandValue(webkitAnimationShorthand());
387     case CSSPropertyMarker: {
388         RefPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(CSSPropertyMarkerStart);
389         if (value)
390             return value->cssText();
391         return String();
392     }
393     case CSSPropertyBorderRadius:
394         return get4Values(borderRadiusShorthand());
395     default:
396         return String();
397     }
398 }
399 
borderSpacingValue(const StylePropertyShorthand & shorthand) const400 String StylePropertySerializer::borderSpacingValue(const StylePropertyShorthand& shorthand) const
401 {
402     RefPtr<CSSValue> horizontalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[0]);
403     RefPtr<CSSValue> verticalValue = m_propertySet.getPropertyCSSValue(shorthand.properties()[1]);
404 
405     // While standard border-spacing property does not allow specifying border-spacing-vertical without
406     // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
407     // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
408     if (!horizontalValue || !verticalValue)
409         return String();
410 
411     String horizontalValueCSSText = horizontalValue->cssText();
412     String verticalValueCSSText = verticalValue->cssText();
413     if (horizontalValueCSSText == verticalValueCSSText)
414         return horizontalValueCSSText;
415     return horizontalValueCSSText + ' ' + verticalValueCSSText;
416 }
417 
appendFontLonghandValueIfExplicit(CSSPropertyID propertyID,StringBuilder & result,String & commonValue) const418 void StylePropertySerializer::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
419 {
420     int foundPropertyIndex = m_propertySet.findPropertyIndex(propertyID);
421     if (foundPropertyIndex == -1)
422         return; // All longhands must have at least implicit values if "font" is specified.
423 
424     if (m_propertySet.propertyAt(foundPropertyIndex).isImplicit()) {
425         commonValue = String();
426         return;
427     }
428 
429     char prefix = '\0';
430     switch (propertyID) {
431     case CSSPropertyFontStyle:
432         break; // No prefix.
433     case CSSPropertyFontFamily:
434     case CSSPropertyFontVariant:
435     case CSSPropertyFontWeight:
436         prefix = ' ';
437         break;
438     case CSSPropertyLineHeight:
439         prefix = '/';
440         break;
441     default:
442         ASSERT_NOT_REACHED();
443     }
444 
445     if (prefix && !result.isEmpty())
446         result.append(prefix);
447     String value = m_propertySet.propertyAt(foundPropertyIndex).value()->cssText();
448     result.append(value);
449     if (!commonValue.isNull() && commonValue != value)
450         commonValue = String();
451 }
452 
fontValue() const453 String StylePropertySerializer::fontValue() const
454 {
455     int fontSizePropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontSize);
456     int fontFamilyPropertyIndex = m_propertySet.findPropertyIndex(CSSPropertyFontFamily);
457     if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
458         return emptyString();
459 
460     StylePropertySet::PropertyReference fontSizeProperty = m_propertySet.propertyAt(fontSizePropertyIndex);
461     StylePropertySet::PropertyReference fontFamilyProperty = m_propertySet.propertyAt(fontFamilyPropertyIndex);
462     if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
463         return emptyString();
464 
465     String commonValue = fontSizeProperty.value()->cssText();
466     StringBuilder result;
467     appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
468     appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result, commonValue);
469     appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
470     if (!result.isEmpty())
471         result.append(' ');
472     result.append(fontSizeProperty.value()->cssText());
473     appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
474     if (!result.isEmpty())
475         result.append(' ');
476     result.append(fontFamilyProperty.value()->cssText());
477     if (isInitialOrInherit(commonValue))
478         return commonValue;
479     return result.toString();
480 }
481 
get4Values(const StylePropertyShorthand & shorthand) const482 String StylePropertySerializer::get4Values(const StylePropertyShorthand& shorthand) const
483 {
484     // Assume the properties are in the usual order top, right, bottom, left.
485     int topValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[0]);
486     int rightValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[1]);
487     int bottomValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[2]);
488     int leftValueIndex = m_propertySet.findPropertyIndex(shorthand.properties()[3]);
489 
490     if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
491         return String();
492 
493     StylePropertySet::PropertyReference top = m_propertySet.propertyAt(topValueIndex);
494     StylePropertySet::PropertyReference right = m_propertySet.propertyAt(rightValueIndex);
495     StylePropertySet::PropertyReference bottom = m_propertySet.propertyAt(bottomValueIndex);
496     StylePropertySet::PropertyReference left = m_propertySet.propertyAt(leftValueIndex);
497 
498         // All 4 properties must be specified.
499     if (!top.value() || !right.value() || !bottom.value() || !left.value())
500         return String();
501 
502     if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
503         return getValueName(CSSValueInherit);
504 
505     if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
506         if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
507             // All components are "initial" and "top" is not implicit.
508             return getValueName(CSSValueInitial);
509         }
510         return String();
511     }
512     if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
513         return String();
514 
515     bool showLeft = !right.value()->equals(*left.value());
516     bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
517     bool showRight = !top.value()->equals(*right.value()) || showBottom;
518 
519     StringBuilder result;
520     result.append(top.value()->cssText());
521     if (showRight) {
522         result.append(' ');
523         result.append(right.value()->cssText());
524     }
525     if (showBottom) {
526         result.append(' ');
527         result.append(bottom.value()->cssText());
528     }
529     if (showLeft) {
530         result.append(' ');
531         result.append(left.value()->cssText());
532     }
533     return result.toString();
534 }
535 
getLayeredShorthandValue(const StylePropertyShorthand & shorthand) const536 String StylePropertySerializer::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
537 {
538     StringBuilder result;
539 
540     const unsigned size = shorthand.length();
541     // Begin by collecting the properties into an array.
542     Vector< RefPtr<CSSValue> > values(size);
543     size_t numLayers = 0;
544 
545     for (unsigned i = 0; i < size; ++i) {
546         values[i] = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
547         if (values[i]) {
548             if (values[i]->isBaseValueList()) {
549                 CSSValueList* valueList = toCSSValueList(values[i].get());
550                 numLayers = max(valueList->length(), numLayers);
551             } else
552                 numLayers = max<size_t>(1U, numLayers);
553         }
554     }
555 
556     String commonValue;
557     bool commonValueInitialized = false;
558 
559     // Now stitch the properties together. Implicit initial values are flagged as such and
560     // can safely be omitted.
561     for (size_t i = 0; i < numLayers; i++) {
562         StringBuilder layerResult;
563         bool useRepeatXShorthand = false;
564         bool useRepeatYShorthand = false;
565         bool useSingleWordShorthand = false;
566         bool foundPositionYCSSProperty = false;
567         for (unsigned j = 0; j < size; j++) {
568             RefPtr<CSSValue> value;
569             if (values[j]) {
570                 if (values[j]->isBaseValueList())
571                     value = toCSSValueList(values[j].get())->item(i);
572                 else {
573                     value = values[j];
574 
575                     // Color only belongs in the last layer.
576                     if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
577                         if (i != numLayers - 1)
578                             value = 0;
579                     } else if (i) // Other singletons only belong in the first layer.
580                         value = 0;
581                 }
582             }
583 
584             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
585             // then it was written with only one value. Here we figure out which value that was so we can
586             // report back correctly.
587             if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))
588                 || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && m_propertySet.isPropertyImplicit(shorthand.properties()[j]))) {
589 
590                 // BUG 49055: make sure the value was not reset in the layer check just above.
591                 if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
592                     || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
593                     RefPtr<CSSValue> yValue;
594                     RefPtr<CSSValue> nextValue = values[j + 1];
595                     if (nextValue->isValueList())
596                         yValue = toCSSValueList(nextValue.get())->itemWithoutBoundsCheck(i);
597                     else
598                         yValue = nextValue;
599 
600                     // background-repeat-x(y) or mask-repeat-x(y) may be like this : "initial, repeat". We can omit the implicit initial values
601                     // before starting to compare their values.
602                     if (value->isImplicitInitialValue() || yValue->isImplicitInitialValue())
603                         continue;
604 
605                     // FIXME: At some point we need to fix this code to avoid returning an invalid shorthand,
606                     // since some longhand combinations are not serializable into a single shorthand.
607                     if (!value->isPrimitiveValue() || !yValue->isPrimitiveValue())
608                         continue;
609 
610                     CSSValueID xId = toCSSPrimitiveValue(value.get())->getValueID();
611                     CSSValueID yId = toCSSPrimitiveValue(yValue.get())->getValueID();
612                     if (xId != yId) {
613                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
614                             useRepeatXShorthand = true;
615                             ++j;
616                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
617                             useRepeatYShorthand = true;
618                             continue;
619                         }
620                     } else {
621                         useSingleWordShorthand = true;
622                         ++j;
623                     }
624                 }
625             }
626 
627             String valueText;
628             if (value && !value->isImplicitInitialValue()) {
629                 if (!layerResult.isEmpty())
630                     layerResult.append(' ');
631                 if (foundPositionYCSSProperty
632                     && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
633                     layerResult.appendLiteral("/ ");
634                 if (!foundPositionYCSSProperty
635                     && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
636                     continue;
637 
638                 if (useRepeatXShorthand) {
639                     useRepeatXShorthand = false;
640                     layerResult.append(getValueName(CSSValueRepeatX));
641                 } else if (useRepeatYShorthand) {
642                     useRepeatYShorthand = false;
643                     layerResult.append(getValueName(CSSValueRepeatY));
644                 } else {
645                     if (useSingleWordShorthand)
646                         useSingleWordShorthand = false;
647                     valueText = value->cssText();
648                     layerResult.append(valueText);
649                 }
650 
651                 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
652                     || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
653                     foundPositionYCSSProperty = true;
654 
655                     // background-position is a special case: if only the first offset is specified,
656                     // the second one defaults to "center", not the same value.
657                     if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
658                         commonValue = String();
659                 }
660             }
661 
662             if (!commonValueInitialized) {
663                 commonValue = valueText;
664                 commonValueInitialized = true;
665             } else if (!commonValue.isNull() && commonValue != valueText)
666                 commonValue = String();
667         }
668 
669         if (!layerResult.isEmpty()) {
670             if (!result.isEmpty())
671                 result.appendLiteral(", ");
672             result.append(layerResult);
673         }
674     }
675 
676     if (isInitialOrInherit(commonValue))
677         return commonValue;
678 
679     if (result.isEmpty())
680         return String();
681     return result.toString();
682 }
683 
getShorthandValue(const StylePropertyShorthand & shorthand) const684 String StylePropertySerializer::getShorthandValue(const StylePropertyShorthand& shorthand) const
685 {
686     String commonValue;
687     StringBuilder result;
688     for (unsigned i = 0; i < shorthand.length(); ++i) {
689         if (!m_propertySet.isPropertyImplicit(shorthand.properties()[i])) {
690             RefPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
691             if (!value)
692                 return String();
693             String valueText = value->cssText();
694             if (!i)
695                 commonValue = valueText;
696             else if (!commonValue.isNull() && commonValue != valueText)
697                 commonValue = String();
698             if (value->isInitialValue())
699                 continue;
700             if (!result.isEmpty())
701                 result.append(' ');
702             result.append(valueText);
703         } else
704             commonValue = String();
705     }
706     if (isInitialOrInherit(commonValue))
707         return commonValue;
708     if (result.isEmpty())
709         return String();
710     return result.toString();
711 }
712 
713 // only returns a non-null value if all properties have the same, non-null value
getCommonValue(const StylePropertyShorthand & shorthand) const714 String StylePropertySerializer::getCommonValue(const StylePropertyShorthand& shorthand) const
715 {
716     String res;
717     bool lastPropertyWasImportant = false;
718     for (unsigned i = 0; i < shorthand.length(); ++i) {
719         RefPtr<CSSValue> value = m_propertySet.getPropertyCSSValue(shorthand.properties()[i]);
720         // FIXME: CSSInitialValue::cssText should generate the right value.
721         if (!value)
722             return String();
723         String text = value->cssText();
724         if (text.isNull())
725             return String();
726         if (res.isNull())
727             res = text;
728         else if (res != text)
729             return String();
730 
731         bool currentPropertyIsImportant = m_propertySet.propertyIsImportant(shorthand.properties()[i]);
732         if (i && lastPropertyWasImportant != currentPropertyIsImportant)
733             return String();
734         lastPropertyWasImportant = currentPropertyIsImportant;
735     }
736     return res;
737 }
738 
borderPropertyValue(CommonValueMode valueMode) const739 String StylePropertySerializer::borderPropertyValue(CommonValueMode valueMode) const
740 {
741     const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
742     String commonValue;
743     StringBuilder result;
744     for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
745         String value = getCommonValue(properties[i]);
746         if (value.isNull()) {
747             if (valueMode == ReturnNullOnUncommonValues)
748                 return String();
749             ASSERT(valueMode == OmitUncommonValues);
750             continue;
751         }
752         if (!i)
753             commonValue = value;
754         else if (!commonValue.isNull() && commonValue != value)
755             commonValue = String();
756         if (value == "initial")
757             continue;
758         if (!result.isEmpty())
759             result.append(' ');
760         result.append(value);
761     }
762     if (isInitialOrInherit(commonValue))
763         return commonValue;
764     return result.isEmpty() ? String() : result.toString();
765 }
766 
767 }
768