fd5e1b477e17200253dc017ddb354118ace2a08e
[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 "StyleSheetContents.h"
35 #include "StyledElement.h"
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 (!s_currentDecl->parentElement())
57             return;
58
59         bool shouldReadOldValue = false;
60
61         m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(*s_currentDecl->parentElement(), HTMLNames::styleAttr);
62         if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested())
63             shouldReadOldValue = true;
64
65         AtomicString oldValue;
66         if (shouldReadOldValue)
67             oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr);
68
69         if (m_mutationRecipients) {
70             AtomicString requestedOldValue = m_mutationRecipients->isOldValueRequested() ? oldValue : nullAtom;
71             m_mutation = MutationRecord::createAttributes(*s_currentDecl->parentElement(), HTMLNames::styleAttr, requestedOldValue);
72         }
73     }
74
75     ~StyleAttributeMutationScope()
76     {
77         --s_scopeCount;
78         if (s_scopeCount)
79             return;
80
81         if (m_mutation && s_shouldDeliver)
82             m_mutationRecipients->enqueueMutationRecord(m_mutation.releaseNonNull());
83
84         s_shouldDeliver = false;
85         if (!s_shouldNotifyInspector) {
86             s_currentDecl = 0;
87             return;
88         }
89         // We have to clear internal state before calling Inspector's code.
90         PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl;
91         s_currentDecl = 0;
92         s_shouldNotifyInspector = false;
93         if (localCopyStyleDecl->parentElement())
94             InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement()->document(), *localCopyStyleDecl->parentElement());
95     }
96
97     void enqueueMutationRecord()
98     {
99         s_shouldDeliver = true;
100     }
101
102     void didInvalidateStyleAttr()
103     {
104         s_shouldNotifyInspector = true;
105     }
106
107 private:
108     static unsigned s_scopeCount;
109     static PropertySetCSSStyleDeclaration* s_currentDecl;
110     static bool s_shouldNotifyInspector;
111     static bool s_shouldDeliver;
112
113     std::unique_ptr<MutationObserverInterestGroup> m_mutationRecipients;
114     RefPtr<MutationRecord> m_mutation;
115 };
116
117 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
118 PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0;
119 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false;
120 bool StyleAttributeMutationScope::s_shouldDeliver = false;
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 unsigned PropertySetCSSStyleDeclaration::length() const
135 {
136     return m_propertySet->propertyCount();
137 }
138
139 String PropertySetCSSStyleDeclaration::item(unsigned i) const
140 {
141     if (i >= m_propertySet->propertyCount())
142         return String();
143     return m_propertySet->propertyAt(i).cssName();
144 }
145
146 String PropertySetCSSStyleDeclaration::cssText() const
147 {
148     return m_propertySet->asText();
149 }
150
151 ExceptionOr<void> PropertySetCSSStyleDeclaration::setCssText(const String& text)
152 {
153     StyleAttributeMutationScope mutationScope(this);
154     if (!willMutate())
155         return { };
156
157     bool changed = m_propertySet->parseDeclaration(text, cssParserContext(), contextStyleSheet());
158
159     didMutate(changed ? PropertyChanged : NoChanges);
160
161     mutationScope.enqueueMutationRecord();
162     return { };
163 }
164
165 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName)
166 {
167     if (isCustomPropertyName(propertyName)) {
168         RefPtr<CSSValue> value = m_propertySet->getCustomPropertyCSSValue(propertyName);
169         if (!value)
170             return nullptr;
171         return cloneAndCacheForCSSOM(value.get());
172     }
173     
174     CSSPropertyID propertyID = cssPropertyID(propertyName);
175     if (!propertyID)
176         return nullptr;
177     return cloneAndCacheForCSSOM(getPropertyCSSValueInternal(propertyID).get());
178 }
179
180 String PropertySetCSSStyleDeclaration::getPropertyValue(const String& propertyName)
181 {
182     if (isCustomPropertyName(propertyName))
183         return m_propertySet->getCustomPropertyValue(propertyName);
184
185     CSSPropertyID propertyID = cssPropertyID(propertyName);
186     if (!propertyID)
187         return String();
188     return getPropertyValueInternal(propertyID);
189 }
190
191 String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName)
192 {
193     if (isCustomPropertyName(propertyName))
194         return m_propertySet->customPropertyIsImportant(propertyName) ? ASCIILiteral("important") : emptyString();
195
196     CSSPropertyID propertyID = cssPropertyID(propertyName);
197     if (!propertyID)
198         return String();
199     return m_propertySet->propertyIsImportant(propertyID) ? ASCIILiteral("important") : emptyString();
200 }
201
202 String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName)
203 {
204     CSSPropertyID propertyID = cssPropertyID(propertyName);
205     if (!propertyID)
206         return String();
207     return m_propertySet->getPropertyShorthand(propertyID);
208 }
209
210 bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName)
211 {
212     CSSPropertyID propertyID = cssPropertyID(propertyName);
213     if (!propertyID)
214         return false;
215     return m_propertySet->isPropertyImplicit(propertyID);
216 }
217
218 ExceptionOr<void> PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority)
219 {
220     StyleAttributeMutationScope mutationScope(this);
221     
222     CSSPropertyID propertyID = cssPropertyID(propertyName);
223     if (isCustomPropertyName(propertyName))
224         propertyID = CSSPropertyCustom;
225     if (!propertyID)
226         return { };
227
228     if (!willMutate())
229         return { };
230
231     bool important = equalIgnoringASCIICase(priority, "important");
232     if (!important && !priority.isEmpty())
233         return { };
234
235     bool changed;
236     if (propertyID == CSSPropertyCustom)
237         changed = m_propertySet->setCustomProperty(propertyName, value, important, cssParserContext(), contextStyleSheet());
238     else
239         changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext(), contextStyleSheet());
240
241     didMutate(changed ? PropertyChanged : NoChanges);
242
243     if (changed) {
244         // CSS DOM requires raising SYNTAX_ERR of parsing failed, but this is too dangerous for compatibility,
245         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
246         mutationScope.enqueueMutationRecord();
247     }
248
249     return { };
250 }
251
252 ExceptionOr<String> PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName)
253 {
254     StyleAttributeMutationScope mutationScope(this);
255     CSSPropertyID propertyID = cssPropertyID(propertyName);
256     if (isCustomPropertyName(propertyName))
257         propertyID = CSSPropertyCustom;
258     if (!propertyID)
259         return String();
260
261     if (!willMutate())
262         return String();
263
264     String result;
265     bool changed = propertyID != CSSPropertyCustom ? m_propertySet->removeProperty(propertyID, &result) : m_propertySet->removeCustomProperty(propertyName, &result);
266
267     didMutate(changed ? PropertyChanged : NoChanges);
268
269     if (changed)
270         mutationScope.enqueueMutationRecord();
271     return WTFMove(result);
272 }
273
274 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID)
275 {
276     return m_propertySet->getPropertyCSSValue(propertyID);
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     return String();
286 }
287
288 ExceptionOr<bool> PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important)
289
290     StyleAttributeMutationScope mutationScope(this);
291     if (!willMutate())
292         return false;
293
294     bool changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext(), contextStyleSheet());
295
296     didMutate(changed ? PropertyChanged : NoChanges);
297
298     if (changed)
299         mutationScope.enqueueMutationRecord();
300     return changed;
301 }
302
303 CSSValue* PropertySetCSSStyleDeclaration::cloneAndCacheForCSSOM(CSSValue* internalValue)
304 {
305     if (!internalValue)
306         return 0;
307
308     // The map is here to maintain the object identity of the CSSValues over multiple invocations.
309     // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed.
310     if (!m_cssomCSSValueClones)
311         m_cssomCSSValueClones = std::make_unique<HashMap<CSSValue*, RefPtr<CSSValue>>>();
312     
313     RefPtr<CSSValue>& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtr<CSSValue>()).iterator->value;
314     if (!clonedValue)
315         clonedValue = internalValue->cloneForCSSOM();
316     return clonedValue.get();
317 }
318
319 StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const
320
321     CSSStyleSheet* cssStyleSheet = parentStyleSheet();
322     return cssStyleSheet ? &cssStyleSheet->contents() : 0;
323 }
324
325 CSSParserContext PropertySetCSSStyleDeclaration::cssParserContext() const
326 {
327     return CSSParserContext(m_propertySet->cssParserMode());
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() : nullptr;
383 }
384
385 CSSParserContext StyleRuleCSSStyleDeclaration::cssParserContext() const
386 {
387     auto* styleSheet = contextStyleSheet();
388     if (!styleSheet)
389         return PropertySetCSSStyleDeclaration::cssParserContext();
390
391     return styleSheet->parserContext();
392 }
393
394 void StyleRuleCSSStyleDeclaration::reattach(MutableStyleProperties& propertySet)
395 {
396     m_propertySet->deref();
397     m_propertySet = &propertySet;
398     m_propertySet->ref();
399 }
400
401 void InlineCSSStyleDeclaration::didMutate(MutationType type)
402 {
403     if (type == NoChanges)
404         return;
405
406     m_cssomCSSValueClones = nullptr;
407
408     if (!m_parentElement)
409         return;
410
411     m_parentElement->invalidateStyleAttribute();
412     StyleAttributeMutationScope(this).didInvalidateStyleAttr();
413 }
414
415 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
416 {
417     return nullptr;
418 }
419
420 CSSParserContext InlineCSSStyleDeclaration::cssParserContext() const
421 {
422     if (!m_parentElement)
423         return PropertySetCSSStyleDeclaration::cssParserContext();
424
425     CSSParserContext context(m_parentElement->document());
426     context.mode = m_propertySet->cssParserMode();
427     return context;
428 }
429
430 } // namespace WebCore