38685df280dc194f1cc0c0c5516473ffb7d43342
[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         return extractInlineProperty(property, m_element.get());
92     }
93
94     explicit StyledElementInlineStylePropertyMap(StyledElement& element)
95         : m_element(makeRef(element))
96     {
97     }
98
99     static RefPtr<TypedOMCSSStyleValue> extractInlineProperty(const String& name, StyledElement& element)
100     {
101         if (!element.inlineStyle())
102             return nullptr;
103
104         if (isCustomPropertyName(name)) {
105             auto value = element.inlineStyle()->getCustomPropertyCSSValue(name);
106             return StylePropertyMapReadOnly::customPropertyValueOrDefault(name, element.document(), value.get(), &element);
107         }
108
109         CSSPropertyID propertyID = cssPropertyID(name);
110         if (!propertyID)
111             return nullptr;
112
113         auto value = element.inlineStyle()->getPropertyCSSValue(propertyID);
114         return StylePropertyMapReadOnly::reifyValue(value.get(), element.document(), &element);
115     }
116
117     Ref<StyledElement> m_element;
118 };
119
120 StylePropertyMap& StyledElement::ensureAttributeStyleMap()
121 {
122     if (!attributeStyleMap())
123         setAttributeStyleMap(StyledElementInlineStylePropertyMap::create(*this));
124     return *attributeStyleMap();
125 }
126 #endif
127
128 MutableStyleProperties& StyledElement::ensureMutableInlineStyle()
129 {
130     RefPtr<StyleProperties>& inlineStyle = ensureUniqueElementData().m_inlineStyle;
131     if (!inlineStyle)
132         inlineStyle = MutableStyleProperties::create(strictToCSSParserMode(isHTMLElement() && !document().inQuirksMode()));
133     else if (!is<MutableStyleProperties>(*inlineStyle))
134         inlineStyle = inlineStyle->mutableCopy();
135     return downcast<MutableStyleProperties>(*inlineStyle);
136 }
137
138 void StyledElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
139 {
140     if (oldValue != newValue) {
141         if (name == styleAttr)
142             styleAttributeChanged(newValue, reason);
143         else if (isPresentationAttribute(name)) {
144             elementData()->setPresentationAttributeStyleIsDirty(true);
145             invalidateStyle();
146         }
147     }
148
149     Element::attributeChanged(name, oldValue, newValue, reason);
150 }
151
152 PropertySetCSSStyleDeclaration* StyledElement::inlineStyleCSSOMWrapper()
153 {
154     if (!inlineStyle() || !inlineStyle()->hasCSSOMWrapper())
155         return 0;
156     PropertySetCSSStyleDeclaration* cssomWrapper = ensureMutableInlineStyle().cssStyleDeclaration();
157     ASSERT(cssomWrapper && cssomWrapper->parentElement() == this);
158     return cssomWrapper;
159 }
160
161 static bool usesStyleBasedEditability(const StyleProperties& properties)
162 {
163     return properties.getPropertyCSSValue(CSSPropertyWebkitUserModify);
164 }
165
166 void StyledElement::setInlineStyleFromString(const AtomicString& newStyleString)
167 {
168     RefPtr<StyleProperties>& inlineStyle = elementData()->m_inlineStyle;
169
170     // Avoid redundant work if we're using shared attribute data with already parsed inline style.
171     if (inlineStyle && !elementData()->isUnique())
172         return;
173
174     // We reconstruct the property set instead of mutating if there is no CSSOM wrapper.
175     // This makes wrapperless property sets immutable and so cacheable.
176     if (inlineStyle && !is<MutableStyleProperties>(*inlineStyle))
177         inlineStyle = nullptr;
178
179     if (!inlineStyle)
180         inlineStyle = CSSParser::parseInlineStyleDeclaration(newStyleString, this);
181     else
182         downcast<MutableStyleProperties>(*inlineStyle).parseDeclaration(newStyleString, document());
183
184     if (usesStyleBasedEditability(*inlineStyle))
185         document().setHasElementUsingStyleBasedEditability();
186 }
187
188 void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, AttributeModificationReason reason)
189 {
190     WTF::OrdinalNumber startLineNumber = WTF::OrdinalNumber::beforeFirst();
191     if (document().scriptableDocumentParser() && !document().isInDocumentWrite())
192         startLineNumber = document().scriptableDocumentParser()->textPosition().m_line;
193
194     if (newStyleString.isNull()) {
195         if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
196             cssomWrapper->clearParentElement();
197         ensureUniqueElementData().m_inlineStyle = nullptr;
198     } else if (reason == ModifiedByCloning || document().contentSecurityPolicy()->allowInlineStyle(document().url(), startLineNumber, String(), isInUserAgentShadowTree()))
199         setInlineStyleFromString(newStyleString);
200
201     elementData()->setStyleAttributeIsDirty(false);
202
203     invalidateStyle();
204     InspectorInstrumentation::didInvalidateStyleAttr(document(), *this);
205 }
206
207 void StyledElement::invalidateStyleAttribute()
208 {
209     if (usesStyleBasedEditability(*inlineStyle()))
210         document().setHasElementUsingStyleBasedEditability();
211
212     elementData()->setStyleAttributeIsDirty(true);
213     invalidateStyle();
214
215     // In the rare case of selectors like "[style] ~ div" we need to synchronize immediately to invalidate.
216     if (styleResolver().ruleSets().hasComplexSelectorsForStyleAttribute()) {
217         if (auto* inlineStyle = this->inlineStyle()) {
218             elementData()->setStyleAttributeIsDirty(false);
219             auto newValue = inlineStyle->asText();
220             Style::AttributeChangeInvalidation styleInvalidation(*this, styleAttr, attributeWithoutSynchronization(styleAttr), newValue);
221             setSynchronizedLazyAttribute(styleAttr, newValue);
222         }
223     }
224 }
225
226 void StyledElement::inlineStyleChanged()
227 {
228     invalidateStyleAttribute();
229     InspectorInstrumentation::didInvalidateStyleAttr(document(), *this);
230 }
231     
232 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
233 {
234     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important);
235     inlineStyleChanged();
236     return true;
237 }
238
239 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
240 {
241     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important);
242     inlineStyleChanged();
243     return true;
244 }
245
246 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitType unit, bool important)
247 {
248     ensureMutableInlineStyle().setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit), important);
249     inlineStyleChanged();
250     return true;
251 }
252
253 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important)
254 {
255     bool changes = ensureMutableInlineStyle().setProperty(propertyID, value, important, CSSParserContext(document()));
256     if (changes)
257         inlineStyleChanged();
258     return changes;
259 }
260
261 bool StyledElement::removeInlineStyleProperty(CSSPropertyID propertyID)
262 {
263     if (!inlineStyle())
264         return false;
265     bool changes = ensureMutableInlineStyle().removeProperty(propertyID);
266     if (changes)
267         inlineStyleChanged();
268     return changes;
269 }
270
271 void StyledElement::removeAllInlineStyleProperties()
272 {
273     if (!inlineStyle() || inlineStyle()->isEmpty())
274         return;
275     ensureMutableInlineStyle().clear();
276     inlineStyleChanged();
277 }
278
279 void StyledElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
280 {
281     auto* inlineStyle = this->inlineStyle();
282     if (!inlineStyle)
283         return;
284     inlineStyle->traverseSubresources([&] (auto& resource) {
285         urls.add(resource.url());
286         return false;
287     });
288 }
289
290 void StyledElement::rebuildPresentationAttributeStyle()
291 {
292     RefPtr<StyleProperties> style = MutableStyleProperties::create(isSVGElement() ? SVGAttributeMode : HTMLQuirksMode);
293     for (const Attribute& attribute : attributesIterator())
294         collectStyleForPresentationAttribute(attribute.name(), attribute.value(), static_cast<MutableStyleProperties&>(*style));
295
296     // ShareableElementData doesn't store presentation attribute style, so make sure we have a UniqueElementData.
297     UniqueElementData& elementData = ensureUniqueElementData();
298
299     elementData.setPresentationAttributeStyleIsDirty(false);
300     elementData.m_presentationAttributeStyle = style->isEmpty() ? nullptr : WTFMove(style);
301 }
302
303 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, CSSValueID identifier)
304 {
305     style.setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier));
306 }
307
308 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitType unit)
309 {
310     style.setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit));
311 }
312     
313 void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value)
314 {
315     style.setProperty(propertyID, value, false, CSSParserContext(document()));
316 }
317
318 }