CSSStyleDeclaration should be annotated with CEReactions
[WebKit-https.git] / Source / WebCore / css / PropertySetCSSStyleDeclaration.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
4  * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23 #include "PropertySetCSSStyleDeclaration.h"
24
25 #include "CSSCustomPropertyValue.h"
26 #include "CSSParser.h"
27 #include "CSSRule.h"
28 #include "CSSStyleSheet.h"
29 #include "CustomElementReactionQueue.h"
30 #include "HTMLNames.h"
31 #include "InspectorInstrumentation.h"
32 #include "MutationObserverInterestGroup.h"
33 #include "MutationRecord.h"
34 #include "StyleProperties.h"
35 #include "StyleSheetContents.h"
36 #include "StyledElement.h"
37
38 namespace WebCore {
39
40 namespace {
41
42 class StyleAttributeMutationScope {
43     WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope);
44 public:
45     StyleAttributeMutationScope(PropertySetCSSStyleDeclaration* decl)
46     {
47         ++s_scopeCount;
48
49         if (s_scopeCount != 1) {
50             ASSERT(s_currentDecl == decl);
51             return;
52         }
53
54         ASSERT(!s_currentDecl);
55         s_currentDecl = decl;
56
57         auto* element = s_currentDecl->parentElement();
58         if (!element)
59             return;
60
61         bool shouldReadOldValue = false;
62
63         m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(*s_currentDecl->parentElement(), HTMLNames::styleAttr);
64         if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested())
65             shouldReadOldValue = true;
66
67 #if ENABLE(CUSTOM_ELEMENTS)
68         if (UNLIKELY(element->isDefinedCustomElement())) {
69             auto* reactionQueue = element->reactionQueue();
70             if (reactionQueue && reactionQueue->observesStyleAttribute()) {
71                 m_customElement = element;
72                 shouldReadOldValue = true;
73             }
74         }
75 #endif
76
77         if (shouldReadOldValue)
78             m_oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr);
79     }
80
81     ~StyleAttributeMutationScope()
82     {
83         --s_scopeCount;
84         if (s_scopeCount)
85             return;
86
87         if (s_shouldDeliver) {
88             if (m_mutationRecipients) {
89                 auto mutation = MutationRecord::createAttributes(*s_currentDecl->parentElement(), HTMLNames::styleAttr, m_oldValue);
90                 m_mutationRecipients->enqueueMutationRecord(WTFMove(mutation));
91             }
92             if (m_customElement) {
93                 AtomicString newValue = m_customElement->getAttribute(HTMLNames::styleAttr);
94                 CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(*m_customElement, HTMLNames::styleAttr, m_oldValue, newValue);
95             }
96         }
97
98         s_shouldDeliver = false;
99         if (!s_shouldNotifyInspector) {
100             s_currentDecl = 0;
101             return;
102         }
103         // We have to clear internal state before calling Inspector's code.
104         PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl;
105         s_currentDecl = 0;
106         s_shouldNotifyInspector = false;
107         if (localCopyStyleDecl->parentElement())
108             InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement()->document(), *localCopyStyleDecl->parentElement());
109     }
110
111     void enqueueMutationRecord()
112     {
113         s_shouldDeliver = true;
114     }
115
116     void didInvalidateStyleAttr()
117     {
118         s_shouldNotifyInspector = true;
119     }
120
121 private:
122     static unsigned s_scopeCount;
123     static PropertySetCSSStyleDeclaration* s_currentDecl;
124     static bool s_shouldNotifyInspector;
125     static bool s_shouldDeliver;
126
127     std::unique_ptr<MutationObserverInterestGroup> m_mutationRecipients;
128     AtomicString m_oldValue;
129     RefPtr<Element> m_customElement;
130 };
131
132 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
133 PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0;
134 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false;
135 bool StyleAttributeMutationScope::s_shouldDeliver = false;
136
137 } // namespace
138
139 void PropertySetCSSStyleDeclaration::ref()
140
141     m_propertySet->ref();
142 }
143
144 void PropertySetCSSStyleDeclaration::deref()
145 {
146     m_propertySet->deref(); 
147 }
148
149 unsigned PropertySetCSSStyleDeclaration::length() const
150 {
151     return m_propertySet->propertyCount();
152 }
153
154 String PropertySetCSSStyleDeclaration::item(unsigned i) const
155 {
156     if (i >= m_propertySet->propertyCount())
157         return String();
158     return m_propertySet->propertyAt(i).cssName();
159 }
160
161 String PropertySetCSSStyleDeclaration::cssText() const
162 {
163     return m_propertySet->asText();
164 }
165
166 ExceptionOr<void> PropertySetCSSStyleDeclaration::setCssText(const String& text)
167 {
168     StyleAttributeMutationScope mutationScope(this);
169     if (!willMutate())
170         return { };
171
172     bool changed = m_propertySet->parseDeclaration(text, cssParserContext(), contextStyleSheet());
173
174     didMutate(changed ? PropertyChanged : NoChanges);
175
176     mutationScope.enqueueMutationRecord();
177     return { };
178 }
179
180 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName)
181 {
182     if (isCustomPropertyName(propertyName)) {
183         RefPtr<CSSValue> value = m_propertySet->getCustomPropertyCSSValue(propertyName);
184         if (!value)
185             return nullptr;
186         return cloneAndCacheForCSSOM(value.get());
187     }
188     
189     CSSPropertyID propertyID = cssPropertyID(propertyName);
190     if (!propertyID)
191         return nullptr;
192     return cloneAndCacheForCSSOM(getPropertyCSSValueInternal(propertyID).get());
193 }
194
195 String PropertySetCSSStyleDeclaration::getPropertyValue(const String& propertyName)
196 {
197     if (isCustomPropertyName(propertyName))
198         return m_propertySet->getCustomPropertyValue(propertyName);
199
200     CSSPropertyID propertyID = cssPropertyID(propertyName);
201     if (!propertyID)
202         return String();
203     return getPropertyValueInternal(propertyID);
204 }
205
206 String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName)
207 {
208     if (isCustomPropertyName(propertyName))
209         return m_propertySet->customPropertyIsImportant(propertyName) ? ASCIILiteral("important") : emptyString();
210
211     CSSPropertyID propertyID = cssPropertyID(propertyName);
212     if (!propertyID)
213         return String();
214     return m_propertySet->propertyIsImportant(propertyID) ? ASCIILiteral("important") : emptyString();
215 }
216
217 String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName)
218 {
219     CSSPropertyID propertyID = cssPropertyID(propertyName);
220     if (!propertyID)
221         return String();
222     return m_propertySet->getPropertyShorthand(propertyID);
223 }
224
225 bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName)
226 {
227     CSSPropertyID propertyID = cssPropertyID(propertyName);
228     if (!propertyID)
229         return false;
230     return m_propertySet->isPropertyImplicit(propertyID);
231 }
232
233 ExceptionOr<void> PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority)
234 {
235     StyleAttributeMutationScope mutationScope(this);
236     
237     CSSPropertyID propertyID = cssPropertyID(propertyName);
238     if (isCustomPropertyName(propertyName))
239         propertyID = CSSPropertyCustom;
240     if (!propertyID)
241         return { };
242
243     if (!willMutate())
244         return { };
245
246     bool important = equalIgnoringASCIICase(priority, "important");
247     if (!important && !priority.isEmpty())
248         return { };
249
250     bool changed;
251     if (propertyID == CSSPropertyCustom)
252         changed = m_propertySet->setCustomProperty(propertyName, value, important, cssParserContext(), contextStyleSheet());
253     else
254         changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext(), contextStyleSheet());
255
256     didMutate(changed ? PropertyChanged : NoChanges);
257
258     if (changed) {
259         // CSS DOM requires raising SYNTAX_ERR of parsing failed, but this is too dangerous for compatibility,
260         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
261         mutationScope.enqueueMutationRecord();
262     }
263
264     return { };
265 }
266
267 ExceptionOr<String> PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName)
268 {
269     StyleAttributeMutationScope mutationScope(this);
270     CSSPropertyID propertyID = cssPropertyID(propertyName);
271     if (isCustomPropertyName(propertyName))
272         propertyID = CSSPropertyCustom;
273     if (!propertyID)
274         return String();
275
276     if (!willMutate())
277         return String();
278
279     String result;
280     bool changed = propertyID != CSSPropertyCustom ? m_propertySet->removeProperty(propertyID, &result) : m_propertySet->removeCustomProperty(propertyName, &result);
281
282     didMutate(changed ? PropertyChanged : NoChanges);
283
284     if (changed)
285         mutationScope.enqueueMutationRecord();
286     return WTFMove(result);
287 }
288
289 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID)
290 {
291     return m_propertySet->getPropertyCSSValue(propertyID);
292 }
293
294 String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID)
295 {
296     String value = m_propertySet->getPropertyValue(propertyID);
297     if (!value.isEmpty())
298         return value;
299
300     return String();
301 }
302
303 ExceptionOr<bool> PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important)
304
305     StyleAttributeMutationScope mutationScope(this);
306     if (!willMutate())
307         return false;
308
309     bool changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext(), contextStyleSheet());
310
311     didMutate(changed ? PropertyChanged : NoChanges);
312
313     if (changed)
314         mutationScope.enqueueMutationRecord();
315     return changed;
316 }
317
318 CSSValue* PropertySetCSSStyleDeclaration::cloneAndCacheForCSSOM(CSSValue* internalValue)
319 {
320     if (!internalValue)
321         return 0;
322
323     // The map is here to maintain the object identity of the CSSValues over multiple invocations.
324     // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed.
325     if (!m_cssomCSSValueClones)
326         m_cssomCSSValueClones = std::make_unique<HashMap<CSSValue*, RefPtr<CSSValue>>>();
327     
328     RefPtr<CSSValue>& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtr<CSSValue>()).iterator->value;
329     if (!clonedValue)
330         clonedValue = internalValue->cloneForCSSOM();
331     return clonedValue.get();
332 }
333
334 StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const
335
336     CSSStyleSheet* cssStyleSheet = parentStyleSheet();
337     return cssStyleSheet ? &cssStyleSheet->contents() : 0;
338 }
339
340 CSSParserContext PropertySetCSSStyleDeclaration::cssParserContext() const
341 {
342     return CSSParserContext(m_propertySet->cssParserMode());
343 }
344
345 Ref<MutableStyleProperties> PropertySetCSSStyleDeclaration::copyProperties() const
346 {
347     return m_propertySet->mutableCopy();
348 }
349     
350 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStyleProperties& propertySet, CSSRule& parentRule)
351     : PropertySetCSSStyleDeclaration(&propertySet)
352     , m_refCount(1)
353     , m_parentRule(&parentRule)
354 {
355     m_propertySet->ref();
356 }
357
358 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration()
359 {
360     m_propertySet->deref();
361 }
362
363 void StyleRuleCSSStyleDeclaration::ref()
364
365     ++m_refCount;
366 }
367
368 void StyleRuleCSSStyleDeclaration::deref()
369
370     ASSERT(m_refCount);
371     if (!--m_refCount)
372         delete this;
373 }
374
375 bool StyleRuleCSSStyleDeclaration::willMutate()
376 {
377     if (!m_parentRule || !m_parentRule->parentStyleSheet())
378         return false;
379     m_parentRule->parentStyleSheet()->willMutateRules();
380     return true;
381 }
382
383 void StyleRuleCSSStyleDeclaration::didMutate(MutationType type)
384 {
385     ASSERT(m_parentRule);
386     ASSERT(m_parentRule->parentStyleSheet());
387
388     if (type == PropertyChanged)
389         m_cssomCSSValueClones = nullptr;
390
391     // Style sheet mutation needs to be signaled even if the change failed. willMutate*/didMutate* must pair.
392     m_parentRule->parentStyleSheet()->didMutateRuleFromCSSStyleDeclaration();
393 }
394
395 CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const
396 {
397     return m_parentRule ? m_parentRule->parentStyleSheet() : nullptr;
398 }
399
400 CSSParserContext StyleRuleCSSStyleDeclaration::cssParserContext() const
401 {
402     auto* styleSheet = contextStyleSheet();
403     if (!styleSheet)
404         return PropertySetCSSStyleDeclaration::cssParserContext();
405
406     return styleSheet->parserContext();
407 }
408
409 void StyleRuleCSSStyleDeclaration::reattach(MutableStyleProperties& propertySet)
410 {
411     m_propertySet->deref();
412     m_propertySet = &propertySet;
413     m_propertySet->ref();
414 }
415
416 void InlineCSSStyleDeclaration::didMutate(MutationType type)
417 {
418     if (type == NoChanges)
419         return;
420
421     m_cssomCSSValueClones = nullptr;
422
423     if (!m_parentElement)
424         return;
425
426     m_parentElement->invalidateStyleAttribute();
427     StyleAttributeMutationScope(this).didInvalidateStyleAttr();
428 }
429
430 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
431 {
432     return nullptr;
433 }
434
435 CSSParserContext InlineCSSStyleDeclaration::cssParserContext() const
436 {
437     if (!m_parentElement)
438         return PropertySetCSSStyleDeclaration::cssParserContext();
439
440     CSSParserContext context(m_parentElement->document());
441     context.mode = m_propertySet->cssParserMode();
442     return context;
443 }
444
445 } // namespace WebCore