Add modern API for overriding the page's specified viewport configuration
[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 "CSSPropertyParser.h"
26 #include "CSSRule.h"
27 #include "CSSStyleSheet.h"
28 #include "CustomElementReactionQueue.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 #include <wtf/IsoMallocInlines.h>
37
38 namespace WebCore {
39
40 WTF_MAKE_ISO_ALLOCATED_IMPL(PropertySetCSSStyleDeclaration);
41 WTF_MAKE_ISO_ALLOCATED_IMPL(StyleRuleCSSStyleDeclaration);
42 WTF_MAKE_ISO_ALLOCATED_IMPL(InlineCSSStyleDeclaration);
43
44 class StyleAttributeMutationScope {
45     WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope);
46 public:
47     StyleAttributeMutationScope(PropertySetCSSStyleDeclaration* decl)
48     {
49         ++s_scopeCount;
50
51         if (s_scopeCount != 1) {
52             ASSERT(s_currentDecl == decl);
53             return;
54         }
55
56         ASSERT(!s_currentDecl);
57         s_currentDecl = decl;
58
59         auto* element = s_currentDecl->parentElement();
60         if (!element)
61             return;
62
63         bool shouldReadOldValue = false;
64
65         m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(*s_currentDecl->parentElement(), HTMLNames::styleAttr);
66         if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested())
67             shouldReadOldValue = true;
68
69         if (UNLIKELY(element->isDefinedCustomElement())) {
70             auto* reactionQueue = element->reactionQueue();
71             if (reactionQueue && reactionQueue->observesStyleAttribute()) {
72                 m_customElement = element;
73                 shouldReadOldValue = true;
74             }
75         }
76
77         if (shouldReadOldValue)
78             m_oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr);
79     }
80
81     ~StyleAttributeMutationScope()
82     {
83         --s_scopeCount;
84         if (s_scopeCount)
85             return;
86
87         if (s_shouldDeliver) {
88             if (m_mutationRecipients) {
89                 auto mutation = MutationRecord::createAttributes(*s_currentDecl->parentElement(), HTMLNames::styleAttr, m_oldValue);
90                 m_mutationRecipients->enqueueMutationRecord(WTFMove(mutation));
91             }
92             if (m_customElement) {
93                 auto& newValue = m_customElement->getAttribute(HTMLNames::styleAttr);
94                 CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(*m_customElement, HTMLNames::styleAttr, m_oldValue, newValue);
95             }
96         }
97
98         s_shouldDeliver = false;
99         if (!s_shouldNotifyInspector) {
100             s_currentDecl = nullptr;
101             return;
102         }
103         // We have to clear internal state before calling Inspector's code.
104         PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl;
105         s_currentDecl = nullptr;
106         s_shouldNotifyInspector = false;
107         if (localCopyStyleDecl->parentElement())
108             InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement()->document(), *localCopyStyleDecl->parentElement());
109     }
110
111     void enqueueMutationRecord()
112     {
113         s_shouldDeliver = true;
114     }
115
116     void didInvalidateStyleAttr()
117     {
118         s_shouldNotifyInspector = true;
119     }
120
121 private:
122     static unsigned s_scopeCount;
123     static PropertySetCSSStyleDeclaration* s_currentDecl;
124     static bool s_shouldNotifyInspector;
125     static bool s_shouldDeliver;
126
127     std::unique_ptr<MutationObserverInterestGroup> m_mutationRecipients;
128     AtomicString m_oldValue;
129     RefPtr<Element> m_customElement;
130 };
131
132 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
133 PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = nullptr;
134 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false;
135 bool StyleAttributeMutationScope::s_shouldDeliver = false;
136
137 void PropertySetCSSStyleDeclaration::ref()
138
139     m_propertySet->ref();
140 }
141
142 void PropertySetCSSStyleDeclaration::deref()
143 {
144     m_propertySet->deref(); 
145 }
146
147 unsigned PropertySetCSSStyleDeclaration::length() const
148 {
149     return m_propertySet->propertyCount();
150 }
151
152 String PropertySetCSSStyleDeclaration::item(unsigned i) const
153 {
154     if (i >= m_propertySet->propertyCount())
155         return String();
156     return m_propertySet->propertyAt(i).cssName();
157 }
158
159 String PropertySetCSSStyleDeclaration::cssText() const
160 {
161     return m_propertySet->asText();
162 }
163
164 ExceptionOr<void> PropertySetCSSStyleDeclaration::setCssText(const String& text)
165 {
166     StyleAttributeMutationScope mutationScope(this);
167     if (!willMutate())
168         return { };
169
170     bool changed = m_propertySet->parseDeclaration(text, cssParserContext());
171
172     didMutate(changed ? PropertyChanged : NoChanges);
173
174     mutationScope.enqueueMutationRecord();
175     return { };
176 }
177
178 RefPtr<DeprecatedCSSOMValue> PropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName)
179 {
180     if (isCustomPropertyName(propertyName)) {
181         RefPtr<CSSValue> value = m_propertySet->getCustomPropertyCSSValue(propertyName);
182         if (!value)
183             return nullptr;
184         return wrapForDeprecatedCSSOM(value.get());
185     }
186     
187     CSSPropertyID propertyID = cssPropertyID(propertyName);
188     if (!propertyID)
189         return nullptr;
190     return wrapForDeprecatedCSSOM(getPropertyCSSValueInternal(propertyID).get());
191 }
192
193 String PropertySetCSSStyleDeclaration::getPropertyValue(const String& propertyName)
194 {
195     if (isCustomPropertyName(propertyName))
196         return m_propertySet->getCustomPropertyValue(propertyName);
197
198     CSSPropertyID propertyID = cssPropertyID(propertyName);
199     if (!propertyID)
200         return String();
201     return getPropertyValueInternal(propertyID);
202 }
203
204 String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName)
205 {
206     if (isCustomPropertyName(propertyName))
207         return m_propertySet->customPropertyIsImportant(propertyName) ? "important"_s : emptyString();
208
209     CSSPropertyID propertyID = cssPropertyID(propertyName);
210     if (!propertyID)
211         return String();
212     return m_propertySet->propertyIsImportant(propertyID) ? "important"_s : emptyString();
213 }
214
215 String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName)
216 {
217     CSSPropertyID propertyID = cssPropertyID(propertyName);
218     if (!propertyID)
219         return String();
220     return m_propertySet->getPropertyShorthand(propertyID);
221 }
222
223 bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName)
224 {
225     CSSPropertyID propertyID = cssPropertyID(propertyName);
226     if (!propertyID)
227         return false;
228     return m_propertySet->isPropertyImplicit(propertyID);
229 }
230
231 ExceptionOr<void> PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority)
232 {
233     StyleAttributeMutationScope mutationScope(this);
234
235     CSSPropertyID propertyID = cssPropertyID(propertyName);
236     if (isCustomPropertyName(propertyName))
237         propertyID = CSSPropertyCustom;
238     if (!propertyID)
239         return { };
240
241     if (!willMutate())
242         return { };
243
244     bool important = equalIgnoringASCIICase(priority, "important");
245     if (!important && !priority.isEmpty())
246         return { };
247
248     bool changed;
249     if (UNLIKELY(propertyID == CSSPropertyCustom)) {
250         Document* document = nullptr;
251
252         if (parentElement())
253             document = &parentElement()->document();
254         else
255             document = parentStyleSheet()->ownerDocument();
256
257         changed = m_propertySet->setCustomProperty(document, propertyName, value, important, cssParserContext());
258     } else
259         changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext());
260
261     didMutate(changed ? PropertyChanged : NoChanges);
262
263     if (changed) {
264         // CSS DOM requires raising SyntaxError of parsing failed, but this is too dangerous for compatibility,
265         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
266         mutationScope.enqueueMutationRecord();
267     }
268
269     return { };
270 }
271
272 ExceptionOr<String> PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName)
273 {
274     StyleAttributeMutationScope mutationScope(this);
275     CSSPropertyID propertyID = cssPropertyID(propertyName);
276     if (isCustomPropertyName(propertyName))
277         propertyID = CSSPropertyCustom;
278     if (!propertyID)
279         return String();
280
281     if (!willMutate())
282         return String();
283
284     String result;
285     bool changed = propertyID != CSSPropertyCustom ? m_propertySet->removeProperty(propertyID, &result) : m_propertySet->removeCustomProperty(propertyName, &result);
286
287     didMutate(changed ? PropertyChanged : NoChanges);
288
289     if (changed)
290         mutationScope.enqueueMutationRecord();
291     return result;
292 }
293
294 RefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID)
295 {
296     return m_propertySet->getPropertyCSSValue(propertyID);
297 }
298
299 String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID)
300 {
301     String value = m_propertySet->getPropertyValue(propertyID);
302     if (!value.isEmpty())
303         return value;
304
305     return String();
306 }
307
308 ExceptionOr<bool> PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important)
309
310     StyleAttributeMutationScope mutationScope(this);
311     if (!willMutate())
312         return false;
313
314     bool changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext());
315
316     didMutate(changed ? PropertyChanged : NoChanges);
317
318     if (changed)
319         mutationScope.enqueueMutationRecord();
320     return changed;
321 }
322
323 RefPtr<DeprecatedCSSOMValue> PropertySetCSSStyleDeclaration::wrapForDeprecatedCSSOM(CSSValue* internalValue)
324 {
325     if (!internalValue)
326         return nullptr;
327
328     // The map is here to maintain the object identity of the CSSValues over multiple invocations.
329     // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed.
330     if (!m_cssomValueWrappers)
331         m_cssomValueWrappers = std::make_unique<HashMap<CSSValue*, WeakPtr<DeprecatedCSSOMValue>>>();
332     
333     auto& clonedValue = m_cssomValueWrappers->add(internalValue, WeakPtr<DeprecatedCSSOMValue>()).iterator->value;
334     if (clonedValue)
335         return clonedValue.get();
336
337     RefPtr<DeprecatedCSSOMValue> wrapper = internalValue->createDeprecatedCSSOMWrapper(*this);
338     clonedValue = makeWeakPtr(wrapper.get());
339     return wrapper;
340 }
341
342 StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const
343
344     CSSStyleSheet* cssStyleSheet = parentStyleSheet();
345     return cssStyleSheet ? &cssStyleSheet->contents() : nullptr;
346 }
347
348 CSSParserContext PropertySetCSSStyleDeclaration::cssParserContext() const
349 {
350     return CSSParserContext(m_propertySet->cssParserMode());
351 }
352
353 Ref<MutableStyleProperties> PropertySetCSSStyleDeclaration::copyProperties() const
354 {
355     return m_propertySet->mutableCopy();
356 }
357     
358 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStyleProperties& propertySet, CSSRule& parentRule)
359     : PropertySetCSSStyleDeclaration(propertySet)
360     , m_refCount(1)
361     , m_parentRule(&parentRule)
362 {
363     m_propertySet->ref();
364 }
365
366 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration()
367 {
368     m_propertySet->deref();
369 }
370
371 void StyleRuleCSSStyleDeclaration::ref()
372
373     ++m_refCount;
374 }
375
376 void StyleRuleCSSStyleDeclaration::deref()
377
378     ASSERT(m_refCount);
379     if (!--m_refCount)
380         delete this;
381 }
382
383 bool StyleRuleCSSStyleDeclaration::willMutate()
384 {
385     if (!m_parentRule || !m_parentRule->parentStyleSheet())
386         return false;
387     m_parentRule->parentStyleSheet()->willMutateRules();
388     return true;
389 }
390
391 void StyleRuleCSSStyleDeclaration::didMutate(MutationType type)
392 {
393     ASSERT(m_parentRule);
394     ASSERT(m_parentRule->parentStyleSheet());
395
396     if (type == PropertyChanged)
397         m_cssomValueWrappers = nullptr;
398
399     // Style sheet mutation needs to be signaled even if the change failed. willMutate*/didMutate* must pair.
400     m_parentRule->parentStyleSheet()->didMutateRuleFromCSSStyleDeclaration();
401 }
402
403 CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const
404 {
405     return m_parentRule ? m_parentRule->parentStyleSheet() : nullptr;
406 }
407
408 CSSParserContext StyleRuleCSSStyleDeclaration::cssParserContext() const
409 {
410     auto* styleSheet = contextStyleSheet();
411     if (!styleSheet)
412         return PropertySetCSSStyleDeclaration::cssParserContext();
413
414     return styleSheet->parserContext();
415 }
416
417 void StyleRuleCSSStyleDeclaration::reattach(MutableStyleProperties& propertySet)
418 {
419     m_propertySet->deref();
420     m_propertySet = &propertySet;
421     m_propertySet->ref();
422 }
423
424 void InlineCSSStyleDeclaration::didMutate(MutationType type)
425 {
426     if (type == NoChanges)
427         return;
428
429     m_cssomValueWrappers = nullptr;
430
431     if (!m_parentElement)
432         return;
433
434     m_parentElement->invalidateStyleAttribute();
435     StyleAttributeMutationScope(this).didInvalidateStyleAttr();
436 }
437
438 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
439 {
440     return nullptr;
441 }
442
443 CSSParserContext InlineCSSStyleDeclaration::cssParserContext() const
444 {
445     if (!m_parentElement)
446         return PropertySetCSSStyleDeclaration::cssParserContext();
447
448     CSSParserContext context(m_parentElement->document());
449     context.mode = m_propertySet->cssParserMode();
450     return context;
451 }
452
453 } // namespace WebCore