92a973a3483125ed79f671edfe0ee2e665f7899e
[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-2018 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 "AttributeChangeInvalidation.h"
28 #include "CSSComputedStyleDeclaration.h"
29 #include "CSSImageValue.h"
30 #include "CSSParser.h"
31 #include "CSSPrimitiveValue.h"
32 #include "CSSPropertyParser.h"
33 #include "CSSStyleSheet.h"
34 #include "CSSValuePool.h"
35 #include "CachedResource.h"
36 #include "ContentSecurityPolicy.h"
37 #include "DOMTokenList.h"
38 #include "ElementRareData.h"
39 #include "HTMLElement.h"
40 #include "HTMLParserIdioms.h"
41 #include "InspectorInstrumentation.h"
42 #include "PropertySetCSSStyleDeclaration.h"
43 #include "ScriptableDocumentParser.h"
44 #include "StyleProperties.h"
45 #include "StylePropertyMap.h"
46 #include "StyleResolver.h"
47 #include "TypedOMCSSUnparsedValue.h"
48 #include <wtf/HashFunctions.h>
49 #include <wtf/IsoMallocInlines.h>
50
51 namespace WebCore {
52
53 WTF_MAKE_ISO_ALLOCATED_IMPL(StyledElement);
54
55 COMPILE_ASSERT(sizeof(StyledElement) == sizeof(Element), styledelement_should_remain_same_size_as_element);
56
57 using namespace HTMLNames;
58
59 void StyledElement::synchronizeStyleAttributeInternal(StyledElement* styledElement)
60 {
61     ASSERT(styledElement->elementData());
62     ASSERT(styledElement->elementData()->styleAttributeIsDirty());
63     styledElement->elementData()->setStyleAttributeIsDirty(false);
64     if (const StyleProperties* inlineStyle = styledElement->inlineStyle())
65         styledElement->setSynchronizedLazyAttribute(styleAttr, inlineStyle->asText());
66 }
67
68 StyledElement::~StyledElement()
69 {
70     if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
71         cssomWrapper->clearParentElement();
72 }
73
74 CSSStyleDeclaration& StyledElement::cssomStyle()
75 {
76     return ensureMutableInlineStyle().ensureInlineCSSStyleDeclaration(*this);
77 }
78
79 #if ENABLE(CSS_TYPED_OM)
80
81 class StyledElementInlineStylePropertyMap final : public StylePropertyMap {
82 public:
83     static Ref<StylePropertyMap> create(StyledElement& element)
84     {
85         return adoptRef(*new StyledElementInlineStylePropertyMap(element));
86     }
87
88 private:
89     RefPtr<TypedOMCSSStyleValue> get(const String& property) const final
90     {
91         ASSERT(m_element); // Hitting this assertion would imply a GC bug. Element is collected while this property map is alive.
92         if (!m_element)
93             return nullptr;
94         return extractInlineProperty(property, *m_element);
95     }
96
97     explicit StyledElementInlineStylePropertyMap(StyledElement& element)
98         : m_element(&element)
99     {
100     }
101
102     void clearElement() override { m_element = nullptr; }
103
104     static RefPtr<TypedOMCSSStyleValue> extractInlineProperty(const String& name, StyledElement& element)
105     {
106         if (!element.inlineStyle())
107             return nullptr;
108
109         if (isCustomPropertyName(name)) {
110             auto value = element.inlineStyle()->getCustomPropertyCSSValue(name);
111             return StylePropertyMapReadOnly::customPropertyValueOrDefault(name, element.document(), value.get(), &element);
112         }
113
114         CSSPropertyID propertyID = cssPropertyID(name);
115         if (!propertyID)
116             return nullptr;
117
118         auto value = element.inlineStyle()->getPropertyCSSValue(propertyID);
119         return StylePropertyMapReadOnly::reifyValue(value.get(), element.document(), &element);
120     }
121
122     StyledElement* m_element { nullptr };
123 };
124
125 StylePropertyMap& StyledElement::ensureAttributeStyleMap()
126 {
127     if (!attributeStyleMap())
128         setAttributeStyleMap(StyledElementInlineStylePropertyMap::create(*this));
129     return *attributeStyleMap();
130 }
131 #endif
132
133 MutableStyleProperties& StyledElement::ensureMutableInlineStyle()
134 {
135     RefPtr<StyleProperties>& inlineStyle = ensureUniqueElementData().m_inlineStyle;
136     if (!inlineStyle)
137         inlineStyle = MutableStyleProperties::create(strictToCSSParserMode(isHTMLElement() && !document().inQuirksMode()));
138     else if (!is<MutableStyleProperties>(*inlineStyle))
139         inlineStyle = inlineStyle->mutableCopy();
140     return downcast<MutableStyleProperties>(*inlineStyle);
141 }
142
143 void StyledElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
144 {
145     if (oldValue != newValue) {
146         if (name == styleAttr)
147             styleAttributeChanged(newValue, reason);
148         else if (isPresentationAttribute(name)) {
149             elementData()->setPresentationAttributeStyleIsDirty(true);
150             invalidateStyle();
151         }
152     }
153
154     Element::attributeChanged(name, oldValue, newValue, reason);
155 }
156
157 PropertySetCSSStyleDeclaration* StyledElement::inlineStyleCSSOMWrapper()
158 {
159     if (!inlineStyle() || !inlineStyle()->hasCSSOMWrapper())
160         return 0;
161     PropertySetCSSStyleDeclaration* cssomWrapper = ensureMutableInlineStyle().cssStyleDeclaration();
162     ASSERT(cssomWrapper && cssomWrapper->parentElement() == this);
163     return cssomWrapper;
164 }
165
166 static bool usesStyleBasedEditability(const StyleProperties& properties)
167 {
168     return properties.getPropertyCSSValue(CSSPropertyWebkitUserModify);
169 }
170
171 void StyledElement::setInlineStyleFromString(const AtomicString& newStyleString)
172 {
173     RefPtr<StyleProperties>& inlineStyle = elementData()->m_inlineStyle;
174
175     // Avoid redundant work if we're using shared attribute data with already parsed inline style.
176     if (inlineStyle && !elementData()->isUnique())
177         return;
178
179     // We reconstruct the property set instead of mutating if there is no CSSOM wrapper.
180     // This makes wrapperless property sets immutable and so cacheable.
181     if (inlineStyle && !is<MutableStyleProperties>(*inlineStyle))
182         inlineStyle = nullptr;
183
184     if (!inlineStyle)
185         inlineStyle = CSSParser::parseInlineStyleDeclaration(newStyleString, this);
186     else
187         downcast<MutableStyleProperties>(*inlineStyle).parseDeclaration(newStyleString, document());
188
189     if (usesStyleBasedEditability(*inlineStyle))
190         document().setHasElementUsingStyleBasedEditability();
191 }
192
193 void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, AttributeModificationReason reason)
194 {
195     WTF::OrdinalNumber startLineNumber = WTF::OrdinalNumber::beforeFirst();
196     if (document().scriptableDocumentParser() && !document().isInDocumentWrite())
197         startLineNumber = document().scriptableDocumentParser()->textPosition().m_line;
198
199     if (newStyleString.isNull()) {
200         if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
201             cssomWrapper->clearParentElement();
202         ensureUniqueElementData().m_inlineStyle = nullptr;
203     } else if (reason == ModifiedByCloning || document().contentSecurityPolicy()->allowInlineStyle(document().url(), startLineNumber, String(), isInUserAgentShadowTree()))
204         setInlineStyleFromString(newStyleString);
205
206     elementData()->setStyleAttributeIsDirty(false);
207
208     invalidateStyle();
209     InspectorInstrumentation::didInvalidateStyleAttr(document(), *this);
210 }
211
212 void StyledElement::invalidateStyleAttribute()
213 {
214     if (usesStyleBasedEditability(*inlineStyle()))
215         document().setHasElementUsingStyleBasedEditability();
216
217     elementData()->setStyleAttributeIsDirty(true);
218     invalidateStyle();
219
220     // In the rare case of selectors like "[style] ~ div" we need to synchronize immediately to invalidate.
221     if (styleResolver().ruleSets().hasComplexSelectorsForStyleAttribute()) {
222         if (auto* inlineStyle = this->inlineStyle()) {
223             elementData()->setStyleAttributeIsDirty(false);
224             auto newValue = inlineStyle->asText();
225             Style::AttributeChangeInvalidation styleInvalidation(*this, styleAttr, attributeWithoutSynchronization(styleAttr), newValue);
226             setSynchronizedLazyAttribute(styleAttr, newValue);
227         }
228     }
229 }
230
231 void StyledElement::inlineStyleChanged()
232 {
233     invalidateStyleAttribute();
234     InspectorInstrumentation::didInvalidateStyleAttr(document(), *this);
235 }
236     
237 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
238 {
239     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important);
240     inlineStyleChanged();
241     return true;
242 }
243
244 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
245 {
246     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important);
247     inlineStyleChanged();
248     return true;
249 }
250
251 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitType unit, bool important)
252 {
253     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit), important);
254     inlineStyleChanged();
255     return true;
256 }
257
258 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important)
259 {
260     bool changes = ensureMutableInlineStyle().setProperty(propertyID, value, important, CSSParserContext(document()));
261     if (changes)
262         inlineStyleChanged();
263     return changes;
264 }
265
266 bool StyledElement::removeInlineStyleProperty(CSSPropertyID propertyID)
267 {
268     if (!inlineStyle())
269         return false;
270     bool changes = ensureMutableInlineStyle().removeProperty(propertyID);
271     if (changes)
272         inlineStyleChanged();
273     return changes;
274 }
275
276 void StyledElement::removeAllInlineStyleProperties()
277 {
278     if (!inlineStyle() || inlineStyle()->isEmpty())
279         return;
280     ensureMutableInlineStyle().clear();
281     inlineStyleChanged();
282 }
283
284 void StyledElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
285 {
286     auto* inlineStyle = this->inlineStyle();
287     if (!inlineStyle)
288         return;
289     inlineStyle->traverseSubresources([&] (auto& resource) {
290         urls.add(resource.url());
291         return false;
292     });
293 }
294
295 void StyledElement::rebuildPresentationAttributeStyle()
296 {
297     RefPtr<StyleProperties> style = MutableStyleProperties::create(isSVGElement() ? SVGAttributeMode : HTMLQuirksMode);
298     for (const Attribute& attribute : attributesIterator())
299         collectStyleForPresentationAttribute(attribute.name(), attribute.value(), static_cast<MutableStyleProperties&>(*style));
300
301     // ShareableElementData doesn't store presentation attribute style, so make sure we have a UniqueElementData.
302     UniqueElementData& elementData = ensureUniqueElementData();
303
304     elementData.setPresentationAttributeStyleIsDirty(false);
305     elementData.m_presentationAttributeStyle = style->isEmpty() ? nullptr : WTFMove(style);
306 }
307
308 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, CSSValueID identifier)
309 {
310     style.setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier));
311 }
312
313 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitType unit)
314 {
315     style.setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit));
316 }
317     
318 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value)
319 {
320     style.setProperty(propertyID, value, false, CSSParserContext(document()));
321 }
322
323 }