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