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.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 #include "StylePropertySet.h"
25 #include "CSSParser.h"
26 #include "CSSValueKeywords.h"
27 #include "CSSValueList.h"
28 #include "CSSValuePool.h"
30 #include "PropertySetCSSStyleDeclaration.h"
31 #include "StylePropertyShorthand.h"
32 #include "StyleSheetContents.h"
33 #include <wtf/BitArray.h>
34 #include <wtf/text/StringBuilder.h>
38 #include <wtf/ASCIICType.h>
39 #include <wtf/text/CString.h>
46 typedef HashMap<const StylePropertySet*, OwnPtr<PropertySetCSSStyleDeclaration> > PropertySetCSSOMWrapperMap;
47 static PropertySetCSSOMWrapperMap& propertySetCSSOMWrapperMap()
49 DEFINE_STATIC_LOCAL(PropertySetCSSOMWrapperMap, propertySetCSSOMWrapperMapInstance, ());
50 return propertySetCSSOMWrapperMapInstance;
53 PassRefPtr<StylePropertySet> StylePropertySet::createImmutable(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
55 void* slot = WTF::fastMalloc(sizeof(StylePropertySet) - sizeof(void*) + sizeof(CSSProperty) * count);
56 return adoptRef(new (slot) StylePropertySet(properties, count, cssParserMode, /* makeMutable */ false));
59 StylePropertySet::StylePropertySet(CSSParserMode cssParserMode)
60 : m_cssParserMode(cssParserMode)
61 , m_ownsCSSOMWrapper(false)
64 , m_mutablePropertyVector(new Vector<CSSProperty>)
68 StylePropertySet::StylePropertySet(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode, bool makeMutable)
69 : m_cssParserMode(cssParserMode)
70 , m_ownsCSSOMWrapper(false)
71 , m_isMutable(makeMutable)
74 m_mutablePropertyVector = new Vector<CSSProperty>;
75 m_mutablePropertyVector->reserveInitialCapacity(count);
76 for (unsigned i = 0; i < count; ++i)
77 m_mutablePropertyVector->uncheckedAppend(properties[i]);
80 for (unsigned i = 0; i < m_arraySize; ++i)
81 new (&array()[i]) CSSProperty(properties[i]);
85 StylePropertySet::StylePropertySet(const StylePropertySet& o)
86 : RefCounted<StylePropertySet>()
87 , m_cssParserMode(o.m_cssParserMode)
88 , m_ownsCSSOMWrapper(false)
91 , m_mutablePropertyVector(new Vector<CSSProperty>)
93 copyPropertiesFrom(o);
96 StylePropertySet::~StylePropertySet()
98 ASSERT(!m_ownsCSSOMWrapper || propertySetCSSOMWrapperMap().contains(this));
99 if (m_ownsCSSOMWrapper)
100 propertySetCSSOMWrapperMap().remove(this);
102 delete m_mutablePropertyVector;
104 for (unsigned i = 0; i < m_arraySize; ++i)
105 array()[i].~CSSProperty();
109 void StylePropertySet::setCSSParserMode(CSSParserMode cssParserMode)
112 m_cssParserMode = cssParserMode;
115 void StylePropertySet::copyPropertiesFrom(const StylePropertySet& other)
119 if (other.isMutable()) {
120 *m_mutablePropertyVector = *other.m_mutablePropertyVector;
124 ASSERT(m_mutablePropertyVector->isEmpty());
125 m_mutablePropertyVector->reserveInitialCapacity(other.m_arraySize);
126 for (unsigned i = 0; i < other.m_arraySize; ++i)
127 m_mutablePropertyVector->uncheckedAppend(other.array()[i]);
130 String StylePropertySet::getPropertyValue(CSSPropertyID propertyID) const
132 RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
134 return value->cssText();
136 // Shorthand and 4-values properties
137 switch (propertyID) {
138 case CSSPropertyBorderSpacing:
139 return borderSpacingValue(borderSpacingShorthand());
140 case CSSPropertyBackgroundPosition:
141 return getLayeredShorthandValue(backgroundPositionShorthand());
142 case CSSPropertyBackgroundRepeat:
143 return getLayeredShorthandValue(backgroundRepeatShorthand());
144 case CSSPropertyBackground:
145 return getLayeredShorthandValue(backgroundShorthand());
146 case CSSPropertyBorder:
147 return borderPropertyValue(OmitUncommonValues);
148 case CSSPropertyBorderTop:
149 return getShorthandValue(borderTopShorthand());
150 case CSSPropertyBorderRight:
151 return getShorthandValue(borderRightShorthand());
152 case CSSPropertyBorderBottom:
153 return getShorthandValue(borderBottomShorthand());
154 case CSSPropertyBorderLeft:
155 return getShorthandValue(borderLeftShorthand());
156 case CSSPropertyOutline:
157 return getShorthandValue(outlineShorthand());
158 case CSSPropertyBorderColor:
159 return get4Values(borderColorShorthand());
160 case CSSPropertyBorderWidth:
161 return get4Values(borderWidthShorthand());
162 case CSSPropertyBorderStyle:
163 return get4Values(borderStyleShorthand());
164 #if ENABLE(CSS3_FLEXBOX)
165 case CSSPropertyWebkitFlex:
166 return getShorthandValue(webkitFlexShorthand());
167 case CSSPropertyWebkitFlexFlow:
168 return getShorthandValue(webkitFlexFlowShorthand());
170 case CSSPropertyFont:
172 case CSSPropertyMargin:
173 return get4Values(marginShorthand());
174 case CSSPropertyOverflow:
175 return getCommonValue(overflowShorthand());
176 case CSSPropertyPadding:
177 return get4Values(paddingShorthand());
178 case CSSPropertyListStyle:
179 return getShorthandValue(listStyleShorthand());
180 case CSSPropertyWebkitMaskPosition:
181 return getLayeredShorthandValue(webkitMaskPositionShorthand());
182 case CSSPropertyWebkitMaskRepeat:
183 return getLayeredShorthandValue(webkitMaskRepeatShorthand());
184 case CSSPropertyWebkitMask:
185 return getLayeredShorthandValue(webkitMaskShorthand());
186 case CSSPropertyWebkitTransformOrigin:
187 return getShorthandValue(webkitTransformOriginShorthand());
188 case CSSPropertyWebkitTransition:
189 return getLayeredShorthandValue(webkitTransitionShorthand());
190 case CSSPropertyWebkitAnimation:
191 return getLayeredShorthandValue(webkitAnimationShorthand());
192 #if ENABLE(CSS_EXCLUSIONS)
193 case CSSPropertyWebkitWrap:
194 return getShorthandValue(webkitWrapShorthand());
197 case CSSPropertyMarker: {
198 RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
200 return value->cssText();
204 case CSSPropertyBorderRadius:
205 return get4Values(borderRadiusShorthand());
211 String StylePropertySet::borderSpacingValue(const StylePropertyShorthand& shorthand) const
213 RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(shorthand.properties()[0]);
214 RefPtr<CSSValue> verticalValue = getPropertyCSSValue(shorthand.properties()[1]);
216 // While standard border-spacing property does not allow specifying border-spacing-vertical without
217 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
218 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
219 if (!horizontalValue || !verticalValue)
222 String horizontalValueCSSText = horizontalValue->cssText();
223 String verticalValueCSSText = verticalValue->cssText();
224 if (horizontalValueCSSText == verticalValueCSSText)
225 return horizontalValueCSSText;
226 return horizontalValueCSSText + ' ' + verticalValueCSSText;
229 bool StylePropertySet::appendFontLonghandValueIfExplicit(CSSPropertyID propertyId, StringBuilder& result) const
231 const CSSProperty* property = findPropertyWithId(propertyId);
233 return false; // All longhands must have at least implicit values if "font" is specified.
234 if (property->isImplicit())
238 switch (propertyId) {
239 case CSSPropertyFontStyle:
241 case CSSPropertyFontFamily:
242 case CSSPropertyFontVariant:
243 case CSSPropertyFontWeight:
246 case CSSPropertyLineHeight:
250 ASSERT_NOT_REACHED();
253 if (prefix && !result.isEmpty())
254 result.append(prefix);
255 result.append(property->value()->cssText());
260 String StylePropertySet::fontValue() const
262 const CSSProperty* fontSizeProperty = findPropertyWithId(CSSPropertyFontSize);
263 if (!fontSizeProperty || fontSizeProperty->isImplicit())
264 return emptyString();
266 StringBuilder result;
268 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result);
269 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result);
270 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result);
271 if (!result.isEmpty())
273 result.append(fontSizeProperty->value()->cssText());
274 success &= appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result);
275 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontFamily, result);
277 // An invalid "font" value has been built (should never happen, as at least implicit values
278 // for mandatory longhands are always found in the style), report empty value instead.
279 ASSERT_NOT_REACHED();
280 return emptyString();
282 return result.toString();
285 String StylePropertySet::get4Values(const StylePropertyShorthand& shorthand) const
287 // Assume the properties are in the usual order top, right, bottom, left.
288 const CSSProperty* top = findPropertyWithId(shorthand.properties()[0]);
289 const CSSProperty* right = findPropertyWithId(shorthand.properties()[1]);
290 const CSSProperty* bottom = findPropertyWithId(shorthand.properties()[2]);
291 const CSSProperty* left = findPropertyWithId(shorthand.properties()[3]);
293 // All 4 properties must be specified.
294 if (!top || !top->value() || !right || !right->value() || !bottom || !bottom->value() || !left || !left->value())
296 if (top->value()->isInitialValue() || right->value()->isInitialValue() || bottom->value()->isInitialValue() || left->value()->isInitialValue())
298 if (top->isImportant() != right->isImportant() || right->isImportant() != bottom->isImportant() || bottom->isImportant() != left->isImportant())
301 bool showLeft = right->value()->cssText() != left->value()->cssText();
302 bool showBottom = (top->value()->cssText() != bottom->value()->cssText()) || showLeft;
303 bool showRight = (top->value()->cssText() != right->value()->cssText()) || showBottom;
305 String res = top->value()->cssText();
307 res += " " + right->value()->cssText();
309 res += " " + bottom->value()->cssText();
311 res += " " + left->value()->cssText();
316 String StylePropertySet::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
320 const unsigned size = shorthand.length();
321 // Begin by collecting the properties into an array.
322 Vector< RefPtr<CSSValue> > values(size);
323 size_t numLayers = 0;
325 for (unsigned i = 0; i < size; ++i) {
326 values[i] = getPropertyCSSValue(shorthand.properties()[i]);
328 if (values[i]->isValueList()) {
329 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
330 numLayers = max(valueList->length(), numLayers);
332 numLayers = max<size_t>(1U, numLayers);
336 // Now stitch the properties together. Implicit initial values are flagged as such and
337 // can safely be omitted.
338 for (size_t i = 0; i < numLayers; i++) {
340 bool useRepeatXShorthand = false;
341 bool useRepeatYShorthand = false;
342 bool useSingleWordShorthand = false;
343 bool foundBackgroundPositionYCSSProperty = false;
344 for (unsigned j = 0; j < size; j++) {
345 RefPtr<CSSValue> value;
347 if (values[j]->isValueList())
348 value = static_cast<CSSValueList*>(values[j].get())->item(i);
352 // Color only belongs in the last layer.
353 if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
354 if (i != numLayers - 1)
356 } else if (i != 0) // Other singletons only belong in the first layer.
361 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
362 // then it was written with only one value. Here we figure out which value that was so we can
363 // report back correctly.
364 if (shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j])) {
366 // BUG 49055: make sure the value was not reset in the layer check just above.
367 if (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value) {
368 RefPtr<CSSValue> yValue;
369 RefPtr<CSSValue> nextValue = values[j + 1];
370 if (nextValue->isValueList())
371 yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
375 int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
376 int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
378 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
379 useRepeatXShorthand = true;
381 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
382 useRepeatYShorthand = true;
386 useSingleWordShorthand = true;
392 if (value && !value->isImplicitInitialValue()) {
393 if (!layerRes.isNull())
395 if (foundBackgroundPositionYCSSProperty && shorthand.properties()[j] == CSSPropertyBackgroundSize)
397 if (!foundBackgroundPositionYCSSProperty && shorthand.properties()[j] == CSSPropertyBackgroundSize)
400 if (useRepeatXShorthand) {
401 useRepeatXShorthand = false;
402 layerRes += getValueName(CSSValueRepeatX);
403 } else if (useRepeatYShorthand) {
404 useRepeatYShorthand = false;
405 layerRes += getValueName(CSSValueRepeatY);
406 } else if (useSingleWordShorthand) {
407 useSingleWordShorthand = false;
408 layerRes += value->cssText();
410 layerRes += value->cssText();
412 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY)
413 foundBackgroundPositionYCSSProperty = true;
417 if (!layerRes.isNull()) {
426 String StylePropertySet::getShorthandValue(const StylePropertyShorthand& shorthand) const
429 for (unsigned i = 0; i < shorthand.length(); ++i) {
430 if (!isPropertyImplicit(shorthand.properties()[i])) {
431 RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
434 if (value->isInitialValue())
438 res += value->cssText();
444 // only returns a non-null value if all properties have the same, non-null value
445 String StylePropertySet::getCommonValue(const StylePropertyShorthand& shorthand) const
448 bool lastPropertyWasImportant = false;
449 for (unsigned i = 0; i < shorthand.length(); ++i) {
450 RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
451 // FIXME: CSSInitialValue::cssText should generate the right value.
454 String text = value->cssText();
459 else if (res != text)
462 bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
463 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
465 lastPropertyWasImportant = currentPropertyIsImportant;
470 String StylePropertySet::borderPropertyValue(CommonValueMode valueMode) const
472 const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
473 StringBuilder result;
474 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
475 String value = getCommonValue(properties[i]);
476 if (value.isNull()) {
477 if (valueMode == ReturnNullOnUncommonValues)
479 ASSERT(valueMode == OmitUncommonValues);
482 if (value == "initial")
484 if (!result.isEmpty())
486 result.append(value);
488 return result.isEmpty() ? String() : result.toString();
491 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID) const
493 const CSSProperty* property = findPropertyWithId(propertyID);
494 return property ? property->value() : 0;
497 bool StylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
500 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
501 if (!shorthand.length())
503 return removePropertiesInSet(shorthand.properties(), shorthand.length());
506 bool StylePropertySet::removeProperty(CSSPropertyID propertyID, String* returnText)
509 if (removeShorthandProperty(propertyID)) {
510 // FIXME: Return an equivalent shorthand when possible.
516 CSSProperty* foundProperty = findPropertyWithId(propertyID);
517 if (!foundProperty) {
524 *returnText = foundProperty->value()->cssText();
526 // A more efficient removal strategy would involve marking entries as empty
527 // and sweeping them when the vector grows too big.
528 m_mutablePropertyVector->remove(foundProperty - m_mutablePropertyVector->data());
533 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID) const
535 const CSSProperty* property = findPropertyWithId(propertyID);
537 return property->isImportant();
539 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
540 if (!shorthand.length())
543 for (unsigned i = 0; i < shorthand.length(); ++i) {
544 if (!propertyIsImportant(shorthand.properties()[i]))
550 CSSPropertyID StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID) const
552 const CSSProperty* property = findPropertyWithId(propertyID);
553 return property ? property->shorthandID() : CSSPropertyInvalid;
556 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID) const
558 const CSSProperty* property = findPropertyWithId(propertyID);
559 return property ? property->isImplicit() : false;
562 bool StylePropertySet::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
565 // Setting the value to an empty string just removes the property in both IE and Gecko.
566 // Setting it to null seems to produce less consistent results, but we treat it just the same.
567 if (value.isEmpty()) {
568 removeProperty(propertyID);
572 // When replacing an existing property value, this moves the property to the end of the list.
573 // Firefox preserves the position, and MSIE moves the property to the beginning.
574 return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
577 void StylePropertySet::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
580 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
581 if (!shorthand.length()) {
582 setProperty(CSSProperty(propertyID, prpValue, important));
586 removePropertiesInSet(shorthand.properties(), shorthand.length());
588 RefPtr<CSSValue> value = prpValue;
589 for (unsigned i = 0; i < shorthand.length(); ++i)
590 append(CSSProperty(shorthand.properties()[i], value, important));
593 void StylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
596 if (!removeShorthandProperty(property.id())) {
597 CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
599 *toReplace = property;
606 bool StylePropertySet::setProperty(CSSPropertyID propertyID, int identifier, bool important)
609 setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
613 void StylePropertySet::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
617 m_mutablePropertyVector->clear();
619 CSSParserContext context(cssParserMode());
620 if (contextStyleSheet) {
621 context = contextStyleSheet->parserContext();
622 context.mode = cssParserMode();
624 CSSParser parser(context);
625 parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
628 void StylePropertySet::addParsedProperties(const Vector<CSSProperty>& properties)
631 m_mutablePropertyVector->reserveCapacity(m_mutablePropertyVector->size() + properties.size());
632 for (unsigned i = 0; i < properties.size(); ++i)
633 addParsedProperty(properties[i]);
636 void StylePropertySet::addParsedProperty(const CSSProperty& property)
639 // Only add properties that have no !important counterpart present
640 if (!propertyIsImportant(property.id()) || property.isImportant())
641 setProperty(property);
644 String StylePropertySet::asText() const
646 StringBuilder result;
648 const CSSProperty* positionXProp = 0;
649 const CSSProperty* positionYProp = 0;
650 const CSSProperty* repeatXProp = 0;
651 const CSSProperty* repeatYProp = 0;
653 BitArray<numCSSProperties> shorthandPropertyUsed;
654 BitArray<numCSSProperties> shorthandPropertyAppeared;
656 unsigned size = propertyCount();
657 for (unsigned n = 0; n < size; ++n) {
658 const CSSProperty& prop = propertyAt(n);
659 CSSPropertyID propertyID = prop.id();
660 CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
661 CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
664 switch (propertyID) {
665 #if ENABLE(CSS_VARIABLES)
666 case CSSPropertyVariable:
667 result.append(prop.cssText());
670 case CSSPropertyBackgroundPositionX:
671 positionXProp = ∝
673 case CSSPropertyBackgroundPositionY:
674 positionYProp = ∝
676 case CSSPropertyBackgroundRepeatX:
679 case CSSPropertyBackgroundRepeatY:
682 case CSSPropertyBorderTopWidth:
683 case CSSPropertyBorderRightWidth:
684 case CSSPropertyBorderBottomWidth:
685 case CSSPropertyBorderLeftWidth:
686 if (!borderFallbackShorthandProperty)
687 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
688 case CSSPropertyBorderTopStyle:
689 case CSSPropertyBorderRightStyle:
690 case CSSPropertyBorderBottomStyle:
691 case CSSPropertyBorderLeftStyle:
692 if (!borderFallbackShorthandProperty)
693 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
694 case CSSPropertyBorderTopColor:
695 case CSSPropertyBorderRightColor:
696 case CSSPropertyBorderBottomColor:
697 case CSSPropertyBorderLeftColor:
698 if (!borderFallbackShorthandProperty)
699 borderFallbackShorthandProperty = CSSPropertyBorderColor;
701 // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
702 if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
703 value = borderPropertyValue(ReturnNullOnUncommonValues);
705 shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
707 shorthandPropertyID = CSSPropertyBorder;
708 } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
709 shorthandPropertyID = CSSPropertyBorder;
710 if (!shorthandPropertyID)
711 shorthandPropertyID = borderFallbackShorthandProperty;
713 case CSSPropertyWebkitBorderHorizontalSpacing:
714 case CSSPropertyWebkitBorderVerticalSpacing:
715 shorthandPropertyID = CSSPropertyBorderSpacing;
717 case CSSPropertyFontFamily:
718 case CSSPropertyLineHeight:
719 case CSSPropertyFontSize:
720 case CSSPropertyFontStyle:
721 case CSSPropertyFontVariant:
722 case CSSPropertyFontWeight:
723 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
725 case CSSPropertyListStyleType:
726 case CSSPropertyListStylePosition:
727 case CSSPropertyListStyleImage:
728 shorthandPropertyID = CSSPropertyListStyle;
730 case CSSPropertyMarginTop:
731 case CSSPropertyMarginRight:
732 case CSSPropertyMarginBottom:
733 case CSSPropertyMarginLeft:
734 shorthandPropertyID = CSSPropertyMargin;
736 case CSSPropertyOutlineWidth:
737 case CSSPropertyOutlineStyle:
738 case CSSPropertyOutlineColor:
739 shorthandPropertyID = CSSPropertyOutline;
741 case CSSPropertyOverflowX:
742 case CSSPropertyOverflowY:
743 shorthandPropertyID = CSSPropertyOverflow;
745 case CSSPropertyPaddingTop:
746 case CSSPropertyPaddingRight:
747 case CSSPropertyPaddingBottom:
748 case CSSPropertyPaddingLeft:
749 shorthandPropertyID = CSSPropertyPadding;
751 case CSSPropertyWebkitAnimationName:
752 case CSSPropertyWebkitAnimationDuration:
753 case CSSPropertyWebkitAnimationTimingFunction:
754 case CSSPropertyWebkitAnimationDelay:
755 case CSSPropertyWebkitAnimationIterationCount:
756 case CSSPropertyWebkitAnimationDirection:
757 case CSSPropertyWebkitAnimationFillMode:
758 shorthandPropertyID = CSSPropertyWebkitAnimation;
760 #if ENABLE(CSS3_FLEXBOX)
761 case CSSPropertyWebkitFlexDirection:
762 case CSSPropertyWebkitFlexWrap:
763 shorthandPropertyID = CSSPropertyWebkitFlexFlow;
765 case CSSPropertyWebkitFlexBasis:
766 case CSSPropertyWebkitFlexGrow:
767 case CSSPropertyWebkitFlexShrink:
768 shorthandPropertyID = CSSPropertyWebkitFlex;
771 case CSSPropertyWebkitMaskPositionX:
772 case CSSPropertyWebkitMaskPositionY:
773 case CSSPropertyWebkitMaskRepeatX:
774 case CSSPropertyWebkitMaskRepeatY:
775 case CSSPropertyWebkitMaskImage:
776 case CSSPropertyWebkitMaskRepeat:
777 case CSSPropertyWebkitMaskAttachment:
778 case CSSPropertyWebkitMaskPosition:
779 case CSSPropertyWebkitMaskClip:
780 case CSSPropertyWebkitMaskOrigin:
781 shorthandPropertyID = CSSPropertyWebkitMask;
783 case CSSPropertyWebkitTransformOriginX:
784 case CSSPropertyWebkitTransformOriginY:
785 case CSSPropertyWebkitTransformOriginZ:
786 shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
788 case CSSPropertyWebkitTransitionProperty:
789 case CSSPropertyWebkitTransitionDuration:
790 case CSSPropertyWebkitTransitionTimingFunction:
791 case CSSPropertyWebkitTransitionDelay:
792 shorthandPropertyID = CSSPropertyWebkitTransition;
794 #if ENABLE(CSS_EXCLUSIONS)
795 case CSSPropertyWebkitWrapFlow:
796 case CSSPropertyWebkitWrapMargin:
797 case CSSPropertyWebkitWrapPadding:
798 shorthandPropertyID = CSSPropertyWebkitWrap;
805 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
806 if (shorthandPropertyID) {
807 if (shorthandPropertyUsed.get(shortPropertyIndex))
809 if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
810 value = getPropertyValue(shorthandPropertyID);
811 shorthandPropertyAppeared.set(shortPropertyIndex);
814 if (!value.isNull()) {
815 propertyID = shorthandPropertyID;
816 shorthandPropertyUsed.set(shortPropertyIndex);
818 value = prop.value()->cssText();
820 if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
823 result.append(getPropertyName(propertyID));
825 result.append(value);
826 result.append(prop.isImportant() ? " !important" : "");
830 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
831 // It is required because background-position-x/y are non-standard properties and WebKit generated output
832 // would not work in Firefox (<rdar://problem/5143183>)
833 // It would be a better solution if background-position was CSS_PAIR.
834 if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
835 result.append("background-position: ");
836 if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
837 result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
839 result.append(positionXProp->value()->cssText());
841 result.append(positionYProp->value()->cssText());
843 if (positionXProp->isImportant())
844 result.append(" !important");
848 result.append(positionXProp->cssText());
850 result.append(positionYProp->cssText());
853 // FIXME: We need to do the same for background-repeat.
854 if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
855 result.append("background-repeat: ");
856 if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
857 result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
859 result.append(repeatXProp->value()->cssText());
861 result.append(repeatYProp->value()->cssText());
863 if (repeatXProp->isImportant())
864 result.append(" !important");
868 result.append(repeatXProp->cssText());
870 result.append(repeatYProp->cssText());
873 return result.toString();
876 void StylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet* other)
879 unsigned size = other->propertyCount();
880 for (unsigned n = 0; n < size; ++n) {
881 const CSSProperty& toMerge = other->propertyAt(n);
882 CSSProperty* old = findPropertyWithId(toMerge.id());
884 setProperty(toMerge, old);
890 void StylePropertySet::addSubresourceStyleURLs(ListHashSet<KURL>& urls, StyleSheetContents* contextStyleSheet) const
892 unsigned size = propertyCount();
893 for (unsigned i = 0; i < size; ++i)
894 propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
897 bool StylePropertySet::hasFailedOrCanceledSubresources() const
899 unsigned size = propertyCount();
900 for (unsigned i = 0; i < size; ++i) {
901 if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
907 // This is the list of properties we want to copy in the copyBlockProperties() function.
908 // It is the list of CSS properties that apply specially to block-level elements.
909 static const CSSPropertyID blockProperties[] = {
911 CSSPropertyOverflow, // This can be also be applied to replaced elements
912 CSSPropertyWebkitAspectRatio,
913 CSSPropertyWebkitColumnCount,
914 CSSPropertyWebkitColumnGap,
915 CSSPropertyWebkitColumnRuleColor,
916 CSSPropertyWebkitColumnRuleStyle,
917 CSSPropertyWebkitColumnRuleWidth,
918 CSSPropertyWebkitColumnBreakBefore,
919 CSSPropertyWebkitColumnBreakAfter,
920 CSSPropertyWebkitColumnBreakInside,
921 CSSPropertyWebkitColumnWidth,
922 CSSPropertyPageBreakAfter,
923 CSSPropertyPageBreakBefore,
924 CSSPropertyPageBreakInside,
925 #if ENABLE(CSS_REGIONS)
926 CSSPropertyWebkitRegionBreakAfter,
927 CSSPropertyWebkitRegionBreakBefore,
928 CSSPropertyWebkitRegionBreakInside,
930 CSSPropertyTextAlign,
931 CSSPropertyTextIndent,
935 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
937 PassRefPtr<StylePropertySet> StylePropertySet::copyBlockProperties() const
939 return copyPropertiesInSet(blockProperties, numBlockProperties);
942 void StylePropertySet::removeBlockProperties()
944 removePropertiesInSet(blockProperties, numBlockProperties);
947 bool StylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
950 if (m_mutablePropertyVector->isEmpty())
953 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
954 HashSet<CSSPropertyID> toRemove;
955 for (unsigned i = 0; i < length; ++i)
956 toRemove.add(set[i]);
958 Vector<CSSProperty> newProperties;
959 newProperties.reserveInitialCapacity(m_mutablePropertyVector->size());
961 unsigned size = m_mutablePropertyVector->size();
962 for (unsigned n = 0; n < size; ++n) {
963 const CSSProperty& property = m_mutablePropertyVector->at(n);
964 // Not quite sure if the isImportant test is needed but it matches the existing behavior.
965 if (!property.isImportant()) {
966 if (toRemove.contains(property.id()))
969 newProperties.append(property);
972 bool changed = newProperties.size() != m_mutablePropertyVector->size();
973 *m_mutablePropertyVector = newProperties;
977 const CSSProperty* StylePropertySet::findPropertyWithId(CSSPropertyID propertyID) const
979 for (int n = propertyCount() - 1 ; n >= 0; --n) {
980 if (propertyID == propertyAt(n).id())
981 return &propertyAt(n);
986 CSSProperty* StylePropertySet::findPropertyWithId(CSSPropertyID propertyID)
989 for (int n = propertyCount() - 1 ; n >= 0; --n) {
990 if (propertyID == propertyAt(n).id())
991 return &propertyAt(n);
996 bool StylePropertySet::propertyMatches(const CSSProperty* property) const
998 RefPtr<CSSValue> value = getPropertyCSSValue(property->id());
999 return value && value->cssText() == property->value()->cssText();
1002 void StylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
1004 ASSERT(isMutable());
1005 Vector<CSSPropertyID> propertiesToRemove;
1006 unsigned size = m_mutablePropertyVector->size();
1007 for (unsigned i = 0; i < size; ++i) {
1008 const CSSProperty& property = m_mutablePropertyVector->at(i);
1009 if (style->propertyMatches(&property))
1010 propertiesToRemove.append(property.id());
1012 // FIXME: This should use mass removal.
1013 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1014 removeProperty(propertiesToRemove[i]);
1017 void StylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
1019 ASSERT(isMutable());
1020 Vector<CSSPropertyID> propertiesToRemove;
1021 unsigned size = m_mutablePropertyVector->size();
1022 for (unsigned i = 0; i < size; ++i) {
1023 const CSSProperty& property = m_mutablePropertyVector->at(i);
1024 if (style->cssPropertyMatches(&property))
1025 propertiesToRemove.append(property.id());
1027 // FIXME: This should use mass removal.
1028 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1029 removeProperty(propertiesToRemove[i]);
1032 PassRefPtr<StylePropertySet> StylePropertySet::copy() const
1034 return adoptRef(new StylePropertySet(*this));
1037 PassRefPtr<StylePropertySet> StylePropertySet::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1039 Vector<CSSProperty, 256> list;
1040 list.reserveInitialCapacity(length);
1041 for (unsigned i = 0; i < length; ++i) {
1042 RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
1044 list.append(CSSProperty(set[i], value.release(), false));
1046 return StylePropertySet::create(list.data(), list.size());
1049 CSSStyleDeclaration* StylePropertySet::ensureCSSStyleDeclaration() const
1051 if (m_ownsCSSOMWrapper) {
1052 ASSERT(!static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule());
1053 ASSERT(!propertySetCSSOMWrapperMap().get(this)->parentElement());
1054 return propertySetCSSOMWrapperMap().get(this);
1056 m_ownsCSSOMWrapper = true;
1057 PropertySetCSSStyleDeclaration* cssomWrapper = new PropertySetCSSStyleDeclaration(const_cast<StylePropertySet*>(this));
1058 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1059 return cssomWrapper;
1062 CSSStyleDeclaration* StylePropertySet::ensureInlineCSSStyleDeclaration(const StyledElement* parentElement) const
1064 if (m_ownsCSSOMWrapper) {
1065 ASSERT(propertySetCSSOMWrapperMap().get(this)->parentElement() == parentElement);
1066 return propertySetCSSOMWrapperMap().get(this);
1068 m_ownsCSSOMWrapper = true;
1069 PropertySetCSSStyleDeclaration* cssomWrapper = new InlineCSSStyleDeclaration(const_cast<StylePropertySet*>(this), const_cast<StyledElement*>(parentElement));
1070 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1071 return cssomWrapper;
1074 void StylePropertySet::clearParentElement(StyledElement* element)
1076 if (!m_ownsCSSOMWrapper)
1078 ASSERT_UNUSED(element, propertySetCSSOMWrapperMap().get(this)->parentElement() == element);
1079 propertySetCSSOMWrapperMap().get(this)->clearParentElement();
1082 unsigned StylePropertySet::averageSizeInBytes()
1084 // Please update this if the storage scheme changes so that this longer reflects the actual size.
1085 return sizeof(StylePropertySet) + sizeof(CSSProperty) * 2;
1088 void StylePropertySet::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
1090 MemoryClassInfo<StylePropertySet> info(memoryObjectInfo, this, MemoryInstrumentation::CSS);
1092 info.addVectorPtr(m_mutablePropertyVector);
1094 info.addRawBuffer(m_properties, m_arraySize * sizeof(CSSProperty));
1097 // See the function above if you need to update this.
1098 struct SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
1102 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
1105 void StylePropertySet::showStyle()
1107 fprintf(stderr, "%s\n", asText().ascii().data());
1111 inline void StylePropertySet::append(const CSSProperty& property)
1113 ASSERT(isMutable());
1114 m_mutablePropertyVector->append(property);
1117 } // namespace WebCore