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
51namespace WebCore {
52
53WTF_MAKE_ISO_ALLOCATED_IMPL(StyledElement);
54
55COMPILE_ASSERT(sizeof(StyledElement) == sizeof(Element), styledelement_should_remain_same_size_as_element);
56
57using namespace HTMLNames;
58
59void 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
68StyledElement::~StyledElement()
69{
70 if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper())
71 cssomWrapper->clearParentElement();
72}
73
74CSSStyleDeclaration& StyledElement::cssomStyle()
75{
76 return ensureMutableInlineStyle().ensureInlineCSSStyleDeclaration(*this);
77}
78
79#if ENABLE(CSS_TYPED_OM)
80
81class StyledElementInlineStylePropertyMap final : public StylePropertyMap {
82public:
83 static Ref<StylePropertyMap> create(StyledElement& element)
84 {
85 return adoptRef(*new StyledElementInlineStylePropertyMap(element));
86 }
87
88private:
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
125StylePropertyMap& StyledElement::ensureAttributeStyleMap()
126{
127 if (!attributeStyleMap())
128 setAttributeStyleMap(StyledElementInlineStylePropertyMap::create(*this));
129 return *attributeStyleMap();
130}
131#endif
132
133MutableStyleProperties& 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
143void StyledElement::attributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& 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
157PropertySetCSSStyleDeclaration* 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
166static bool usesStyleBasedEditability(const StyleProperties& properties)
167{
168 return properties.getPropertyCSSValue(CSSPropertyWebkitUserModify);
169}
170
171void StyledElement::setInlineStyleFromString(const AtomString& 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
193void StyledElement::styleAttributeChanged(const AtomString& 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(*this);
210}
211
212void 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
231void StyledElement::inlineStyleChanged()
232{
233 invalidateStyleAttribute();
234 InspectorInstrumentation::didInvalidateStyleAttr(*this);
235}
236
237bool 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
244bool 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
251bool 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
258bool 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
266bool 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
276void StyledElement::removeAllInlineStyleProperties()
277{
278 if (!inlineStyle() || inlineStyle()->isEmpty())
279 return;
280 ensureMutableInlineStyle().clear();
281 inlineStyleChanged();
282}
283
284void 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
295void 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
308void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, CSSValueID identifier)
309{
310 style.setProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier));
311}
312
313void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitType unit)
314{
315 style.setProperty(propertyID, CSSValuePool::singleton().createValue(value, unit));
316}
317
318void StyledElement::addPropertyToPresentationAttributeStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value)
319{
320 style.setProperty(propertyID, value, false, CSSParserContext(document()));
321}
322
323}
324