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