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 "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 36using namespace std; 37 38namespace WebCore { 39 40namespace { 41 42class StyleAttributeMutationScope { 43 WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope); 44public: 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 111private: 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 121unsigned StyleAttributeMutationScope::s_scopeCount = 0; 122PropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0; 123bool StyleAttributeMutationScope::s_shouldNotifyInspector = false; 124bool StyleAttributeMutationScope::s_shouldDeliver = false; 125 126} // namespace 127 128void PropertySetCSSStyleDeclaration::ref() 129{ 130 m_propertySet->ref(); 131} 132 133void PropertySetCSSStyleDeclaration::deref() 134{ 135 m_propertySet->deref(); 136} 137 138unsigned PropertySetCSSStyleDeclaration::length() const 139{ 140 return m_propertySet->propertyCount(); 141} 142 143String PropertySetCSSStyleDeclaration::item(unsigned i) const 144{ 145 if (i >= m_propertySet->propertyCount()) 146 return ""; 147 return m_propertySet->propertyAt(i).cssName(); 148} 149 150String PropertySetCSSStyleDeclaration::cssText() const 151{ 152 return m_propertySet->asText(); 153} 154 155void 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 168PassRefPtr<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 176String 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 184String 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 192String 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 203bool 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 211void 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 233String 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 252PassRefPtr<CSSValue> PropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID) 253{ 254 return m_propertySet->getPropertyCSSValue(propertyID); 255} 256 257String PropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID) 258{ 259 return m_propertySet->getPropertyValue(propertyID); 260} 261 262void 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 275unsigned PropertySetCSSStyleDeclaration::variableCount() const 276{ 277 ASSERT(RuntimeEnabledFeatures::cssVariablesEnabled()); 278 return m_propertySet->variableCount(); 279} 280 281String PropertySetCSSStyleDeclaration::variableValue(const AtomicString& name) const 282{ 283 ASSERT(RuntimeEnabledFeatures::cssVariablesEnabled()); 284 return m_propertySet->variableValue(name); 285} 286 287bool 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 299bool 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 311bool 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 323PassRefPtr<CSSVariablesIterator> PropertySetCSSStyleDeclaration::variablesIterator() const 324{ 325 return m_propertySet->variablesIterator(); 326} 327 328CSSValue* 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 344StyleSheetContents* PropertySetCSSStyleDeclaration::contextStyleSheet() const 345{ 346 CSSStyleSheet* cssStyleSheet = parentStyleSheet(); 347 return cssStyleSheet ? cssStyleSheet->contents() : 0; 348} 349 350PassRefPtr<MutableStylePropertySet> PropertySetCSSStyleDeclaration::copyProperties() const 351{ 352 return m_propertySet->mutableCopy(); 353} 354 355bool PropertySetCSSStyleDeclaration::cssPropertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const 356{ 357 return m_propertySet->propertyMatches(propertyID, propertyValue); 358} 359 360StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStylePropertySet* propertySet, CSSRule* parentRule) 361 : PropertySetCSSStyleDeclaration(propertySet) 362 , m_refCount(1) 363 , m_parentRule(parentRule) 364{ 365 m_propertySet->ref(); 366} 367 368StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration() 369{ 370 m_propertySet->deref(); 371} 372 373void StyleRuleCSSStyleDeclaration::ref() 374{ 375 ++m_refCount; 376} 377 378void StyleRuleCSSStyleDeclaration::deref() 379{ 380 ASSERT(m_refCount); 381 if (!--m_refCount) 382 delete this; 383} 384 385void StyleRuleCSSStyleDeclaration::willMutate() 386{ 387 if (m_parentRule && m_parentRule->parentStyleSheet()) 388 m_parentRule->parentStyleSheet()->willMutateRules(); 389} 390 391void 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 401CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const 402{ 403 return m_parentRule ? m_parentRule->parentStyleSheet() : 0; 404} 405 406void StyleRuleCSSStyleDeclaration::reattach(MutableStylePropertySet* propertySet) 407{ 408 ASSERT(propertySet); 409 m_propertySet->deref(); 410 m_propertySet = propertySet; 411 m_propertySet->ref(); 412} 413 414void 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 429CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const 430{ 431 return m_parentElement ? m_parentElement->document().elementSheet() : 0; 432} 433 434} // namespace WebCore 435