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