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/MemoryInstrumentationVector.h>
35 #include <wtf/text/StringBuilder.h>
37 #if ENABLE(CSS_VARIABLES)
38 #include "CSSVariableValue.h"
43 #include <wtf/ASCIICType.h>
44 #include <wtf/text/CString.h>
51 typedef HashMap<const StylePropertySet*, OwnPtr<PropertySetCSSStyleDeclaration> > PropertySetCSSOMWrapperMap;
52 static PropertySetCSSOMWrapperMap& propertySetCSSOMWrapperMap()
54 DEFINE_STATIC_LOCAL(PropertySetCSSOMWrapperMap, propertySetCSSOMWrapperMapInstance, ());
55 return propertySetCSSOMWrapperMapInstance;
58 static size_t sizeForImmutableStylePropertySetWithPropertyCount(unsigned count)
60 return sizeof(ImmutableStylePropertySet) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
63 static bool isInitialOrInherit(const String& value)
65 DEFINE_STATIC_LOCAL(String, initial, ("initial"));
66 DEFINE_STATIC_LOCAL(String, inherit, ("inherit"));
67 return value.length() == 7 && (value == initial || value == inherit);
70 PassRefPtr<StylePropertySet> StylePropertySet::createImmutable(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
72 void* slot = WTF::fastMalloc(sizeForImmutableStylePropertySetWithPropertyCount(count));
73 return adoptRef(new (slot) ImmutableStylePropertySet(properties, count, cssParserMode));
76 PassRefPtr<StylePropertySet> StylePropertySet::immutableCopyIfNeeded() const
79 return const_cast<StylePropertySet*>(this);
80 return createImmutable(mutablePropertyVector().data(), mutablePropertyVector().size(), cssParserMode());
83 MutableStylePropertySet::MutableStylePropertySet(const CSSProperty* properties, unsigned length)
84 : StylePropertySet(CSSStrictMode)
86 m_propertyVector.reserveInitialCapacity(length);
87 for (unsigned i = 0; i < length; ++i)
88 m_propertyVector.uncheckedAppend(properties[i]);
91 ImmutableStylePropertySet::ImmutableStylePropertySet(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
92 : StylePropertySet(cssParserMode, length)
94 StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(immutableMetadataArray());
95 CSSValue** valueArray = const_cast<CSSValue**>(immutableValueArray());
96 for (unsigned i = 0; i < length; ++i) {
97 metadataArray[i] = properties[i].metadata();
98 valueArray[i] = properties[i].value();
103 ImmutableStylePropertySet::~ImmutableStylePropertySet()
105 CSSValue** valueArray = const_cast<CSSValue**>(immutableValueArray());
106 for (unsigned i = 0; i < m_arraySize; ++i)
107 valueArray[i]->deref();
110 MutableStylePropertySet::MutableStylePropertySet(const StylePropertySet& other)
111 : StylePropertySet(other.cssParserMode())
113 if (other.isMutable())
114 m_propertyVector = static_cast<const MutableStylePropertySet&>(other).mutablePropertyVector();
116 m_propertyVector.reserveInitialCapacity(other.propertyCount());
117 for (unsigned i = 0; i < other.propertyCount(); ++i)
118 m_propertyVector.uncheckedAppend(other.propertyAt(i).toCSSProperty());
122 StylePropertySet::~StylePropertySet()
124 ASSERT(!m_ownsCSSOMWrapper || propertySetCSSOMWrapperMap().contains(this));
125 if (m_ownsCSSOMWrapper)
126 propertySetCSSOMWrapperMap().remove(this);
129 String StylePropertySet::getPropertyValue(CSSPropertyID propertyID) const
131 RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
133 return value->cssText();
135 // Shorthand and 4-values properties
136 switch (propertyID) {
137 case CSSPropertyBorderSpacing:
138 return borderSpacingValue(borderSpacingShorthand());
139 case CSSPropertyBackgroundPosition:
140 return getLayeredShorthandValue(backgroundPositionShorthand());
141 case CSSPropertyBackgroundRepeat:
142 return getLayeredShorthandValue(backgroundRepeatShorthand());
143 case CSSPropertyBackground:
144 return getLayeredShorthandValue(backgroundShorthand());
145 case CSSPropertyBorder:
146 return borderPropertyValue(OmitUncommonValues);
147 case CSSPropertyBorderTop:
148 return getShorthandValue(borderTopShorthand());
149 case CSSPropertyBorderRight:
150 return getShorthandValue(borderRightShorthand());
151 case CSSPropertyBorderBottom:
152 return getShorthandValue(borderBottomShorthand());
153 case CSSPropertyBorderLeft:
154 return getShorthandValue(borderLeftShorthand());
155 case CSSPropertyOutline:
156 return getShorthandValue(outlineShorthand());
157 case CSSPropertyBorderColor:
158 return get4Values(borderColorShorthand());
159 case CSSPropertyBorderWidth:
160 return get4Values(borderWidthShorthand());
161 case CSSPropertyBorderStyle:
162 return get4Values(borderStyleShorthand());
163 case CSSPropertyWebkitFlex:
164 return getShorthandValue(webkitFlexShorthand());
165 case CSSPropertyWebkitFlexFlow:
166 return getShorthandValue(webkitFlexFlowShorthand());
167 case CSSPropertyFont:
169 case CSSPropertyMargin:
170 return get4Values(marginShorthand());
171 case CSSPropertyWebkitMarginCollapse:
172 return getShorthandValue(webkitMarginCollapseShorthand());
173 case CSSPropertyOverflow:
174 return getCommonValue(overflowShorthand());
175 case CSSPropertyPadding:
176 return get4Values(paddingShorthand());
177 case CSSPropertyListStyle:
178 return getShorthandValue(listStyleShorthand());
179 case CSSPropertyWebkitMarquee:
180 return getShorthandValue(webkitMarqueeShorthand());
181 case CSSPropertyWebkitMaskPosition:
182 return getLayeredShorthandValue(webkitMaskPositionShorthand());
183 case CSSPropertyWebkitMaskRepeat:
184 return getLayeredShorthandValue(webkitMaskRepeatShorthand());
185 case CSSPropertyWebkitMask:
186 return getLayeredShorthandValue(webkitMaskShorthand());
187 case CSSPropertyWebkitTextEmphasis:
188 return getShorthandValue(webkitTextEmphasisShorthand());
189 case CSSPropertyWebkitTransformOrigin:
190 return getShorthandValue(webkitTransformOriginShorthand());
191 case CSSPropertyWebkitTransition:
192 return getLayeredShorthandValue(webkitTransitionShorthand());
193 case CSSPropertyWebkitAnimation:
194 return getLayeredShorthandValue(webkitAnimationShorthand());
195 #if ENABLE(CSS_EXCLUSIONS)
196 case CSSPropertyWebkitWrap:
197 return getShorthandValue(webkitWrapShorthand());
200 case CSSPropertyMarker: {
201 RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
203 return value->cssText();
207 case CSSPropertyBorderRadius:
208 return get4Values(borderRadiusShorthand());
214 String StylePropertySet::borderSpacingValue(const StylePropertyShorthand& shorthand) const
216 RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(shorthand.properties()[0]);
217 RefPtr<CSSValue> verticalValue = getPropertyCSSValue(shorthand.properties()[1]);
219 // While standard border-spacing property does not allow specifying border-spacing-vertical without
220 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
221 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
222 if (!horizontalValue || !verticalValue)
225 String horizontalValueCSSText = horizontalValue->cssText();
226 String verticalValueCSSText = verticalValue->cssText();
227 if (horizontalValueCSSText == verticalValueCSSText)
228 return horizontalValueCSSText;
229 return horizontalValueCSSText + ' ' + verticalValueCSSText;
232 void StylePropertySet::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
234 int foundPropertyIndex = findPropertyIndex(propertyID);
235 if (foundPropertyIndex == -1)
236 return; // All longhands must have at least implicit values if "font" is specified.
238 if (propertyAt(foundPropertyIndex).isImplicit()) {
239 commonValue = String();
244 switch (propertyID) {
245 case CSSPropertyFontStyle:
247 case CSSPropertyFontFamily:
248 case CSSPropertyFontVariant:
249 case CSSPropertyFontWeight:
252 case CSSPropertyLineHeight:
256 ASSERT_NOT_REACHED();
259 if (prefix && !result.isEmpty())
260 result.append(prefix);
261 String value = propertyAt(foundPropertyIndex).value()->cssText();
262 result.append(value);
263 if (!commonValue.isNull() && commonValue != value)
264 commonValue = String();
267 String StylePropertySet::fontValue() const
269 int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize);
270 int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily);
271 if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
272 return emptyString();
274 PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex);
275 PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex);
276 if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
277 return emptyString();
279 String commonValue = fontSizeProperty.value()->cssText();
280 StringBuilder result;
281 appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
282 appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result, commonValue);
283 appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
284 if (!result.isEmpty())
286 result.append(fontSizeProperty.value()->cssText());
287 appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
288 if (!result.isEmpty())
290 result.append(fontFamilyProperty.value()->cssText());
291 if (isInitialOrInherit(commonValue))
293 return result.toString();
296 String StylePropertySet::get4Values(const StylePropertyShorthand& shorthand) const
298 // Assume the properties are in the usual order top, right, bottom, left.
299 int topValueIndex = findPropertyIndex(shorthand.properties()[0]);
300 int rightValueIndex = findPropertyIndex(shorthand.properties()[1]);
301 int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]);
302 int leftValueIndex = findPropertyIndex(shorthand.properties()[3]);
304 if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
307 PropertyReference top = propertyAt(topValueIndex);
308 PropertyReference right = propertyAt(rightValueIndex);
309 PropertyReference bottom = propertyAt(bottomValueIndex);
310 PropertyReference left = propertyAt(leftValueIndex);
312 // All 4 properties must be specified.
313 if (!top.value() || !right.value() || !bottom.value() || !left.value())
316 if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
317 return getValueName(CSSValueInherit);
319 if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
320 if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
321 // All components are "initial" and "top" is not implicit.
322 return getValueName(CSSValueInitial);
326 if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
329 bool showLeft = !right.value()->equals(*left.value());
330 bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
331 bool showRight = !top.value()->equals(*right.value()) || showBottom;
333 StringBuilder result;
334 result.append(top.value()->cssText());
337 result.append(right.value()->cssText());
341 result.append(bottom.value()->cssText());
345 result.append(left.value()->cssText());
347 return result.toString();
350 String StylePropertySet::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
352 StringBuilder result;
354 const unsigned size = shorthand.length();
355 // Begin by collecting the properties into an array.
356 Vector< RefPtr<CSSValue> > values(size);
357 size_t numLayers = 0;
359 for (unsigned i = 0; i < size; ++i) {
360 values[i] = getPropertyCSSValue(shorthand.properties()[i]);
362 if (values[i]->isBaseValueList()) {
363 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
364 numLayers = max(valueList->length(), numLayers);
366 numLayers = max<size_t>(1U, numLayers);
371 bool commonValueInitialized = false;
373 // Now stitch the properties together. Implicit initial values are flagged as such and
374 // can safely be omitted.
375 for (size_t i = 0; i < numLayers; i++) {
376 StringBuilder layerResult;
377 bool useRepeatXShorthand = false;
378 bool useRepeatYShorthand = false;
379 bool useSingleWordShorthand = false;
380 bool foundPositionYCSSProperty = false;
381 for (unsigned j = 0; j < size; j++) {
382 RefPtr<CSSValue> value;
384 if (values[j]->isBaseValueList())
385 value = static_cast<CSSValueList*>(values[j].get())->item(i);
389 // Color only belongs in the last layer.
390 if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
391 if (i != numLayers - 1)
393 } else if (i != 0) // Other singletons only belong in the first layer.
398 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
399 // then it was written with only one value. Here we figure out which value that was so we can
400 // report back correctly.
401 if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j]))
402 || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && isPropertyImplicit(shorthand.properties()[j]))) {
404 // BUG 49055: make sure the value was not reset in the layer check just above.
405 if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
406 || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
407 RefPtr<CSSValue> yValue;
408 RefPtr<CSSValue> nextValue = values[j + 1];
409 if (nextValue->isValueList())
410 yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
414 int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
415 int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
417 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
418 useRepeatXShorthand = true;
420 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
421 useRepeatYShorthand = true;
425 useSingleWordShorthand = true;
432 if (value && !value->isImplicitInitialValue()) {
433 if (!layerResult.isEmpty())
434 layerResult.append(' ');
435 if (foundPositionYCSSProperty
436 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
437 layerResult.appendLiteral("/ ");
438 if (!foundPositionYCSSProperty
439 && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
442 if (useRepeatXShorthand) {
443 useRepeatXShorthand = false;
444 layerResult.append(getValueName(CSSValueRepeatX));
445 } else if (useRepeatYShorthand) {
446 useRepeatYShorthand = false;
447 layerResult.append(getValueName(CSSValueRepeatY));
449 if (useSingleWordShorthand)
450 useSingleWordShorthand = false;
451 valueText = value->cssText();
452 layerResult.append(valueText);
455 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
456 || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
457 foundPositionYCSSProperty = true;
459 // background-position is a special case: if only the first offset is specified,
460 // the second one defaults to "center", not the same value.
461 if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
462 commonValue = String();
466 if (!commonValueInitialized) {
467 commonValue = valueText;
468 commonValueInitialized = true;
469 } else if (!commonValue.isNull() && commonValue != valueText)
470 commonValue = String();
473 if (!layerResult.isEmpty()) {
474 if (!result.isEmpty())
475 result.appendLiteral(", ");
476 result.append(layerResult);
480 if (isInitialOrInherit(commonValue))
483 if (result.isEmpty())
485 return result.toString();
488 String StylePropertySet::getShorthandValue(const StylePropertyShorthand& shorthand) const
491 StringBuilder result;
492 for (unsigned i = 0; i < shorthand.length(); ++i) {
493 if (!isPropertyImplicit(shorthand.properties()[i])) {
494 RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
497 String valueText = value->cssText();
499 commonValue = valueText;
500 else if (!commonValue.isNull() && commonValue != valueText)
501 commonValue = String();
502 if (value->isInitialValue())
504 if (!result.isEmpty())
506 result.append(valueText);
508 commonValue = String();
510 if (isInitialOrInherit(commonValue))
512 if (result.isEmpty())
514 return result.toString();
517 // only returns a non-null value if all properties have the same, non-null value
518 String StylePropertySet::getCommonValue(const StylePropertyShorthand& shorthand) const
521 bool lastPropertyWasImportant = false;
522 for (unsigned i = 0; i < shorthand.length(); ++i) {
523 RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
524 // FIXME: CSSInitialValue::cssText should generate the right value.
527 String text = value->cssText();
532 else if (res != text)
535 bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
536 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
538 lastPropertyWasImportant = currentPropertyIsImportant;
543 String StylePropertySet::borderPropertyValue(CommonValueMode valueMode) const
545 const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
547 StringBuilder result;
548 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
549 String value = getCommonValue(properties[i]);
550 if (value.isNull()) {
551 if (valueMode == ReturnNullOnUncommonValues)
553 ASSERT(valueMode == OmitUncommonValues);
558 else if (!commonValue.isNull() && commonValue != value)
559 commonValue = String();
560 if (value == "initial")
562 if (!result.isEmpty())
564 result.append(value);
566 if (isInitialOrInherit(commonValue))
568 return result.isEmpty() ? String() : result.toString();
571 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID) const
573 int foundPropertyIndex = findPropertyIndex(propertyID);
574 if (foundPropertyIndex == -1)
576 return propertyAt(foundPropertyIndex).value();
579 bool StylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
582 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
583 if (!shorthand.length())
585 return removePropertiesInSet(shorthand.properties(), shorthand.length());
588 bool StylePropertySet::removeProperty(CSSPropertyID propertyID, String* returnText)
591 if (removeShorthandProperty(propertyID)) {
592 // FIXME: Return an equivalent shorthand when possible.
598 int foundPropertyIndex = findPropertyIndex(propertyID);
599 if (foundPropertyIndex == -1) {
606 *returnText = propertyAt(foundPropertyIndex).value()->cssText();
608 // A more efficient removal strategy would involve marking entries as empty
609 // and sweeping them when the vector grows too big.
610 mutablePropertyVector().remove(foundPropertyIndex);
615 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID) const
617 int foundPropertyIndex = findPropertyIndex(propertyID);
618 if (foundPropertyIndex != -1)
619 return propertyAt(foundPropertyIndex).isImportant();
621 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
622 if (!shorthand.length())
625 for (unsigned i = 0; i < shorthand.length(); ++i) {
626 if (!propertyIsImportant(shorthand.properties()[i]))
632 CSSPropertyID StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID) const
634 int foundPropertyIndex = findPropertyIndex(propertyID);
635 if (foundPropertyIndex == -1)
636 return CSSPropertyInvalid;
637 return propertyAt(foundPropertyIndex).shorthandID();
640 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID) const
642 int foundPropertyIndex = findPropertyIndex(propertyID);
643 if (foundPropertyIndex == -1)
645 return propertyAt(foundPropertyIndex).isImplicit();
648 bool StylePropertySet::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
651 // Setting the value to an empty string just removes the property in both IE and Gecko.
652 // Setting it to null seems to produce less consistent results, but we treat it just the same.
653 if (value.isEmpty()) {
654 removeProperty(propertyID);
658 // When replacing an existing property value, this moves the property to the end of the list.
659 // Firefox preserves the position, and MSIE moves the property to the beginning.
660 return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
663 void StylePropertySet::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
666 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
667 if (!shorthand.length()) {
668 setProperty(CSSProperty(propertyID, prpValue, important));
672 removePropertiesInSet(shorthand.properties(), shorthand.length());
674 RefPtr<CSSValue> value = prpValue;
675 for (unsigned i = 0; i < shorthand.length(); ++i)
676 mutablePropertyVector().append(CSSProperty(shorthand.properties()[i], value, important));
679 void StylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
682 if (!removeShorthandProperty(property.id())) {
683 CSSProperty* toReplace = slot ? slot : findMutableCSSPropertyWithID(property.id());
685 *toReplace = property;
689 mutablePropertyVector().append(property);
692 bool StylePropertySet::setProperty(CSSPropertyID propertyID, int identifier, bool important)
695 setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
699 void StylePropertySet::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
703 mutablePropertyVector().clear();
705 CSSParserContext context(cssParserMode());
706 if (contextStyleSheet) {
707 context = contextStyleSheet->parserContext();
708 context.mode = cssParserMode();
710 CSSParser parser(context);
711 parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
714 void StylePropertySet::addParsedProperties(const Vector<CSSProperty>& properties)
717 mutablePropertyVector().reserveCapacity(mutablePropertyVector().size() + properties.size());
718 for (unsigned i = 0; i < properties.size(); ++i)
719 addParsedProperty(properties[i]);
722 void StylePropertySet::addParsedProperty(const CSSProperty& property)
725 // Only add properties that have no !important counterpart present
726 if (!propertyIsImportant(property.id()) || property.isImportant())
727 setProperty(property);
730 String StylePropertySet::asText() const
732 StringBuilder result;
734 int positionXPropertyIndex = -1;
735 int positionYPropertyIndex = -1;
736 int repeatXPropertyIndex = -1;
737 int repeatYPropertyIndex = -1;
739 BitArray<numCSSProperties> shorthandPropertyUsed;
740 BitArray<numCSSProperties> shorthandPropertyAppeared;
742 unsigned size = propertyCount();
743 unsigned numDecls = 0;
744 for (unsigned n = 0; n < size; ++n) {
745 PropertyReference property = propertyAt(n);
746 CSSPropertyID propertyID = property.id();
747 CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
748 CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
751 switch (propertyID) {
752 #if ENABLE(CSS_VARIABLES)
753 case CSSPropertyVariable:
756 result.append(property.cssText());
759 case CSSPropertyBackgroundPositionX:
760 positionXPropertyIndex = n;
762 case CSSPropertyBackgroundPositionY:
763 positionYPropertyIndex = n;
765 case CSSPropertyBackgroundRepeatX:
766 repeatXPropertyIndex = n;
768 case CSSPropertyBackgroundRepeatY:
769 repeatYPropertyIndex = n;
771 case CSSPropertyBorderTopWidth:
772 case CSSPropertyBorderRightWidth:
773 case CSSPropertyBorderBottomWidth:
774 case CSSPropertyBorderLeftWidth:
775 if (!borderFallbackShorthandProperty)
776 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
777 case CSSPropertyBorderTopStyle:
778 case CSSPropertyBorderRightStyle:
779 case CSSPropertyBorderBottomStyle:
780 case CSSPropertyBorderLeftStyle:
781 if (!borderFallbackShorthandProperty)
782 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
783 case CSSPropertyBorderTopColor:
784 case CSSPropertyBorderRightColor:
785 case CSSPropertyBorderBottomColor:
786 case CSSPropertyBorderLeftColor:
787 if (!borderFallbackShorthandProperty)
788 borderFallbackShorthandProperty = CSSPropertyBorderColor;
790 // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
791 if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
792 value = borderPropertyValue(ReturnNullOnUncommonValues);
794 shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
796 shorthandPropertyID = CSSPropertyBorder;
797 } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
798 shorthandPropertyID = CSSPropertyBorder;
799 if (!shorthandPropertyID)
800 shorthandPropertyID = borderFallbackShorthandProperty;
802 case CSSPropertyWebkitBorderHorizontalSpacing:
803 case CSSPropertyWebkitBorderVerticalSpacing:
804 shorthandPropertyID = CSSPropertyBorderSpacing;
806 case CSSPropertyFontFamily:
807 case CSSPropertyLineHeight:
808 case CSSPropertyFontSize:
809 case CSSPropertyFontStyle:
810 case CSSPropertyFontVariant:
811 case CSSPropertyFontWeight:
812 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
814 case CSSPropertyListStyleType:
815 case CSSPropertyListStylePosition:
816 case CSSPropertyListStyleImage:
817 shorthandPropertyID = CSSPropertyListStyle;
819 case CSSPropertyMarginTop:
820 case CSSPropertyMarginRight:
821 case CSSPropertyMarginBottom:
822 case CSSPropertyMarginLeft:
823 shorthandPropertyID = CSSPropertyMargin;
825 case CSSPropertyOutlineWidth:
826 case CSSPropertyOutlineStyle:
827 case CSSPropertyOutlineColor:
828 shorthandPropertyID = CSSPropertyOutline;
830 case CSSPropertyOverflowX:
831 case CSSPropertyOverflowY:
832 shorthandPropertyID = CSSPropertyOverflow;
834 case CSSPropertyPaddingTop:
835 case CSSPropertyPaddingRight:
836 case CSSPropertyPaddingBottom:
837 case CSSPropertyPaddingLeft:
838 shorthandPropertyID = CSSPropertyPadding;
840 case CSSPropertyWebkitAnimationName:
841 case CSSPropertyWebkitAnimationDuration:
842 case CSSPropertyWebkitAnimationTimingFunction:
843 case CSSPropertyWebkitAnimationDelay:
844 case CSSPropertyWebkitAnimationIterationCount:
845 case CSSPropertyWebkitAnimationDirection:
846 case CSSPropertyWebkitAnimationFillMode:
847 shorthandPropertyID = CSSPropertyWebkitAnimation;
849 case CSSPropertyWebkitFlexDirection:
850 case CSSPropertyWebkitFlexWrap:
851 shorthandPropertyID = CSSPropertyWebkitFlexFlow;
853 case CSSPropertyWebkitFlexBasis:
854 case CSSPropertyWebkitFlexGrow:
855 case CSSPropertyWebkitFlexShrink:
856 shorthandPropertyID = CSSPropertyWebkitFlex;
858 case CSSPropertyWebkitMaskPositionX:
859 case CSSPropertyWebkitMaskPositionY:
860 case CSSPropertyWebkitMaskRepeatX:
861 case CSSPropertyWebkitMaskRepeatY:
862 case CSSPropertyWebkitMaskImage:
863 case CSSPropertyWebkitMaskRepeat:
864 case CSSPropertyWebkitMaskPosition:
865 case CSSPropertyWebkitMaskClip:
866 case CSSPropertyWebkitMaskOrigin:
867 shorthandPropertyID = CSSPropertyWebkitMask;
869 case CSSPropertyWebkitTransformOriginX:
870 case CSSPropertyWebkitTransformOriginY:
871 case CSSPropertyWebkitTransformOriginZ:
872 shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
874 case CSSPropertyWebkitTransitionProperty:
875 case CSSPropertyWebkitTransitionDuration:
876 case CSSPropertyWebkitTransitionTimingFunction:
877 case CSSPropertyWebkitTransitionDelay:
878 shorthandPropertyID = CSSPropertyWebkitTransition;
880 #if ENABLE(CSS_EXCLUSIONS)
881 case CSSPropertyWebkitWrapFlow:
882 case CSSPropertyWebkitShapeMargin:
883 case CSSPropertyWebkitShapePadding:
884 shorthandPropertyID = CSSPropertyWebkitWrap;
891 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
892 if (shorthandPropertyID) {
893 if (shorthandPropertyUsed.get(shortPropertyIndex))
895 if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
896 value = getPropertyValue(shorthandPropertyID);
897 shorthandPropertyAppeared.set(shortPropertyIndex);
900 if (!value.isNull()) {
901 propertyID = shorthandPropertyID;
902 shorthandPropertyUsed.set(shortPropertyIndex);
904 value = property.value()->cssText();
906 if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
911 result.append(getPropertyName(propertyID));
912 result.appendLiteral(": ");
913 result.append(value);
914 if (property.isImportant())
915 result.appendLiteral(" !important");
919 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
920 // It is required because background-position-x/y are non-standard properties and WebKit generated output
921 // would not work in Firefox (<rdar://problem/5143183>)
922 // It would be a better solution if background-position was CSS_PAIR.
923 if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
924 PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
925 PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
929 result.appendLiteral("background-position: ");
930 if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
931 result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
933 result.append(positionXProperty.value()->cssText());
935 result.append(positionYProperty.value()->cssText());
937 if (positionXProperty.isImportant())
938 result.appendLiteral(" !important");
941 if (positionXPropertyIndex != -1) {
944 result.append(propertyAt(positionXPropertyIndex).cssText());
946 if (positionYPropertyIndex != -1) {
949 result.append(propertyAt(positionYPropertyIndex).cssText());
953 // FIXME: We need to do the same for background-repeat.
954 if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
955 PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
956 PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
960 result.appendLiteral("background-repeat: ");
961 if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
962 result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
964 result.append(repeatXProperty.value()->cssText());
966 result.append(repeatYProperty.value()->cssText());
968 if (repeatXProperty.isImportant())
969 result.appendLiteral(" !important");
972 if (repeatXPropertyIndex != -1) {
975 result.append(propertyAt(repeatXPropertyIndex).cssText());
977 if (repeatYPropertyIndex != -1) {
980 result.append(propertyAt(repeatYPropertyIndex).cssText());
984 ASSERT(!numDecls ^ !result.isEmpty());
985 return result.toString();
988 void StylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet* other)
991 unsigned size = other->propertyCount();
992 for (unsigned n = 0; n < size; ++n) {
993 PropertyReference toMerge = other->propertyAt(n);
994 CSSProperty* old = findMutableCSSPropertyWithID(toMerge.id());
996 setProperty(toMerge.toCSSProperty(), old);
998 mutablePropertyVector().append(toMerge.toCSSProperty());
1002 void StylePropertySet::addSubresourceStyleURLs(ListHashSet<KURL>& urls, StyleSheetContents* contextStyleSheet) const
1004 unsigned size = propertyCount();
1005 for (unsigned i = 0; i < size; ++i)
1006 propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
1009 bool StylePropertySet::hasFailedOrCanceledSubresources() const
1011 unsigned size = propertyCount();
1012 for (unsigned i = 0; i < size; ++i) {
1013 if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
1019 // This is the list of properties we want to copy in the copyBlockProperties() function.
1020 // It is the list of CSS properties that apply specially to block-level elements.
1021 static const CSSPropertyID blockProperties[] = {
1023 CSSPropertyOverflow, // This can be also be applied to replaced elements
1024 CSSPropertyWebkitAspectRatio,
1025 CSSPropertyWebkitColumnCount,
1026 CSSPropertyWebkitColumnGap,
1027 CSSPropertyWebkitColumnRuleColor,
1028 CSSPropertyWebkitColumnRuleStyle,
1029 CSSPropertyWebkitColumnRuleWidth,
1030 CSSPropertyWebkitColumnBreakBefore,
1031 CSSPropertyWebkitColumnBreakAfter,
1032 CSSPropertyWebkitColumnBreakInside,
1033 CSSPropertyWebkitColumnWidth,
1034 CSSPropertyPageBreakAfter,
1035 CSSPropertyPageBreakBefore,
1036 CSSPropertyPageBreakInside,
1037 #if ENABLE(CSS_REGIONS)
1038 CSSPropertyWebkitRegionBreakAfter,
1039 CSSPropertyWebkitRegionBreakBefore,
1040 CSSPropertyWebkitRegionBreakInside,
1042 CSSPropertyTextAlign,
1043 #if ENABLE(CSS3_TEXT)
1044 CSSPropertyWebkitTextAlignLast,
1046 CSSPropertyTextIndent,
1050 void StylePropertySet::clear()
1052 ASSERT(isMutable());
1053 mutablePropertyVector().clear();
1056 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
1058 PassRefPtr<StylePropertySet> StylePropertySet::copyBlockProperties() const
1060 return copyPropertiesInSet(blockProperties, numBlockProperties);
1063 void StylePropertySet::removeBlockProperties()
1065 removePropertiesInSet(blockProperties, numBlockProperties);
1068 bool StylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1070 ASSERT(isMutable());
1071 if (mutablePropertyVector().isEmpty())
1074 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1075 HashSet<CSSPropertyID> toRemove;
1076 for (unsigned i = 0; i < length; ++i)
1077 toRemove.add(set[i]);
1079 Vector<CSSProperty> newProperties;
1080 newProperties.reserveInitialCapacity(mutablePropertyVector().size());
1082 unsigned size = mutablePropertyVector().size();
1083 for (unsigned n = 0; n < size; ++n) {
1084 const CSSProperty& property = mutablePropertyVector().at(n);
1085 // Not quite sure if the isImportant test is needed but it matches the existing behavior.
1086 if (!property.isImportant()) {
1087 if (toRemove.contains(property.id()))
1090 newProperties.append(property);
1093 bool changed = newProperties.size() != mutablePropertyVector().size();
1094 mutablePropertyVector() = newProperties;
1098 int StylePropertySet::findPropertyIndex(CSSPropertyID propertyID) const
1100 for (int n = propertyCount() - 1 ; n >= 0; --n) {
1101 if (propertyID == propertyAt(n).id())
1107 CSSProperty* StylePropertySet::findMutableCSSPropertyWithID(CSSPropertyID propertyID)
1109 ASSERT(isMutable());
1110 int foundPropertyIndex = findPropertyIndex(propertyID);
1111 if (foundPropertyIndex == -1)
1113 return &mutablePropertyVector().at(foundPropertyIndex);
1116 bool StylePropertySet::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
1118 int foundPropertyIndex = findPropertyIndex(propertyID);
1119 if (foundPropertyIndex == -1)
1121 return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
1124 void StylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
1126 ASSERT(isMutable());
1127 Vector<CSSPropertyID> propertiesToRemove;
1128 unsigned size = mutablePropertyVector().size();
1129 for (unsigned i = 0; i < size; ++i) {
1130 PropertyReference property = propertyAt(i);
1131 if (style->propertyMatches(property.id(), property.value()))
1132 propertiesToRemove.append(property.id());
1134 // FIXME: This should use mass removal.
1135 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1136 removeProperty(propertiesToRemove[i]);
1139 void StylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
1141 ASSERT(isMutable());
1142 Vector<CSSPropertyID> propertiesToRemove;
1143 unsigned size = mutablePropertyVector().size();
1144 for (unsigned i = 0; i < size; ++i) {
1145 PropertyReference property = propertyAt(i);
1146 if (style->cssPropertyMatches(property.id(), property.value()))
1147 propertiesToRemove.append(property.id());
1149 // FIXME: This should use mass removal.
1150 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1151 removeProperty(propertiesToRemove[i]);
1154 PassRefPtr<StylePropertySet> StylePropertySet::copy() const
1156 return adoptRef(new MutableStylePropertySet(*this));
1159 PassRefPtr<StylePropertySet> StylePropertySet::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1161 Vector<CSSProperty, 256> list;
1162 list.reserveInitialCapacity(length);
1163 for (unsigned i = 0; i < length; ++i) {
1164 RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
1166 list.append(CSSProperty(set[i], value.release(), false));
1168 return StylePropertySet::create(list.data(), list.size());
1171 PropertySetCSSStyleDeclaration* StylePropertySet::cssStyleDeclaration()
1173 if (!m_ownsCSSOMWrapper)
1175 ASSERT(isMutable());
1176 return propertySetCSSOMWrapperMap().get(this);
1179 CSSStyleDeclaration* StylePropertySet::ensureCSSStyleDeclaration()
1181 ASSERT(isMutable());
1183 if (m_ownsCSSOMWrapper) {
1184 ASSERT(!static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule());
1185 ASSERT(!propertySetCSSOMWrapperMap().get(this)->parentElement());
1186 return propertySetCSSOMWrapperMap().get(this);
1188 m_ownsCSSOMWrapper = true;
1189 PropertySetCSSStyleDeclaration* cssomWrapper = new PropertySetCSSStyleDeclaration(const_cast<StylePropertySet*>(this));
1190 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1191 return cssomWrapper;
1194 CSSStyleDeclaration* StylePropertySet::ensureInlineCSSStyleDeclaration(const StyledElement* parentElement)
1196 ASSERT(isMutable());
1198 if (m_ownsCSSOMWrapper) {
1199 ASSERT(propertySetCSSOMWrapperMap().get(this)->parentElement() == parentElement);
1200 return propertySetCSSOMWrapperMap().get(this);
1202 m_ownsCSSOMWrapper = true;
1203 PropertySetCSSStyleDeclaration* cssomWrapper = new InlineCSSStyleDeclaration(const_cast<StylePropertySet*>(this), const_cast<StyledElement*>(parentElement));
1204 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1205 return cssomWrapper;
1208 unsigned StylePropertySet::averageSizeInBytes()
1210 // Please update this if the storage scheme changes so that this longer reflects the actual size.
1211 return sizeForImmutableStylePropertySetWithPropertyCount(4);
1214 void StylePropertySet::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
1216 size_t actualSize = m_isMutable ? sizeof(StylePropertySet) : sizeForImmutableStylePropertySetWithPropertyCount(m_arraySize);
1217 MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS, actualSize);
1219 info.addMember(mutablePropertyVector(), "mutablePropertyVector()");
1221 for (unsigned i = 0; i < propertyCount(); ++i)
1222 info.addMember(propertyAt(i).value(), "value");
1226 // See the function above if you need to update this.
1227 struct SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
1230 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
1233 void StylePropertySet::showStyle()
1235 fprintf(stderr, "%s\n", asText().ascii().data());
1239 PassRefPtr<StylePropertySet> StylePropertySet::create(CSSParserMode cssParserMode)
1241 return adoptRef(new MutableStylePropertySet(cssParserMode));
1244 PassRefPtr<StylePropertySet> StylePropertySet::create(const CSSProperty* properties, unsigned count)
1246 return adoptRef(new MutableStylePropertySet(properties, count));
1249 String StylePropertySet::PropertyReference::cssName() const
1251 #if ENABLE(CSS_VARIABLES)
1252 if (id() == CSSPropertyVariable) {
1253 ASSERT(propertyValue()->isVariableValue());
1254 if (!propertyValue()->isVariableValue())
1255 return emptyString(); // Should not happen, but if it does, avoid a bad cast.
1256 return "-webkit-var-" + static_cast<const CSSVariableValue*>(propertyValue())->name();
1259 return getPropertyNameString(id());
1262 String StylePropertySet::PropertyReference::cssText() const
1264 StringBuilder result;
1265 result.append(cssName());
1266 result.appendLiteral(": ");
1267 result.append(propertyValue()->cssText());
1269 result.appendLiteral(" !important");
1271 return result.toString();
1275 } // namespace WebCore