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