Home | History | Annotate | Download | only in css
      1 /*
      2  * (C) 1999-2003 Lars Knoll (knoll (at) 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/page/RuntimeCSSEnabled.h"
     29 #include "wtf/BitArray.h"
     30 #include "wtf/text/StringBuilder.h"
     31 
     32 using namespace std;
     33 
     34 namespace WebCore {
     35 
     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 
     43 StylePropertySerializer::StylePropertySerializer(const StylePropertySet& properties)
     44     : m_propertySet(properties)
     45 {
     46 }
     47 
     48 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 properties should be part of the style.
     66         ASSERT(RuntimeCSSEnabled::isCSSPropertyEnabled(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 
    311 String StylePropertySerializer::getPropertyValue(CSSPropertyID propertyID) const
    312 {
    313     // Shorthand and 4-values properties
    314     switch (propertyID) {
    315     case CSSPropertyBorderSpacing:
    316         return borderSpacingValue(borderSpacingShorthand());
    317     case CSSPropertyBackgroundPosition:
    318         return getLayeredShorthandValue(backgroundPositionShorthand());
    319     case CSSPropertyBackgroundRepeat:
    320         return getLayeredShorthandValue(backgroundRepeatShorthand());
    321     case CSSPropertyBackground:
    322         return getLayeredShorthandValue(backgroundShorthand());
    323     case CSSPropertyBorder:
    324         return borderPropertyValue(OmitUncommonValues);
    325     case CSSPropertyBorderTop:
    326         return getShorthandValue(borderTopShorthand());
    327     case CSSPropertyBorderRight:
    328         return getShorthandValue(borderRightShorthand());
    329     case CSSPropertyBorderBottom:
    330         return getShorthandValue(borderBottomShorthand());
    331     case CSSPropertyBorderLeft:
    332         return getShorthandValue(borderLeftShorthand());
    333     case CSSPropertyOutline:
    334         return getShorthandValue(outlineShorthand());
    335     case CSSPropertyBorderColor:
    336         return get4Values(borderColorShorthand());
    337     case CSSPropertyBorderWidth:
    338         return get4Values(borderWidthShorthand());
    339     case CSSPropertyBorderStyle:
    340         return get4Values(borderStyleShorthand());
    341     case CSSPropertyWebkitColumnRule:
    342         return getShorthandValue(webkitColumnRuleShorthand());
    343     case CSSPropertyWebkitColumns:
    344         return getShorthandValue(webkitColumnsShorthand());
    345     case CSSPropertyFlex:
    346         return getShorthandValue(flexShorthand());
    347     case CSSPropertyFlexFlow:
    348         return getShorthandValue(flexFlowShorthand());
    349     case CSSPropertyGridColumn:
    350         return getShorthandValue(gridColumnShorthand());
    351     case CSSPropertyGridRow:
    352         return getShorthandValue(gridRowShorthand());
    353     case CSSPropertyGridArea:
    354         return getShorthandValue(gridAreaShorthand());
    355     case CSSPropertyFont:
    356         return fontValue();
    357     case CSSPropertyMargin:
    358         return get4Values(marginShorthand());
    359     case CSSPropertyWebkitMarginCollapse:
    360         return getShorthandValue(webkitMarginCollapseShorthand());
    361     case CSSPropertyOverflow:
    362         return getCommonValue(overflowShorthand());
    363     case CSSPropertyPadding:
    364         return get4Values(paddingShorthand());
    365     case CSSPropertyTransition:
    366         return getLayeredShorthandValue(transitionShorthand());
    367     case CSSPropertyListStyle:
    368         return getShorthandValue(listStyleShorthand());
    369     case CSSPropertyWebkitMarquee:
    370         return getShorthandValue(webkitMarqueeShorthand());
    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 
    400 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 
    418 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 
    453 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 
    482 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 
    536 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 
    684 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
    714 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 
    739 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