nsDOMCSSAttrDeclaration.cpp (10344B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* DOM object for element.style */ 8 9 #include "nsDOMCSSAttrDeclaration.h" 10 11 #include "ActiveLayerTracker.h" 12 #include "mozAutoDocUpdate.h" 13 #include "mozilla/DeclarationBlock.h" 14 #include "mozilla/SMILCSSValueType.h" 15 #include "mozilla/SMILValue.h" 16 #include "mozilla/dom/Document.h" 17 #include "mozilla/dom/Element.h" 18 #include "mozilla/dom/SVGElement.h" 19 #include "mozilla/layers/ScrollLinkedEffectDetector.h" 20 #include "nsIFrame.h" 21 #include "nsWrapperCacheInlines.h" 22 23 using namespace mozilla; 24 using namespace mozilla::dom; 25 26 nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(Element* aElement, 27 bool aIsSMILOverride) 28 : mElement(aElement), mIsSMILOverride(aIsSMILOverride) { 29 NS_ASSERTION(aElement, "Inline style for a NULL element?"); 30 } 31 32 nsDOMCSSAttributeDeclaration::~nsDOMCSSAttributeDeclaration() = default; 33 34 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCSSAttributeDeclaration, mElement) 35 36 // mElement holds a strong ref to us, so if it's going to be 37 // skipped, the attribute declaration can't be part of a garbage 38 // cycle. 39 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMCSSAttributeDeclaration) 40 if (tmp->mElement && Element::CanSkip(tmp->mElement, true)) { 41 if (tmp->PreservingWrapper()) { 42 tmp->MarkWrapperLive(); 43 } 44 return true; 45 } 46 return tmp->HasKnownLiveWrapper(); 47 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 48 49 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMCSSAttributeDeclaration) 50 return tmp->HasKnownLiveWrapper() || 51 (tmp->mElement && Element::CanSkipInCC(tmp->mElement)); 52 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 53 54 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMCSSAttributeDeclaration) 55 return tmp->HasKnownLiveWrapper() || 56 (tmp->mElement && Element::CanSkipThis(tmp->mElement)); 57 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 58 59 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCSSAttributeDeclaration) 60 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 61 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) 62 63 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCSSAttributeDeclaration) 64 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCSSAttributeDeclaration) 65 66 nsresult nsDOMCSSAttributeDeclaration::SetCSSDeclaration( 67 DeclarationBlock* aDecl, MutationClosureData* aClosureData) { 68 NS_ASSERTION(mElement, "Must have Element to set the declaration!"); 69 70 // Whenever changing element.style values, aClosureData must be non-null. 71 // SMIL doesn't update Element's attribute values, so closure data isn't 72 // needed. 73 MOZ_ASSERT_IF(!mIsSMILOverride, aClosureData); 74 75 // The closure needs to have been called by now, otherwise we shouldn't be 76 // getting here when the attribute hasn't changed. 77 MOZ_ASSERT_IF(aClosureData && aClosureData->mShouldBeCalled, 78 aClosureData->mWasCalled); 79 80 aDecl->SetDirty(); 81 if (mIsSMILOverride) { 82 mElement->SetSMILOverrideStyleDeclaration(*aDecl); 83 return NS_OK; 84 } 85 return mElement->SetInlineStyleDeclaration(*aDecl, *aClosureData); 86 } 87 88 Document* nsDOMCSSAttributeDeclaration::DocToUpdate() { 89 // We need OwnerDoc() rather than GetUncomposedDoc() because it might 90 // be the BeginUpdate call that inserts mElement into the document. 91 return mElement->OwnerDoc(); 92 } 93 94 DeclarationBlock* nsDOMCSSAttributeDeclaration::GetOrCreateCSSDeclaration( 95 Operation aOperation, DeclarationBlock** aCreated) { 96 MOZ_ASSERT(aOperation != Operation::Modify || aCreated); 97 98 if (!mElement) { 99 return nullptr; 100 } 101 102 DeclarationBlock* declaration; 103 if (mIsSMILOverride) { 104 declaration = mElement->GetSMILOverrideStyleDeclaration(); 105 } else { 106 declaration = mElement->GetInlineStyleDeclaration(); 107 } 108 109 if (declaration) { 110 return declaration; 111 } 112 113 if (aOperation != Operation::Modify) { 114 return nullptr; 115 } 116 117 // cannot fail 118 auto decl = MakeRefPtr<DeclarationBlock>(); 119 // Mark the declaration dirty so that it can be reused by the caller. 120 // Normally SetDirty is called later in SetCSSDeclaration. 121 decl->SetDirty(); 122 #ifdef DEBUG 123 RefPtr<DeclarationBlock> mutableDecl = decl->EnsureMutable(); 124 MOZ_ASSERT(mutableDecl == decl); 125 #endif 126 decl.swap(*aCreated); 127 return *aCreated; 128 } 129 130 nsDOMCSSDeclaration::ParsingEnvironment 131 nsDOMCSSAttributeDeclaration::GetParsingEnvironment( 132 nsIPrincipal* aSubjectPrincipal) const { 133 return { 134 mElement->GetURLDataForStyleAttr(aSubjectPrincipal), 135 mElement->OwnerDoc()->GetCompatibilityMode(), 136 mElement->OwnerDoc()->GetExistingCSSLoader(), // For error reporting only 137 }; 138 } 139 140 template <typename SetterFunc> 141 nsresult nsDOMCSSAttributeDeclaration::SetSMILValueHelper(SetterFunc aFunc) { 142 MOZ_ASSERT(mIsSMILOverride); 143 144 // No need to do the ActiveLayerTracker / ScrollLinkedEffectDetector bits, 145 // since we're in a SMIL animation anyway, no need to try to detect we're a 146 // scripted animation. 147 RefPtr<DeclarationBlock> created; 148 DeclarationBlock* olddecl = 149 GetOrCreateCSSDeclaration(Operation::Modify, getter_AddRefs(created)); 150 if (!olddecl) { 151 return NS_ERROR_NOT_AVAILABLE; 152 } 153 mozAutoDocUpdate autoUpdate(DocToUpdate(), true); 154 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable(); 155 156 bool changed = aFunc(*decl); 157 158 if (changed) { 159 // We can pass nullptr as the latter param, since this is 160 // mIsSMILOverride == true case. 161 SetCSSDeclaration(decl, nullptr); 162 } 163 return NS_OK; 164 } 165 166 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( 167 const NonCustomCSSPropertyId aPropId, const SMILValue& aValue) { 168 MOZ_ASSERT(aValue.mType == &SMILCSSValueType::sSingleton, 169 "We should only try setting a CSS value type"); 170 return SetSMILValueHelper([&](DeclarationBlock& aDecl) { 171 return SMILCSSValueType::SetPropertyValues(aPropId, aValue, aDecl); 172 }); 173 } 174 175 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( 176 const NonCustomCSSPropertyId aPropId, const SVGAnimatedLength& aLength) { 177 return SetSMILValueHelper([aPropId, &aLength](DeclarationBlock& aDecl) { 178 MOZ_ASSERT(aDecl.IsMutable()); 179 return SVGElement::UpdateDeclarationBlockFromLength( 180 *aDecl.Raw(), aPropId, aLength, SVGElement::ValToUse::Anim); 181 }); 182 } 183 184 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( 185 const NonCustomCSSPropertyId aPropId, const SVGAnimatedPathSegList& aPath) { 186 MOZ_ASSERT(aPropId == eCSSProperty_d); 187 return SetSMILValueHelper([&aPath](DeclarationBlock& aDecl) { 188 MOZ_ASSERT(aDecl.IsMutable()); 189 return SVGElement::UpdateDeclarationBlockFromPath( 190 *aDecl.Raw(), aPath, SVGElement::ValToUse::Anim); 191 }); 192 } 193 194 nsresult nsDOMCSSAttributeDeclaration::SetSMILValue( 195 const NonCustomCSSPropertyId aPropId, 196 const SVGAnimatedTransformList* aTransform, 197 const gfx::Matrix* aAnimateMotionTransform) { 198 MOZ_ASSERT(aPropId == eCSSProperty_transform); 199 return SetSMILValueHelper( 200 [aTransform, aAnimateMotionTransform](DeclarationBlock& aDecl) { 201 MOZ_ASSERT(aDecl.IsMutable()); 202 return SVGElement::UpdateDeclarationBlockFromTransform( 203 *aDecl.Raw(), aTransform, aAnimateMotionTransform, 204 SVGElement::ValToUse::Anim); 205 }); 206 } 207 208 // Scripted modifications to style.opacity or style.transform (or other 209 // transform-like properties, e.g. style.translate, style.rotate, style.scale) 210 // could immediately force us into the animated state if heuristics suggest 211 // this is a scripted animation. 212 // 213 // FIXME: This is missing the margin shorthand and the logical versions of 214 // the margin properties, see bug 1266287. 215 static bool IsActiveLayerProperty(NonCustomCSSPropertyId aPropId) { 216 switch (aPropId) { 217 case eCSSProperty_opacity: 218 case eCSSProperty_transform: 219 case eCSSProperty_translate: 220 case eCSSProperty_rotate: 221 case eCSSProperty_scale: 222 case eCSSProperty_offset_path: 223 case eCSSProperty_offset_distance: 224 case eCSSProperty_offset_rotate: 225 case eCSSProperty_offset_anchor: 226 case eCSSProperty_offset_position: 227 return true; 228 default: 229 return false; 230 } 231 } 232 233 void nsDOMCSSAttributeDeclaration::SetPropertyValue( 234 const NonCustomCSSPropertyId aPropId, const nsACString& aValue, 235 nsIPrincipal* aSubjectPrincipal, ErrorResult& aRv) { 236 nsDOMCSSDeclaration::SetPropertyValue(aPropId, aValue, aSubjectPrincipal, 237 aRv); 238 } 239 240 static bool IsScrollLinkedEffectiveProperty( 241 const NonCustomCSSPropertyId aPropId) { 242 switch (aPropId) { 243 case eCSSProperty_background_position: 244 case eCSSProperty_background_position_x: 245 case eCSSProperty_background_position_y: 246 case eCSSProperty_transform: 247 case eCSSProperty_translate: 248 case eCSSProperty_rotate: 249 case eCSSProperty_scale: 250 case eCSSProperty_offset_path: 251 case eCSSProperty_offset_distance: 252 case eCSSProperty_offset_rotate: 253 case eCSSProperty_offset_anchor: 254 case eCSSProperty_offset_position: 255 case eCSSProperty_top: 256 case eCSSProperty_left: 257 case eCSSProperty_bottom: 258 case eCSSProperty_right: 259 case eCSSProperty_margin: 260 case eCSSProperty_margin_top: 261 case eCSSProperty_margin_left: 262 case eCSSProperty_margin_bottom: 263 case eCSSProperty_margin_right: 264 case eCSSProperty_margin_inline_start: 265 case eCSSProperty_margin_inline_end: 266 case eCSSProperty_margin_block_start: 267 case eCSSProperty_margin_block_end: 268 return true; 269 default: 270 return false; 271 } 272 } 273 274 void nsDOMCSSAttributeDeclaration::MutationClosureFunction( 275 void* aData, NonCustomCSSPropertyId aPropId) { 276 auto* data = static_cast<MutationClosureData*>(aData); 277 MOZ_ASSERT( 278 data->mShouldBeCalled, 279 "Did we pass a non-null closure to the style system unnecessarily?"); 280 if (data->mWasCalled) { 281 return; 282 } 283 if (IsScrollLinkedEffectiveProperty(aPropId)) { 284 mozilla::layers::ScrollLinkedEffectDetector::PositioningPropertyMutated(); 285 } 286 if (IsActiveLayerProperty(aPropId)) { 287 if (nsIFrame* frame = data->mElement->GetPrimaryFrame()) { 288 ActiveLayerTracker::NotifyInlineStyleRuleModified(frame, aPropId); 289 } 290 } 291 292 data->mWasCalled = true; 293 data->mElement->InlineStyleDeclarationWillChange(*data); 294 }