Replace 0 and NULL with nullptr in WebCore/dom.
[WebKit-https.git] / Source / WebCore / dom / StyledElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Peter Kelly (pmk@post.com)
5  *           (C) 2001 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2004, 2005, 2006, 2008, 2010, 2014 Apple Inc. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #include "config.h"
25 #include "StyledElement.h"
26
27 #include "CSSImageValue.h"
28 #include "CSSParser.h"
29 #include "CSSStyleSheet.h"
30 #include "CSSValuePool.h"
31 #include "ContentSecurityPolicy.h"
32 #include "DOMTokenList.h"
33 #include "HTMLElement.h"
34 #include "HTMLParserIdioms.h"
35 #include "InspectorInstrumentation.h"
36 #include "PropertySetCSSStyleDeclaration.h"
37 #include "ScriptableDocumentParser.h"
38 #include "StyleProperties.h"
39 #include "StyleResolver.h"
40 #include <wtf/HashFunctions.h>
41
42 namespace WebCore {
43
44 COMPILE_ASSERT(sizeof(StyledElement) == sizeof(Element), styledelement_should_remain_same_size_as_element);
45
46 using namespace HTMLNames;
47
48 struct PresentationAttributeCacheKey {
49     AtomicStringImpl* tagName { nullptr };
50     // Only the values need refcounting.
51     Vector<std::pair<AtomicStringImpl*, AtomicString>, 3> attributesAndValues;
52 };
53
54 struct PresentationAttributeCacheEntry {
55     WTF_MAKE_FAST_ALLOCATED;
56 public:
57     PresentationAttributeCacheKey key;
58     RefPtr<StyleProperties> value;
59 };
60
61 typedef HashMap<unsigned, std::unique_ptr<PresentationAttributeCacheEntry>, AlreadyHashed> PresentationAttributeCache;
62     
63 static bool operator!=(const PresentationAttributeCacheKey& a, const PresentationAttributeCacheKey& b)
64 {
65     if (a.tagName != b.tagName)
66         return true;
67     return a.attributesAndValues != b.attributesAndValues;
68 }
69
70 static PresentationAttributeCache& presentationAttributeCache()
71 {
72     static NeverDestroyed<PresentationAttributeCache> cache;
73     return cache;
74 }
75
76 class PresentationAttributeCacheCleaner {
77     WTF_MAKE_NONCOPYABLE(PresentationAttributeCacheCleaner); WTF_MAKE_FAST_ALLOCATED;
78 public:
79     PresentationAttributeCacheCleaner()
80         : m_hitCount(0)
81         , m_cleanTimer(*this, &PresentationAttributeCacheCleaner::cleanCache)
82     {
83     }
84
85     void didHitPresentationAttributeCache()
86     {
87         if (presentationAttributeCache().size() < minimumPresentationAttributeCacheSizeForCleaning)
88             return;
89
90         m_hitCount++;
91
92         if (!m_cleanTimer.isActive())
93             m_cleanTimer.startOneShot(presentationAttributeCacheCleanTimeInSeconds);
94      }
95
96 private:
97     static const unsigned presentationAttributeCacheCleanTimeInSeconds = 60;
98     static const int minimumPresentationAttributeCacheSizeForCleaning = 100;
99     static const unsigned minimumPresentationAttributeCacheHitCountPerMinute = (100 * presentationAttributeCacheCleanTimeInSeconds) / 60;
100
101     void cleanCache()
102     {
103         unsigned hitCount = m_hitCount;
104         m_hitCount = 0;
105         if (hitCount > minimumPresentationAttributeCacheHitCountPerMinute)
106             return;
107         presentationAttributeCache().clear();
108     }
109
110     unsigned m_hitCount;
111     Timer m_cleanTimer;
112 };
113
114 static PresentationAttributeCacheCleaner& presentationAttributeCacheCleaner()
115 {
116     static NeverDestroyed<PresentationAttributeCacheCleaner> cleaner;
117     return cleaner;
118 }
119
120 void StyledElement::clearPresentationAttributeCache()
121 {
122     presentationAttributeCache().clear();
123 }
124
125 void StyledElement::synchronizeStyleAttributeInternal(StyledElement* styledElement)
126 {
127     ASSERT(styledElement->elementData());
128     ASSERT(styledElement->elementData()->styleAttributeIsDirty());
129     styledElement->elementData()->setStyleAttributeIsDirty(false);
130     if (const StyleProperties* inlineStyle = styledElement->inlineStyle())
131         styledElement->setSynchronizedLazyAttribute(styleAttr, inlineStyle->asText());
132 }
133
134 StyledElement::~StyledElement()
135 {
136     if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
137         cssomWrapper->clearParentElement();
138 }
139
140 CSSStyleDeclaration* StyledElement::style()
141 {
142     return ensureMutableInlineStyle().ensureInlineCSSStyleDeclaration(this);
143 }
144
145 MutableStyleProperties& StyledElement::ensureMutableInlineStyle()
146 {
147     RefPtr<StyleProperties>& inlineStyle = ensureUniqueElementData().m_inlineStyle;
148     if (!inlineStyle)
149         inlineStyle = MutableStyleProperties::create(strictToCSSParserMode(isHTMLElement() && !document().inQuirksMode()));
150     else if (!is<MutableStyleProperties>(*inlineStyle))
151         inlineStyle = inlineStyle->mutableCopy();
152     return downcast<MutableStyleProperties>(*inlineStyle);
153 }
154
155 void StyledElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
156 {
157     if (name == styleAttr)
158         styleAttributeChanged(newValue, reason);
159     else if (isPresentationAttribute(name)) {
160         elementData()->setPresentationAttributeStyleIsDirty(true);
161         setNeedsStyleRecalc(InlineStyleChange);
162     }
163
164     Element::attributeChanged(name, oldValue, newValue, reason);
165 }
166
167 PropertySetCSSStyleDeclaration* StyledElement::inlineStyleCSSOMWrapper()
168 {
169     if (!inlineStyle() || !inlineStyle()->hasCSSOMWrapper())
170         return 0;
171     PropertySetCSSStyleDeclaration* cssomWrapper = ensureMutableInlineStyle().cssStyleDeclaration();
172     ASSERT(cssomWrapper && cssomWrapper->parentElement() == this);
173     return cssomWrapper;
174 }
175
176 inline void StyledElement::setInlineStyleFromString(const AtomicString& newStyleString)
177 {
178     RefPtr<StyleProperties>& inlineStyle = elementData()->m_inlineStyle;
179
180     // Avoid redundant work if we're using shared attribute data with already parsed inline style.
181     if (inlineStyle && !elementData()->isUnique())
182         return;
183
184     // We reconstruct the property set instead of mutating if there is no CSSOM wrapper.
185     // This makes wrapperless property sets immutable and so cacheable.
186     if (inlineStyle && !is<MutableStyleProperties>(*inlineStyle))
187         inlineStyle = nullptr;
188
189     if (!inlineStyle)
190         inlineStyle = CSSParser::parseInlineStyleDeclaration(newStyleString, this);
191     else
192         downcast<MutableStyleProperties>(*inlineStyle).parseDeclaration(newStyleString, &document().elementSheet().contents());
193 }
194
195 void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, AttributeModificationReason reason)
196 {
197     WTF::OrdinalNumber startLineNumber = WTF::OrdinalNumber::beforeFirst();
198     if (document().scriptableDocumentParser() && !document().isInDocumentWrite())
199         startLineNumber = document().scriptableDocumentParser()->textPosition().m_line;
200
201     if (newStyleString.isNull()) {
202         if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
203             cssomWrapper->clearParentElement();
204         ensureUniqueElementData().m_inlineStyle = nullptr;
205     } else if (reason == ModifiedByCloning || document().contentSecurityPolicy()->allowInlineStyle(document().url(), startLineNumber, isInUserAgentShadowTree()))
206         setInlineStyleFromString(newStyleString);
207
208     elementData()->setStyleAttributeIsDirty(false);
209
210     setNeedsStyleRecalc(InlineStyleChange);
211     InspectorInstrumentation::didInvalidateStyleAttr(document(), *this);
212 }
213
214 void StyledElement::inlineStyleChanged()
215 {
216     invalidateStyleAttribute();
217     InspectorInstrumentation::didInvalidateStyleAttr(document(), *this);
218 }
219     
220 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
221 {
222     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important);
223     inlineStyleChanged();
224     return true;
225 }
226
227 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
228 {
229     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important);
230     inlineStyleChanged();
231     return true;
232 }
233
234 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important)
235 {
236     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit), important);
237     inlineStyleChanged();
238     return true;
239 }
240
241 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important)
242 {
243     bool changes = ensureMutableInlineStyle().setProperty(propertyID, value, important, &document().elementSheet().contents());
244     if (changes)
245         inlineStyleChanged();
246     return changes;
247 }
248
249 bool StyledElement::removeInlineStyleProperty(CSSPropertyID propertyID)
250 {
251     if (!inlineStyle())
252         return false;
253     bool changes = ensureMutableInlineStyle().removeProperty(propertyID);
254     if (changes)
255         inlineStyleChanged();
256     return changes;
257 }
258
259 void StyledElement::removeAllInlineStyleProperties()
260 {
261     if (!inlineStyle() || inlineStyle()->isEmpty())
262         return;
263     ensureMutableInlineStyle().clear();
264     inlineStyleChanged();
265 }
266
267 void StyledElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
268 {
269     if (const StyleProperties* inlineStyle = elementData() ? elementData()->inlineStyle() : nullptr)
270         inlineStyle->addSubresourceStyleURLs(urls, &document().elementSheet().contents());
271 }
272
273 static inline bool attributeNameSort(const std::pair<AtomicStringImpl*, AtomicString>& p1, const std::pair<AtomicStringImpl*, AtomicString>& p2)
274 {
275     // Sort based on the attribute name pointers. It doesn't matter what the order is as long as it is always the same. 
276     return p1.first < p2.first;
277 }
278
279 void StyledElement::makePresentationAttributeCacheKey(PresentationAttributeCacheKey& result) const
280 {    
281     // FIXME: Enable for SVG.
282     if (namespaceURI() != xhtmlNamespaceURI)
283         return;
284     // Interpretation of the size attributes on <input> depends on the type attribute.
285     if (hasTagName(inputTag))
286         return;
287     for (const Attribute& attribute : attributesIterator()) {
288         if (!isPresentationAttribute(attribute.name()))
289             continue;
290         if (!attribute.namespaceURI().isNull())
291             return;
292         // FIXME: Background URL may depend on the base URL and can't be shared. Disallow caching.
293         if (attribute.name() == backgroundAttr)
294             return;
295         result.attributesAndValues.append(std::make_pair(attribute.localName().impl(), attribute.value()));
296     }
297     if (result.attributesAndValues.isEmpty())
298         return;
299     // Attribute order doesn't matter. Sort for easy equality comparison.
300     std::sort(result.attributesAndValues.begin(), result.attributesAndValues.end(), attributeNameSort);
301     // The cache key is non-null when the tagName is set.
302     result.tagName = localName().impl();
303 }
304
305 static unsigned computePresentationAttributeCacheHash(const PresentationAttributeCacheKey& key)
306 {
307     if (!key.tagName)
308         return 0;
309     ASSERT(key.attributesAndValues.size());
310     unsigned attributeHash = StringHasher::hashMemory(key.attributesAndValues.data(), key.attributesAndValues.size() * sizeof(key.attributesAndValues[0]));
311     return WTF::pairIntHash(key.tagName->existingHash(), attributeHash);
312 }
313
314 void StyledElement::rebuildPresentationAttributeStyle()
315 {
316     PresentationAttributeCacheKey cacheKey;
317     makePresentationAttributeCacheKey(cacheKey);
318
319     unsigned cacheHash = computePresentationAttributeCacheHash(cacheKey);
320
321     PresentationAttributeCache::iterator cacheIterator;
322     if (cacheHash) {
323         cacheIterator = presentationAttributeCache().add(cacheHash, nullptr).iterator;
324         if (cacheIterator->value && cacheIterator->value->key != cacheKey)
325             cacheHash = 0;
326     } else
327         cacheIterator = presentationAttributeCache().end();
328
329     RefPtr<StyleProperties> style;
330     if (cacheHash && cacheIterator->value) {
331         style = cacheIterator->value->value;
332         presentationAttributeCacheCleaner().didHitPresentationAttributeCache();
333     } else {
334         style = MutableStyleProperties::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode);
335         for (const Attribute& attribute : attributesIterator())
336             collectStyleForPresentationAttribute(attribute.name(), attribute.value(), static_cast<MutableStyleProperties&>(*style));
337     }
338
339     // ShareableElementData doesn't store presentation attribute style, so make sure we have a UniqueElementData.
340     UniqueElementData& elementData = ensureUniqueElementData();
341
342     elementData.setPresentationAttributeStyleIsDirty(false);
343     elementData.m_presentationAttributeStyle = style->isEmpty() ? nullptr : style;
344
345     if (!cacheHash || cacheIterator->value)
346         return;
347
348     std::unique_ptr<PresentationAttributeCacheEntry> newEntry = std::make_unique<PresentationAttributeCacheEntry>();
349     newEntry->key = cacheKey;
350     newEntry->value = style.release();
351
352     static const int presentationAttributeCacheMaximumSize = 4096;
353     if (presentationAttributeCache().size() > presentationAttributeCacheMaximumSize) {
354         // Start building from scratch if the cache ever gets big.
355         presentationAttributeCache().clear();
356         presentationAttributeCache().set(cacheHash, WTF::move(newEntry));
357     } else
358         cacheIterator->value = WTF::move(newEntry);
359 }
360
361 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, CSSValueID identifier)
362 {
363     style.setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier));
364 }
365
366 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit)
367 {
368     style.setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit));
369 }
370     
371 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value)
372 {
373     style.setProperty(propertyID, value, false, &document().elementSheet().contents());
374 }
375
376 }