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