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