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 "CSSParser.h" 26#include "CSSStyleSheet.h" 27#include "HTMLNames.h" 28#include "InspectorInstrumentation.h" 29#include "MutationObserverInterestGroup.h" 30#include "MutationRecord.h" 31#include "StylePropertySet.h" 32#include "StyledElement.h" 33 34using namespace std; 35 36namespace WebCore { 37 38namespace { 39 40class StyleAttributeMutationScope { 41 WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope); 42public: 43 StyleAttributeMutationScope(PropertySetCSSStyleDeclaration* decl) 44 { 45 ++s_scopeCount; 46 47 if (s_scopeCount != 1) { 48 ASSERT(s_currentDecl == decl); 49 return; 50 } 51 52 ASSERT(!s_currentDecl); 53 s_currentDecl = decl; 54 55 if (!s_currentDecl->parentElement()) 56 return; 57 58 bool shouldReadOldValue = false; 59 60 m_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(s_currentDecl->parentElement(), HTMLNames::styleAttr); 61 if (m_mutationRecipients && m_mutationRecipients->isOldValueRequested()) 62 shouldReadOldValue = true; 63 64 AtomicString oldValue; 65 if (shouldReadOldValue) 66 oldValue = s_currentDecl->parentElement()->getAttribute(HTMLNames::styleAttr); 67 68 if (m_mutationRecipients) { 69 AtomicString requestedOldValue = m_mutationRecipients->isOldValueRequested() ? oldValue : nullAtom; 70 m_mutation = MutationRecord::createAttributes(s_currentDecl->parentElement(), HTMLNames::styleAttr, requestedOldValue); 71 } 72 } 73 74 ~StyleAttributeMutationScope() 75 { 76 --s_scopeCount; 77 if (s_scopeCount) 78 return; 79 80 if (m_mutation && s_shouldDeliver) 81 m_mutationRecipients->enqueueMutationRecord(m_mutation); 82 83 s_shouldDeliver = false; 84 if (!s_shouldNotifyInspector) { 85 s_currentDecl = 0; 86 return; 87 } 88 // We have to clear internal state before calling Inspector's code. 89 PropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl; 90 s_currentDecl = 0; 91 s_shouldNotifyInspector = false; 92 if (localCopyStyleDecl->parentElement() && localCopyStyleDecl->parentElement()->document()) 93 InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement()->document(), localCopyStyleDecl->parentElement()); 94 } 95 96 void enqueueMutationRecord() 97 { 98 s_shouldDeliver = true; 99 } 100 101 void didInvalidateStyleAttr() 102 { 103 s_shouldNotifyInspector = true; 104 } 105 106private: 107 static unsigned s_scopeCount; 108 static PropertySetCSSStyleDeclaration* s_currentDecl; 109 static bool s_shouldNotifyInspector; 110 static bool s_shouldDeliver; 111 112 OwnPtr<MutationObserverInterestGroup> m_mutationRecipients; 113 RefPtr<MutationRecord> m_mutation; 114}; 115 116unsigned StyleAttributeMutationScope::s_scopeCount = 0; 117PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0; 118bool StyleAttributeMutationScope::s_shouldNotifyInspector = false; 119bool StyleAttributeMutationScope::s_shouldDeliver = false; 120 121} // namespace 122 123void PropertySetCSSStyleDeclaration::ref() 124{ 125 m_propertySet->ref(); 126} 127 128void PropertySetCSSStyleDeclaration::deref() 129{ 130 m_propertySet->deref(); 131} 132 133unsigned PropertySetCSSStyleDeclaration::length() const 134{ 135 return m_propertySet->propertyCount(); 136} 137 138String PropertySetCSSStyleDeclaration::item(unsigned i) const 139{ 140 if (i >= m_propertySet->propertyCount()) 141 return ""; 142 return m_propertySet->propertyAt(i).cssName(); 143} 144 145String PropertySetCSSStyleDeclaration::cssText() const 146{ 147 return m_propertySet->asText(); 148} 149 150void PropertySetCSSStyleDeclaration::setCssText(const String& text, ExceptionCode& ec) 151{ 152 StyleAttributeMutationScope mutationScope(this); 153 if (!willMutate()) 154 return; 155 156 ec = 0; 157 // FIXME: Detect syntax errors and set ec. 158 m_propertySet->parseDeclaration(text, contextStyleSheet()); 159 160 didMutate(PropertyChanged); 161 162 mutationScope.enqueueMutationRecord(); 163} 164 165PassRefPtr<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 173String 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 181String 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 189String PropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName) 190{ 191 CSSPropertyID propertyID = cssPropertyID(propertyName); 192 if (!propertyID) 193 return String(); 194 return m_propertySet->getPropertyShorthand(propertyID); 195} 196 197bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName) 198{ 199 CSSPropertyID propertyID = cssPropertyID(propertyName); 200 if (!propertyID) 201 return false; 202 return m_propertySet->isPropertyImplicit(propertyID); 203} 204 205void PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority, ExceptionCode& ec) 206{ 207 StyleAttributeMutationScope mutationScope(this); 208 CSSPropertyID propertyID = cssPropertyID(propertyName); 209 if (!propertyID) 210 return; 211 212 if (!willMutate()) 213 return; 214 215 bool important = priority.find("important", 0, false) != notFound; 216 217 ec = 0; 218 bool changed = m_propertySet->setProperty(propertyID, value, important, contextStyleSheet()); 219 220 didMutate(changed ? PropertyChanged : NoChanges); 221 222 if (changed) { 223 // CSS DOM requires raising SYNTAX_ERR of parsing failed, but this is too dangerous for compatibility, 224 // see <http://bugs.webkit.org/show_bug.cgi?id=7296>. 225 mutationScope.enqueueMutationRecord(); 226 } 227} 228 229String PropertySetCSSStyleDeclaration::removeProperty(const String& propertyName, ExceptionCode& ec) 230{ 231 StyleAttributeMutationScope mutationScope(this); 232 CSSPropertyID propertyID = cssPropertyID(propertyName); 233 if (!propertyID) 234 return String(); 235 236 if (!willMutate()) 237 return String(); 238 239 ec = 0; 240 String result; 241 bool changed = m_propertySet->removeProperty(propertyID, &result); 242 243 didMutate(changed ? PropertyChanged : NoChanges); 244 245 if (changed) 246 mutationScope.enqueueMutationRecord(); 247 return result; 248} 249 250PassRefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID) 251{ 252 return m_propertySet->getPropertyCSSValue(propertyID); 253} 254 255String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID) 256{ 257 return m_propertySet->getPropertyValue(propertyID); 258} 259 260void PropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important, ExceptionCode& ec) 261{ 262 StyleAttributeMutationScope mutationScope(this); 263 if (!willMutate()) 264 return; 265 266 ec = 0; 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 275CSSValue* PropertySetCSSStyleDeclaration::cloneAndCacheForCSSOM(CSSValue* internalValue) 276{ 277 if (!internalValue) 278 return 0; 279 280 // The map is here to maintain the object identity of the CSSValues over multiple invocations. 281 // FIXME: It is likely that the identity is not important for web compatibility and this code should be removed. 282 if (!m_cssomCSSValueClones) 283 m_cssomCSSValueClones = adoptPtr(new HashMap<CSSValue*, RefPtr<CSSValue> >); 284 285 RefPtr<CSSValue>& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtr<CSSValue>()).iterator->value; 286 if (!clonedValue) 287 clonedValue = internalValue->cloneForCSSOM(); 288 return clonedValue.get(); 289} 290 291StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const 292{ 293 CSSStyleSheet* cssStyleSheet = parentStyleSheet(); 294 return cssStyleSheet ? cssStyleSheet->contents() : 0; 295} 296 297PassRefPtr<MutableStylePropertySet> PropertySetCSSStyleDeclaration::copyProperties() const 298{ 299 return m_propertySet->mutableCopy(); 300} 301 302StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStylePropertySet* propertySet, CSSRule* parentRule) 303 : PropertySetCSSStyleDeclaration(propertySet) 304 , m_refCount(1) 305 , m_parentRule(parentRule) 306{ 307 m_propertySet->ref(); 308} 309 310StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration() 311{ 312 m_propertySet->deref(); 313} 314 315void StyleRuleCSSStyleDeclaration::ref() 316{ 317 ++m_refCount; 318} 319 320void StyleRuleCSSStyleDeclaration::deref() 321{ 322 ASSERT(m_refCount); 323 if (!--m_refCount) 324 delete this; 325} 326 327bool StyleRuleCSSStyleDeclaration::willMutate() 328{ 329 if (!m_parentRule || !m_parentRule->parentStyleSheet()) 330 return false; 331 m_parentRule->parentStyleSheet()->willMutateRules(); 332 return true; 333} 334 335void StyleRuleCSSStyleDeclaration::didMutate(MutationType type) 336{ 337 ASSERT(m_parentRule); 338 ASSERT(m_parentRule->parentStyleSheet()); 339 340 if (type == PropertyChanged) 341 m_cssomCSSValueClones.clear(); 342 343 // Style sheet mutation needs to be signaled even if the change failed. willMutate*/didMutate* must pair. 344 m_parentRule->parentStyleSheet()->didMutateRuleFromCSSStyleDeclaration(); 345} 346 347CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const 348{ 349 return m_parentRule ? m_parentRule->parentStyleSheet() : 0; 350} 351 352void StyleRuleCSSStyleDeclaration::reattach(MutableStylePropertySet* propertySet) 353{ 354 ASSERT(propertySet); 355 m_propertySet->deref(); 356 m_propertySet = propertySet; 357 m_propertySet->ref(); 358} 359 360void InlineCSSStyleDeclaration::didMutate(MutationType type) 361{ 362 if (type == NoChanges) 363 return; 364 365 m_cssomCSSValueClones.clear(); 366 367 if (!m_parentElement) 368 return; 369 370 m_parentElement->setNeedsStyleRecalc(InlineStyleChange); 371 m_parentElement->invalidateStyleAttribute(); 372 StyleAttributeMutationScope(this).didInvalidateStyleAttr(); 373} 374 375CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const 376{ 377 return m_parentElement ? m_parentElement->document()->elementSheet() : 0; 378} 379 380} // namespace WebCore 381