/* * (C) 1999-2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved. * Copyright (C) 2011 Research In Motion Limited. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "PropertySetCSSStyleDeclaration.h" #include "CSSParser.h" #include "CSSStyleSheet.h" #include "HTMLNames.h" #include "InspectorInstrumentation.h" #include "MutationObserverInterestGroup.h" #include "MutationRecord.h" #include "StyleProperties.h" #include "StyledElement.h" namespace WebCore { namespace { class StyleAttributeMutationScope { WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope); public: StyleAttributeMutationScope(PropertySetCSSStyleDeclaration* decl) { ++s_scopeCount; if (s_scopeCount != 1) { ASSERT(s_currentDecl == decl); return; } ASSERT(!s_currentDecl); s_currentDecl = decl; if (!s_currentDecl->parentElement()) return; bool shouldReadOldValue = false; m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(*s_currentDecl->parentElement(), HTMLNames::styleAttr); if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested()) shouldReadOldValue = true; AtomicString oldValue; if (shouldReadOldValue) oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr); if (m_mutationRecipients) { AtomicString requestedOldValue = m_mutationRecipients->isOldValueRequested() ? oldValue : nullAtom; m_mutation = MutationRecord::createAttributes(*s_currentDecl->parentElement(), HTMLNames::styleAttr, requestedOldValue); } } ~StyleAttributeMutationScope() { --s_scopeCount; if (s_scopeCount) return; if (m_mutation && s_shouldDeliver) m_mutationRecipients->enqueueMutationRecord(m_mutation); s_shouldDeliver = false; if (!s_shouldNotifyInspector) { s_currentDecl = 0; return; } // We have to clear internal state before calling Inspector's code. PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl; s_currentDecl = 0; s_shouldNotifyInspector = false; if (localCopyStyleDecl->parentElement()) InspectorInstrumentation::didInvalidateStyleAttr(&localCopyStyleDecl->parentElement()->document(), localCopyStyleDecl->parentElement()); } void enqueueMutationRecord() { s_shouldDeliver = true; } void didInvalidateStyleAttr() { s_shouldNotifyInspector = true; } private: static unsigned s_scopeCount; static PropertySetCSSStyleDeclaration* s_currentDecl; static bool s_shouldNotifyInspector; static bool s_shouldDeliver; std::unique_ptr m_mutationRecipients; RefPtr m_mutation; }; unsigned StyleAttributeMutationScope::s_scopeCount = 0; PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0; bool StyleAttributeMutationScope::s_shouldNotifyInspector = false; bool StyleAttributeMutationScope::s_shouldDeliver = false; } // namespace void PropertySetCSSStyleDeclaration::ref() { m_propertySet->ref(); } void PropertySetCSSStyleDeclaration::deref() { m_propertySet->deref(); } unsigned PropertySetCSSStyleDeclaration::length() const { return m_propertySet->propertyCount(); } String PropertySetCSSStyleDeclaration::item(unsigned i) const { if (i >= m_propertySet->propertyCount()) return ""; return m_propertySet->propertyAt(i).cssName(); } String PropertySetCSSStyleDeclaration::cssText() const { return m_propertySet->asText(); } void PropertySetCSSStyleDeclaration::setCssText(const String& text, ExceptionCode& ec) { StyleAttributeMutationScope mutationScope(this); if (!willMutate()) return; ec = 0; // FIXME: Detect syntax errors and set ec. m_propertySet->parseDeclaration(text, contextStyleSheet()); didMutate(PropertyChanged); mutationScope.enqueueMutationRecord(); } PassRefPtr PropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName) { CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return 0; return cloneAndCacheForCSSOM(m_propertySet->getPropertyCSSValue(propertyID).get()); } String PropertySetCSSStyleDeclaration::getPropertyValue(const String &propertyName) { CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return String(); return m_propertySet->getPropertyValue(propertyID); } String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName) { CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return String(); return m_propertySet->propertyIsImportant(propertyID) ? "important" : ""; } String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName) { CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return String(); return m_propertySet->getPropertyShorthand(propertyID); } bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName) { CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return false; return m_propertySet->isPropertyImplicit(propertyID); } void PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority, ExceptionCode& ec) { StyleAttributeMutationScope mutationScope(this); CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return; if (!willMutate()) return; bool important = priority.find("important", 0, false) != notFound; ec = 0; bool changed = m_propertySet->setProperty(propertyID, value, important, contextStyleSheet()); didMutate(changed ? PropertyChanged : NoChanges); if (changed) { // CSS DOM requires raising SYNTAX_ERR of parsing failed, but this is too dangerous for compatibility, // see . mutationScope.enqueueMutationRecord(); } } String PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName, ExceptionCode& ec) { StyleAttributeMutationScope mutationScope(this); CSSPropertyID propertyID = cssPropertyID(propertyName); if (!propertyID) return String(); if (!willMutate()) return String(); ec = 0; String result; bool changed = m_propertySet->removeProperty(propertyID, &result); didMutate(changed ? PropertyChanged : NoChanges); if (changed) mutationScope.enqueueMutationRecord(); return result; } PassRefPtr PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID) { return m_propertySet->getPropertyCSSValue(propertyID); } String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID) { return m_propertySet->getPropertyValue(propertyID); } void PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important, ExceptionCode& ec) { StyleAttributeMutationScope mutationScope(this); if (!willMutate()) return; ec = 0; bool changed = m_propertySet->setProperty(propertyID, value, important, contextStyleSheet()); didMutate(changed ? PropertyChanged : NoChanges); if (changed) mutationScope.enqueueMutationRecord(); } CSSValue* PropertySetCSSStyleDeclaration::cloneAndCacheForCSSOM(CSSValue* internalValue) { if (!internalValue) return 0; // The map is here to maintain the object identity of the CSSValues over multiple invocations. // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed. if (!m_cssomCSSValueClones) m_cssomCSSValueClones = std::make_unique>>(); RefPtr& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtr()).iterator->value; if (!clonedValue) clonedValue = internalValue->cloneForCSSOM(); return clonedValue.get(); } StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const { CSSStyleSheet* cssStyleSheet = parentStyleSheet(); return cssStyleSheet ? &cssStyleSheet->contents() : 0; } PassRef PropertySetCSSStyleDeclaration::copyProperties() const { return m_propertySet->mutableCopy(); } StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStyleProperties& propertySet, CSSRule& parentRule) : PropertySetCSSStyleDeclaration(&propertySet) , m_refCount(1) , m_parentRule(&parentRule) { m_propertySet->ref(); } StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration() { m_propertySet->deref(); } void StyleRuleCSSStyleDeclaration::ref() { ++m_refCount; } void StyleRuleCSSStyleDeclaration::deref() { ASSERT(m_refCount); if (!--m_refCount) delete this; } bool StyleRuleCSSStyleDeclaration::willMutate() { if (!m_parentRule || !m_parentRule->parentStyleSheet()) return false; m_parentRule->parentStyleSheet()->willMutateRules(); return true; } void StyleRuleCSSStyleDeclaration::didMutate(MutationType type) { ASSERT(m_parentRule); ASSERT(m_parentRule->parentStyleSheet()); if (type == PropertyChanged) m_cssomCSSValueClones = nullptr; // Style sheet mutation needs to be signaled even if the change failed. willMutate*/didMutate* must pair. m_parentRule->parentStyleSheet()->didMutateRuleFromCSSStyleDeclaration(); } CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const { return m_parentRule ? m_parentRule->parentStyleSheet() : 0; } void StyleRuleCSSStyleDeclaration::reattach(MutableStyleProperties& propertySet) { m_propertySet->deref(); m_propertySet = &propertySet; m_propertySet->ref(); } void InlineCSSStyleDeclaration::didMutate(MutationType type) { if (type == NoChanges) return; m_cssomCSSValueClones = nullptr; if (!m_parentElement) return; m_parentElement->setNeedsStyleRecalc(InlineStyleChange); m_parentElement->invalidateStyleAttribute(); StyleAttributeMutationScope(this).didInvalidateStyleAttr(); } CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const { return m_parentElement ? &m_parentElement->document().elementSheet() : 0; } } // namespace WebCore