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