REGRESSION (r202950): Image zoom animations are broken at medium.com (159861)
[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(m_propertySet->getPropertyCSSValue(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 m_propertySet->getPropertyValue(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     return m_propertySet->getPropertyCSSValue(propertyID);
269 }
270
271 String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID)
272
273     return m_propertySet->getPropertyValue(propertyID);
274 }
275
276 bool PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important, ExceptionCode& ec)
277
278     StyleAttributeMutationScope mutationScope(this);
279     if (!willMutate())
280         return false;
281
282     ec = 0;
283     bool changed = m_propertySet->setProperty(propertyID, value, important, contextStyleSheet());
284
285     didMutate(changed ? PropertyChanged : NoChanges);
286
287     if (changed)
288         mutationScope.enqueueMutationRecord();
289     return changed;
290 }
291
292 CSSValue* PropertySetCSSStyleDeclaration::cloneAndCacheForCSSOM(CSSValue* internalValue)
293 {
294     if (!internalValue)
295         return 0;
296
297     // The map is here to maintain the object identity of the CSSValues over multiple invocations.
298     // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed.
299     if (!m_cssomCSSValueClones)
300         m_cssomCSSValueClones = std::make_unique<HashMap<CSSValue*, RefPtr<CSSValue>>>();
301     
302     RefPtr<CSSValue>& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtr<CSSValue>()).iterator->value;
303     if (!clonedValue)
304         clonedValue = internalValue->cloneForCSSOM();
305     return clonedValue.get();
306 }
307
308 StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const
309
310     CSSStyleSheet* cssStyleSheet = parentStyleSheet();
311     return cssStyleSheet ? &cssStyleSheet->contents() : 0;
312 }
313
314 Ref<MutableStyleProperties> PropertySetCSSStyleDeclaration::copyProperties() const
315 {
316     return m_propertySet->mutableCopy();
317 }
318     
319 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStyleProperties& propertySet, CSSRule& parentRule)
320     : PropertySetCSSStyleDeclaration(&propertySet)
321     , m_refCount(1)
322     , m_parentRule(&parentRule)
323 {
324     m_propertySet->ref();
325 }
326
327 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration()
328 {
329     m_propertySet->deref();
330 }
331
332 void StyleRuleCSSStyleDeclaration::ref()
333
334     ++m_refCount;
335 }
336
337 void StyleRuleCSSStyleDeclaration::deref()
338
339     ASSERT(m_refCount);
340     if (!--m_refCount)
341         delete this;
342 }
343
344 bool StyleRuleCSSStyleDeclaration::willMutate()
345 {
346     if (!m_parentRule || !m_parentRule->parentStyleSheet())
347         return false;
348     m_parentRule->parentStyleSheet()->willMutateRules();
349     return true;
350 }
351
352 void StyleRuleCSSStyleDeclaration::didMutate(MutationType type)
353 {
354     ASSERT(m_parentRule);
355     ASSERT(m_parentRule->parentStyleSheet());
356
357     if (type == PropertyChanged)
358         m_cssomCSSValueClones = nullptr;
359
360     // Style sheet mutation needs to be signaled even if the change failed. willMutate*/didMutate* must pair.
361     m_parentRule->parentStyleSheet()->didMutateRuleFromCSSStyleDeclaration();
362 }
363
364 CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const
365 {
366     return m_parentRule ? m_parentRule->parentStyleSheet() : 0;
367 }
368
369 void StyleRuleCSSStyleDeclaration::reattach(MutableStyleProperties& propertySet)
370 {
371     m_propertySet->deref();
372     m_propertySet = &propertySet;
373     m_propertySet->ref();
374 }
375
376 void InlineCSSStyleDeclaration::didMutate(MutationType type)
377 {
378     if (type == NoChanges)
379         return;
380
381     m_cssomCSSValueClones = nullptr;
382
383     if (!m_parentElement)
384         return;
385
386     m_parentElement->invalidateStyleAttribute();
387     StyleAttributeMutationScope(this).didInvalidateStyleAttr();
388 }
389
390 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
391 {
392     return m_parentElement ? &m_parentElement->document().elementSheet() : 0;
393 }
394
395 } // namespace WebCore