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