Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / StylePropertySet.cpp
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.
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 #include "config.h"
24 #include "core/css/StylePropertySet.h"
25
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"
35
36 #ifndef NDEBUG
37 #include "wtf/text/CString.h"
38 #include <stdio.h>
39 #endif
40
41 using namespace std;
42
43 namespace WebCore {
44
45 static size_t sizeForImmutableStylePropertySetWithPropertyCount(unsigned count)
46 {
47     return sizeof(ImmutableStylePropertySet) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
48 }
49
50 PassRefPtr<ImmutableStylePropertySet> ImmutableStylePropertySet::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
51 {
52     ASSERT(count <= MaxArraySize);
53     void* slot = WTF::fastMalloc(sizeForImmutableStylePropertySetWithPropertyCount(count));
54     return adoptRef(new (slot) ImmutableStylePropertySet(properties, count, cssParserMode));
55 }
56
57 PassRefPtr<ImmutableStylePropertySet> StylePropertySet::immutableCopyIfNeeded() const
58 {
59     if (!isMutable())
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());
63 }
64
65 MutableStylePropertySet::MutableStylePropertySet(CSSParserMode cssParserMode)
66     : StylePropertySet(cssParserMode)
67 {
68 }
69
70 MutableStylePropertySet::MutableStylePropertySet(const CSSProperty* properties, unsigned length)
71     : StylePropertySet(HTMLStandardMode)
72 {
73     m_propertyVector.reserveInitialCapacity(length);
74     for (unsigned i = 0; i < length; ++i)
75         m_propertyVector.uncheckedAppend(properties[i]);
76 }
77
78 ImmutableStylePropertySet::ImmutableStylePropertySet(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
79     : StylePropertySet(cssParserMode, length)
80 {
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();
86         valueArray[i]->ref();
87     }
88 }
89
90 ImmutableStylePropertySet::~ImmutableStylePropertySet()
91 {
92     CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
93     for (unsigned i = 0; i < m_arraySize; ++i)
94         valueArray[i]->deref();
95 }
96
97 MutableStylePropertySet::MutableStylePropertySet(const StylePropertySet& other)
98     : StylePropertySet(other.cssParserMode())
99 {
100     if (other.isMutable()) {
101         m_propertyVector = toMutableStylePropertySet(other).m_propertyVector;
102     } else {
103         m_propertyVector.reserveInitialCapacity(other.propertyCount());
104         for (unsigned i = 0; i < other.propertyCount(); ++i)
105             m_propertyVector.uncheckedAppend(other.propertyAt(i).toCSSProperty());
106     }
107 }
108
109 String StylePropertySet::getPropertyValue(CSSPropertyID propertyID) const
110 {
111     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
112     if (value)
113         return value->cssText();
114
115     return StylePropertySerializer(*this).getPropertyValue(propertyID);
116 }
117
118 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID) const
119 {
120     int foundPropertyIndex = findPropertyIndex(propertyID);
121     if (foundPropertyIndex == -1)
122         return 0;
123     return propertyAt(foundPropertyIndex).value();
124 }
125
126 bool MutableStylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
127 {
128     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
129     if (!shorthand.length())
130         return false;
131
132     bool ret = removePropertiesInSet(shorthand.properties(), shorthand.length());
133
134     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propertyID);
135     if (prefixingVariant == propertyID)
136         return ret;
137
138     StylePropertyShorthand shorthandPrefixingVariant = shorthandForProperty(prefixingVariant);
139     return removePropertiesInSet(shorthandPrefixingVariant.properties(), shorthandPrefixingVariant.length());
140 }
141
142 bool MutableStylePropertySet::removeProperty(CSSPropertyID propertyID, String* returnText)
143 {
144     if (removeShorthandProperty(propertyID)) {
145         // FIXME: Return an equivalent shorthand when possible.
146         if (returnText)
147             *returnText = "";
148         return true;
149     }
150
151     int foundPropertyIndex = findPropertyIndex(propertyID);
152     if (foundPropertyIndex == -1) {
153         if (returnText)
154             *returnText = "";
155         return false;
156     }
157
158     if (returnText)
159         *returnText = propertyAt(foundPropertyIndex).value()->cssText();
160
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);
164
165     removePrefixedOrUnprefixedProperty(propertyID);
166
167     return true;
168 }
169
170 void MutableStylePropertySet::removePrefixedOrUnprefixedProperty(CSSPropertyID propertyID)
171 {
172     int foundPropertyIndex = findPropertyIndex(prefixingVariantForPropertyId(propertyID));
173     if (foundPropertyIndex == -1)
174         return;
175     m_propertyVector.remove(foundPropertyIndex);
176 }
177
178 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID) const
179 {
180     int foundPropertyIndex = findPropertyIndex(propertyID);
181     if (foundPropertyIndex != -1)
182         return propertyAt(foundPropertyIndex).isImportant();
183
184     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
185     if (!shorthand.length())
186         return false;
187
188     for (unsigned i = 0; i < shorthand.length(); ++i) {
189         if (!propertyIsImportant(shorthand.properties()[i]))
190             return false;
191     }
192     return true;
193 }
194
195 CSSPropertyID StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID) const
196 {
197     int foundPropertyIndex = findPropertyIndex(propertyID);
198     if (foundPropertyIndex == -1)
199         return CSSPropertyInvalid;
200     return propertyAt(foundPropertyIndex).shorthandID();
201 }
202
203 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID) const
204 {
205     int foundPropertyIndex = findPropertyIndex(propertyID);
206     if (foundPropertyIndex == -1)
207         return false;
208     return propertyAt(foundPropertyIndex).isImplicit();
209 }
210
211 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
212 {
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.
215     if (value.isEmpty())
216         return removeProperty(propertyID);
217
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);
221 }
222
223 void MutableStylePropertySet::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
224 {
225     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
226     if (!shorthand.length()) {
227         setProperty(CSSProperty(propertyID, prpValue, important));
228         return;
229     }
230
231     removePropertiesInSet(shorthand.properties(), shorthand.length());
232
233     RefPtr<CSSValue> value = prpValue;
234     for (unsigned i = 0; i < shorthand.length(); ++i)
235         m_propertyVector.append(CSSProperty(shorthand.properties()[i], value, important));
236 }
237
238 void MutableStylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
239 {
240     if (!removeShorthandProperty(property.id())) {
241         CSSProperty* toReplace = slot ? slot : findCSSPropertyWithID(property.id());
242         if (toReplace) {
243             *toReplace = property;
244             setPrefixingVariantProperty(property);
245             return;
246         }
247     }
248     appendPrefixingVariantProperty(property);
249 }
250
251 unsigned getIndexInShorthandVectorForPrefixingVariant(const CSSProperty& property, CSSPropertyID prefixingVariant)
252 {
253     if (!property.isSetFromShorthand())
254         return 0;
255
256     CSSPropertyID prefixedShorthand = prefixingVariantForPropertyId(property.shorthandID());
257     Vector<StylePropertyShorthand, 4> shorthands;
258     getMatchingShorthandsForLonghand(prefixingVariant, &shorthands);
259     return indexOfShorthandForLonghand(prefixedShorthand, shorthands);
260 }
261
262 void MutableStylePropertySet::appendPrefixingVariantProperty(const CSSProperty& property)
263 {
264     m_propertyVector.append(property);
265     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
266     if (prefixingVariant == property.id())
267         return;
268
269     m_propertyVector.append(CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit));
270 }
271
272 void MutableStylePropertySet::setPrefixingVariantProperty(const CSSProperty& property)
273 {
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);
278 }
279
280 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
281 {
282     setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
283     return true;
284 }
285
286 bool MutableStylePropertySet::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
287 {
288     setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
289     return true;
290 }
291
292 void MutableStylePropertySet::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
293 {
294     m_propertyVector.clear();
295
296     CSSParserContext context(cssParserMode(), UseCounter::getFrom(contextStyleSheet));
297     if (contextStyleSheet) {
298         context = contextStyleSheet->parserContext();
299         context.setMode(cssParserMode());
300     }
301
302     BisonCSSParser parser(context);
303     parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
304 }
305
306 void MutableStylePropertySet::addParsedProperties(const Vector<CSSProperty, 256>& properties)
307 {
308     m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
309     for (unsigned i = 0; i < properties.size(); ++i)
310         addParsedProperty(properties[i]);
311 }
312
313 void MutableStylePropertySet::addParsedProperty(const CSSProperty& property)
314 {
315     // Only add properties that have no !important counterpart present
316     if (!propertyIsImportant(property.id()) || property.isImportant())
317         setProperty(property);
318 }
319
320 String StylePropertySet::asText() const
321 {
322     return StylePropertySerializer(*this).asText();
323 }
324
325 void MutableStylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet* other)
326 {
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());
331         if (old)
332             setProperty(toMerge.toCSSProperty(), old);
333         else
334             appendPrefixingVariantProperty(toMerge.toCSSProperty());
335     }
336 }
337
338 bool StylePropertySet::hasFailedOrCanceledSubresources() const
339 {
340     unsigned size = propertyCount();
341     for (unsigned i = 0; i < size; ++i) {
342         if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
343             return true;
344     }
345     return false;
346 }
347
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[] = {
351     CSSPropertyOrphans,
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,
370     CSSPropertyWidows
371 };
372
373 static const Vector<CSSPropertyID>& blockProperties()
374 {
375     DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
376     if (properties.isEmpty())
377         RuntimeCSSEnabled::filterEnabledCSSPropertiesIntoVector(staticBlockProperties, WTF_ARRAY_LENGTH(staticBlockProperties), properties);
378     return properties;
379 }
380
381 void MutableStylePropertySet::clear()
382 {
383     m_propertyVector.clear();
384 }
385
386 PassRefPtr<MutableStylePropertySet> StylePropertySet::copyBlockProperties() const
387 {
388     return copyPropertiesInSet(blockProperties());
389 }
390
391 void MutableStylePropertySet::removeBlockProperties()
392 {
393     removePropertiesInSet(blockProperties().data(), blockProperties().size());
394 }
395
396 bool MutableStylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
397 {
398     if (m_propertyVector.isEmpty())
399         return false;
400
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]);
405
406     Vector<CSSProperty> newProperties;
407     newProperties.reserveInitialCapacity(m_propertyVector.size());
408
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()))
415                 continue;
416         }
417         newProperties.append(property);
418     }
419
420     bool changed = newProperties.size() != m_propertyVector.size();
421     m_propertyVector = newProperties;
422     return changed;
423 }
424
425 int StylePropertySet::findPropertyIndex(CSSPropertyID propertyID) const
426 {
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));
434             return n;
435         }
436     }
437     return -1;
438 }
439
440 CSSProperty* MutableStylePropertySet::findCSSPropertyWithID(CSSPropertyID propertyID)
441 {
442     int foundPropertyIndex = findPropertyIndex(propertyID);
443     if (foundPropertyIndex == -1)
444         return 0;
445     return &m_propertyVector.at(foundPropertyIndex);
446 }
447
448 bool StylePropertySet::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
449 {
450     int foundPropertyIndex = findPropertyIndex(propertyID);
451     if (foundPropertyIndex == -1)
452         return false;
453     return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
454 }
455
456 void MutableStylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
457 {
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());
464     }
465     // FIXME: This should use mass removal.
466     for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
467         removeProperty(propertiesToRemove[i]);
468 }
469
470 void MutableStylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
471 {
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());
478     }
479     // FIXME: This should use mass removal.
480     for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
481         removeProperty(propertiesToRemove[i]);
482 }
483
484 PassRefPtr<MutableStylePropertySet> StylePropertySet::mutableCopy() const
485 {
486     return adoptRef(new MutableStylePropertySet(*this));
487 }
488
489 PassRefPtr<MutableStylePropertySet> StylePropertySet::copyPropertiesInSet(const Vector<CSSPropertyID>& properties) const
490 {
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]);
495         if (value)
496             list.append(CSSProperty(properties[i], value.release(), false));
497     }
498     return MutableStylePropertySet::create(list.data(), list.size());
499 }
500
501 CSSStyleDeclaration* MutableStylePropertySet::ensureCSSStyleDeclaration()
502 {
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();
509     }
510     m_cssomWrapper = adoptPtr(new PropertySetCSSStyleDeclaration(this));
511     return m_cssomWrapper.get();
512 }
513
514 unsigned StylePropertySet::averageSizeInBytes()
515 {
516     // Please update this if the storage scheme changes so that this longer reflects the actual size.
517     return sizeForImmutableStylePropertySetWithPropertyCount(4);
518 }
519
520 // See the function above if you need to update this.
521 struct SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
522     unsigned bitfield;
523 };
524 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
525
526 #ifndef NDEBUG
527 void StylePropertySet::showStyle()
528 {
529     fprintf(stderr, "%s\n", asText().ascii().data());
530 }
531 #endif
532
533 PassRefPtr<MutableStylePropertySet> MutableStylePropertySet::create(CSSParserMode cssParserMode)
534 {
535     return adoptRef(new MutableStylePropertySet(cssParserMode));
536 }
537
538 PassRefPtr<MutableStylePropertySet> MutableStylePropertySet::create(const CSSProperty* properties, unsigned count)
539 {
540     return adoptRef(new MutableStylePropertySet(properties, count));
541 }
542
543 String StylePropertySet::PropertyReference::cssName() const
544 {
545     return getPropertyNameString(id());
546 }
547
548 String StylePropertySet::PropertyReference::cssText() const
549 {
550     StringBuilder result;
551     result.append(cssName());
552     result.appendLiteral(": ");
553     result.append(propertyValue()->cssText());
554     if (isImportant())
555         result.appendLiteral(" !important");
556     result.append(';');
557     return result.toString();
558 }
559
560
561 } // namespace WebCore