Reduce the number of required tag bits for the JSValue.
[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 "CSSPropertyParser.h"
26 #include "CSSRule.h"
27 #include "CSSStyleSheet.h"
28 #include "CustomElementReactionQueue.h"
29 #include "HTMLNames.h"
30 #include "InspectorInstrumentation.h"
31 #include "MutationObserverInterestGroup.h"
32 #include "MutationRecord.h"
33 #include "StyleProperties.h"
34 #include "StyleSheetContents.h"
35 #include "StyledElement.h"
36 #include <wtf/IsoMallocInlines.h>
37
38 namespace WebCore {
39
40 WTF_MAKE_ISO_ALLOCATED_IMPL(PropertySetCSSStyleDeclaration);
41 WTF_MAKE_ISO_ALLOCATED_IMPL(StyleRuleCSSStyleDeclaration);
42 WTF_MAKE_ISO_ALLOCATED_IMPL(InlineCSSStyleDeclaration);
43
44 class StyleAttributeMutationScope {
45     WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope);
46 public:
47     StyleAttributeMutationScope(PropertySetCSSStyleDeclaration* decl)
48     {
49         ++s_scopeCount;
50
51         if (s_scopeCount != 1) {
52             ASSERT(s_currentDecl == decl);
53             return;
54         }
55
56         ASSERT(!s_currentDecl);
57         s_currentDecl = decl;
58
59         auto* element = s_currentDecl->parentElement();
60         if (!element)
61             return;
62
63         bool shouldReadOldValue = false;
64
65         m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(*s_currentDecl->parentElement(), HTMLNames::styleAttr);
66         if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested())
67             shouldReadOldValue = true;
68
69         if (UNLIKELY(element->isDefinedCustomElement())) {
70             auto* reactionQueue = element->reactionQueue();
71             if (reactionQueue && reactionQueue->observesStyleAttribute()) {
72                 m_customElement = element;
73                 shouldReadOldValue = true;
74             }
75         }
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                 auto& 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 = nullptr;
101             return;
102         }
103         // We have to clear internal state before calling Inspector's code.
104         PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl;
105         s_currentDecl = nullptr;
106         s_shouldNotifyInspector = false;
107
108         if (auto* parentElement = localCopyStyleDecl->parentElement())
109             InspectorInstrumentation::didInvalidateStyleAttr(*parentElement);
110     }
111
112     void enqueueMutationRecord()
113     {
114         s_shouldDeliver = true;
115     }
116
117     void didInvalidateStyleAttr()
118     {
119         s_shouldNotifyInspector = true;
120     }
121
122 private:
123     static unsigned s_scopeCount;
124     static PropertySetCSSStyleDeclaration* s_currentDecl;
125     static bool s_shouldNotifyInspector;
126     static bool s_shouldDeliver;
127
128     std::unique_ptr<MutationObserverInterestGroup> m_mutationRecipients;
129     AtomString m_oldValue;
130     RefPtr<Element> m_customElement;
131 };
132
133 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
134 PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = nullptr;
135 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false;
136 bool StyleAttributeMutationScope::s_shouldDeliver = false;
137
138 void PropertySetCSSStyleDeclaration::ref()
139
140     m_propertySet->ref();
141 }
142
143 void PropertySetCSSStyleDeclaration::deref()
144 {
145     m_propertySet->deref(); 
146 }
147
148 unsigned PropertySetCSSStyleDeclaration::length() const
149 {
150     return m_propertySet->propertyCount();
151 }
152
153 String PropertySetCSSStyleDeclaration::item(unsigned i) const
154 {
155     if (i >= m_propertySet->propertyCount())
156         return String();
157     return m_propertySet->propertyAt(i).cssName();
158 }
159
160 String PropertySetCSSStyleDeclaration::cssText() const
161 {
162     return m_propertySet->asText();
163 }
164
165 ExceptionOr<void> PropertySetCSSStyleDeclaration::setCssText(const String& text)
166 {
167     StyleAttributeMutationScope mutationScope(this);
168     if (!willMutate())
169         return { };
170
171     bool changed = m_propertySet->parseDeclaration(text, cssParserContext());
172
173     didMutate(changed ? PropertyChanged : NoChanges);
174
175     mutationScope.enqueueMutationRecord();
176     return { };
177 }
178
179 RefPtr<DeprecatedCSSOMValue> PropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName)
180 {
181     if (isCustomPropertyName(propertyName)) {
182         RefPtr<CSSValue> value = m_propertySet->getCustomPropertyCSSValue(propertyName);
183         if (!value)
184             return nullptr;
185         return wrapForDeprecatedCSSOM(value.get());
186     }
187     
188     CSSPropertyID propertyID = cssPropertyID(propertyName);
189     if (!propertyID)
190         return nullptr;
191     return wrapForDeprecatedCSSOM(getPropertyCSSValueInternal(propertyID).get());
192 }
193
194 String PropertySetCSSStyleDeclaration::getPropertyValue(const String& propertyName)
195 {
196     if (isCustomPropertyName(propertyName))
197         return m_propertySet->getCustomPropertyValue(propertyName);
198
199     CSSPropertyID propertyID = cssPropertyID(propertyName);
200     if (!propertyID)
201         return String();
202     return getPropertyValueInternal(propertyID);
203 }
204
205 String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName)
206 {
207     if (isCustomPropertyName(propertyName))
208         return m_propertySet->customPropertyIsImportant(propertyName) ? "important"_s : emptyString();
209
210     CSSPropertyID propertyID = cssPropertyID(propertyName);
211     if (!propertyID)
212         return String();
213     return m_propertySet->propertyIsImportant(propertyID) ? "important"_s : emptyString();
214 }
215
216 String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName)
217 {
218     CSSPropertyID propertyID = cssPropertyID(propertyName);
219     if (!propertyID)
220         return String();
221     return m_propertySet->getPropertyShorthand(propertyID);
222 }
223
224 bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName)
225 {
226     CSSPropertyID propertyID = cssPropertyID(propertyName);
227     if (!propertyID)
228         return false;
229     return m_propertySet->isPropertyImplicit(propertyID);
230 }
231
232 ExceptionOr<void> PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority)
233 {
234     StyleAttributeMutationScope mutationScope(this);
235
236     CSSPropertyID propertyID = cssPropertyID(propertyName);
237     if (isCustomPropertyName(propertyName))
238         propertyID = CSSPropertyCustom;
239     if (!propertyID)
240         return { };
241
242     if (!willMutate())
243         return { };
244
245     bool important = equalIgnoringASCIICase(priority, "important");
246     if (!important && !priority.isEmpty())
247         return { };
248
249     bool changed;
250     if (UNLIKELY(propertyID == CSSPropertyCustom)) {
251         Document* document = nullptr;
252
253         if (parentElement())
254             document = &parentElement()->document();
255         else
256             document = parentStyleSheet()->ownerDocument();
257
258         changed = m_propertySet->setCustomProperty(document, propertyName, value, important, cssParserContext());
259     } else
260         changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext());
261
262     didMutate(changed ? PropertyChanged : NoChanges);
263
264     if (changed) {
265         // CSS DOM requires raising SyntaxError of parsing failed, but this is too dangerous for compatibility,
266         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
267         mutationScope.enqueueMutationRecord();
268     }
269
270     return { };
271 }
272
273 ExceptionOr<String> PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName)
274 {
275     StyleAttributeMutationScope mutationScope(this);
276     CSSPropertyID propertyID = cssPropertyID(propertyName);
277     if (isCustomPropertyName(propertyName))
278         propertyID = CSSPropertyCustom;
279     if (!propertyID)
280         return String();
281
282     if (!willMutate())
283         return String();
284
285     String result;
286     bool changed = propertyID != CSSPropertyCustom ? m_propertySet->removeProperty(propertyID, &result) : m_propertySet->removeCustomProperty(propertyName, &result);
287
288     didMutate(changed ? PropertyChanged : NoChanges);
289
290     if (changed)
291         mutationScope.enqueueMutationRecord();
292     return result;
293 }
294
295 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID)
296 {
297     return m_propertySet->getPropertyCSSValue(propertyID);
298 }
299
300 String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID)
301 {
302     String value = m_propertySet->getPropertyValue(propertyID);
303     if (!value.isEmpty())
304         return value;
305
306     return String();
307 }
308
309 ExceptionOr<bool> PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important)
310
311     StyleAttributeMutationScope mutationScope(this);
312     if (!willMutate())
313         return false;
314
315     bool changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext());
316
317     didMutate(changed ? PropertyChanged : NoChanges);
318
319     if (changed)
320         mutationScope.enqueueMutationRecord();
321     return changed;
322 }
323
324 RefPtr<DeprecatedCSSOMValue> PropertySetCSSStyleDeclaration::wrapForDeprecatedCSSOM(CSSValue* internalValue)
325 {
326     if (!internalValue)
327         return nullptr;
328
329     // The map is here to maintain the object identity of the CSSValues over multiple invocations.
330     // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed.
331     if (!m_cssomValueWrappers)
332         m_cssomValueWrappers = makeUnique<HashMap<CSSValue*, WeakPtr<DeprecatedCSSOMValue>>>();
333     
334     auto& clonedValue = m_cssomValueWrappers->add(internalValue, WeakPtr<DeprecatedCSSOMValue>()).iterator->value;
335     if (clonedValue)
336         return clonedValue.get();
337
338     RefPtr<DeprecatedCSSOMValue> wrapper = internalValue->createDeprecatedCSSOMWrapper(*this);
339     clonedValue = makeWeakPtr(wrapper.get());
340     return wrapper;
341 }
342
343 StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const
344
345     CSSStyleSheet* cssStyleSheet = parentStyleSheet();
346     return cssStyleSheet ? &cssStyleSheet->contents() : nullptr;
347 }
348
349 CSSParserContext PropertySetCSSStyleDeclaration::cssParserContext() const
350 {
351     return CSSParserContext(m_propertySet->cssParserMode());
352 }
353
354 Ref<MutableStyleProperties> PropertySetCSSStyleDeclaration::copyProperties() const
355 {
356     return m_propertySet->mutableCopy();
357 }
358     
359 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStyleProperties& propertySet, CSSRule& parentRule)
360     : PropertySetCSSStyleDeclaration(propertySet)
361     , m_refCount(1)
362     , m_parentRule(&parentRule)
363 {
364     m_propertySet->ref();
365 }
366
367 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration()
368 {
369     m_propertySet->deref();
370 }
371
372 void StyleRuleCSSStyleDeclaration::ref()
373
374     ++m_refCount;
375 }
376
377 void StyleRuleCSSStyleDeclaration::deref()
378
379     ASSERT(m_refCount);
380     if (!--m_refCount)
381         delete this;
382 }
383
384 bool StyleRuleCSSStyleDeclaration::willMutate()
385 {
386     if (!m_parentRule || !m_parentRule->parentStyleSheet())
387         return false;
388     m_parentRule->parentStyleSheet()->willMutateRules();
389     return true;
390 }
391
392 void StyleRuleCSSStyleDeclaration::didMutate(MutationType type)
393 {
394     ASSERT(m_parentRule);
395     ASSERT(m_parentRule->parentStyleSheet());
396
397     if (type == PropertyChanged)
398         m_cssomValueWrappers = nullptr;
399
400     // Style sheet mutation needs to be signaled even if the change failed. willMutate*/didMutate* must pair.
401     m_parentRule->parentStyleSheet()->didMutateRuleFromCSSStyleDeclaration();
402 }
403
404 CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const
405 {
406     return m_parentRule ? m_parentRule->parentStyleSheet() : nullptr;
407 }
408
409 CSSParserContext StyleRuleCSSStyleDeclaration::cssParserContext() const
410 {
411     auto* styleSheet = contextStyleSheet();
412     if (!styleSheet)
413         return PropertySetCSSStyleDeclaration::cssParserContext();
414
415     return styleSheet->parserContext();
416 }
417
418 void StyleRuleCSSStyleDeclaration::reattach(MutableStyleProperties& propertySet)
419 {
420     m_propertySet->deref();
421     m_propertySet = &propertySet;
422     m_propertySet->ref();
423 }
424
425 bool InlineCSSStyleDeclaration::willMutate()
426 {
427     if (m_parentElement)
428         InspectorInstrumentation::willInvalidateStyleAttr(*m_parentElement);
429     return true;
430 }
431
432 void InlineCSSStyleDeclaration::didMutate(MutationType type)
433 {
434     if (type == NoChanges)
435         return;
436
437     m_cssomValueWrappers = nullptr;
438
439     if (!m_parentElement)
440         return;
441
442     m_parentElement->invalidateStyleAttribute();
443     StyleAttributeMutationScope(this).didInvalidateStyleAttr();
444 }
445
446 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
447 {
448     return nullptr;
449 }
450
451 CSSParserContext InlineCSSStyleDeclaration::cssParserContext() const
452 {
453     if (!m_parentElement)
454         return PropertySetCSSStyleDeclaration::cssParserContext();
455
456     CSSParserContext context(m_parentElement->document());
457     context.mode = m_propertySet->cssParserMode();
458     return context;
459 }
460
461 } // namespace WebCore