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 "CSSPropertyLonghand.h"
27 #include "CSSPropertyNames.h"
28 #include "CSSValueKeywords.h"
29 #include "CSSValueList.h"
30 #include "CSSValuePool.h"
32 #include "PropertySetCSSStyleDeclaration.h"
33 #include <wtf/BitVector.h>
34 #include <wtf/text/StringBuilder.h>
40 typedef HashMap<const StylePropertySet*, OwnPtr<PropertySetCSSStyleDeclaration> > PropertySetCSSOMWrapperMap;
41 static PropertySetCSSOMWrapperMap& propertySetCSSOMWrapperMap()
43 DEFINE_STATIC_LOCAL(PropertySetCSSOMWrapperMap, propertySetCSSOMWrapperMapInstance, ());
44 return propertySetCSSOMWrapperMapInstance;
47 StylePropertySet::StylePropertySet()
48 : m_strictParsing(false)
49 , m_hasCSSOMWrapper(false)
53 StylePropertySet::StylePropertySet(const Vector<CSSProperty>& properties)
54 : m_properties(properties)
55 , m_strictParsing(true)
56 , m_hasCSSOMWrapper(false)
58 m_properties.shrinkToFit();
61 StylePropertySet::StylePropertySet(const CSSProperty* properties, int numProperties, bool useStrictParsing)
62 : m_strictParsing(useStrictParsing)
63 , m_hasCSSOMWrapper(false)
65 // FIXME: This logic belongs in CSSParser.
67 m_properties.reserveInitialCapacity(numProperties);
68 HashMap<int, bool> candidates;
69 for (int i = 0; i < numProperties; ++i) {
70 const CSSProperty& property = properties[i];
71 bool important = property.isImportant();
73 HashMap<int, bool>::iterator it = candidates.find(property.id());
74 if (it != candidates.end()) {
75 if (!important && it->second)
77 removeProperty(property.id());
80 m_properties.append(property);
81 candidates.set(property.id(), important);
85 StylePropertySet::~StylePropertySet()
87 ASSERT(!m_hasCSSOMWrapper || propertySetCSSOMWrapperMap().contains(this));
88 if (m_hasCSSOMWrapper)
89 propertySetCSSOMWrapperMap().remove(this);
92 void StylePropertySet::copyPropertiesFrom(const StylePropertySet& other)
94 m_properties = other.m_properties;
97 String StylePropertySet::getPropertyValue(int propertyID) const
99 RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
101 return value->cssText();
103 // Shorthand and 4-values properties
104 switch (propertyID) {
105 case CSSPropertyBorderSpacing:
106 return borderSpacingValue(borderSpacingLonghand());
107 case CSSPropertyBackgroundPosition:
108 return getLayeredShorthandValue(backgroundPositionLonghand());
109 case CSSPropertyBackgroundRepeat:
110 return getLayeredShorthandValue(backgroundRepeatLonghand());
111 case CSSPropertyBackground:
112 return getLayeredShorthandValue(backgroundLonghand());
113 case CSSPropertyBorder: {
114 const CSSPropertyLonghand properties[3] = { borderWidthLonghand(), borderStyleLonghand(), borderColorLonghand() };
116 for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
117 String value = getCommonValue(properties[i]);
118 if (!value.isNull()) {
126 case CSSPropertyBorderTop:
127 return getShorthandValue(borderTopLonghand());
128 case CSSPropertyBorderRight:
129 return getShorthandValue(borderRightLonghand());
130 case CSSPropertyBorderBottom:
131 return getShorthandValue(borderBottomLonghand());
132 case CSSPropertyBorderLeft:
133 return getShorthandValue(borderLeftLonghand());
134 case CSSPropertyOutline:
135 return getShorthandValue(outlineLonghand());
136 case CSSPropertyBorderColor:
137 return get4Values(borderColorLonghand());
138 case CSSPropertyBorderWidth:
139 return get4Values(borderWidthLonghand());
140 case CSSPropertyBorderStyle:
141 return get4Values(borderStyleLonghand());
142 case CSSPropertyWebkitFlexFlow:
143 return getShorthandValue(webkitFlexFlowLonghand());
144 case CSSPropertyFont:
146 case CSSPropertyMargin:
147 return get4Values(marginLonghand());
148 case CSSPropertyOverflow:
149 return getCommonValue(overflowLonghand());
150 case CSSPropertyPadding:
151 return get4Values(paddingLonghand());
152 case CSSPropertyListStyle:
153 return getShorthandValue(listStyleLonghand());
154 case CSSPropertyWebkitMaskPosition:
155 return getLayeredShorthandValue(webkitMaskPositionLonghand());
156 case CSSPropertyWebkitMaskRepeat:
157 return getLayeredShorthandValue(webkitMaskRepeatLonghand());
158 case CSSPropertyWebkitMask:
159 return getLayeredShorthandValue(webkitMaskLonghand());
160 case CSSPropertyWebkitTransformOrigin:
161 return getShorthandValue(webkitTransformOriginLonghand());
162 case CSSPropertyWebkitTransition:
163 return getLayeredShorthandValue(webkitTransitionLonghand());
164 case CSSPropertyWebkitAnimation:
165 return getLayeredShorthandValue(webkitAnimationLonghand());
166 case CSSPropertyWebkitWrap:
167 return getShorthandValue(webkitWrapLonghand());
169 case CSSPropertyMarker: {
170 RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
172 return value->cssText();
179 String StylePropertySet::borderSpacingValue(const CSSPropertyLonghand& longhand) const
181 RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(longhand.properties()[0]);
182 RefPtr<CSSValue> verticalValue = getPropertyCSSValue(longhand.properties()[1]);
184 if (!horizontalValue)
186 ASSERT(verticalValue); // By <http://www.w3.org/TR/CSS21/tables.html#separated-borders>.
188 String horizontalValueCSSText = horizontalValue->cssText();
189 String verticalValueCSSText = verticalValue->cssText();
190 if (horizontalValueCSSText == verticalValueCSSText)
191 return horizontalValueCSSText;
192 return horizontalValueCSSText + ' ' + verticalValueCSSText;
195 bool StylePropertySet::appendFontLonghandValueIfExplicit(int propertyId, StringBuilder& result) const
197 const CSSProperty* property = findPropertyWithId(propertyId);
199 return false; // All longhands must have at least implicit values if "font" is specified.
200 if (property->isImplicit())
204 switch (propertyId) {
205 case CSSPropertyFontStyle:
207 case CSSPropertyFontFamily:
208 case CSSPropertyFontVariant:
209 case CSSPropertyFontWeight:
212 case CSSPropertyLineHeight:
216 ASSERT_NOT_REACHED();
219 if (prefix && !result.isEmpty())
220 result.append(prefix);
221 result.append(property->value()->cssText());
226 String StylePropertySet::fontValue() const
228 const CSSProperty* fontSizeProperty = findPropertyWithId(CSSPropertyFontSize);
229 if (!fontSizeProperty || fontSizeProperty->isImplicit())
230 return emptyString();
232 StringBuilder result;
234 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result);
235 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result);
236 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result);
237 if (!result.isEmpty())
239 result.append(fontSizeProperty->value()->cssText());
240 success &= appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result);
241 success &= appendFontLonghandValueIfExplicit(CSSPropertyFontFamily, result);
243 // An invalid "font" value has been built (should never happen, as at least implicit values
244 // for mandatory longhands are always found in the style), report empty value instead.
245 ASSERT_NOT_REACHED();
246 return emptyString();
248 return result.toString();
251 String StylePropertySet::get4Values(const CSSPropertyLonghand& longhand) const
253 // Assume the properties are in the usual order top, right, bottom, left.
254 RefPtr<CSSValue> topValue = getPropertyCSSValue(longhand.properties()[0]);
255 RefPtr<CSSValue> rightValue = getPropertyCSSValue(longhand.properties()[1]);
256 RefPtr<CSSValue> bottomValue = getPropertyCSSValue(longhand.properties()[2]);
257 RefPtr<CSSValue> leftValue = getPropertyCSSValue(longhand.properties()[3]);
259 // All 4 properties must be specified.
260 if (!topValue || !rightValue || !bottomValue || !leftValue)
263 bool showLeft = rightValue->cssText() != leftValue->cssText();
264 bool showBottom = (topValue->cssText() != bottomValue->cssText()) || showLeft;
265 bool showRight = (topValue->cssText() != rightValue->cssText()) || showBottom;
267 String res = topValue->cssText();
269 res += " " + rightValue->cssText();
271 res += " " + bottomValue->cssText();
273 res += " " + leftValue->cssText();
278 String StylePropertySet::getLayeredShorthandValue(const CSSPropertyLonghand& longhand) const
282 const unsigned size = longhand.length();
283 // Begin by collecting the properties into an array.
284 Vector< RefPtr<CSSValue> > values(size);
285 size_t numLayers = 0;
287 for (unsigned i = 0; i < size; ++i) {
288 values[i] = getPropertyCSSValue(longhand.properties()[i]);
290 if (values[i]->isValueList()) {
291 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
292 numLayers = max(valueList->length(), numLayers);
294 numLayers = max<size_t>(1U, numLayers);
298 // Now stitch the properties together. Implicit initial values are flagged as such and
299 // can safely be omitted.
300 for (size_t i = 0; i < numLayers; i++) {
302 bool useRepeatXShorthand = false;
303 bool useRepeatYShorthand = false;
304 bool useSingleWordShorthand = false;
305 for (unsigned j = 0; j < size; j++) {
306 RefPtr<CSSValue> value;
308 if (values[j]->isValueList())
309 value = static_cast<CSSValueList*>(values[j].get())->item(i);
313 // Color only belongs in the last layer.
314 if (longhand.properties()[j] == CSSPropertyBackgroundColor) {
315 if (i != numLayers - 1)
317 } else if (i != 0) // Other singletons only belong in the first layer.
322 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
323 // then it was written with only one value. Here we figure out which value that was so we can
324 // report back correctly.
325 if (longhand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(longhand.properties()[j])) {
327 // BUG 49055: make sure the value was not reset in the layer check just above.
328 if (j < size - 1 && longhand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value) {
329 RefPtr<CSSValue> yValue;
330 RefPtr<CSSValue> nextValue = values[j + 1];
331 if (nextValue->isValueList())
332 yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
336 int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
337 int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
339 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
340 useRepeatXShorthand = true;
342 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
343 useRepeatYShorthand = true;
347 useSingleWordShorthand = true;
353 if (value && !value->isImplicitInitialValue()) {
354 if (!layerRes.isNull())
356 if (useRepeatXShorthand) {
357 useRepeatXShorthand = false;
358 layerRes += getValueName(CSSValueRepeatX);
359 } else if (useRepeatYShorthand) {
360 useRepeatYShorthand = false;
361 layerRes += getValueName(CSSValueRepeatY);
362 } else if (useSingleWordShorthand) {
363 useSingleWordShorthand = false;
364 layerRes += value->cssText();
366 layerRes += value->cssText();
370 if (!layerRes.isNull()) {
379 String StylePropertySet::getShorthandValue(const CSSPropertyLonghand& longhand) const
382 for (unsigned i = 0; i < longhand.length(); ++i) {
383 if (!isPropertyImplicit(longhand.properties()[i])) {
384 RefPtr<CSSValue> value = getPropertyCSSValue(longhand.properties()[i]);
385 // FIXME: provide default value if !value
389 res += value->cssText();
396 // only returns a non-null value if all properties have the same, non-null value
397 String StylePropertySet::getCommonValue(const CSSPropertyLonghand& longhand) const
400 for (unsigned i = 0; i < longhand.length(); ++i) {
401 RefPtr<CSSValue> value = getPropertyCSSValue(longhand.properties()[i]);
404 String text = value->cssText();
409 else if (res != text)
415 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(int propertyID) const
417 const CSSProperty* property = findPropertyWithId(propertyID);
418 return property ? property->value() : 0;
421 bool StylePropertySet::removeShorthandProperty(int propertyID)
423 CSSPropertyLonghand longhand = longhandForProperty(propertyID);
424 if (!longhand.length())
426 return removePropertiesInSet(longhand.properties(), longhand.length());
429 bool StylePropertySet::removeProperty(int propertyID, String* returnText)
431 if (removeShorthandProperty(propertyID)) {
432 // FIXME: Return an equivalent shorthand when possible.
438 CSSProperty* foundProperty = findPropertyWithId(propertyID);
439 if (!foundProperty) {
446 *returnText = foundProperty->value()->cssText();
448 // A more efficient removal strategy would involve marking entries as empty
449 // and sweeping them when the vector grows too big.
450 m_properties.remove(foundProperty - m_properties.data());
455 bool StylePropertySet::propertyIsImportant(int propertyID) const
457 const CSSProperty* property = findPropertyWithId(propertyID);
459 return property->isImportant();
461 CSSPropertyLonghand longhands = longhandForProperty(propertyID);
462 if (!longhands.length())
465 for (unsigned i = 0; i < longhands.length(); ++i) {
466 if (!propertyIsImportant(longhands.properties()[i]))
472 int StylePropertySet::getPropertyShorthand(int propertyID) const
474 const CSSProperty* property = findPropertyWithId(propertyID);
475 return property ? property->shorthandID() : 0;
478 bool StylePropertySet::isPropertyImplicit(int propertyID) const
480 const CSSProperty* property = findPropertyWithId(propertyID);
481 return property ? property->isImplicit() : false;
484 bool StylePropertySet::setProperty(int propertyID, const String& value, bool important, CSSStyleSheet* contextStyleSheet)
486 // Setting the value to an empty string just removes the property in both IE and Gecko.
487 // Setting it to null seems to produce less consistent results, but we treat it just the same.
488 if (value.isEmpty()) {
489 removeProperty(propertyID);
493 // When replacing an existing property value, this moves the property to the end of the list.
494 // Firefox preserves the position, and MSIE moves the property to the beginning.
495 return CSSParser::parseValue(this, propertyID, value, important, useStrictParsing(), contextStyleSheet);
498 void StylePropertySet::setProperty(int propertyID, PassRefPtr<CSSValue> prpValue, bool important)
500 CSSPropertyLonghand longhand = longhandForProperty(propertyID);
501 if (!longhand.length()) {
502 setProperty(CSSProperty(propertyID, prpValue, important));
506 removePropertiesInSet(longhand.properties(), longhand.length());
508 RefPtr<CSSValue> value = prpValue;
509 for (unsigned i = 0; i < longhand.length(); ++i)
510 m_properties.append(CSSProperty(longhand.properties()[i], value, important));
513 void StylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
515 if (!removeShorthandProperty(property.id())) {
516 CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
518 *toReplace = property;
522 m_properties.append(property);
525 bool StylePropertySet::setProperty(int propertyID, int identifier, bool important, CSSStyleSheet* contextStyleSheet)
527 RefPtr<CSSPrimitiveValue> value;
528 if (Document* document = contextStyleSheet ? contextStyleSheet->findDocument() : 0)
529 value = document->cssValuePool()->createIdentifierValue(identifier);
531 value = CSSPrimitiveValue::createIdentifier(identifier);
533 setProperty(CSSProperty(propertyID, value.release(), important));
537 void StylePropertySet::parseDeclaration(const String& styleDeclaration, CSSStyleSheet* contextStyleSheet)
539 m_properties.clear();
540 CSSParser parser(useStrictParsing());
541 parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
544 void StylePropertySet::addParsedProperties(const CSSProperty* properties, int numProperties)
546 m_properties.reserveCapacity(numProperties);
547 for (int i = 0; i < numProperties; ++i)
548 addParsedProperty(properties[i]);
551 void StylePropertySet::addParsedProperty(const CSSProperty& property)
553 // Only add properties that have no !important counterpart present
554 if (!propertyIsImportant(property.id()) || property.isImportant()) {
555 removeProperty(property.id());
556 m_properties.append(property);
560 String StylePropertySet::asText() const
562 StringBuilder result;
564 const CSSProperty* positionXProp = 0;
565 const CSSProperty* positionYProp = 0;
566 const CSSProperty* repeatXProp = 0;
567 const CSSProperty* repeatYProp = 0;
569 // FIXME: Stack-allocate the buffer for these BitVectors.
570 BitVector shorthandPropertyUsed;
571 BitVector shorthandPropertyAppeared;
573 unsigned size = m_properties.size();
574 for (unsigned n = 0; n < size; ++n) {
575 const CSSProperty& prop = m_properties[n];
576 int propertyID = prop.id();
577 int shorthandPropertyID = 0;
579 switch (propertyID) {
580 case CSSPropertyBackgroundPositionX:
581 positionXProp = ∝
583 case CSSPropertyBackgroundPositionY:
584 positionYProp = ∝
586 case CSSPropertyBackgroundRepeatX:
589 case CSSPropertyBackgroundRepeatY:
592 case CSSPropertyBorderWidth:
593 case CSSPropertyBorderTopWidth:
594 case CSSPropertyBorderRightWidth:
595 case CSSPropertyBorderBottomWidth:
596 case CSSPropertyBorderLeftWidth:
597 case CSSPropertyBorderStyle:
598 case CSSPropertyBorderTopStyle:
599 case CSSPropertyBorderRightStyle:
600 case CSSPropertyBorderBottomStyle:
601 case CSSPropertyBorderLeftStyle:
602 case CSSPropertyBorderColor:
603 case CSSPropertyBorderTopColor:
604 case CSSPropertyBorderRightColor:
605 case CSSPropertyBorderBottomColor:
606 case CSSPropertyBorderLeftColor:
607 // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
608 shorthandPropertyID = CSSPropertyBorder;
609 if (shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty))
611 for (unsigned i = 0; i < borderAbridgedLonghand().length() && shorthandPropertyID; i++) {
612 const CSSPropertyLonghand& longhand = *(borderAbridgedLonghand().longhandsForInitialization()[i]);
614 bool commonImportance = false;
615 for (size_t j = 0; j < longhand.length(); ++j) {
616 int id = longhand.properties()[j];
617 RefPtr<CSSValue> value = getPropertyCSSValue(id);
618 String currentValue = value ? value->cssText() : String();
619 bool isImportant = propertyIsImportant(id);
620 if (j && (currentValue != commonValue || commonImportance != isImportant)) {
621 shorthandPropertyID = 0;
625 commonValue = currentValue;
626 commonImportance = isImportant;
631 case CSSPropertyWebkitBorderHorizontalSpacing:
632 case CSSPropertyWebkitBorderVerticalSpacing:
633 shorthandPropertyID = CSSPropertyBorderSpacing;
635 case CSSPropertyFontFamily:
636 case CSSPropertyLineHeight:
637 case CSSPropertyFontSize:
638 case CSSPropertyFontStyle:
639 case CSSPropertyFontVariant:
640 case CSSPropertyFontWeight:
641 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
643 case CSSPropertyListStyleType:
644 case CSSPropertyListStylePosition:
645 case CSSPropertyListStyleImage:
646 shorthandPropertyID = CSSPropertyListStyle;
648 case CSSPropertyMarginTop:
649 case CSSPropertyMarginRight:
650 case CSSPropertyMarginBottom:
651 case CSSPropertyMarginLeft:
652 shorthandPropertyID = CSSPropertyMargin;
654 case CSSPropertyOutlineWidth:
655 case CSSPropertyOutlineStyle:
656 case CSSPropertyOutlineColor:
657 shorthandPropertyID = CSSPropertyOutline;
659 case CSSPropertyOverflowX:
660 case CSSPropertyOverflowY:
661 shorthandPropertyID = CSSPropertyOverflow;
663 case CSSPropertyPaddingTop:
664 case CSSPropertyPaddingRight:
665 case CSSPropertyPaddingBottom:
666 case CSSPropertyPaddingLeft:
667 shorthandPropertyID = CSSPropertyPadding;
669 case CSSPropertyWebkitAnimationName:
670 case CSSPropertyWebkitAnimationDuration:
671 case CSSPropertyWebkitAnimationTimingFunction:
672 case CSSPropertyWebkitAnimationDelay:
673 case CSSPropertyWebkitAnimationIterationCount:
674 case CSSPropertyWebkitAnimationDirection:
675 case CSSPropertyWebkitAnimationFillMode:
676 shorthandPropertyID = CSSPropertyWebkitAnimation;
678 case CSSPropertyWebkitFlexDirection:
679 case CSSPropertyWebkitFlexWrap:
680 shorthandPropertyID = CSSPropertyWebkitFlexFlow;
682 case CSSPropertyWebkitMaskPositionX:
683 case CSSPropertyWebkitMaskPositionY:
684 case CSSPropertyWebkitMaskRepeatX:
685 case CSSPropertyWebkitMaskRepeatY:
686 case CSSPropertyWebkitMaskImage:
687 case CSSPropertyWebkitMaskRepeat:
688 case CSSPropertyWebkitMaskAttachment:
689 case CSSPropertyWebkitMaskPosition:
690 case CSSPropertyWebkitMaskClip:
691 case CSSPropertyWebkitMaskOrigin:
692 shorthandPropertyID = CSSPropertyWebkitMask;
694 case CSSPropertyWebkitTransformOriginX:
695 case CSSPropertyWebkitTransformOriginY:
696 case CSSPropertyWebkitTransformOriginZ:
697 shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
699 case CSSPropertyWebkitTransitionProperty:
700 case CSSPropertyWebkitTransitionDuration:
701 case CSSPropertyWebkitTransitionTimingFunction:
702 case CSSPropertyWebkitTransitionDelay:
703 shorthandPropertyID = CSSPropertyWebkitTransition;
705 case CSSPropertyWebkitWrapFlow:
706 case CSSPropertyWebkitWrapMargin:
707 case CSSPropertyWebkitWrapPadding:
708 shorthandPropertyID = CSSPropertyWebkitWrap;
713 unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
714 if (shorthandPropertyID) {
715 if (shorthandPropertyUsed.get(shortPropertyIndex))
717 if (!shorthandPropertyAppeared.get(shortPropertyIndex))
718 value = getPropertyValue(shorthandPropertyID);
719 shorthandPropertyAppeared.ensureSizeAndSet(shortPropertyIndex, numCSSProperties);
722 if (!value.isNull()) {
723 propertyID = shorthandPropertyID;
724 shorthandPropertyUsed.ensureSizeAndSet(shortPropertyIndex, numCSSProperties);
726 value = prop.value()->cssText();
728 if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
731 result.append(getPropertyName(static_cast<CSSPropertyID>(propertyID)));
733 result.append(value);
734 result.append(prop.isImportant() ? " !important" : "");
738 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
739 // It is required because background-position-x/y are non-standard properties and WebKit generated output
740 // would not work in Firefox (<rdar://problem/5143183>)
741 // It would be a better solution if background-position was CSS_PAIR.
742 if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
743 result.append("background-position: ");
744 if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
745 result.append(getLayeredShorthandValue(backgroundPositionLonghand()));
747 result.append(positionXProp->value()->cssText());
749 result.append(positionYProp->value()->cssText());
751 if (positionXProp->isImportant())
752 result.append(" !important");
756 result.append(positionXProp->cssText());
758 result.append(positionYProp->cssText());
761 // FIXME: We need to do the same for background-repeat.
762 if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
763 result.append("background-repeat: ");
764 if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
765 result.append(getLayeredShorthandValue(backgroundRepeatLonghand()));
767 result.append(repeatXProp->value()->cssText());
769 result.append(repeatYProp->value()->cssText());
771 if (repeatXProp->isImportant())
772 result.append(" !important");
776 result.append(repeatXProp->cssText());
778 result.append(repeatYProp->cssText());
781 return result.toString();
784 void StylePropertySet::merge(const StylePropertySet* other, bool argOverridesOnConflict)
786 unsigned size = other->m_properties.size();
787 for (unsigned n = 0; n < size; ++n) {
788 const CSSProperty& toMerge = other->m_properties[n];
789 CSSProperty* old = findPropertyWithId(toMerge.id());
791 if (!argOverridesOnConflict && old->value())
793 setProperty(toMerge, old);
795 m_properties.append(toMerge);
799 void StylePropertySet::addSubresourceStyleURLs(ListHashSet<KURL>& urls, CSSStyleSheet* contextStyleSheet)
801 size_t size = m_properties.size();
802 for (size_t i = 0; i < size; ++i)
803 m_properties[i].value()->addSubresourceStyleURLs(urls, contextStyleSheet);
806 // This is the list of properties we want to copy in the copyBlockProperties() function.
807 // It is the list of CSS properties that apply specially to block-level elements.
808 static const int blockProperties[] = {
810 CSSPropertyOverflow, // This can be also be applied to replaced elements
811 CSSPropertyWebkitAspectRatio,
812 CSSPropertyWebkitColumnCount,
813 CSSPropertyWebkitColumnGap,
814 CSSPropertyWebkitColumnRuleColor,
815 CSSPropertyWebkitColumnRuleStyle,
816 CSSPropertyWebkitColumnRuleWidth,
817 CSSPropertyWebkitColumnBreakBefore,
818 CSSPropertyWebkitColumnBreakAfter,
819 CSSPropertyWebkitColumnBreakInside,
820 CSSPropertyWebkitColumnWidth,
821 CSSPropertyPageBreakAfter,
822 CSSPropertyPageBreakBefore,
823 CSSPropertyPageBreakInside,
824 CSSPropertyWebkitRegionBreakAfter,
825 CSSPropertyWebkitRegionBreakBefore,
826 CSSPropertyWebkitRegionBreakInside,
827 CSSPropertyTextAlign,
828 CSSPropertyTextIndent,
832 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
834 PassRefPtr<StylePropertySet> StylePropertySet::copyBlockProperties() const
836 return copyPropertiesInSet(blockProperties, numBlockProperties);
839 void StylePropertySet::removeBlockProperties()
841 removePropertiesInSet(blockProperties, numBlockProperties);
844 bool StylePropertySet::removePropertiesInSet(const int* set, unsigned length)
846 if (m_properties.isEmpty())
849 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
850 HashSet<int> toRemove;
851 for (unsigned i = 0; i < length; ++i)
852 toRemove.add(set[i]);
854 Vector<CSSProperty, 4> newProperties;
855 newProperties.reserveInitialCapacity(m_properties.size());
857 unsigned size = m_properties.size();
858 for (unsigned n = 0; n < size; ++n) {
859 const CSSProperty& property = m_properties[n];
860 // Not quite sure if the isImportant test is needed but it matches the existing behavior.
861 if (!property.isImportant()) {
862 if (toRemove.contains(property.id()))
865 newProperties.append(property);
868 bool changed = newProperties.size() != m_properties.size();
869 m_properties = newProperties;
873 const CSSProperty* StylePropertySet::findPropertyWithId(int propertyID) const
875 for (int n = m_properties.size() - 1 ; n >= 0; --n) {
876 if (propertyID == m_properties[n].m_id)
877 return &m_properties[n];
882 CSSProperty* StylePropertySet::findPropertyWithId(int propertyID)
884 for (int n = m_properties.size() - 1 ; n >= 0; --n) {
885 if (propertyID == m_properties[n].m_id)
886 return &m_properties[n];
891 bool StylePropertySet::propertyMatches(const CSSProperty* property) const
893 RefPtr<CSSValue> value = getPropertyCSSValue(property->id());
894 return value && value->cssText() == property->value()->cssText();
897 void StylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
899 Vector<int> propertiesToRemove;
900 size_t size = m_properties.size();
901 for (size_t i = 0; i < size; ++i) {
902 const CSSProperty& property = m_properties[i];
903 if (style->propertyMatches(&property))
904 propertiesToRemove.append(property.id());
906 // FIXME: This should use mass removal.
907 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
908 removeProperty(propertiesToRemove[i]);
911 void StylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
913 Vector<int> propertiesToRemove;
914 size_t size = m_properties.size();
915 for (size_t i = 0; i < size; ++i) {
916 const CSSProperty& property = m_properties[i];
917 if (style->cssPropertyMatches(&property))
918 propertiesToRemove.append(property.id());
920 // FIXME: This should use mass removal.
921 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
922 removeProperty(propertiesToRemove[i]);
925 PassRefPtr<StylePropertySet> StylePropertySet::copy() const
927 return adoptRef(new StylePropertySet(m_properties));
930 PassRefPtr<StylePropertySet> StylePropertySet::copyPropertiesInSet(const int* set, unsigned length) const
932 Vector<CSSProperty> list;
933 list.reserveInitialCapacity(length);
934 for (unsigned i = 0; i < length; ++i) {
935 RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
937 list.append(CSSProperty(set[i], value.release(), false));
939 return StylePropertySet::create(list);
942 CSSStyleDeclaration* StylePropertySet::ensureCSSStyleDeclaration() const
944 if (m_hasCSSOMWrapper) {
945 ASSERT(!static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule());
946 ASSERT(!propertySetCSSOMWrapperMap().get(this)->parentElement());
947 return propertySetCSSOMWrapperMap().get(this);
949 m_hasCSSOMWrapper = true;
950 PropertySetCSSStyleDeclaration* cssomWrapper = new PropertySetCSSStyleDeclaration(const_cast<StylePropertySet*>(this));
951 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
955 CSSStyleDeclaration* StylePropertySet::ensureRuleCSSStyleDeclaration(const CSSRule* parentRule) const
957 if (m_hasCSSOMWrapper) {
958 ASSERT(static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule() == parentRule);
959 return propertySetCSSOMWrapperMap().get(this);
961 m_hasCSSOMWrapper = true;
962 PropertySetCSSStyleDeclaration* cssomWrapper = new RuleCSSStyleDeclaration(const_cast<StylePropertySet*>(this), const_cast<CSSRule*>(parentRule));
963 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
967 CSSStyleDeclaration* StylePropertySet::ensureInlineCSSStyleDeclaration(const StyledElement* parentElement) const
969 if (m_hasCSSOMWrapper) {
970 ASSERT(propertySetCSSOMWrapperMap().get(this)->parentElement() == parentElement);
971 return propertySetCSSOMWrapperMap().get(this);
973 m_hasCSSOMWrapper = true;
974 PropertySetCSSStyleDeclaration* cssomWrapper = new InlineCSSStyleDeclaration(const_cast<StylePropertySet*>(this), const_cast<StyledElement*>(parentElement));
975 propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
979 void StylePropertySet::clearParentRule(CSSRule* rule)
981 if (!m_hasCSSOMWrapper)
983 ASSERT_UNUSED(rule, static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule() == rule);
984 propertySetCSSOMWrapperMap().get(this)->clearParentRule();
987 void StylePropertySet::clearParentElement(StyledElement* element)
989 if (!m_hasCSSOMWrapper)
991 ASSERT_UNUSED(element, propertySetCSSOMWrapperMap().get(this)->parentElement() == element);
992 propertySetCSSOMWrapperMap().get(this)->clearParentElement();
995 class SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
996 Vector<CSSProperty, 4> properties;
999 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
1001 } // namespace WebCore