e0361647659567b1e5962e2a4410b8bfed39a9f7
[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 "HTMLNames.h"
30 #include "InspectorInstrumentation.h"
31 #include "MutationObserverInterestGroup.h"
32 #include "MutationRecord.h"
33 #include "StyleProperties.h"
34 #include "StyledElement.h"
35
36 namespace WebCore {
37
38 namespace {
39
40 class StyleAttributeMutationScope {
41     WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope);
42 public:
43     StyleAttributeMutationScope(PropertySetCSSStyleDeclaration* decl)
44     {
45         ++s_scopeCount;
46
47         if (s_scopeCount != 1) {
48             ASSERT(s_currentDecl == decl);
49             return;
50         }
51
52         ASSERT(!s_currentDecl);
53         s_currentDecl = decl;
54
55         if (!s_currentDecl->parentElement())
56             return;
57
58         bool shouldReadOldValue = false;
59
60         m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(*s_currentDecl->parentElement(), HTMLNames::styleAttr);
61         if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested())
62             shouldReadOldValue = true;
63
64         AtomicString oldValue;
65         if (shouldReadOldValue)
66             oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr);
67
68         if (m_mutationRecipients) {
69             AtomicString requestedOldValue = m_mutationRecipients->isOldValueRequested() ? oldValue : nullAtom;
70             m_mutation = MutationRecord::createAttributes(*s_currentDecl->parentElement(), HTMLNames::styleAttr, requestedOldValue);
71         }
72     }
73
74     ~StyleAttributeMutationScope()
75     {
76         --s_scopeCount;
77         if (s_scopeCount)
78             return;
79
80         if (m_mutation && s_shouldDeliver)
81             m_mutationRecipients->enqueueMutationRecord(m_mutation.releaseNonNull());
82
83         s_shouldDeliver = false;
84         if (!s_shouldNotifyInspector) {
85             s_currentDecl = 0;
86             return;
87         }
88         // We have to clear internal state before calling Inspector's code.
89         PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl;
90         s_currentDecl = 0;
91         s_shouldNotifyInspector = false;
92         if (localCopyStyleDecl->parentElement())
93             InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement()->document(), *localCopyStyleDecl->parentElement());
94     }
95
96     void enqueueMutationRecord()
97     {
98         s_shouldDeliver = true;
99     }
100
101     void didInvalidateStyleAttr()
102     {
103         s_shouldNotifyInspector = true;
104     }
105
106 private:
107     static unsigned s_scopeCount;
108     static PropertySetCSSStyleDeclaration* s_currentDecl;
109     static bool s_shouldNotifyInspector;
110     static bool s_shouldDeliver;
111
112     std::unique_ptr<MutationObserverInterestGroup> m_mutationRecipients;
113     RefPtr<MutationRecord> m_mutation;
114 };
115
116 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
117 PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0;
118 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false;
119 bool StyleAttributeMutationScope::s_shouldDeliver = false;
120
121 } // namespace
122
123 void PropertySetCSSStyleDeclaration::ref()
124
125     m_propertySet->ref();
126 }
127
128 void PropertySetCSSStyleDeclaration::deref()
129 {
130     m_propertySet->deref(); 
131 }
132
133 unsigned PropertySetCSSStyleDeclaration::length() const
134 {
135     return m_propertySet->propertyCount();
136 }
137
138 String PropertySetCSSStyleDeclaration::item(unsigned i) const
139 {
140     if (i >= m_propertySet->propertyCount())
141         return String();
142     return m_propertySet->propertyAt(i).cssName();
143 }
144
145 String PropertySetCSSStyleDeclaration::cssText() const
146 {
147     return m_propertySet->asText();
148 }
149
150 void PropertySetCSSStyleDeclaration::setCssText(const String& text, ExceptionCode&)
151 {
152     StyleAttributeMutationScope mutationScope(this);
153     if (!willMutate())
154         return;
155
156     bool changed = m_propertySet->parseDeclaration(text, contextStyleSheet());
157
158     didMutate(changed ? PropertyChanged : NoChanges);
159
160     mutationScope.enqueueMutationRecord();    
161 }
162
163 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName)
164 {
165     if (isCustomPropertyName(propertyName)) {
166         RefPtr<CSSValue> value = m_propertySet->getCustomPropertyCSSValue(propertyName);
167         if (!value)
168             return nullptr;
169         return cloneAndCacheForCSSOM(value.get());
170     }
171     
172     CSSPropertyID propertyID = cssPropertyID(propertyName);
173     if (!propertyID)
174         return nullptr;
175     return cloneAndCacheForCSSOM(getPropertyCSSValueInternal(propertyID).get());
176 }
177
178 String PropertySetCSSStyleDeclaration::getPropertyValue(const String& propertyName)
179 {
180     if (isCustomPropertyName(propertyName))
181         return m_propertySet->getCustomPropertyValue(propertyName);
182
183     CSSPropertyID propertyID = cssPropertyID(propertyName);
184     if (!propertyID)
185         return String();
186     return getPropertyValueInternal(propertyID);
187 }
188
189 String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName)
190 {
191     if (isCustomPropertyName(propertyName))
192         return m_propertySet->customPropertyIsImportant(propertyName) ? ASCIILiteral("important") : emptyString();
193
194     CSSPropertyID propertyID = cssPropertyID(propertyName);
195     if (!propertyID)
196         return String();
197     return m_propertySet->propertyIsImportant(propertyID) ? ASCIILiteral("important") : emptyString();
198 }
199
200 String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName)
201 {
202     CSSPropertyID propertyID = cssPropertyID(propertyName);
203     if (!propertyID)
204         return String();
205     return m_propertySet->getPropertyShorthand(propertyID);
206 }
207
208 bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName)
209 {
210     CSSPropertyID propertyID = cssPropertyID(propertyName);
211     if (!propertyID)
212         return false;
213     return m_propertySet->isPropertyImplicit(propertyID);
214 }
215
216 void PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority, ExceptionCode& ec)
217 {
218     StyleAttributeMutationScope mutationScope(this);
219     
220     CSSPropertyID propertyID = cssPropertyID(propertyName);
221     if (isCustomPropertyName(propertyName))
222         propertyID = CSSPropertyCustom;
223     if (!propertyID)
224         return;
225
226     if (!willMutate())
227         return;
228
229     bool important = priority.find("important", 0, false) != notFound;
230
231     ec = 0;
232     bool changed = propertyID != CSSPropertyCustom ? m_propertySet->setProperty(propertyID, value, important, contextStyleSheet()) : m_propertySet->setCustomProperty(propertyName, value, important, contextStyleSheet());
233
234     didMutate(changed ? PropertyChanged : NoChanges);
235
236     if (changed) {
237         // CSS DOM requires raising SYNTAX_ERR of parsing failed, but this is too dangerous for compatibility,
238         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
239         mutationScope.enqueueMutationRecord();
240     }
241 }
242
243 String PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName, ExceptionCode& ec)
244 {
245     StyleAttributeMutationScope mutationScope(this);
246     CSSPropertyID propertyID = cssPropertyID(propertyName);
247     if (isCustomPropertyName(propertyName))
248         propertyID = CSSPropertyCustom;
249     if (!propertyID)
250         return String();
251
252     if (!willMutate())
253         return String();
254
255     ec = 0;
256     String result;
257     bool changed = propertyID != CSSPropertyCustom ? m_propertySet->removeProperty(propertyID, &result) : m_propertySet->removeCustomProperty(propertyName, &result);
258
259     didMutate(changed ? PropertyChanged : NoChanges);
260
261     if (changed)
262         mutationScope.enqueueMutationRecord();
263     return result;
264 }
265
266 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID)
267 {
268     RefPtr<CSSValue> value = m_propertySet->getPropertyCSSValue(propertyID);
269     if (value)
270         return value;
271
272     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propertyID);
273     if (prefixingVariant != propertyID)
274         return m_propertySet->getPropertyCSSValue(prefixingVariant);
275
276     return nullptr;
277 }
278
279 String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID)
280 {
281     String value = m_propertySet->getPropertyValue(propertyID);
282     if (!value.isEmpty())
283         return value;
284
285     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propertyID);
286     if (prefixingVariant != propertyID)
287         return m_propertySet->getPropertyValue(prefixingVariant);
288
289     return String();
290 }
291
292 bool PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important, ExceptionCode& ec)
293
294     StyleAttributeMutationScope mutationScope(this);
295     if (!willMutate())
296         return false;
297
298     ec = 0;
299     bool changed = m_propertySet->setProperty(propertyID, value, important, contextStyleSheet());
300
301     didMutate(changed ? PropertyChanged : NoChanges);
302
303     if (changed)
304         mutationScope.enqueueMutationRecord();
305     return changed;
306 }
307
308 CSSValue* PropertySetCSSStyleDeclaration::cloneAndCacheForCSSOM(CSSValue* internalValue)
309 {
310     if (!internalValue)
311         return 0;
312
313     // The map is here to maintain the object identity of the CSSValues over multiple invocations.
314     // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed.
315     if (!m_cssomCSSValueClones)
316         m_cssomCSSValueClones = std::make_unique<HashMap<CSSValue*, RefPtr<CSSValue>>>();
317     
318     RefPtr<CSSValue>& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtr<CSSValue>()).iterator->value;
319     if (!clonedValue)
320         clonedValue = internalValue->cloneForCSSOM();
321     return clonedValue.get();
322 }
323
324 StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const
325
326     CSSStyleSheet* cssStyleSheet = parentStyleSheet();
327     return cssStyleSheet ? &cssStyleSheet->contents() : 0;
328 }
329
330 Ref<MutableStyleProperties> PropertySetCSSStyleDeclaration::copyProperties() const
331 {
332     return m_propertySet->mutableCopy();
333 }
334     
335 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStyleProperties& propertySet, CSSRule& parentRule)
336     : PropertySetCSSStyleDeclaration(&propertySet)
337     , m_refCount(1)
338     , m_parentRule(&parentRule)
339 {
340     m_propertySet->ref();
341 }
342
343 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration()
344 {
345     m_propertySet->deref();
346 }
347
348 void StyleRuleCSSStyleDeclaration::ref()
349
350     ++m_refCount;
351 }
352
353 void StyleRuleCSSStyleDeclaration::deref()
354
355     ASSERT(m_refCount);
356     if (!--m_refCount)
357         delete this;
358 }
359
360 bool StyleRuleCSSStyleDeclaration::willMutate()
361 {
362     if (!m_parentRule || !m_parentRule->parentStyleSheet())
363         return false;
364     m_parentRule->parentStyleSheet()->willMutateRules();
365     return true;
366 }
367
368 void StyleRuleCSSStyleDeclaration::didMutate(MutationType type)
369 {
370     ASSERT(m_parentRule);
371     ASSERT(m_parentRule->parentStyleSheet());
372
373     if (type == PropertyChanged)
374         m_cssomCSSValueClones = nullptr;
375
376     // Style sheet mutation needs to be signaled even if the change failed. willMutate*/didMutate* must pair.
377     m_parentRule->parentStyleSheet()->didMutateRuleFromCSSStyleDeclaration();
378 }
379
380 CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const
381 {
382     return m_parentRule ? m_parentRule->parentStyleSheet() : 0;
383 }
384
385 void StyleRuleCSSStyleDeclaration::reattach(MutableStyleProperties& propertySet)
386 {
387     m_propertySet->deref();
388     m_propertySet = &propertySet;
389     m_propertySet->ref();
390 }
391
392 void InlineCSSStyleDeclaration::didMutate(MutationType type)
393 {
394     if (type == NoChanges)
395         return;
396
397     m_cssomCSSValueClones = nullptr;
398
399     if (!m_parentElement)
400         return;
401
402     m_parentElement->invalidateStyleAttribute();
403     StyleAttributeMutationScope(this).didInvalidateStyleAttr();
404 }
405
406 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
407 {
408     return m_parentElement ? &m_parentElement->document().elementSheet() : 0;
409 }
410
411 } // namespace WebCore