nsDOMCSSDeclaration.cpp (12737B)
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 /* base class for DOM objects for element.style and cssStyleRule.style */ 8 9 #include "nsDOMCSSDeclaration.h" 10 11 #include "mozAutoDocUpdate.h" 12 #include "mozilla/DeclarationBlock.h" 13 #include "mozilla/ProfilerLabels.h" 14 #include "mozilla/StyleSheetInlines.h" 15 #include "mozilla/css/Rule.h" 16 #include "mozilla/dom/BindingUtils.h" 17 #include "mozilla/dom/CSSStylePropertiesBinding.h" 18 #include "nsCOMPtr.h" 19 #include "nsCSSProps.h" 20 #include "nsQueryObject.h" 21 22 using namespace mozilla; 23 using namespace mozilla::dom; 24 25 nsDOMCSSDeclaration::~nsDOMCSSDeclaration() = default; 26 27 /* virtual */ 28 JSObject* nsDOMCSSDeclaration::WrapObject(JSContext* aCx, 29 JS::Handle<JSObject*> aGivenProto) { 30 return CSSStyleProperties_Binding::Wrap(aCx, this, aGivenProto); 31 } 32 33 NS_IMPL_QUERY_INTERFACE(nsDOMCSSDeclaration, nsICSSDeclaration) 34 35 void nsDOMCSSDeclaration::GetPropertyValue(const NonCustomCSSPropertyId aPropId, 36 nsACString& aValue) { 37 MOZ_ASSERT(aPropId != eCSSProperty_UNKNOWN, 38 "Should never pass eCSSProperty_UNKNOWN around"); 39 MOZ_ASSERT(aValue.IsEmpty()); 40 41 if (DeclarationBlock* decl = 42 GetOrCreateCSSDeclaration(Operation::Read, nullptr)) { 43 decl->GetPropertyValueById(aPropId, aValue); 44 } 45 } 46 47 void nsDOMCSSDeclaration::SetPropertyValue(const NonCustomCSSPropertyId aPropId, 48 const nsACString& aValue, 49 nsIPrincipal* aSubjectPrincipal, 50 ErrorResult& aRv) { 51 if (IsReadOnly()) { 52 return; 53 } 54 55 if (aValue.IsEmpty()) { 56 // If the new value of the property is an empty string we remove the 57 // property. 58 return RemovePropertyInternal(aPropId, aRv); 59 } 60 61 aRv = ParsePropertyValue(aPropId, aValue, false, aSubjectPrincipal); 62 } 63 64 void nsDOMCSSDeclaration::GetCssText(nsACString& aCssText) { 65 MOZ_ASSERT(aCssText.IsEmpty()); 66 67 if (auto* decl = GetOrCreateCSSDeclaration(Operation::Read, nullptr)) { 68 decl->ToString(aCssText); 69 } 70 } 71 72 void nsDOMCSSDeclaration::SetCssText(const nsACString& aCssText, 73 nsIPrincipal* aSubjectPrincipal, 74 ErrorResult& aRv) { 75 if (IsReadOnly()) { 76 return; 77 } 78 79 // We don't need to *do* anything with the old declaration, but we need 80 // to ensure that it exists, or else SetCSSDeclaration may crash. 81 RefPtr<DeclarationBlock> created; 82 DeclarationBlock* olddecl = 83 GetOrCreateCSSDeclaration(Operation::Modify, getter_AddRefs(created)); 84 if (!olddecl) { 85 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 86 return; 87 } 88 89 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to 90 // Attribute setting code, which leads in turn to BeginUpdate. We 91 // need to start the update now so that the old rule doesn't get used 92 // between when we mutate the declaration and when we set the new 93 // rule (see stack in bug 209575). 94 mozAutoDocUpdate autoUpdate(DocToUpdate(), true); 95 DeclarationBlockMutationClosure closure = {}; 96 MutationClosureData closureData; 97 GetPropertyChangeClosure(&closure, &closureData); 98 99 ParsingEnvironment servoEnv = GetParsingEnvironment(aSubjectPrincipal); 100 if (!servoEnv.mUrlExtraData) { 101 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 102 return; 103 } 104 105 // Need to special case closure calling here, since parsing css text 106 // doesn't modify any existing declaration and that is why the callback isn't 107 // called implicitly. 108 if (closure.function && !closureData.mWasCalled) { 109 closure.function(&closureData, eCSSProperty_UNKNOWN); 110 } 111 112 RefPtr<DeclarationBlock> newdecl = DeclarationBlock::FromCssText( 113 aCssText, servoEnv.mUrlExtraData, servoEnv.mCompatMode, servoEnv.mLoader, 114 servoEnv.mRuleType); 115 116 aRv = SetCSSDeclaration(newdecl, &closureData); 117 } 118 119 uint32_t nsDOMCSSDeclaration::Length() { 120 DeclarationBlock* decl = GetOrCreateCSSDeclaration(Operation::Read, nullptr); 121 122 if (decl) { 123 return decl->Count(); 124 } 125 126 return 0; 127 } 128 129 void nsDOMCSSDeclaration::IndexedGetter(uint32_t aIndex, bool& aFound, 130 nsACString& aPropName) { 131 DeclarationBlock* decl = GetOrCreateCSSDeclaration(Operation::Read, nullptr); 132 aFound = decl && decl->GetNthProperty(aIndex, aPropName); 133 } 134 135 void nsDOMCSSDeclaration::GetPropertyValue(const nsACString& aPropertyName, 136 nsACString& aReturn) { 137 MOZ_ASSERT(aReturn.IsEmpty()); 138 if (auto* decl = GetOrCreateCSSDeclaration(Operation::Read, nullptr)) { 139 decl->GetPropertyValue(aPropertyName, aReturn); 140 } 141 } 142 143 bool nsDOMCSSDeclaration::HasLonghandProperty(const nsACString& aPropertyName) { 144 if (auto* decl = GetOrCreateCSSDeclaration(Operation::Read, nullptr)) { 145 return Servo_DeclarationBlock_HasLonghandProperty(decl->Raw(), 146 &aPropertyName); 147 } 148 149 return false; 150 } 151 152 void nsDOMCSSDeclaration::GetPropertyPriority(const nsACString& aPropertyName, 153 nsACString& aPriority) { 154 MOZ_ASSERT(aPriority.IsEmpty()); 155 DeclarationBlock* decl = GetOrCreateCSSDeclaration(Operation::Read, nullptr); 156 if (decl && decl->GetPropertyIsImportant(aPropertyName)) { 157 aPriority.AssignLiteral("important"); 158 } 159 } 160 161 void nsDOMCSSDeclaration::SetProperty(const nsACString& aPropertyName, 162 const nsACString& aValue, 163 const nsACString& aPriority, 164 nsIPrincipal* aSubjectPrincipal, 165 ErrorResult& aRv) { 166 if (IsReadOnly()) { 167 return; 168 } 169 170 if (aValue.IsEmpty()) { 171 // If the new value of the property is an empty string we remove the 172 // property. 173 // XXX this ignores the priority string, should it? 174 return RemovePropertyInternal(aPropertyName, aRv); 175 } 176 177 // In the common (and fast) cases we can use the property id 178 NonCustomCSSPropertyId propId = nsCSSProps::LookupProperty(aPropertyName); 179 if (propId == eCSSProperty_UNKNOWN) { 180 return; 181 } 182 183 bool important; 184 if (aPriority.IsEmpty()) { 185 important = false; 186 } else if (aPriority.LowerCaseEqualsASCII("important")) { 187 important = true; 188 } else { 189 // XXX silent failure? 190 return; 191 } 192 193 if (propId == eCSSPropertyExtra_variable) { 194 aRv = ParseCustomPropertyValue(aPropertyName, aValue, important, 195 aSubjectPrincipal); 196 return; 197 } 198 aRv = ParsePropertyValue(propId, aValue, important, aSubjectPrincipal); 199 } 200 201 void nsDOMCSSDeclaration::RemoveProperty(const nsACString& aPropertyName, 202 nsACString& aReturn, 203 ErrorResult& aRv) { 204 if (IsReadOnly()) { 205 return; 206 } 207 GetPropertyValue(aPropertyName, aReturn); 208 RemovePropertyInternal(aPropertyName, aRv); 209 } 210 211 /* static */ nsDOMCSSDeclaration::ParsingEnvironment 212 nsDOMCSSDeclaration::GetParsingEnvironmentForRule(const css::Rule* aRule, 213 StyleCssRuleType aRuleType) { 214 if (!aRule) { 215 return {}; 216 } 217 218 MOZ_ASSERT(aRule->Type() == aRuleType); 219 220 StyleSheet* sheet = aRule->GetStyleSheet(); 221 if (!sheet) { 222 return {}; 223 } 224 225 if (Document* document = sheet->GetAssociatedDocument()) { 226 return { 227 sheet->URLData(), 228 document->GetCompatibilityMode(), 229 document->GetExistingCSSLoader(), 230 aRuleType, 231 }; 232 } 233 234 return { 235 sheet->URLData(), 236 eCompatibility_FullStandards, 237 nullptr, 238 aRuleType, 239 }; 240 } 241 242 template <typename Func> 243 nsresult nsDOMCSSDeclaration::ModifyDeclaration( 244 nsIPrincipal* aSubjectPrincipal, MutationClosureData* aClosureData, 245 Func aFunc) { 246 RefPtr<DeclarationBlock> created; 247 DeclarationBlock* olddecl = 248 GetOrCreateCSSDeclaration(Operation::Modify, getter_AddRefs(created)); 249 if (!olddecl) { 250 return NS_ERROR_NOT_AVAILABLE; 251 } 252 253 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to 254 // Attribute setting code, which leads in turn to BeginUpdate. We 255 // need to start the update now so that the old rule doesn't get used 256 // between when we mutate the declaration and when we set the new 257 // rule (see stack in bug 209575). 258 mozAutoDocUpdate autoUpdate(DocToUpdate(), true); 259 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable(); 260 261 bool changed; 262 ParsingEnvironment servoEnv = GetParsingEnvironment(aSubjectPrincipal); 263 if (!servoEnv.mUrlExtraData) { 264 return NS_ERROR_NOT_AVAILABLE; 265 } 266 267 changed = aFunc(decl, servoEnv); 268 269 if (!changed) { 270 // Parsing failed -- but we don't throw an exception for that. 271 return NS_OK; 272 } 273 274 return SetCSSDeclaration(decl, aClosureData); 275 } 276 277 nsresult nsDOMCSSDeclaration::ParsePropertyValue( 278 const NonCustomCSSPropertyId aPropId, const nsACString& aPropValue, 279 bool aIsImportant, nsIPrincipal* aSubjectPrincipal) { 280 AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(LAYOUT_CSSParsing); 281 282 if (IsReadOnly()) { 283 return NS_OK; 284 } 285 286 DeclarationBlockMutationClosure closure = {}; 287 MutationClosureData closureData; 288 GetPropertyChangeClosure(&closure, &closureData); 289 290 return ModifyDeclaration( 291 aSubjectPrincipal, &closureData, 292 [&](DeclarationBlock* decl, ParsingEnvironment& env) { 293 return Servo_DeclarationBlock_SetPropertyById( 294 decl->Raw(), aPropId, &aPropValue, aIsImportant, env.mUrlExtraData, 295 StyleParsingMode::DEFAULT, env.mCompatMode, env.mLoader, 296 env.mRuleType, closure); 297 }); 298 } 299 300 nsresult nsDOMCSSDeclaration::ParseCustomPropertyValue( 301 const nsACString& aPropertyName, const nsACString& aPropValue, 302 bool aIsImportant, nsIPrincipal* aSubjectPrincipal) { 303 MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName)); 304 305 if (IsReadOnly()) { 306 return NS_OK; 307 } 308 309 DeclarationBlockMutationClosure closure = {}; 310 MutationClosureData closureData; 311 GetPropertyChangeClosure(&closure, &closureData); 312 313 return ModifyDeclaration( 314 aSubjectPrincipal, &closureData, 315 [&](DeclarationBlock* decl, ParsingEnvironment& env) { 316 return Servo_DeclarationBlock_SetProperty( 317 decl->Raw(), &aPropertyName, &aPropValue, aIsImportant, 318 env.mUrlExtraData, StyleParsingMode::DEFAULT, env.mCompatMode, 319 env.mLoader, env.mRuleType, closure); 320 }); 321 } 322 323 void nsDOMCSSDeclaration::RemovePropertyInternal(NonCustomCSSPropertyId aPropId, 324 ErrorResult& aRv) { 325 DeclarationBlock* olddecl = 326 GetOrCreateCSSDeclaration(Operation::RemoveProperty, nullptr); 327 if (IsReadOnly()) { 328 return; 329 } 330 331 if (!olddecl) { 332 return; // no decl, so nothing to remove 333 } 334 335 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to 336 // Attribute setting code, which leads in turn to BeginUpdate. We 337 // need to start the update now so that the old rule doesn't get used 338 // between when we mutate the declaration and when we set the new 339 // rule (see stack in bug 209575). 340 mozAutoDocUpdate autoUpdate(DocToUpdate(), true); 341 342 DeclarationBlockMutationClosure closure = {}; 343 MutationClosureData closureData; 344 GetPropertyChangeClosure(&closure, &closureData); 345 346 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable(); 347 if (!decl->RemovePropertyById(aPropId, closure)) { 348 return; 349 } 350 aRv = SetCSSDeclaration(decl, &closureData); 351 } 352 353 void nsDOMCSSDeclaration::RemovePropertyInternal( 354 const nsACString& aPropertyName, ErrorResult& aRv) { 355 if (IsReadOnly()) { 356 return; 357 } 358 359 DeclarationBlock* olddecl = 360 GetOrCreateCSSDeclaration(Operation::RemoveProperty, nullptr); 361 if (!olddecl) { 362 return; // no decl, so nothing to remove 363 } 364 365 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to 366 // Attribute setting code, which leads in turn to BeginUpdate. We 367 // need to start the update now so that the old rule doesn't get used 368 // between when we mutate the declaration and when we set the new 369 // rule (see stack in bug 209575). 370 mozAutoDocUpdate autoUpdate(DocToUpdate(), true); 371 372 DeclarationBlockMutationClosure closure = {}; 373 MutationClosureData closureData; 374 GetPropertyChangeClosure(&closure, &closureData); 375 376 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable(); 377 if (!decl->RemoveProperty(aPropertyName, closure)) { 378 return; 379 } 380 aRv = SetCSSDeclaration(decl, &closureData); 381 }