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 "core/StylePropertyShorthand.h"
27 #include "core/css/CSSPropertyMetadata.h"
28 #include "core/css/CSSValuePool.h"
29 #include "core/css/StylePropertySerializer.h"
30 #include "core/css/StyleSheetContents.h"
31 #include "core/css/parser/CSSParser.h"
32 #include "core/frame/UseCounter.h"
33 #include "platform/RuntimeEnabledFeatures.h"
34 #include "wtf/text/StringBuilder.h"
37 #include "wtf/text/CString.h"
43 static size_t sizeForImmutableStylePropertySetWithPropertyCount(unsigned count
)
45 return sizeof(ImmutableStylePropertySet
) - sizeof(void*) + sizeof(CSSValue
*) * count
+ sizeof(StylePropertyMetadata
) * count
;
48 PassRefPtrWillBeRawPtr
<ImmutableStylePropertySet
> ImmutableStylePropertySet::create(const CSSProperty
* properties
, unsigned count
, CSSParserMode cssParserMode
)
50 ASSERT(count
<= MaxArraySize
);
52 void* slot
= Heap::allocate
<StylePropertySet
>(sizeForImmutableStylePropertySetWithPropertyCount(count
));
54 void* slot
= WTF::fastMalloc(sizeForImmutableStylePropertySetWithPropertyCount(count
));
55 #endif // ENABLE(OILPAN)
56 return adoptRefWillBeNoop(new (slot
) ImmutableStylePropertySet(properties
, count
, cssParserMode
));
59 PassRefPtrWillBeRawPtr
<ImmutableStylePropertySet
> StylePropertySet::immutableCopyIfNeeded() const
62 return toImmutableStylePropertySet(const_cast<StylePropertySet
*>(this));
63 const MutableStylePropertySet
* mutableThis
= toMutableStylePropertySet(this);
64 return ImmutableStylePropertySet::create(mutableThis
->m_propertyVector
.data(), mutableThis
->m_propertyVector
.size(), cssParserMode());
67 MutableStylePropertySet::MutableStylePropertySet(CSSParserMode cssParserMode
)
68 : StylePropertySet(cssParserMode
)
72 MutableStylePropertySet::MutableStylePropertySet(const CSSProperty
* properties
, unsigned length
)
73 : StylePropertySet(HTMLStandardMode
)
75 m_propertyVector
.reserveInitialCapacity(length
);
76 for (unsigned i
= 0; i
< length
; ++i
)
77 m_propertyVector
.uncheckedAppend(properties
[i
]);
80 ImmutableStylePropertySet::ImmutableStylePropertySet(const CSSProperty
* properties
, unsigned length
, CSSParserMode cssParserMode
)
81 : StylePropertySet(cssParserMode
, length
)
83 StylePropertyMetadata
* metadataArray
= const_cast<StylePropertyMetadata
*>(this->metadataArray());
84 RawPtrWillBeMember
<CSSValue
>* valueArray
= const_cast<RawPtrWillBeMember
<CSSValue
>*>(this->valueArray());
85 for (unsigned i
= 0; i
< m_arraySize
; ++i
) {
86 metadataArray
[i
] = properties
[i
].metadata();
87 valueArray
[i
] = properties
[i
].value();
94 ImmutableStylePropertySet::~ImmutableStylePropertySet()
97 RawPtrWillBeMember
<CSSValue
>* valueArray
= const_cast<RawPtrWillBeMember
<CSSValue
>*>(this->valueArray());
98 for (unsigned i
= 0; i
< m_arraySize
; ++i
) {
99 // Checking for nullptr here is a workaround to prevent crashing. http://crbug.com/449032
101 valueArray
[i
]->deref();
106 int ImmutableStylePropertySet::findPropertyIndex(CSSPropertyID propertyID
) const
108 // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
109 // the compiler converting it to an int multiple times in the loop.
110 uint16_t id
= static_cast<uint16_t>(propertyID
);
111 for (int n
= m_arraySize
- 1 ; n
>= 0; --n
) {
112 if (metadataArray()[n
].m_propertyID
== id
) {
113 // Only enabled properties should be part of the style.
114 ASSERT(CSSPropertyMetadata::isEnabledProperty(propertyID
));
122 DEFINE_TRACE_AFTER_DISPATCH(ImmutableStylePropertySet
)
124 const RawPtrWillBeMember
<CSSValue
>* values
= valueArray();
125 for (unsigned i
= 0; i
< m_arraySize
; i
++)
126 visitor
->trace(values
[i
]);
127 StylePropertySet::traceAfterDispatch(visitor
);
130 MutableStylePropertySet::MutableStylePropertySet(const StylePropertySet
& other
)
131 : StylePropertySet(other
.cssParserMode())
133 if (other
.isMutable()) {
134 m_propertyVector
= toMutableStylePropertySet(other
).m_propertyVector
;
136 m_propertyVector
.reserveInitialCapacity(other
.propertyCount());
137 for (unsigned i
= 0; i
< other
.propertyCount(); ++i
)
138 m_propertyVector
.uncheckedAppend(other
.propertyAt(i
).toCSSProperty());
142 String
StylePropertySet::getPropertyValue(CSSPropertyID propertyID
) const
144 RefPtrWillBeRawPtr
<CSSValue
> value
= getPropertyCSSValue(propertyID
);
146 return value
->cssText();
148 return StylePropertySerializer(*this).getPropertyValue(propertyID
);
151 PassRefPtrWillBeRawPtr
<CSSValue
> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID
) const
153 int foundPropertyIndex
= findPropertyIndex(propertyID
);
154 if (foundPropertyIndex
== -1)
156 return propertyAt(foundPropertyIndex
).value();
159 DEFINE_TRACE(StylePropertySet
)
162 toMutableStylePropertySet(this)->traceAfterDispatch(visitor
);
164 toImmutableStylePropertySet(this)->traceAfterDispatch(visitor
);
168 void StylePropertySet::finalizeGarbageCollectedObject()
171 toMutableStylePropertySet(this)->~MutableStylePropertySet();
173 toImmutableStylePropertySet(this)->~ImmutableStylePropertySet();
177 bool MutableStylePropertySet::removeShorthandProperty(CSSPropertyID propertyID
)
179 StylePropertyShorthand shorthand
= shorthandForProperty(propertyID
);
180 if (!shorthand
.length())
183 return removePropertiesInSet(shorthand
.properties(), shorthand
.length());
186 bool MutableStylePropertySet::removeProperty(CSSPropertyID propertyID
, String
* returnText
)
188 if (removeShorthandProperty(propertyID
)) {
189 // FIXME: Return an equivalent shorthand when possible.
195 int foundPropertyIndex
= findPropertyIndex(propertyID
);
196 if (foundPropertyIndex
== -1) {
203 *returnText
= propertyAt(foundPropertyIndex
).value()->cssText();
205 // A more efficient removal strategy would involve marking entries as empty
206 // and sweeping them when the vector grows too big.
207 m_propertyVector
.remove(foundPropertyIndex
);
212 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID
) const
214 int foundPropertyIndex
= findPropertyIndex(propertyID
);
215 if (foundPropertyIndex
!= -1)
216 return propertyAt(foundPropertyIndex
).isImportant();
218 StylePropertyShorthand shorthand
= shorthandForProperty(propertyID
);
219 if (!shorthand
.length())
222 for (unsigned i
= 0; i
< shorthand
.length(); ++i
) {
223 if (!propertyIsImportant(shorthand
.properties()[i
]))
229 CSSPropertyID
StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID
) const
231 int foundPropertyIndex
= findPropertyIndex(propertyID
);
232 if (foundPropertyIndex
== -1)
233 return CSSPropertyInvalid
;
234 return propertyAt(foundPropertyIndex
).shorthandID();
237 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID
) const
239 int foundPropertyIndex
= findPropertyIndex(propertyID
);
240 if (foundPropertyIndex
== -1)
242 return propertyAt(foundPropertyIndex
).isImplicit();
245 bool MutableStylePropertySet::setProperty(CSSPropertyID unresolvedProperty
, const String
& value
, bool important
, StyleSheetContents
* contextStyleSheet
)
247 // Setting the value to an empty string just removes the property in both IE and Gecko.
248 // Setting it to null seems to produce less consistent results, but we treat it just the same.
250 return removeProperty(resolveCSSPropertyID(unresolvedProperty
));
252 // When replacing an existing property value, this moves the property to the end of the list.
253 // Firefox preserves the position, and MSIE moves the property to the beginning.
254 return CSSParser::parseValue(this, unresolvedProperty
, value
, important
, contextStyleSheet
);
257 void MutableStylePropertySet::setProperty(CSSPropertyID propertyID
, PassRefPtrWillBeRawPtr
<CSSValue
> prpValue
, bool important
)
259 StylePropertyShorthand shorthand
= shorthandForProperty(propertyID
);
260 if (!shorthand
.length()) {
261 setProperty(CSSProperty(propertyID
, prpValue
, important
));
265 removePropertiesInSet(shorthand
.properties(), shorthand
.length());
267 RefPtrWillBeRawPtr
<CSSValue
> value
= prpValue
;
268 for (unsigned i
= 0; i
< shorthand
.length(); ++i
)
269 m_propertyVector
.append(CSSProperty(shorthand
.properties()[i
], value
, important
));
272 bool MutableStylePropertySet::setProperty(const CSSProperty
& property
, CSSProperty
* slot
)
274 if (!removeShorthandProperty(property
.id())) {
275 CSSProperty
* toReplace
= slot
? slot
: findCSSPropertyWithID(property
.id());
276 if (toReplace
&& *toReplace
== property
)
279 *toReplace
= property
;
283 m_propertyVector
.append(property
);
287 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID
, CSSValueID identifier
, bool important
)
289 setProperty(CSSProperty(propertyID
, cssValuePool().createIdentifierValue(identifier
), important
));
293 void MutableStylePropertySet::parseDeclarationList(const String
& styleDeclaration
, StyleSheetContents
* contextStyleSheet
)
295 m_propertyVector
.clear();
297 CSSParserContext
context(cssParserMode(), UseCounter::getFrom(contextStyleSheet
));
298 if (contextStyleSheet
) {
299 context
= contextStyleSheet
->parserContext();
300 context
.setMode(cssParserMode());
303 CSSParser::parseDeclarationList(context
, this, styleDeclaration
);
306 bool MutableStylePropertySet::addParsedProperties(const WillBeHeapVector
<CSSProperty
, 256>& properties
)
308 bool changed
= false;
309 m_propertyVector
.reserveCapacity(m_propertyVector
.size() + properties
.size());
310 for (unsigned i
= 0; i
< properties
.size(); ++i
)
311 changed
|= setProperty(properties
[i
]);
315 bool MutableStylePropertySet::addRespectingCascade(const CSSProperty
& property
)
317 // Only add properties that have no !important counterpart present
318 if (!propertyIsImportant(property
.id()) || property
.isImportant())
319 return setProperty(property
);
323 String
StylePropertySet::asText() const
325 return StylePropertySerializer(*this).asText();
328 void MutableStylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet
* other
)
330 unsigned size
= other
->propertyCount();
331 for (unsigned n
= 0; n
< size
; ++n
) {
332 PropertyReference toMerge
= other
->propertyAt(n
);
333 CSSProperty
* old
= findCSSPropertyWithID(toMerge
.id());
335 setProperty(toMerge
.toCSSProperty(), old
);
337 m_propertyVector
.append(toMerge
.toCSSProperty());
341 bool StylePropertySet::hasFailedOrCanceledSubresources() const
343 unsigned size
= propertyCount();
344 for (unsigned i
= 0; i
< size
; ++i
) {
345 if (propertyAt(i
).value()->hasFailedOrCanceledSubresources())
351 void MutableStylePropertySet::clear()
353 m_propertyVector
.clear();
356 inline bool containsId(const CSSPropertyID
* set
, unsigned length
, CSSPropertyID id
)
358 for (unsigned i
= 0; i
< length
; ++i
) {
365 bool MutableStylePropertySet::removePropertiesInSet(const CSSPropertyID
* set
, unsigned length
)
367 if (m_propertyVector
.isEmpty())
370 CSSProperty
* properties
= m_propertyVector
.data();
371 unsigned oldSize
= m_propertyVector
.size();
372 unsigned newIndex
= 0;
373 for (unsigned oldIndex
= 0; oldIndex
< oldSize
; ++oldIndex
) {
374 const CSSProperty
& property
= properties
[oldIndex
];
375 if (containsId(set
, length
, property
.id()))
377 // Modify m_propertyVector in-place since this method is performance-sensitive.
378 properties
[newIndex
++] = properties
[oldIndex
];
380 if (newIndex
!= oldSize
) {
381 m_propertyVector
.shrink(newIndex
);
387 CSSProperty
* MutableStylePropertySet::findCSSPropertyWithID(CSSPropertyID propertyID
)
389 int foundPropertyIndex
= findPropertyIndex(propertyID
);
390 if (foundPropertyIndex
== -1)
392 return &m_propertyVector
.at(foundPropertyIndex
);
395 bool StylePropertySet::propertyMatches(CSSPropertyID propertyID
, const CSSValue
* propertyValue
) const
397 int foundPropertyIndex
= findPropertyIndex(propertyID
);
398 if (foundPropertyIndex
== -1)
400 return propertyAt(foundPropertyIndex
).value()->equals(*propertyValue
);
403 void MutableStylePropertySet::removeEquivalentProperties(const StylePropertySet
* style
)
405 Vector
<CSSPropertyID
> propertiesToRemove
;
406 unsigned size
= m_propertyVector
.size();
407 for (unsigned i
= 0; i
< size
; ++i
) {
408 PropertyReference property
= propertyAt(i
);
409 if (style
->propertyMatches(property
.id(), property
.value()))
410 propertiesToRemove
.append(property
.id());
412 // FIXME: This should use mass removal.
413 for (unsigned i
= 0; i
< propertiesToRemove
.size(); ++i
)
414 removeProperty(propertiesToRemove
[i
]);
417 void MutableStylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration
* style
)
419 Vector
<CSSPropertyID
> propertiesToRemove
;
420 unsigned size
= m_propertyVector
.size();
421 for (unsigned i
= 0; i
< size
; ++i
) {
422 PropertyReference property
= propertyAt(i
);
423 if (style
->cssPropertyMatches(property
.id(), property
.value()))
424 propertiesToRemove
.append(property
.id());
426 // FIXME: This should use mass removal.
427 for (unsigned i
= 0; i
< propertiesToRemove
.size(); ++i
)
428 removeProperty(propertiesToRemove
[i
]);
431 PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> StylePropertySet::mutableCopy() const
433 return adoptRefWillBeNoop(new MutableStylePropertySet(*this));
436 PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> StylePropertySet::copyPropertiesInSet(const Vector
<CSSPropertyID
>& properties
) const
438 WillBeHeapVector
<CSSProperty
, 256> list
;
439 list
.reserveInitialCapacity(properties
.size());
440 for (unsigned i
= 0; i
< properties
.size(); ++i
) {
441 RefPtrWillBeRawPtr
<CSSValue
> value
= getPropertyCSSValue(properties
[i
]);
443 list
.append(CSSProperty(properties
[i
], value
.release(), false));
445 return MutableStylePropertySet::create(list
.data(), list
.size());
448 CSSStyleDeclaration
* MutableStylePropertySet::ensureCSSStyleDeclaration()
450 // FIXME: get rid of this weirdness of a CSSStyleDeclaration inside of a
451 // style property set.
452 if (m_cssomWrapper
) {
453 ASSERT(!static_cast<CSSStyleDeclaration
*>(m_cssomWrapper
.get())->parentRule());
454 ASSERT(!m_cssomWrapper
->parentElement());
455 return m_cssomWrapper
.get();
457 m_cssomWrapper
= adoptPtrWillBeNoop(new PropertySetCSSStyleDeclaration(*this));
458 return m_cssomWrapper
.get();
461 int MutableStylePropertySet::findPropertyIndex(CSSPropertyID propertyID
) const
463 const CSSProperty
* begin
= m_propertyVector
.data();
464 const CSSProperty
* end
= begin
+ m_propertyVector
.size();
465 // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
466 // the compiler converting it to an int multiple times in the loop.
467 uint16_t id
= static_cast<uint16_t>(propertyID
);
469 auto compare
= [propertyID
, id
](const CSSProperty
& property
) -> bool {
470 if (property
.metadata().m_propertyID
== id
) {
471 // Only enabled properties should be part of the style.
472 ASSERT(CSSPropertyMetadata::isEnabledProperty(propertyID
));
478 const CSSProperty
* it
= std::find_if(begin
, end
, compare
);
480 return (it
== end
) ? -1 : it
- begin
;
483 DEFINE_TRACE_AFTER_DISPATCH(MutableStylePropertySet
)
486 visitor
->trace(m_cssomWrapper
);
487 visitor
->trace(m_propertyVector
);
489 StylePropertySet::traceAfterDispatch(visitor
);
492 unsigned StylePropertySet::averageSizeInBytes()
494 // Please update this if the storage scheme changes so that this longer reflects the actual size.
495 return sizeForImmutableStylePropertySetWithPropertyCount(4);
498 // See the function above if you need to update this.
499 struct SameSizeAsStylePropertySet
: public RefCountedWillBeGarbageCollectedFinalized
<SameSizeAsStylePropertySet
> {
502 static_assert(sizeof(StylePropertySet
) == sizeof(SameSizeAsStylePropertySet
), "StylePropertySet should stay small");
505 void StylePropertySet::showStyle()
507 fprintf(stderr
, "%s\n", asText().ascii().data());
511 PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> MutableStylePropertySet::create(CSSParserMode cssParserMode
)
513 return adoptRefWillBeNoop(new MutableStylePropertySet(cssParserMode
));
516 PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> MutableStylePropertySet::create(const CSSProperty
* properties
, unsigned count
)
518 return adoptRefWillBeNoop(new MutableStylePropertySet(properties
, count
));