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 "bindings/core/v8/ExceptionState.h" 26 #include "core/HTMLNames.h" 27 #include "core/css/CSSStyleSheet.h" 28 #include "core/css/StylePropertySet.h" 29 #include "core/css/parser/CSSParser.h" 30 #include "core/dom/Element.h" 31 #include "core/dom/MutationObserverInterestGroup.h" 32 #include "core/dom/MutationRecord.h" 33 #include "core/inspector/InspectorInstrumentation.h" 34 #include "platform/RuntimeEnabledFeatures.h" 35 36 namespace blink { 37 38 namespace { 39 40 class StyleAttributeMutationScope { 41 WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope); 42 STACK_ALLOCATED(); 43 public: 44 StyleAttributeMutationScope(AbstractPropertySetCSSStyleDeclaration* decl) 45 { 46 InspectorInstrumentation::willMutateStyle(decl); 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 87 // We have to clear internal state before calling Inspector's code. 88 AbstractPropertySetCSSStyleDeclaration* localCopyStyleDecl = s_currentDecl; 89 s_currentDecl = 0; 90 InspectorInstrumentation::didMutateStyle(localCopyStyleDecl, localCopyStyleDecl->parentElement()); 91 92 if (!s_shouldNotifyInspector) 93 return; 94 95 s_shouldNotifyInspector = false; 96 if (localCopyStyleDecl->parentElement()) 97 InspectorInstrumentation::didInvalidateStyleAttr(localCopyStyleDecl->parentElement()); 98 } 99 100 void enqueueMutationRecord() 101 { 102 s_shouldDeliver = true; 103 } 104 105 void didInvalidateStyleAttr() 106 { 107 s_shouldNotifyInspector = true; 108 } 109 110 private: 111 static unsigned s_scopeCount; 112 static AbstractPropertySetCSSStyleDeclaration* s_currentDecl; 113 static bool s_shouldNotifyInspector; 114 static bool s_shouldDeliver; 115 116 OwnPtrWillBeMember<MutationObserverInterestGroup> m_mutationRecipients; 117 RefPtrWillBeMember<MutationRecord> m_mutation; 118 }; 119 120 unsigned StyleAttributeMutationScope::s_scopeCount = 0; 121 AbstractPropertySetCSSStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0; 122 bool StyleAttributeMutationScope::s_shouldNotifyInspector = false; 123 bool StyleAttributeMutationScope::s_shouldDeliver = false; 124 125 } // namespace 126 127 #if !ENABLE(OILPAN) 128 void PropertySetCSSStyleDeclaration::ref() 129 { 130 m_propertySet->ref(); 131 } 132 133 void PropertySetCSSStyleDeclaration::deref() 134 { 135 m_propertySet->deref(); 136 } 137 #endif 138 139 void PropertySetCSSStyleDeclaration::trace(Visitor* visitor) 140 { 141 visitor->trace(m_propertySet); 142 AbstractPropertySetCSSStyleDeclaration::trace(visitor); 143 } 144 145 unsigned AbstractPropertySetCSSStyleDeclaration::length() const 146 { 147 return propertySet().propertyCount(); 148 } 149 150 String AbstractPropertySetCSSStyleDeclaration::item(unsigned i) const 151 { 152 if (i >= propertySet().propertyCount()) 153 return ""; 154 return propertySet().propertyAt(i).cssName(); 155 } 156 157 String AbstractPropertySetCSSStyleDeclaration::cssText() const 158 { 159 return propertySet().asText(); 160 } 161 162 void AbstractPropertySetCSSStyleDeclaration::setCSSText(const String& text, ExceptionState& exceptionState) 163 { 164 StyleAttributeMutationScope mutationScope(this); 165 willMutate(); 166 167 // FIXME: Detect syntax errors and set exceptionState. 168 propertySet().parseDeclaration(text, contextStyleSheet()); 169 170 didMutate(PropertyChanged); 171 172 mutationScope.enqueueMutationRecord(); 173 } 174 175 PassRefPtrWillBeRawPtr<CSSValue> AbstractPropertySetCSSStyleDeclaration::getPropertyCSSValue(const String& propertyName) 176 { 177 CSSPropertyID propertyID = cssPropertyID(propertyName); 178 if (!propertyID) 179 return nullptr; 180 return cloneAndCacheForCSSOM(propertySet().getPropertyCSSValue(propertyID).get()); 181 } 182 183 String AbstractPropertySetCSSStyleDeclaration::getPropertyValue(const String &propertyName) 184 { 185 CSSPropertyID propertyID = cssPropertyID(propertyName); 186 if (!propertyID) 187 return String(); 188 return propertySet().getPropertyValue(propertyID); 189 } 190 191 String AbstractPropertySetCSSStyleDeclaration::getPropertyPriority(const String& propertyName) 192 { 193 CSSPropertyID propertyID = cssPropertyID(propertyName); 194 if (!propertyID) 195 return String(); 196 return propertySet().propertyIsImportant(propertyID) ? "important" : ""; 197 } 198 199 String AbstractPropertySetCSSStyleDeclaration::getPropertyShorthand(const String& propertyName) 200 { 201 CSSPropertyID propertyID = cssPropertyID(propertyName); 202 if (!propertyID) 203 return String(); 204 CSSPropertyID shorthandID = propertySet().getPropertyShorthand(propertyID); 205 if (!shorthandID) 206 return String(); 207 return getPropertyNameString(shorthandID); 208 } 209 210 bool AbstractPropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyName) 211 { 212 CSSPropertyID propertyID = cssPropertyID(propertyName); 213 if (!propertyID) 214 return false; 215 return propertySet().isPropertyImplicit(propertyID); 216 } 217 218 void AbstractPropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority, ExceptionState& exceptionState) 219 { 220 CSSPropertyID propertyID = cssPropertyID(propertyName); 221 if (!propertyID) 222 return; 223 224 bool important = equalIgnoringCase(priority, "important"); 225 if (!important && !priority.isEmpty()) 226 return; 227 228 setPropertyInternal(propertyID, value, important, exceptionState); 229 } 230 231 String AbstractPropertySetCSSStyleDeclaration::removeProperty(const String& propertyName, ExceptionState& exceptionState) 232 { 233 StyleAttributeMutationScope mutationScope(this); 234 CSSPropertyID propertyID = cssPropertyID(propertyName); 235 if (!propertyID) 236 return String(); 237 238 willMutate(); 239 240 String result; 241 bool changed = propertySet().removeProperty(propertyID, &result); 242 243 didMutate(changed ? PropertyChanged : NoChanges); 244 245 if (changed) 246 mutationScope.enqueueMutationRecord(); 247 return result; 248 } 249 250 PassRefPtrWillBeRawPtr<CSSValue> AbstractPropertySetCSSStyleDeclaration::getPropertyCSSValueInternal(CSSPropertyID propertyID) 251 { 252 return propertySet().getPropertyCSSValue(propertyID); 253 } 254 255 String AbstractPropertySetCSSStyleDeclaration::getPropertyValueInternal(CSSPropertyID propertyID) 256 { 257 return propertySet().getPropertyValue(propertyID); 258 } 259 260 void AbstractPropertySetCSSStyleDeclaration::setPropertyInternal(CSSPropertyID propertyID, const String& value, bool important, ExceptionState&) 261 { 262 StyleAttributeMutationScope mutationScope(this); 263 willMutate(); 264 265 bool changed = propertySet().setProperty(propertyID, value, important, contextStyleSheet()); 266 267 didMutate(changed ? PropertyChanged : NoChanges); 268 269 if (changed) 270 mutationScope.enqueueMutationRecord(); 271 } 272 273 CSSValue* AbstractPropertySetCSSStyleDeclaration::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 = adoptPtrWillBeNoop(new WillBeHeapHashMap<RawPtrWillBeMember<CSSValue>, RefPtrWillBeMember<CSSValue> >); 282 283 RefPtrWillBeMember<CSSValue>& clonedValue = m_cssomCSSValueClones->add(internalValue, RefPtrWillBeMember<CSSValue>()).storedValue->value; 284 if (!clonedValue) 285 clonedValue = internalValue->cloneForCSSOM(); 286 return clonedValue.get(); 287 } 288 289 StyleSheetContents* AbstractPropertySetCSSStyleDeclaration::contextStyleSheet() const 290 { 291 CSSStyleSheet* cssStyleSheet = parentStyleSheet(); 292 return cssStyleSheet ? cssStyleSheet->contents() : 0; 293 } 294 295 PassRefPtrWillBeRawPtr<MutableStylePropertySet> AbstractPropertySetCSSStyleDeclaration::copyProperties() const 296 { 297 return propertySet().mutableCopy(); 298 } 299 300 bool AbstractPropertySetCSSStyleDeclaration::cssPropertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const 301 { 302 return propertySet().propertyMatches(propertyID, propertyValue); 303 } 304 305 void AbstractPropertySetCSSStyleDeclaration::trace(Visitor* visitor) 306 { 307 #if ENABLE(OILPAN) 308 visitor->trace(m_cssomCSSValueClones); 309 #endif 310 CSSStyleDeclaration::trace(visitor); 311 } 312 313 StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration(MutableStylePropertySet& propertySetArg, CSSRule* parentRule) 314 : PropertySetCSSStyleDeclaration(propertySetArg) 315 #if !ENABLE(OILPAN) 316 , m_refCount(1) 317 #endif 318 , m_parentRule(parentRule) 319 { 320 #if !ENABLE(OILPAN) 321 m_propertySet->ref(); 322 #endif 323 } 324 325 StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration() 326 { 327 #if !ENABLE(OILPAN) 328 m_propertySet->deref(); 329 #endif 330 } 331 332 #if !ENABLE(OILPAN) 333 void StyleRuleCSSStyleDeclaration::ref() 334 { 335 ++m_refCount; 336 } 337 338 void StyleRuleCSSStyleDeclaration::deref() 339 { 340 ASSERT(m_refCount); 341 if (!--m_refCount) 342 delete this; 343 } 344 #endif 345 346 void StyleRuleCSSStyleDeclaration::willMutate() 347 { 348 if (m_parentRule && m_parentRule->parentStyleSheet()) 349 m_parentRule->parentStyleSheet()->willMutateRules(); 350 } 351 352 void StyleRuleCSSStyleDeclaration::didMutate(MutationType type) 353 { 354 if (type == PropertyChanged) 355 m_cssomCSSValueClones.clear(); 356 357 // Style sheet mutation needs to be signaled even if the change failed. willMutateRules/didMutateRules must pair. 358 if (m_parentRule && m_parentRule->parentStyleSheet()) 359 m_parentRule->parentStyleSheet()->didMutateRules(); 360 } 361 362 CSSStyleSheet* StyleRuleCSSStyleDeclaration::parentStyleSheet() const 363 { 364 return m_parentRule ? m_parentRule->parentStyleSheet() : 0; 365 } 366 367 void StyleRuleCSSStyleDeclaration::reattach(MutableStylePropertySet& propertySet) 368 { 369 #if !ENABLE(OILPAN) 370 m_propertySet->deref(); 371 #endif 372 m_propertySet = &propertySet; 373 #if !ENABLE(OILPAN) 374 m_propertySet->ref(); 375 #endif 376 } 377 378 void StyleRuleCSSStyleDeclaration::trace(Visitor* visitor) 379 { 380 visitor->trace(m_parentRule); 381 PropertySetCSSStyleDeclaration::trace(visitor); 382 } 383 384 MutableStylePropertySet& InlineCSSStyleDeclaration::propertySet() const 385 { 386 return m_parentElement->ensureMutableInlineStyle(); 387 } 388 389 void InlineCSSStyleDeclaration::didMutate(MutationType type) 390 { 391 if (type == NoChanges) 392 return; 393 394 m_cssomCSSValueClones.clear(); 395 396 if (!m_parentElement) 397 return; 398 399 m_parentElement->clearMutableInlineStyleIfEmpty(); 400 m_parentElement->setNeedsStyleRecalc(LocalStyleChange); 401 m_parentElement->invalidateStyleAttribute(); 402 StyleAttributeMutationScope(this).didInvalidateStyleAttr(); 403 } 404 405 CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const 406 { 407 return m_parentElement ? &m_parentElement->document().elementSheet() : 0; 408 } 409 410 #if !ENABLE(OILPAN) 411 void InlineCSSStyleDeclaration::ref() 412 { 413 m_parentElement->ref(); 414 } 415 416 void InlineCSSStyleDeclaration::deref() 417 { 418 m_parentElement->deref(); 419 } 420 #endif 421 422 void InlineCSSStyleDeclaration::trace(Visitor* visitor) 423 { 424 visitor->trace(m_parentElement); 425 AbstractPropertySetCSSStyleDeclaration::trace(visitor); 426 } 427 428 } // namespace blink 429