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 PassRefPtr<StylePropertySet> StylePropertySet::createImmutable(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
65 void* slot = WTF::fastMalloc(sizeForImmutableStylePropertySetWithPropertyCount(count));
66 return adoptRef(new (slot) ImmutableStylePropertySet(properties, count, cssParserMode));
69 PassRefPtr<StylePropertySet> StylePropertySet::immutableCopyIfNeeded() const
72 return const_cast<StylePropertySet*>(this);
73 return createImmutable(mutablePropertyVector().data(), mutablePropertyVector().size(), cssParserMode());
76 MutableStylePropertySet::MutableStylePropertySet(const CSSProperty* properties, unsigned length)
77 : StylePropertySet(CSSStrictMode)
79 m_propertyVector.reserveInitialCapacity(length);
80 for (unsigned i = 0; i < length; ++i)
81 m_propertyVector.uncheckedAppend(properties[i]);
84 ImmutableStylePropertySet::ImmutableStylePropertySet(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
85 : StylePropertySet(cssParserMode, length)
87 StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(immutableMetadataArray());
88 CSSValue** valueArray = const_cast<CSSValue**>(immutableValueArray());
89 for (unsigned i = 0; i < length; ++i) {
90 metadataArray[i] = properties[i].metadata();
91 valueArray[i] = properties[i].value();
96 ImmutableStylePropertySet::~ImmutableStylePropertySet()
98 CSSValue** valueArray = const_cast<CSSValue**>(immutableValueArray());
99 for (unsigned i = 0; i < m_arraySize; ++i)
100 valueArray[i]->deref();
103 MutableStylePropertySet::MutableStylePropertySet(const StylePropertySet& other)
104 : StylePropertySet(other.cssParserMode())
106 if (other.isMutable())
107 m_propertyVector = static_cast<const MutableStylePropertySet&>(other).mutablePropertyVector();
109 m_propertyVector.reserveInitialCapacity(other.propertyCount());
110 for (unsigned i = 0; i < other.propertyCount(); ++i)
111 m_propertyVector.uncheckedAppend(other.propertyAt(i).toCSSProperty());
115 StylePropertySet::~StylePropertySet()
117 ASSERT(!m_ownsCSSOMWrapper || propertySetCSSOMWrapperMap().contains(this));
118 if (m_ownsCSSOMWrapper)
119 propertySetCSSOMWrapperMap().remove(this);
122 String StylePropertySet::getPropertyValue(CSSPropertyID propertyID) const
124 RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
126 return value->cssText();
128 // Shorthand and 4-values properties
129 switch (propertyID) {
130 case CSSPropertyBorderSpacing:
131 return borderSpacingValue(borderSpacingShorthand());
132 case CSSPropertyBackgroundPosition:
133 return getLayeredShorthandValue(backgroundPositionShorthand());
134 case CSSPropertyBackgroundRepeat:
135 return getLayeredShorthandValue(backgroundRepeatShorthand());
136 case CSSPropertyBackground:
137 return getLayeredShorthandValue(backgroundShorthand());
138 case CSSPropertyBorder:
139 return borderPropertyValue(OmitUncommonValues);
140 case CSSPropertyBorderTop:
141 return getShorthandValue(borderTopShorthand());
142 case CSSPropertyBorderRight:
143 return getShorthandValue(borderRightShorthand());
144 case CSSPropertyBorderBottom:
145 return getShorthandValue(borderBottomShorthand());
146 case CSSPropertyBorderLeft:
147 return getShorthandValue(borderLeftShorthand());
148 case CSSPropertyOutline:
149 return getShorthandValue(outlineShorthand());
150 case CSSPropertyBorderColor:
151 return get4Values(borderColorShorthand());
152 case CSSPropertyBorderWidth:
153 return get4Values(borderWidthShorthand());
154 case CSSPropertyBorderStyle:
155 return get4Values(borderStyleShorthand());
156 case CSSPropertyWebkitFlex:
157 return getShorthandValue(webkitFlexShorthand());
158 case CSSPropertyWebkitFlexFlow:
159 return getShorthandValue(webkitFlexFlowShorthand());
160 case CSSPropertyFont:
162 case CSSPropertyMargin:
163 return get4Values(marginShorthand());
164 case CSSPropertyOverflow:
165 return getCommonValue(overflowShorthand());
166 case CSSPropertyPadding:
167 return get4Values(paddingShorthand());
168 case CSSPropertyListStyle:
169 return getShorthandValue(listStyleShorthand());
170 case CSSPropertyWebkitMaskPosition:
171 return getLayeredShorthandValue(webkitMaskPositionShorthand());
172 case CSSPropertyWebkitMaskRepeat:
173 return getLayeredShorthandValue(webkitMaskRepeatShorthand());
174 case CSSPropertyWebkitMask:
175 return getLayeredShorthandValue(webkitMaskShorthand());
176 case CSSPropertyWebkitTransformOrigin:
177 return getShorthandValue(webkitTransformOriginShorthand());
178 case CSSPropertyWebkitTransition:
179 return getLayeredShorthandValue(webkitTransitionShorthand());
180 case CSSPropertyWebkitAnimation:
181 return getLayeredShorthandValue(webkitAnimationShorthand());
182 #if ENABLE(CSS_EXCLUSIONS)
183 case CSSPropertyWebkitWrap:
184 return getShorthandValue(webkitWrapShorthand());
187 case CSSPropertyMarker: {
188 RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
190 return value->cssText();
194 case CSSPropertyBorderRadius:
195 return get4Values(borderRadiusShorthand());
201 String StylePropertySet::borderSpacingValue(const StylePropertyShorthand& shorthand) const
203 RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(shorthand.properties()[0]);
204 RefPtr<CSSValue> verticalValue = getPropertyCSSValue(shorthand.properties()[1]);
206 // While standard border-spacing property does not allow specifying border-spacing-vertical without
207 // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
208 // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
209 if (!horizontalValue || !verticalValue)
212 String horizontalValueCSSText = horizontalValue->cssText();
213 String verticalValueCSSText = verticalValue->cssText();
214 if (horizontalValueCSSText == verticalValueCSSText)
215 return horizontalValueCSSText;
216 return horizontalValueCSSText + ' ' + verticalValueCSSText;
219 bool StylePropertySet::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result) const
221 int foundPropertyIndex = findPropertyIndex(propertyID);
222 if (foundPropertyIndex == -1)
223 return false; // All longhands must have at least implicit values if "font" is specified.
225 if (propertyAt(foundPropertyIndex).isImplicit())
229 switch (propertyID) {
230 case CSSPropertyFontStyle:
232 case CSSPropertyFontFamily:
233 case CSSPropertyFontVariant:
234 case CSSPropertyFontWeight:
237 case CSSPropertyLineHeight:
241 ASSERT_NOT_REACHED();
244 if (prefix && !result.isEmpty())
245 result.append(prefix);
246 result.append(propertyAt(foundPropertyIndex).value()->cssText());
251 String StylePropertySet::fontValue() const
253 int foundPropertyIndex = findPropertyIndex(CSSPropertyFontSize);
254 if (foundPropertyIndex == -1)
255 return emptyString();
257 PropertyReference fontSizeProperty = propertyAt(foundPropertyIndex);
258 if (fontSizeProperty.isImplicit())
259 return emptyString();
261 StringBuilder result;
263 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result);
264 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result);
265 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result);
266 if (!result.isEmpty())
268 result.append(fontSizeProperty.value()->cssText());
269 success &= appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result);
270 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontFamily, result);
272 // An invalid "font" value has been built (should never happen, as at least implicit values
273 // for mandatory longhands are always found in the style), report empty value instead.
274 ASSERT_NOT_REACHED();
275 return emptyString();
277 return result.toString();
280 String StylePropertySet::get4Values(const StylePropertyShorthand& shorthand) const
282 // Assume the properties are in the usual order top, right, bottom, left.
283 int topValueIndex = findPropertyIndex(shorthand.properties()[0]);
284 int rightValueIndex = findPropertyIndex(shorthand.properties()[1]);
285 int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]);
286 int leftValueIndex = findPropertyIndex(shorthand.properties()[3]);
288 if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
291 PropertyReference top = propertyAt(topValueIndex);
292 PropertyReference right = propertyAt(rightValueIndex);
293 PropertyReference bottom = propertyAt(bottomValueIndex);
294 PropertyReference left = propertyAt(leftValueIndex);
296 // All 4 properties must be specified.
297 if (!top.value() || !right.value() || !bottom.value() || !left.value())
299 if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue())
301 if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
304 bool showLeft = right.value()->cssText() != left.value()->cssText();
305 bool showBottom = (top.value()->cssText() != bottom.value()->cssText()) || showLeft;
306 bool showRight = (top.value()->cssText() != right.value()->cssText()) || showBottom;
308 StringBuilder result;
309 result.append(top.value()->cssText());
312 result.append(right.value()->cssText());
316 result.append(bottom.value()->cssText());
320 result.append(left.value()->cssText());
322 return result.toString();
325 String StylePropertySet::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
327 StringBuilder result;
329 const unsigned size = shorthand.length();
330 // Begin by collecting the properties into an array.
331 Vector< RefPtr<CSSValue> > values(size);
332 size_t numLayers = 0;
334 for (unsigned i = 0; i < size; ++i) {
335 values[i] = getPropertyCSSValue(shorthand.properties()[i]);
337 if (values[i]->isBaseValueList()) {
338 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
339 numLayers = max(valueList->length(), numLayers);
341 numLayers = max<size_t>(1U, numLayers);
345 // Now stitch the properties together. Implicit initial values are flagged as such and
346 // can safely be omitted.
347 for (size_t i = 0; i < numLayers; i++) {
348 StringBuilder layerResult;
349 bool useRepeatXShorthand = false;
350 bool useRepeatYShorthand = false;
351 bool useSingleWordShorthand = false;
352 bool foundBackgroundPositionYCSSProperty = false;
353 for (unsigned j = 0; j < size; j++) {
354 RefPtr<CSSValue> value;
356 if (values[j]->isBaseValueList())
357 value = static_cast<CSSValueList*>(values[j].get())->item(i);
361 // Color only belongs in the last layer.
362 if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
363 if (i != numLayers - 1)
365 } else if (i != 0) // Other singletons only belong in the first layer.
370 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
371 // then it was written with only one value. Here we figure out which value that was so we can
372 // report back correctly.
373 if (shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j])) {
375 // BUG 49055: make sure the value was not reset in the layer check just above.
376 if (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value) {
377 RefPtr<CSSValue> yValue;
378 RefPtr<CSSValue> nextValue = values[j + 1];
379 if (nextValue->isValueList())
380 yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
384 int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
385 int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
387 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
388 useRepeatXShorthand = true;
390 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
391 useRepeatYShorthand = true;
395 useSingleWordShorthand = true;
401 if (value && !value->isImplicitInitialValue()) {
402 if (!layerResult.isEmpty())
403 layerResult.append(' ');
404 if (foundBackgroundPositionYCSSProperty && shorthand.properties()[j] == CSSPropertyBackgroundSize)
405 layerResult.appendLiteral("/ ");
406 if (!foundBackgroundPositionYCSSProperty && shorthand.properties()[j] == CSSPropertyBackgroundSize)
409 if (useRepeatXShorthand) {
410 useRepeatXShorthand = false;
411 layerResult.append(getValueName(CSSValueRepeatX));
412 } else if (useRepeatYShorthand) {
413 useRepeatYShorthand = false;
414 layerResult.append(getValueName(CSSValueRepeatY));
415 } else if (useSingleWordShorthand) {
416 useSingleWordShorthand = false;
417 layerResult.append(value->cssText());
419 layerResult.append(value->cssText());
421 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY)
422 foundBackgroundPositionYCSSProperty = true;
426 if (!layerResult.isEmpty()) {
427 if (!result.isEmpty())
428 result.appendLiteral(", ");
429 result.append(layerResult);
432 if (result.isEmpty())
434 return result.toString();
437 String StylePropertySet::getShorthandValue(const StylePropertyShorthand& shorthand) const
439 StringBuilder result;
440 for (unsigned i = 0; i < shorthand.length(); ++i) {
441 if (!isPropertyImplicit(shorthand.properties()[i])) {
442 RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
445 if (value->isInitialValue())
447 if (!result.isEmpty())
449 result.append(value->cssText());
452 if (result.isEmpty())
454 return result.toString();
457 // only returns a non-null value if all properties have the same, non-null value
458 String StylePropertySet::getCommonValue(const StylePropertyShorthand& shorthand) const
461 bool lastPropertyWasImportant = false;
462 for (unsigned i = 0; i < shorthand.length(); ++i) {
463 RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
464 // FIXME: CSSInitialValue::cssText should generate the right value.
467 String text = value->cssText();
472 else if (res != text)
475 bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
476 if (i && lastPropertyWasImportant != currentPropertyIsImportant)
478 lastPropertyWasImportant = currentPropertyIsImportant;
483 String StylePropertySet::borderPropertyValue(CommonValueMode valueMode) const
485 const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
486 StringBuilder result;
487 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
488 String value = getCommonValue(properties[i]);
489 if (value.isNull()) {
490 if (valueMode == ReturnNullOnUncommonValues)
492 ASSERT(valueMode == OmitUncommonValues);
495 if (value == "initial")
497 if (!result.isEmpty())
499 result.append(value);
501 return result.isEmpty() ? String() : result.toString();
504 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID) const
506 int foundPropertyIndex = findPropertyIndex(propertyID);
507 if (foundPropertyIndex == -1)
509 return propertyAt(foundPropertyIndex).value();
512 bool StylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
515 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
516 if (!shorthand.length())
518 return removePropertiesInSet(shorthand.properties(), shorthand.length());
521 bool StylePropertySet::removeProperty(CSSPropertyID propertyID, String* returnText)
524 if (removeShorthandProperty(propertyID)) {
525 // FIXME: Return an equivalent shorthand when possible.
531 int foundPropertyIndex = findPropertyIndex(propertyID);
532 if (foundPropertyIndex == -1) {
539 *returnText = propertyAt(foundPropertyIndex).value()->cssText();
541 // A more efficient removal strategy would involve marking entries as empty
542 // and sweeping them when the vector grows too big.
543 mutablePropertyVector().remove(foundPropertyIndex);
548 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID) const
550 int foundPropertyIndex = findPropertyIndex(propertyID);
551 if (foundPropertyIndex != -1)
552 return propertyAt(foundPropertyIndex).isImportant();
554 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
555 if (!shorthand.length())
558 for (unsigned i = 0; i < shorthand.length(); ++i) {
559 if (!propertyIsImportant(shorthand.properties()[i]))
565 CSSPropertyID StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID) const
567 int foundPropertyIndex = findPropertyIndex(propertyID);
568 if (foundPropertyIndex == -1)
569 return CSSPropertyInvalid;
570 return propertyAt(foundPropertyIndex).shorthandID();
573 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID) const
575 int foundPropertyIndex = findPropertyIndex(propertyID);
576 if (foundPropertyIndex == -1)
578 return propertyAt(foundPropertyIndex).isImplicit();
581 bool StylePropertySet::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
584 // Setting the value to an empty string just removes the property in both IE and Gecko.
585 // Setting it to null seems to produce less consistent results, but we treat it just the same.
586 if (value.isEmpty()) {
587 removeProperty(propertyID);
591 // When replacing an existing property value, this moves the property to the end of the list.
592 // Firefox preserves the position, and MSIE moves the property to the beginning.
593 return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
596 void StylePropertySet::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
599 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
600 if (!shorthand.length()) {
601 setProperty(CSSProperty(propertyID, prpValue, important));
605 removePropertiesInSet(shorthand.properties(), shorthand.length());
607 RefPtr<CSSValue> value = prpValue;
608 for (unsigned i = 0; i < shorthand.length(); ++i)
609 mutablePropertyVector().append(CSSProperty(shorthand.properties()[i], value, important));
612 void StylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
615 if (!removeShorthandProperty(property.id())) {
616 CSSProperty* toReplace = slot ? slot : findMutableCSSPropertyWithID(property.id());
618 *toReplace = property;
622 mutablePropertyVector().append(property);
625 bool StylePropertySet::setProperty(CSSPropertyID propertyID, int identifier, bool important)
628 setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
632 void StylePropertySet::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
636 mutablePropertyVector().clear();
638 CSSParserContext context(cssParserMode());
639 if (contextStyleSheet) {
640 context = contextStyleSheet->parserContext();
641 context.mode = cssParserMode();
643 CSSParser parser(context);
644 parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
647 void StylePropertySet::addParsedProperties(const Vector<CSSProperty>& properties)
650 mutablePropertyVector().reserveCapacity(mutablePropertyVector().size() + properties.size());
651 for (unsigned i = 0; i < properties.size(); ++i)
652 addParsedProperty(properties[i]);
655 void StylePropertySet::addParsedProperty(const CSSProperty& property)
658 // Only add properties that have no !important counterpart present
659 if (!propertyIsImportant(property.id()) || property.isImportant())
660 setProperty(property);
663 String StylePropertySet::asText() const
665 StringBuilder result;
667 int positionXPropertyIndex = -1;
668 int positionYPropertyIndex = -1;
669 int repeatXPropertyIndex = -1;
670 int repeatYPropertyIndex = -1;
672 BitArray<numCSSProperties> shorthandPropertyUsed;
673 BitArray<numCSSProperties> shorthandPropertyAppeared;
675 unsigned size = propertyCount();
676 unsigned numDecls = 0;
677 for (unsigned n = 0; n < size; ++n) {
678 PropertyReference property = propertyAt(n);
679 CSSPropertyID propertyID = property.id();
680 CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
681 CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
684 switch (propertyID) {
685 #if ENABLE(CSS_VARIABLES)
686 case CSSPropertyVariable:
689 result.append(property.cssText());
692 case CSSPropertyBackgroundPositionX:
693 positionXPropertyIndex = n;
695 case CSSPropertyBackgroundPositionY:
696 positionYPropertyIndex = n;
698 case CSSPropertyBackgroundRepeatX:
699 repeatXPropertyIndex = n;
701 case CSSPropertyBackgroundRepeatY:
702 repeatYPropertyIndex = n;
704 case CSSPropertyBorderTopWidth:
705 case CSSPropertyBorderRightWidth:
706 case CSSPropertyBorderBottomWidth:
707 case CSSPropertyBorderLeftWidth:
708 if (!borderFallbackShorthandProperty)
709 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
710 case CSSPropertyBorderTopStyle:
711 case CSSPropertyBorderRightStyle:
712 case CSSPropertyBorderBottomStyle:
713 case CSSPropertyBorderLeftStyle:
714 if (!borderFallbackShorthandProperty)
715 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
716 case CSSPropertyBorderTopColor:
717 case CSSPropertyBorderRightColor:
718 case CSSPropertyBorderBottomColor:
719 case CSSPropertyBorderLeftColor:
720 if (!borderFallbackShorthandProperty)
721 borderFallbackShorthandProperty = CSSPropertyBorderColor;
723 // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
724 if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
725 value = borderPropertyValue(ReturnNullOnUncommonValues);
727 shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
729 shorthandPropertyID = CSSPropertyBorder;
730 } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
731 shorthandPropertyID = CSSPropertyBorder;
732 if (!shorthandPropertyID)
733 shorthandPropertyID = borderFallbackShorthandProperty;
735 case CSSPropertyWebkitBorderHorizontalSpacing:
736 case CSSPropertyWebkitBorderVerticalSpacing:
737 shorthandPropertyID = CSSPropertyBorderSpacing;
739 case CSSPropertyFontFamily:
740 case CSSPropertyLineHeight:
741 case CSSPropertyFontSize:
742 case CSSPropertyFontStyle:
743 case CSSPropertyFontVariant:
744 case CSSPropertyFontWeight:
745 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
747 case CSSPropertyListStyleType:
748 case CSSPropertyListStylePosition:
749 case CSSPropertyListStyleImage:
750 shorthandPropertyID = CSSPropertyListStyle;
752 case CSSPropertyMarginTop:
753 case CSSPropertyMarginRight:
754 case CSSPropertyMarginBottom:
755 case CSSPropertyMarginLeft:
756 shorthandPropertyID = CSSPropertyMargin;
758 case CSSPropertyOutlineWidth:
759 case CSSPropertyOutlineStyle:
760 case CSSPropertyOutlineColor:
761 shorthandPropertyID = CSSPropertyOutline;
763 case CSSPropertyOverflowX:
764 case CSSPropertyOverflowY:
765 shorthandPropertyID = CSSPropertyOverflow;
767 case CSSPropertyPaddingTop:
768 case CSSPropertyPaddingRight:
769 case CSSPropertyPaddingBottom:
770 case CSSPropertyPaddingLeft:
771 shorthandPropertyID = CSSPropertyPadding;
773 case CSSPropertyWebkitAnimationName:
774 case CSSPropertyWebkitAnimationDuration:
775 case CSSPropertyWebkitAnimationTimingFunction:
776 case CSSPropertyWebkitAnimationDelay:
777 case CSSPropertyWebkitAnimationIterationCount:
778 case CSSPropertyWebkitAnimationDirection:
779 case CSSPropertyWebkitAnimationFillMode:
780 shorthandPropertyID = CSSPropertyWebkitAnimation;
782 case CSSPropertyWebkitFlexDirection:
783 case CSSPropertyWebkitFlexWrap:
784 shorthandPropertyID = CSSPropertyWebkitFlexFlow;
786 case CSSPropertyWebkitFlexBasis:
787 case CSSPropertyWebkitFlexGrow:
788 case CSSPropertyWebkitFlexShrink:
789 shorthandPropertyID = CSSPropertyWebkitFlex;
791 case CSSPropertyWebkitMaskPositionX:
792 case CSSPropertyWebkitMaskPositionY:
793 case CSSPropertyWebkitMaskRepeatX:
794 case CSSPropertyWebkitMaskRepeatY:
795 case CSSPropertyWebkitMaskImage:
796 case CSSPropertyWebkitMaskRepeat:
797 case CSSPropertyWebkitMaskAttachment:
798 case CSSPropertyWebkitMaskPosition:
799 case CSSPropertyWebkitMaskClip:
800 case CSSPropertyWebkitMaskOrigin:
801 shorthandPropertyID = CSSPropertyWebkitMask;
803 case CSSPropertyWebkitTransformOriginX:
804 case CSSPropertyWebkitTransformOriginY:
805 case CSSPropertyWebkitTransformOriginZ:
806 shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
808 case CSSPropertyWebkitTransitionProperty:
809 case CSSPropertyWebkitTransitionDuration:
810 case CSSPropertyWebkitTransitionTimingFunction:
811 case CSSPropertyWebkitTransitionDelay:
812 shorthandPropertyID = CSSPropertyWebkitTransition;
814 #if ENABLE(CSS_EXCLUSIONS)
815 case CSSPropertyWebkitWrapFlow:
816 case CSSPropertyWebkitShapeMargin:
817 case CSSPropertyWebkitShapePadding:
818 shorthandPropertyID = CSSPropertyWebkitWrap;
825 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
826 if (shorthandPropertyID) {
827 if (shorthandPropertyUsed.get(shortPropertyIndex))
829 if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
830 value = getPropertyValue(shorthandPropertyID);
831 shorthandPropertyAppeared.set(shortPropertyIndex);
834 if (!value.isNull()) {
835 propertyID = shorthandPropertyID;
836 shorthandPropertyUsed.set(shortPropertyIndex);
838 value = property.value()->cssText();
840 if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
845 result.append(getPropertyName(propertyID));
846 result.appendLiteral(": ");
847 result.append(value);
848 if (property.isImportant())
849 result.appendLiteral(" !important");
853 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
854 // It is required because background-position-x/y are non-standard properties and WebKit generated output
855 // would not work in Firefox (<rdar://problem/5143183>)
856 // It would be a better solution if background-position was CSS_PAIR.
857 if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
858 PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
859 PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
863 result.appendLiteral("background-position: ");
864 if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
865 result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
867 result.append(positionXProperty.value()->cssText());
869 result.append(positionYProperty.value()->cssText());
871 if (positionXProperty.isImportant())
872 result.appendLiteral(" !important");
875 if (positionXPropertyIndex != -1) {
878 result.append(propertyAt(positionXPropertyIndex).cssText());
880 if (positionYPropertyIndex != -1) {
883 result.append(propertyAt(positionYPropertyIndex).cssText());
887 // FIXME: We need to do the same for background-repeat.
888 if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
889 PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
890 PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
894 result.appendLiteral("background-repeat: ");
895 if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
896 result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
898 result.append(repeatXProperty.value()->cssText());
900 result.append(repeatYProperty.value()->cssText());
902 if (repeatXProperty.isImportant())
903 result.appendLiteral(" !important");
906 if (repeatXPropertyIndex != -1) {
909 result.append(propertyAt(repeatXPropertyIndex).cssText());
911 if (repeatYPropertyIndex != -1) {
914 result.append(propertyAt(repeatYPropertyIndex).cssText());
918 ASSERT(!numDecls ^ !result.isEmpty());
919 return result.toString();
922 void StylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet* other)
925 unsigned size = other->propertyCount();
926 for (unsigned n = 0; n < size; ++n) {
927 PropertyReference toMerge = other->propertyAt(n);
928 CSSProperty* old = findMutableCSSPropertyWithID(toMerge.id());
930 setProperty(toMerge.toCSSProperty(), old);
932 mutablePropertyVector().append(toMerge.toCSSProperty());
936 void StylePropertySet::addSubresourceStyleURLs(ListHashSet<KURL>& urls, StyleSheetContents* contextStyleSheet) const
938 unsigned size = propertyCount();
939 for (unsigned i = 0; i < size; ++i)
940 propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
943 bool StylePropertySet::hasFailedOrCanceledSubresources() const
945 unsigned size = propertyCount();
946 for (unsigned i = 0; i < size; ++i) {
947 if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
953 // This is the list of properties we want to copy in the copyBlockProperties() function.
954 // It is the list of CSS properties that apply specially to block-level elements.
955 static const CSSPropertyID blockProperties[] = {
957 CSSPropertyOverflow, // This can be also be applied to replaced elements
958 CSSPropertyWebkitAspectRatio,
959 CSSPropertyWebkitColumnCount,
960 CSSPropertyWebkitColumnGap,
961 CSSPropertyWebkitColumnRuleColor,
962 CSSPropertyWebkitColumnRuleStyle,
963 CSSPropertyWebkitColumnRuleWidth,
964 CSSPropertyWebkitColumnBreakBefore,
965 CSSPropertyWebkitColumnBreakAfter,
966 CSSPropertyWebkitColumnBreakInside,
967 CSSPropertyWebkitColumnWidth,
968 CSSPropertyPageBreakAfter,
969 CSSPropertyPageBreakBefore,
970 CSSPropertyPageBreakInside,
971 #if ENABLE(CSS_REGIONS)
972 CSSPropertyWebkitRegionBreakAfter,
973 CSSPropertyWebkitRegionBreakBefore,
974 CSSPropertyWebkitRegionBreakInside,
976 CSSPropertyTextAlign,
977 #if ENABLE(CSS3_TEXT)
978 CSSPropertyWebkitTextAlignLast,
980 CSSPropertyTextIndent,
984 void StylePropertySet::clear()
987 mutablePropertyVector().clear();
990 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
992 PassRefPtr<StylePropertySet> StylePropertySet::copyBlockProperties() const
994 return copyPropertiesInSet(blockProperties, numBlockProperties);
997 void StylePropertySet::removeBlockProperties()
999 removePropertiesInSet(blockProperties, numBlockProperties);
1002 bool StylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1004 ASSERT(isMutable());
1005 if (mutablePropertyVector().isEmpty())
1008 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1009 HashSet<CSSPropertyID> toRemove;
1010 for (unsigned i = 0; i < length; ++i)
1011 toRemove.add(set[i]);
1013 Vector<CSSProperty> newProperties;
1014 newProperties.reserveInitialCapacity(mutablePropertyVector().size());
1016 unsigned size = mutablePropertyVector().size();
1017 for (unsigned n = 0; n < size; ++n) {
1018 const CSSProperty& property = mutablePropertyVector().at(n);
1019 // Not quite sure if the isImportant test is needed but it matches the existing behavior.
1020 if (!property.isImportant()) {
1021 if (toRemove.contains(property.id()))
1024 newProperties.append(property);
1027 bool changed = newProperties.size() != mutablePropertyVector().size();
1028 mutablePropertyVector() = newProperties;
1032 int StylePropertySet::findPropertyIndex(CSSPropertyID propertyID) const
1034 for (int n = propertyCount() - 1 ; n >= 0; --n) {
1035 if (propertyID == propertyAt(n).id())
1041 CSSProperty* StylePropertySet::findMutableCSSPropertyWithID(CSSPropertyID propertyID)
1043 ASSERT(isMutable());
1044 int foundPropertyIndex = findPropertyIndex(propertyID);
1045 if (foundPropertyIndex == -1)
1047 return &mutablePropertyVector().at(foundPropertyIndex);
1050 bool StylePropertySet::propertyMatches(const PropertyReference& property) const
1052 int foundPropertyIndex = findPropertyIndex(property.id());
1053 if (foundPropertyIndex == -1)
1055 return propertyAt(foundPropertyIndex).value()->cssText() == property.value()->cssText();
1058 void StylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
1060 ASSERT(isMutable());
1061 Vector<CSSPropertyID> propertiesToRemove;
1062 unsigned size = mutablePropertyVector().size();
1063 for (unsigned i = 0; i < size; ++i) {
1064 PropertyReference property = propertyAt(i);
1065 if (style->propertyMatches(property))
1066 propertiesToRemove.append(property.id());
1068 // FIXME: This should use mass removal.
1069 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1070 removeProperty(propertiesToRemove[i]);
1073 void StylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
1075 ASSERT(isMutable());
1076 Vector<CSSPropertyID> propertiesToRemove;
1077 unsigned size = mutablePropertyVector().size();
1078 for (unsigned i = 0; i < size; ++i) {
1079 PropertyReference property = propertyAt(i);
1080 if (style->cssPropertyMatches(property))
1081 propertiesToRemove.append(property.id());
1083 // FIXME: This should use mass removal.
1084 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1085 removeProperty(propertiesToRemove[i]);
1088 PassRefPtr<StylePropertySet> StylePropertySet::copy() const
1090 return adoptRef(new MutableStylePropertySet(*this));
1093 PassRefPtr<StylePropertySet> StylePropertySet::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1095 Vector<CSSProperty, 256> list;
1096 list.reserveInitialCapacity(length);
1097 for (unsigned i = 0; i < length; ++i) {
1098 RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
1100 list.append(CSSProperty(set[i], value.release(), false));
1102 return StylePropertySet::create(list.data(), list.size());
1105 CSSStyleDeclaration* StylePropertySet::ensureCSSStyleDeclaration()
1107 ASSERT(isMutable());
1109 if (m_ownsCSSOMWrapper) {
1110 ASSERT(!static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule());
1111 ASSERT(!propertySetCSSOMWrapperMap().get(this)->parentElement());
1112 return propertySetCSSOMWrapperMap().get(this);
1114 m_ownsCSSOMWrapper = true;
1115 PropertySetCSSStyleDeclaration* cssomWrapper = new PropertySetCSSStyleDeclaration(const_cast<StylePropertySet*>(this));
1116 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1117 return cssomWrapper;
1120 CSSStyleDeclaration* StylePropertySet::ensureInlineCSSStyleDeclaration(const StyledElement* parentElement)
1122 ASSERT(isMutable());
1124 if (m_ownsCSSOMWrapper) {
1125 ASSERT(propertySetCSSOMWrapperMap().get(this)->parentElement() == parentElement);
1126 return propertySetCSSOMWrapperMap().get(this);
1128 m_ownsCSSOMWrapper = true;
1129 PropertySetCSSStyleDeclaration* cssomWrapper = new InlineCSSStyleDeclaration(const_cast<StylePropertySet*>(this), const_cast<StyledElement*>(parentElement));
1130 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1131 return cssomWrapper;
1134 void StylePropertySet::clearParentElement(StyledElement* element)
1136 if (!m_ownsCSSOMWrapper)
1138 ASSERT_UNUSED(element, propertySetCSSOMWrapperMap().get(this)->parentElement() == element);
1139 propertySetCSSOMWrapperMap().get(this)->clearParentElement();
1142 unsigned StylePropertySet::averageSizeInBytes()
1144 // Please update this if the storage scheme changes so that this longer reflects the actual size.
1145 return sizeForImmutableStylePropertySetWithPropertyCount(2);
1148 void StylePropertySet::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
1150 size_t actualSize = m_isMutable ? sizeof(StylePropertySet) : sizeForImmutableStylePropertySetWithPropertyCount(m_arraySize);
1151 MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS, actualSize);
1153 info.addMember(mutablePropertyVector());
1155 for (unsigned i = 0; i < propertyCount(); ++i)
1156 info.addMember(propertyAt(i).value());
1160 // See the function above if you need to update this.
1161 struct SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
1164 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
1167 void StylePropertySet::showStyle()
1169 fprintf(stderr, "%s\n", asText().ascii().data());
1173 PassRefPtr<StylePropertySet> StylePropertySet::create(CSSParserMode cssParserMode)
1175 return adoptRef(new MutableStylePropertySet(cssParserMode));
1178 PassRefPtr<StylePropertySet> StylePropertySet::create(const CSSProperty* properties, unsigned count)
1180 return adoptRef(new MutableStylePropertySet(properties, count));
1183 String StylePropertySet::PropertyReference::cssName() const
1185 #if ENABLE(CSS_VARIABLES)
1186 if (id() == CSSPropertyVariable) {
1187 ASSERT(propertyValue()->isVariableValue());
1188 return "-webkit-var-" + static_cast<const CSSVariableValue*>(propertyValue())->name();
1191 return getPropertyNameString(id());
1194 String StylePropertySet::PropertyReference::cssText() const
1196 StringBuilder result;
1197 result.append(cssName());
1198 result.appendLiteral(": ");
1199 result.append(propertyValue()->cssText());
1201 result.appendLiteral(" !important");
1203 return result.toString();
1207 } // namespace WebCore