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