2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
4 * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
5 * Copyright (C) 2013 Intel Corporation. All rights reserved.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 #include "core/css/StylePropertySet.h"
26 #include "RuntimeEnabledFeatures.h"
27 #include "StylePropertyShorthand.h"
28 #include "core/css/parser/BisonCSSParser.h"
29 #include "core/css/CSSValuePool.h"
30 #include "core/css/RuntimeCSSEnabled.h"
31 #include "core/css/StylePropertySerializer.h"
32 #include "core/css/StyleSheetContents.h"
33 #include "core/frame/UseCounter.h"
34 #include "wtf/text/StringBuilder.h"
37 #include "wtf/text/CString.h"
45 static size_t sizeForImmutableStylePropertySetWithPropertyCount(unsigned count)
47 return sizeof(ImmutableStylePropertySet) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
50 PassRefPtr<ImmutableStylePropertySet> ImmutableStylePropertySet::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
52 ASSERT(count <= MaxArraySize);
53 void* slot = WTF::fastMalloc(sizeForImmutableStylePropertySetWithPropertyCount(count));
54 return adoptRef(new (slot) ImmutableStylePropertySet(properties, count, cssParserMode));
57 PassRefPtr<ImmutableStylePropertySet> StylePropertySet::immutableCopyIfNeeded() const
60 return toImmutableStylePropertySet(const_cast<StylePropertySet*>(this));
61 const MutableStylePropertySet* mutableThis = toMutableStylePropertySet(this);
62 return ImmutableStylePropertySet::create(mutableThis->m_propertyVector.data(), mutableThis->m_propertyVector.size(), cssParserMode());
65 MutableStylePropertySet::MutableStylePropertySet(CSSParserMode cssParserMode)
66 : StylePropertySet(cssParserMode)
70 MutableStylePropertySet::MutableStylePropertySet(const CSSProperty* properties, unsigned length)
71 : StylePropertySet(HTMLStandardMode)
73 m_propertyVector.reserveInitialCapacity(length);
74 for (unsigned i = 0; i < length; ++i)
75 m_propertyVector.uncheckedAppend(properties[i]);
78 ImmutableStylePropertySet::ImmutableStylePropertySet(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
79 : StylePropertySet(cssParserMode, length)
81 StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(this->metadataArray());
82 CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
83 for (unsigned i = 0; i < m_arraySize; ++i) {
84 metadataArray[i] = properties[i].metadata();
85 valueArray[i] = properties[i].value();
90 ImmutableStylePropertySet::~ImmutableStylePropertySet()
92 CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
93 for (unsigned i = 0; i < m_arraySize; ++i)
94 valueArray[i]->deref();
97 MutableStylePropertySet::MutableStylePropertySet(const StylePropertySet& other)
98 : StylePropertySet(other.cssParserMode())
100 if (other.isMutable()) {
101 m_propertyVector = toMutableStylePropertySet(other).m_propertyVector;
103 m_propertyVector.reserveInitialCapacity(other.propertyCount());
104 for (unsigned i = 0; i < other.propertyCount(); ++i)
105 m_propertyVector.uncheckedAppend(other.propertyAt(i).toCSSProperty());
109 String StylePropertySet::getPropertyValue(CSSPropertyID propertyID) const
111 RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
113 return value->cssText();
115 return StylePropertySerializer(*this).getPropertyValue(propertyID);
118 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID) const
120 int foundPropertyIndex = findPropertyIndex(propertyID);
121 if (foundPropertyIndex == -1)
123 return propertyAt(foundPropertyIndex).value();
126 bool MutableStylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
128 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
129 if (!shorthand.length())
132 bool ret = removePropertiesInSet(shorthand.properties(), shorthand.length());
134 CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propertyID);
135 if (prefixingVariant == propertyID)
138 StylePropertyShorthand shorthandPrefixingVariant = shorthandForProperty(prefixingVariant);
139 return removePropertiesInSet(shorthandPrefixingVariant.properties(), shorthandPrefixingVariant.length());
142 bool MutableStylePropertySet::removeProperty(CSSPropertyID propertyID, String* returnText)
144 if (removeShorthandProperty(propertyID)) {
145 // FIXME: Return an equivalent shorthand when possible.
151 int foundPropertyIndex = findPropertyIndex(propertyID);
152 if (foundPropertyIndex == -1) {
159 *returnText = propertyAt(foundPropertyIndex).value()->cssText();
161 // A more efficient removal strategy would involve marking entries as empty
162 // and sweeping them when the vector grows too big.
163 m_propertyVector.remove(foundPropertyIndex);
165 removePrefixedOrUnprefixedProperty(propertyID);
170 void MutableStylePropertySet::removePrefixedOrUnprefixedProperty(CSSPropertyID propertyID)
172 int foundPropertyIndex = findPropertyIndex(prefixingVariantForPropertyId(propertyID));
173 if (foundPropertyIndex == -1)
175 m_propertyVector.remove(foundPropertyIndex);
178 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID) const
180 int foundPropertyIndex = findPropertyIndex(propertyID);
181 if (foundPropertyIndex != -1)
182 return propertyAt(foundPropertyIndex).isImportant();
184 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
185 if (!shorthand.length())
188 for (unsigned i = 0; i < shorthand.length(); ++i) {
189 if (!propertyIsImportant(shorthand.properties()[i]))
195 CSSPropertyID StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID) const
197 int foundPropertyIndex = findPropertyIndex(propertyID);
198 if (foundPropertyIndex == -1)
199 return CSSPropertyInvalid;
200 return propertyAt(foundPropertyIndex).shorthandID();
203 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID) const
205 int foundPropertyIndex = findPropertyIndex(propertyID);
206 if (foundPropertyIndex == -1)
208 return propertyAt(foundPropertyIndex).isImplicit();
211 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
213 // Setting the value to an empty string just removes the property in both IE and Gecko.
214 // Setting it to null seems to produce less consistent results, but we treat it just the same.
216 return removeProperty(propertyID);
218 // When replacing an existing property value, this moves the property to the end of the list.
219 // Firefox preserves the position, and MSIE moves the property to the beginning.
220 return BisonCSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
223 void MutableStylePropertySet::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
225 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
226 if (!shorthand.length()) {
227 setProperty(CSSProperty(propertyID, prpValue, important));
231 removePropertiesInSet(shorthand.properties(), shorthand.length());
233 RefPtr<CSSValue> value = prpValue;
234 for (unsigned i = 0; i < shorthand.length(); ++i)
235 m_propertyVector.append(CSSProperty(shorthand.properties()[i], value, important));
238 void MutableStylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
240 if (!removeShorthandProperty(property.id())) {
241 CSSProperty* toReplace = slot ? slot : findCSSPropertyWithID(property.id());
243 *toReplace = property;
244 setPrefixingVariantProperty(property);
248 appendPrefixingVariantProperty(property);
251 unsigned getIndexInShorthandVectorForPrefixingVariant(const CSSProperty& property, CSSPropertyID prefixingVariant)
253 if (!property.isSetFromShorthand())
256 CSSPropertyID prefixedShorthand = prefixingVariantForPropertyId(property.shorthandID());
257 Vector<StylePropertyShorthand, 4> shorthands;
258 getMatchingShorthandsForLonghand(prefixingVariant, &shorthands);
259 return indexOfShorthandForLonghand(prefixedShorthand, shorthands);
262 void MutableStylePropertySet::appendPrefixingVariantProperty(const CSSProperty& property)
264 m_propertyVector.append(property);
265 CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
266 if (prefixingVariant == property.id())
269 m_propertyVector.append(CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit));
272 void MutableStylePropertySet::setPrefixingVariantProperty(const CSSProperty& property)
274 CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
275 CSSProperty* toReplace = findCSSPropertyWithID(prefixingVariant);
276 if (toReplace && prefixingVariant != property.id())
277 *toReplace = CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit);
280 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
282 setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
286 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
288 setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
292 void MutableStylePropertySet::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
294 m_propertyVector.clear();
296 CSSParserContext context(cssParserMode(), UseCounter::getFrom(contextStyleSheet));
297 if (contextStyleSheet) {
298 context = contextStyleSheet->parserContext();
299 context.setMode(cssParserMode());
302 BisonCSSParser parser(context);
303 parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
306 void MutableStylePropertySet::addParsedProperties(const Vector<CSSProperty, 256>& properties)
308 m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
309 for (unsigned i = 0; i < properties.size(); ++i)
310 addParsedProperty(properties[i]);
313 void MutableStylePropertySet::addParsedProperty(const CSSProperty& property)
315 // Only add properties that have no !important counterpart present
316 if (!propertyIsImportant(property.id()) || property.isImportant())
317 setProperty(property);
320 String StylePropertySet::asText() const
322 return StylePropertySerializer(*this).asText();
325 void MutableStylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet* other)
327 unsigned size = other->propertyCount();
328 for (unsigned n = 0; n < size; ++n) {
329 PropertyReference toMerge = other->propertyAt(n);
330 CSSProperty* old = findCSSPropertyWithID(toMerge.id());
332 setProperty(toMerge.toCSSProperty(), old);
334 appendPrefixingVariantProperty(toMerge.toCSSProperty());
338 bool StylePropertySet::hasFailedOrCanceledSubresources() const
340 unsigned size = propertyCount();
341 for (unsigned i = 0; i < size; ++i) {
342 if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
348 // This is the list of properties we want to copy in the copyBlockProperties() function.
349 // It is the list of CSS properties that apply specially to block-level elements.
350 static const CSSPropertyID staticBlockProperties[] = {
352 CSSPropertyOverflow, // This can be also be applied to replaced elements
353 CSSPropertyWebkitAspectRatio,
354 CSSPropertyWebkitColumnCount,
355 CSSPropertyWebkitColumnGap,
356 CSSPropertyWebkitColumnRuleColor,
357 CSSPropertyWebkitColumnRuleStyle,
358 CSSPropertyWebkitColumnRuleWidth,
359 CSSPropertyWebkitColumnBreakBefore,
360 CSSPropertyWebkitColumnBreakAfter,
361 CSSPropertyWebkitColumnBreakInside,
362 CSSPropertyWebkitColumnWidth,
363 CSSPropertyPageBreakAfter,
364 CSSPropertyPageBreakBefore,
365 CSSPropertyPageBreakInside,
366 CSSPropertyTextAlign,
367 CSSPropertyTextAlignLast,
368 CSSPropertyTextIndent,
369 CSSPropertyTextJustify,
373 static const Vector<CSSPropertyID>& blockProperties()
375 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
376 if (properties.isEmpty())
377 RuntimeCSSEnabled::filterEnabledCSSPropertiesIntoVector(staticBlockProperties, WTF_ARRAY_LENGTH(staticBlockProperties), properties);
381 void MutableStylePropertySet::clear()
383 m_propertyVector.clear();
386 PassRefPtr<MutableStylePropertySet> StylePropertySet::copyBlockProperties() const
388 return copyPropertiesInSet(blockProperties());
391 void MutableStylePropertySet::removeBlockProperties()
393 removePropertiesInSet(blockProperties().data(), blockProperties().size());
396 bool MutableStylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
398 if (m_propertyVector.isEmpty())
401 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
402 HashSet<CSSPropertyID> toRemove;
403 for (unsigned i = 0; i < length; ++i)
404 toRemove.add(set[i]);
406 Vector<CSSProperty> newProperties;
407 newProperties.reserveInitialCapacity(m_propertyVector.size());
409 unsigned size = m_propertyVector.size();
410 for (unsigned n = 0; n < size; ++n) {
411 const CSSProperty& property = m_propertyVector.at(n);
412 // Not quite sure if the isImportant test is needed but it matches the existing behavior.
413 if (!property.isImportant()) {
414 if (toRemove.contains(property.id()))
417 newProperties.append(property);
420 bool changed = newProperties.size() != m_propertyVector.size();
421 m_propertyVector = newProperties;
425 int StylePropertySet::findPropertyIndex(CSSPropertyID propertyID) const
427 // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
428 // the compiler converting it to an int multiple times in the loop.
429 uint16_t id = static_cast<uint16_t>(propertyID);
430 for (int n = propertyCount() - 1 ; n >= 0; --n) {
431 if (id == propertyAt(n).propertyMetadata().m_propertyID) {
432 // Only enabled or internal properties should be part of the style.
433 ASSERT(RuntimeCSSEnabled::isCSSPropertyEnabled(propertyID) || isInternalProperty(propertyID));
440 CSSProperty* MutableStylePropertySet::findCSSPropertyWithID(CSSPropertyID propertyID)
442 int foundPropertyIndex = findPropertyIndex(propertyID);
443 if (foundPropertyIndex == -1)
445 return &m_propertyVector.at(foundPropertyIndex);
448 bool StylePropertySet::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
450 int foundPropertyIndex = findPropertyIndex(propertyID);
451 if (foundPropertyIndex == -1)
453 return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
456 void MutableStylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
458 Vector<CSSPropertyID> propertiesToRemove;
459 unsigned size = m_propertyVector.size();
460 for (unsigned i = 0; i < size; ++i) {
461 PropertyReference property = propertyAt(i);
462 if (style->propertyMatches(property.id(), property.value()))
463 propertiesToRemove.append(property.id());
465 // FIXME: This should use mass removal.
466 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
467 removeProperty(propertiesToRemove[i]);
470 void MutableStylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
472 Vector<CSSPropertyID> propertiesToRemove;
473 unsigned size = m_propertyVector.size();
474 for (unsigned i = 0; i < size; ++i) {
475 PropertyReference property = propertyAt(i);
476 if (style->cssPropertyMatches(property.id(), property.value()))
477 propertiesToRemove.append(property.id());
479 // FIXME: This should use mass removal.
480 for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
481 removeProperty(propertiesToRemove[i]);
484 PassRefPtr<MutableStylePropertySet> StylePropertySet::mutableCopy() const
486 return adoptRef(new MutableStylePropertySet(*this));
489 PassRefPtr<MutableStylePropertySet> StylePropertySet::copyPropertiesInSet(const Vector<CSSPropertyID>& properties) const
491 Vector<CSSProperty, 256> list;
492 list.reserveInitialCapacity(properties.size());
493 for (unsigned i = 0; i < properties.size(); ++i) {
494 RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
496 list.append(CSSProperty(properties[i], value.release(), false));
498 return MutableStylePropertySet::create(list.data(), list.size());
501 CSSStyleDeclaration* MutableStylePropertySet::ensureCSSStyleDeclaration()
503 // FIXME: get rid of this weirdness of a CSSStyleDeclaration inside of a
504 // style property set.
505 if (m_cssomWrapper) {
506 ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule());
507 ASSERT(!m_cssomWrapper->parentElement());
508 return m_cssomWrapper.get();
510 m_cssomWrapper = adoptPtr(new PropertySetCSSStyleDeclaration(this));
511 return m_cssomWrapper.get();
514 unsigned StylePropertySet::averageSizeInBytes()
516 // Please update this if the storage scheme changes so that this longer reflects the actual size.
517 return sizeForImmutableStylePropertySetWithPropertyCount(4);
520 // See the function above if you need to update this.
521 struct SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
524 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
527 void StylePropertySet::showStyle()
529 fprintf(stderr, "%s\n", asText().ascii().data());
533 PassRefPtr<MutableStylePropertySet> MutableStylePropertySet::create(CSSParserMode cssParserMode)
535 return adoptRef(new MutableStylePropertySet(cssParserMode));
538 PassRefPtr<MutableStylePropertySet> MutableStylePropertySet::create(const CSSProperty* properties, unsigned count)
540 return adoptRef(new MutableStylePropertySet(properties, count));
543 String StylePropertySet::PropertyReference::cssName() const
545 return getPropertyNameString(id());
548 String StylePropertySet::PropertyReference::cssText() const
550 StringBuilder result;
551 result.append(cssName());
552 result.appendLiteral(": ");
553 result.append(propertyValue()->cssText());
555 result.appendLiteral(" !important");
557 return result.toString();
561 } // namespace WebCore