Home | History | Annotate | Download | only in css
      1 /*
      2  * (C) 1999-2003 Lars Knoll (knoll (at) 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 "core/css/PropertySetCSSStyleDeclaration.h"
     24 
     25 #include "HTMLNames.h"
     26 #include "RuntimeEnabledFeatures.h"
     27 #include "bindings/v8/ExceptionState.h"
     28 #include "core/css/CSSParser.h"
     29 #include "core/css/CSSStyleSheet.h"
     30 #include "core/css/StylePropertySet.h"
     31 #include "core/dom/Element.h"
     32 #include "core/dom/MutationObserverInterestGroup.h"
     33 #include "core/dom/MutationRecord.h"
     34 #include "core/inspector/InspectorInstrumentation.h"
     35 
     36 using namespace std;
     37 
     38 namespace WebCore {
     39 
     40 namespace {
     41 
     42 class StyleAttributeMutationScope {
     43     WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope);
     44 public:
     45     StyleAttributeMutationScope(PropertySetCSSStyleDeclaration* decl)
     46     {
     47         InspectorInstrumentation::willMutateStyle(decl);
     48         ++s_scopeCount;
     49 
     50         if (s_scopeCount != 1) {
     51             ASSERT(s_currentDecl == decl);
     52             return;
     53         }
     54 
     55         ASSERT(!s_currentDecl);
     56         s_currentDecl = decl;
     57 
     58         if (!s_currentDecl->parentElement())
     59             return;
     60 
     61         bool shouldReadOldValue = false;
     62 
     63         m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(*s_currentDecl->parentElement(), HTMLNames::styleAttr);
     64         if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested())
     65             shouldReadOldValue = true;
     66 
     67         AtomicString oldValue;
     68         if (shouldReadOldValue)
     69             oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr);
     70 
     71         if (m_mutationRecipients) {
     72             AtomicString requestedOldValue = m_mutationRecipients->isOldValueRequested() ? oldValue : nullAtom;
     73             m_mutation = MutationRecord::createAttributes(s_currentDecl->parentElement(), HTMLNames::styleAttr, requestedOldValue);
     74         }
     75     }
     76 
     77     ~StyleAttributeMutationScope()
     78     {
     79         --s_scopeCount;
     80         if (s_scopeCount)
     81             return;
     82 
     83         if (m_mutation && s_shouldDeliver)
     84             m_mutationRecipients->enqueueMutationRecord(m_mutation);
     85 
     86         s_shouldDeliver = false;
     87 
     88         // We have to clear internal state before calling Inspector's code.
     89         PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl;
     90         s_currentDecl = 0;
     91         InspectorInstrumentation::didMutateStyle(localCopyStyleDecl, localCopyStyleDecl->parentElement());
     92 
     93         if (!s_shouldNotifyInspector)
     94             return;
     95 
     96         s_shouldNotifyInspector = false;
     97         if (localCopyStyleDecl->parentElement())
     98             InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement());
     99     }
    100 
    101     void enqueueMutationRecord()
    102     {
    103         s_shouldDeliver = true;
    104     }
    105 
    106     void didInvalidateStyleAttr()
    107     {
    108         s_shouldNotifyInspector = true;
    109     }
    110 
    111 private:
    112     static unsigned s_scopeCount;
    113     static PropertySetCSSStyleDeclaration* s_currentDecl;
    114     static bool s_shouldNotifyInspector;
    115     static bool s_shouldDeliver;
    116 
    117     OwnPtr<MutationObserverInterestGroup> m_mutationRecipients;
    118     RefPtr<MutationRecord> m_mutation;
    119 };
    120 
    121 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
    122 PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0;
    123 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false;
    124 bool StyleAttributeMutationScope::s_shouldDeliver = false;
    125 
    126 } // namespace
    127 
    128 void PropertySetCSSStyleDeclaration::ref()
    129 {
    130     m_propertySet->ref();
    131 }
    132 
    133 void PropertySetCSSStyleDeclaration::deref()
    134 {
    135     m_propertySet->deref();
    136 }
    137 
    138 unsigned PropertySetCSSStyleDeclaration::length() const
    139 {
    140     return m_propertySet->propertyCount();
    141 }
    142 
    143 String PropertySetCSSStyleDeclaration::item(unsigned i) const
    144 {
    145     if (i >= m_propertySet->propertyCount())
    146         return "";
    147     return m_propertySet->propertyAt(i).cssName();
    148 }
    149 
    150 String PropertySetCSSStyleDeclaration::cssText() const
    151 {
    152     return m_propertySet->asText();
    153 }
    154 
    155 void PropertySetCSSStyleDeclaration::setCSSText(const String& text, ExceptionState& exceptionState)
    156 {
    157     StyleAttributeMutationScope mutationScope(this);
    158     willMutate();
    159 
    160     // FIXME: Detect syntax errors and set exceptionState.
    161     m_propertySet->parseDeclaration(text, contextStyleSheet());
    162 
    163     didMutate(PropertyChanged);
    164 
    165     mutationScope.enqueueMutationRecord();
    166 }
    167 
    168 PassRefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName)
    169 {
    170     CSSPropertyID propertyID = cssPropertyID(propertyName);
    171     if (!propertyID)
    172         return 0;
    173     return cloneAndCacheForCSSOM(m_propertySet->getPropertyCSSValue(propertyID).get());
    174 }
    175 
    176 String PropertySetCSSStyleDeclaration::getPropertyValue(const String &propertyName)
    177 {
    178     CSSPropertyID propertyID = cssPropertyID(propertyName);
    179     if (!propertyID)
    180         return String();
    181     return m_propertySet->getPropertyValue(propertyID);
    182 }
    183 
    184 String PropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName)
    185 {
    186     CSSPropertyID propertyID = cssPropertyID(propertyName);
    187     if (!propertyID)
    188         return String();
    189     return m_propertySet->propertyIsImportant(propertyID) ? "important" : "";
    190 }
    191 
    192 String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName)
    193 {
    194     CSSPropertyID propertyID = cssPropertyID(propertyName);
    195     if (!propertyID)
    196         return String();
    197     CSSPropertyID shorthandID = m_propertySet->getPropertyShorthand(propertyID);
    198     if (!shorthandID)
    199         return String();
    200     return getPropertyNameString(shorthandID);
    201 }
    202 
    203 bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName)
    204 {
    205     CSSPropertyID propertyID = cssPropertyID(propertyName);
    206     if (!propertyID)
    207         return false;
    208     return m_propertySet->isPropertyImplicit(propertyID);
    209 }
    210 
    211 void PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority, ExceptionState& exceptionState)
    212 {
    213     StyleAttributeMutationScope mutationScope(this);
    214     CSSPropertyID propertyID = cssPropertyID(propertyName);
    215     if (!propertyID)
    216         return;
    217 
    218     bool important = priority.find("important", 0, false) != kNotFound;
    219 
    220     willMutate();
    221 
    222     bool changed = m_propertySet->setProperty(propertyID, value, important, contextStyleSheet());
    223 
    224     didMutate(changed ? PropertyChanged : NoChanges);
    225 
    226     if (changed) {
    227         // CSS DOM requires raising SyntaxError of parsing failed, but this is too dangerous for compatibility,
    228         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
    229         mutationScope.enqueueMutationRecord();
    230     }
    231 }
    232 
    233 String PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName, ExceptionState& exceptionState)
    234 {
    235     StyleAttributeMutationScope mutationScope(this);
    236     CSSPropertyID propertyID = cssPropertyID(propertyName);
    237     if (!propertyID)
    238         return String();
    239 
    240     willMutate();
    241 
    242     String result;
    243     bool changed = m_propertySet->removeProperty(propertyID, &result);
    244 
    245     didMutate(changed ? PropertyChanged : NoChanges);
    246 
    247     if (changed)
    248         mutationScope.enqueueMutationRecord();
    249     return result;
    250 }
    251 
    252 PassRefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID)
    253 {
    254     return m_propertySet->getPropertyCSSValue(propertyID);
    255 }
    256 
    257 String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID)
    258 {
    259     return m_propertySet->getPropertyValue(propertyID);
    260 }
    261 
    262 void PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important, ExceptionState&)
    263 {
    264     StyleAttributeMutationScope mutationScope(this);
    265     willMutate();
    266 
    267     bool changed = m_propertySet->setProperty(propertyID, value, important, contextStyleSheet());
    268 
    269     didMutate(changed ? PropertyChanged : NoChanges);
    270 
    271     if (changed)
    272         mutationScope.enqueueMutationRecord();
    273 }
    274 
    275 unsigned PropertySetCSSStyleDeclaration::variableCount() const
    276 {
    277     ASSERT(RuntimeEnabledFeatures::cssVariablesEnabled());
    278     return m_propertySet->variableCount();
    279 }
    280 
    281 String PropertySetCSSStyleDeclaration::variableValue(const AtomicString& name) const
    282 {
    283     ASSERT(RuntimeEnabledFeatures::cssVariablesEnabled());
    284     return m_propertySet->variableValue(name);
    285 }
    286 
    287 bool PropertySetCSSStyleDeclaration::setVariableValue(const AtomicString& name, const String& value, ExceptionState&)
    288 {
    289     ASSERT(RuntimeEnabledFeatures::cssVariablesEnabled());
    290     StyleAttributeMutationScope mutationScope(this);
    291     willMutate();
    292     bool changed = m_propertySet->setVariableValue(name, value);
    293     didMutate(changed ? PropertyChanged : NoChanges);
    294     if (changed)
    295         mutationScope.enqueueMutationRecord();
    296     return changed;
    297 }
    298 
    299 bool PropertySetCSSStyleDeclaration::removeVariable(const AtomicString& name)
    300 {
    301     ASSERT(RuntimeEnabledFeatures::cssVariablesEnabled());
    302     StyleAttributeMutationScope mutationScope(this);
    303     willMutate();
    304     bool changed = m_propertySet->removeVariable(name);
    305     didMutate(changed ? PropertyChanged : NoChanges);
    306     if (changed)
    307         mutationScope.enqueueMutationRecord();
    308     return changed;
    309 }
    310 
    311 bool PropertySetCSSStyleDeclaration::clearVariables(ExceptionState&)
    312 {
    313     ASSERT(RuntimeEnabledFeatures::cssVariablesEnabled());
    314     StyleAttributeMutationScope mutationScope(this);
    315     willMutate();
    316     bool changed = m_propertySet->clearVariables();
    317     didMutate(changed ? PropertyChanged : NoChanges);
    318     if (changed)
    319         mutationScope.enqueueMutationRecord();
    320     return changed;
    321 }
    322 
    323 PassRefPtr<CSSVariablesIterator> PropertySetCSSStyleDeclaration::variablesIterator() const
    324 {
    325     return m_propertySet->variablesIterator();
    326 }
    327 
    328 CSSValue* PropertySetCSSStyleDeclaration::cloneAndCacheForCSSOM(CSSValue* internalValue)
    329 {
    330     if (!internalValue)
    331         return 0;
    332 
    333     // The map is here to maintain the object identity of the CSSValues over multiple invocations.
    334     // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed.
    335     if (!m_cssomCSSValueClones)
    336         m_cssomCSSValueClones = adoptPtr(new HashMap<CSSValue*, RefPtr<CSSValue> >);
    337 
    338     RefPtr<CSSValue>& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtr<CSSValue>()).iterator->value;
    339     if (!clonedValue)
    340         clonedValue = internalValue->cloneForCSSOM();
    341     return clonedValue.get();
    342 }
    343 
    344 StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const
    345 {
    346     CSSStyleSheet* cssStyleSheet = parentStyleSheet();
    347     return cssStyleSheet ? cssStyleSheet->contents() : 0;
    348 }
    349 
    350 PassRefPtr<MutableStylePropertySet> PropertySetCSSStyleDeclaration::copyProperties() const
    351 {
    352     return m_propertySet->mutableCopy();
    353 }
    354 
    355 bool PropertySetCSSStyleDeclaration::cssPropertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
    356 {
    357     return m_propertySet->propertyMatches(propertyID, propertyValue);
    358 }
    359 
    360 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStylePropertySet* propertySet, CSSRule* parentRule)
    361     : PropertySetCSSStyleDeclaration(propertySet)
    362     , m_refCount(1)
    363     , m_parentRule(parentRule)
    364 {
    365     m_propertySet->ref();
    366 }
    367 
    368 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration()
    369 {
    370     m_propertySet->deref();
    371 }
    372 
    373 void StyleRuleCSSStyleDeclaration::ref()
    374 {
    375     ++m_refCount;
    376 }
    377 
    378 void StyleRuleCSSStyleDeclaration::deref()
    379 {
    380     ASSERT(m_refCount);
    381     if (!--m_refCount)
    382         delete this;
    383 }
    384 
    385 void StyleRuleCSSStyleDeclaration::willMutate()
    386 {
    387     if (m_parentRule && m_parentRule->parentStyleSheet())
    388         m_parentRule->parentStyleSheet()->willMutateRules();
    389 }
    390 
    391 void StyleRuleCSSStyleDeclaration::didMutate(MutationType type)
    392 {
    393     if (type == PropertyChanged)
    394         m_cssomCSSValueClones.clear();
    395 
    396     // Style sheet mutation needs to be signaled even if the change failed. willMutateRules/didMutateRules must pair.
    397     if (m_parentRule && m_parentRule->parentStyleSheet())
    398         m_parentRule->parentStyleSheet()->didMutateRules();
    399 }
    400 
    401 CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const
    402 {
    403     return m_parentRule ? m_parentRule->parentStyleSheet() : 0;
    404 }
    405 
    406 void StyleRuleCSSStyleDeclaration::reattach(MutableStylePropertySet* propertySet)
    407 {
    408     ASSERT(propertySet);
    409     m_propertySet->deref();
    410     m_propertySet = propertySet;
    411     m_propertySet->ref();
    412 }
    413 
    414 void InlineCSSStyleDeclaration::didMutate(MutationType type)
    415 {
    416     if (type == NoChanges)
    417         return;
    418 
    419     m_cssomCSSValueClones.clear();
    420 
    421     if (!m_parentElement)
    422         return;
    423 
    424     m_parentElement->setNeedsStyleRecalc(LocalStyleChange);
    425     m_parentElement->invalidateStyleAttribute();
    426     StyleAttributeMutationScope(this).didInvalidateStyleAttr();
    427 }
    428 
    429 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
    430 {
    431     return m_parentElement ? m_parentElement->document().elementSheet() : 0;
    432 }
    433 
    434 } // namespace WebCore
    435