Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / css / StylePropertySet.cpp
blobf5f57618f363d5c9d27355fa1c70521503751d65
1 /*
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.
23 #include "config.h"
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"
36 #ifndef NDEBUG
37 #include "wtf/text/CString.h"
38 #include <stdio.h>
39 #endif
41 namespace blink {
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);
51 #if ENABLE(OILPAN)
52 void* slot = Heap::allocate<StylePropertySet>(sizeForImmutableStylePropertySetWithPropertyCount(count));
53 #else
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
61 if (!isMutable())
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();
88 #if !ENABLE(OILPAN)
89 valueArray[i]->ref();
90 #endif
94 ImmutableStylePropertySet::~ImmutableStylePropertySet()
96 #if !ENABLE(OILPAN)
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
100 if (valueArray[i])
101 valueArray[i]->deref();
103 #endif
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));
115 return n;
119 return -1;
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;
135 } else {
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);
145 if (value)
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)
155 return nullptr;
156 return propertyAt(foundPropertyIndex).value();
159 DEFINE_TRACE(StylePropertySet)
161 if (m_isMutable)
162 toMutableStylePropertySet(this)->traceAfterDispatch(visitor);
163 else
164 toImmutableStylePropertySet(this)->traceAfterDispatch(visitor);
167 #if ENABLE(OILPAN)
168 void StylePropertySet::finalizeGarbageCollectedObject()
170 if (m_isMutable)
171 toMutableStylePropertySet(this)->~MutableStylePropertySet();
172 else
173 toImmutableStylePropertySet(this)->~ImmutableStylePropertySet();
175 #endif
177 bool MutableStylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
179 StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
180 if (!shorthand.length())
181 return false;
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.
190 if (returnText)
191 *returnText = "";
192 return true;
195 int foundPropertyIndex = findPropertyIndex(propertyID);
196 if (foundPropertyIndex == -1) {
197 if (returnText)
198 *returnText = "";
199 return false;
202 if (returnText)
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);
209 return true;
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())
220 return false;
222 for (unsigned i = 0; i < shorthand.length(); ++i) {
223 if (!propertyIsImportant(shorthand.properties()[i]))
224 return false;
226 return true;
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)
241 return false;
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.
249 if (value.isEmpty())
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));
262 return;
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)
277 return false;
278 if (toReplace) {
279 *toReplace = property;
280 return true;
283 m_propertyVector.append(property);
284 return true;
287 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
289 setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
290 return true;
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]);
312 return changed;
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);
320 return false;
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());
334 if (old)
335 setProperty(toMerge.toCSSProperty(), old);
336 else
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())
346 return true;
348 return false;
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) {
359 if (set[i] == id)
360 return true;
362 return false;
365 bool MutableStylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
367 if (m_propertyVector.isEmpty())
368 return false;
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()))
376 continue;
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);
382 return true;
384 return false;
387 CSSProperty* MutableStylePropertySet::findCSSPropertyWithID(CSSPropertyID propertyID)
389 int foundPropertyIndex = findPropertyIndex(propertyID);
390 if (foundPropertyIndex == -1)
391 return nullptr;
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)
399 return false;
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]);
442 if (value)
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));
473 return true;
475 return false;
478 const CSSProperty* it = std::find_if(begin, end, compare);
480 return (it == end) ? -1 : it - begin;
483 DEFINE_TRACE_AFTER_DISPATCH(MutableStylePropertySet)
485 #if ENABLE(OILPAN)
486 visitor->trace(m_cssomWrapper);
487 visitor->trace(m_propertyVector);
488 #endif
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> {
500 unsigned bitfield;
502 static_assert(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), "StylePropertySet should stay small");
504 #ifndef NDEBUG
505 void StylePropertySet::showStyle()
507 fprintf(stderr, "%s\n", asText().ascii().data());
509 #endif
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));
521 } // namespace blink