layout/style/nsDOMCSSDeclaration.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /* base class for DOM objects for element.style and cssStyleRule.style */
michael@0 7
michael@0 8 #include "nsDOMCSSDeclaration.h"
michael@0 9
michael@0 10 #include "nsCSSParser.h"
michael@0 11 #include "nsCSSStyleSheet.h"
michael@0 12 #include "mozilla/css/Rule.h"
michael@0 13 #include "mozilla/css/Declaration.h"
michael@0 14 #include "mozilla/dom/CSS2PropertiesBinding.h"
michael@0 15 #include "nsCSSProps.h"
michael@0 16 #include "nsCOMPtr.h"
michael@0 17 #include "mozAutoDocUpdate.h"
michael@0 18 #include "nsIURI.h"
michael@0 19 #include "mozilla/dom/BindingUtils.h"
michael@0 20 #include "nsContentUtils.h"
michael@0 21
michael@0 22 using namespace mozilla;
michael@0 23
michael@0 24 nsDOMCSSDeclaration::~nsDOMCSSDeclaration()
michael@0 25 {
michael@0 26 }
michael@0 27
michael@0 28 /* virtual */ JSObject*
michael@0 29 nsDOMCSSDeclaration::WrapObject(JSContext* aCx)
michael@0 30 {
michael@0 31 return dom::CSS2PropertiesBinding::Wrap(aCx, this);
michael@0 32 }
michael@0 33
michael@0 34 NS_INTERFACE_TABLE_HEAD(nsDOMCSSDeclaration)
michael@0 35 NS_INTERFACE_TABLE(nsDOMCSSDeclaration,
michael@0 36 nsICSSDeclaration,
michael@0 37 nsIDOMCSSStyleDeclaration)
michael@0 38 NS_INTERFACE_TABLE_TO_MAP_SEGUE
michael@0 39 NS_INTERFACE_MAP_END
michael@0 40
michael@0 41 NS_IMETHODIMP
michael@0 42 nsDOMCSSDeclaration::GetPropertyValue(const nsCSSProperty aPropID,
michael@0 43 nsAString& aValue)
michael@0 44 {
michael@0 45 NS_PRECONDITION(aPropID != eCSSProperty_UNKNOWN,
michael@0 46 "Should never pass eCSSProperty_UNKNOWN around");
michael@0 47
michael@0 48 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 49
michael@0 50 aValue.Truncate();
michael@0 51 if (decl) {
michael@0 52 decl->GetValue(aPropID, aValue);
michael@0 53 }
michael@0 54 return NS_OK;
michael@0 55 }
michael@0 56
michael@0 57 void
michael@0 58 nsDOMCSSDeclaration::GetCustomPropertyValue(const nsAString& aPropertyName,
michael@0 59 nsAString& aValue)
michael@0 60 {
michael@0 61 MOZ_ASSERT(Substring(aPropertyName, 0,
michael@0 62 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
michael@0 63
michael@0 64 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 65 if (!decl) {
michael@0 66 aValue.Truncate();
michael@0 67 return;
michael@0 68 }
michael@0 69
michael@0 70 decl->GetVariableDeclaration(Substring(aPropertyName,
michael@0 71 CSS_CUSTOM_NAME_PREFIX_LENGTH),
michael@0 72 aValue);
michael@0 73 }
michael@0 74
michael@0 75 NS_IMETHODIMP
michael@0 76 nsDOMCSSDeclaration::SetPropertyValue(const nsCSSProperty aPropID,
michael@0 77 const nsAString& aValue)
michael@0 78 {
michael@0 79 if (aValue.IsEmpty()) {
michael@0 80 // If the new value of the property is an empty string we remove the
michael@0 81 // property.
michael@0 82 return RemoveProperty(aPropID);
michael@0 83 }
michael@0 84
michael@0 85 return ParsePropertyValue(aPropID, aValue, false);
michael@0 86 }
michael@0 87
michael@0 88
michael@0 89 NS_IMETHODIMP
michael@0 90 nsDOMCSSDeclaration::GetCssText(nsAString& aCssText)
michael@0 91 {
michael@0 92 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 93 aCssText.Truncate();
michael@0 94
michael@0 95 if (decl) {
michael@0 96 decl->ToString(aCssText);
michael@0 97 }
michael@0 98
michael@0 99 return NS_OK;
michael@0 100 }
michael@0 101
michael@0 102 NS_IMETHODIMP
michael@0 103 nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText)
michael@0 104 {
michael@0 105 // We don't need to *do* anything with the old declaration, but we need
michael@0 106 // to ensure that it exists, or else SetCSSDeclaration may crash.
michael@0 107 css::Declaration* olddecl = GetCSSDeclaration(true);
michael@0 108 if (!olddecl) {
michael@0 109 return NS_ERROR_FAILURE;
michael@0 110 }
michael@0 111
michael@0 112 CSSParsingEnvironment env;
michael@0 113 GetCSSParsingEnvironment(env);
michael@0 114 if (!env.mPrincipal) {
michael@0 115 return NS_ERROR_NOT_AVAILABLE;
michael@0 116 }
michael@0 117
michael@0 118 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
michael@0 119 // Attribute setting code, which leads in turn to BeginUpdate. We
michael@0 120 // need to start the update now so that the old rule doesn't get used
michael@0 121 // between when we mutate the declaration and when we set the new
michael@0 122 // rule (see stack in bug 209575).
michael@0 123 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
michael@0 124
michael@0 125 nsAutoPtr<css::Declaration> decl(new css::Declaration());
michael@0 126 decl->InitializeEmpty();
michael@0 127 nsCSSParser cssParser(env.mCSSLoader);
michael@0 128 bool changed;
michael@0 129 nsresult result = cssParser.ParseDeclarations(aCssText, env.mSheetURI,
michael@0 130 env.mBaseURI,
michael@0 131 env.mPrincipal, decl, &changed);
michael@0 132 if (NS_FAILED(result) || !changed) {
michael@0 133 return result;
michael@0 134 }
michael@0 135
michael@0 136 return SetCSSDeclaration(decl.forget());
michael@0 137 }
michael@0 138
michael@0 139 NS_IMETHODIMP
michael@0 140 nsDOMCSSDeclaration::GetLength(uint32_t* aLength)
michael@0 141 {
michael@0 142 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 143
michael@0 144 if (decl) {
michael@0 145 *aLength = decl->Count();
michael@0 146 } else {
michael@0 147 *aLength = 0;
michael@0 148 }
michael@0 149
michael@0 150 return NS_OK;
michael@0 151 }
michael@0 152
michael@0 153 already_AddRefed<dom::CSSValue>
michael@0 154 nsDOMCSSDeclaration::GetPropertyCSSValue(const nsAString& aPropertyName, ErrorResult& aRv)
michael@0 155 {
michael@0 156 // We don't support CSSValue yet so we'll just return null...
michael@0 157
michael@0 158 return nullptr;
michael@0 159 }
michael@0 160
michael@0 161 void
michael@0 162 nsDOMCSSDeclaration::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName)
michael@0 163 {
michael@0 164 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 165 aFound = decl && decl->GetNthProperty(aIndex, aPropName);
michael@0 166 }
michael@0 167
michael@0 168 NS_IMETHODIMP
michael@0 169 nsDOMCSSDeclaration::GetPropertyValue(const nsAString& aPropertyName,
michael@0 170 nsAString& aReturn)
michael@0 171 {
michael@0 172 const nsCSSProperty propID =
michael@0 173 nsCSSProps::LookupProperty(aPropertyName,
michael@0 174 nsCSSProps::eEnabledForAllContent);
michael@0 175 if (propID == eCSSProperty_UNKNOWN) {
michael@0 176 aReturn.Truncate();
michael@0 177 return NS_OK;
michael@0 178 }
michael@0 179
michael@0 180 if (propID == eCSSPropertyExtra_variable) {
michael@0 181 GetCustomPropertyValue(aPropertyName, aReturn);
michael@0 182 return NS_OK;
michael@0 183 }
michael@0 184
michael@0 185 return GetPropertyValue(propID, aReturn);
michael@0 186 }
michael@0 187
michael@0 188 NS_IMETHODIMP
michael@0 189 nsDOMCSSDeclaration::GetAuthoredPropertyValue(const nsAString& aPropertyName,
michael@0 190 nsAString& aReturn)
michael@0 191 {
michael@0 192 const nsCSSProperty propID =
michael@0 193 nsCSSProps::LookupProperty(aPropertyName,
michael@0 194 nsCSSProps::eEnabledForAllContent);
michael@0 195 if (propID == eCSSProperty_UNKNOWN) {
michael@0 196 aReturn.Truncate();
michael@0 197 return NS_OK;
michael@0 198 }
michael@0 199
michael@0 200 if (propID == eCSSPropertyExtra_variable) {
michael@0 201 GetCustomPropertyValue(aPropertyName, aReturn);
michael@0 202 return NS_OK;
michael@0 203 }
michael@0 204
michael@0 205 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 206 if (!decl) {
michael@0 207 return NS_ERROR_FAILURE;
michael@0 208 }
michael@0 209
michael@0 210 decl->GetAuthoredValue(propID, aReturn);
michael@0 211 return NS_OK;
michael@0 212 }
michael@0 213
michael@0 214 NS_IMETHODIMP
michael@0 215 nsDOMCSSDeclaration::GetPropertyPriority(const nsAString& aPropertyName,
michael@0 216 nsAString& aReturn)
michael@0 217 {
michael@0 218 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 219
michael@0 220 aReturn.Truncate();
michael@0 221 if (decl && decl->GetValueIsImportant(aPropertyName)) {
michael@0 222 aReturn.AssignLiteral("important");
michael@0 223 }
michael@0 224
michael@0 225 return NS_OK;
michael@0 226 }
michael@0 227
michael@0 228 NS_IMETHODIMP
michael@0 229 nsDOMCSSDeclaration::SetProperty(const nsAString& aPropertyName,
michael@0 230 const nsAString& aValue,
michael@0 231 const nsAString& aPriority)
michael@0 232 {
michael@0 233 // In the common (and fast) cases we can use the property id
michael@0 234 nsCSSProperty propID =
michael@0 235 nsCSSProps::LookupProperty(aPropertyName,
michael@0 236 nsCSSProps::eEnabledForAllContent);
michael@0 237 if (propID == eCSSProperty_UNKNOWN) {
michael@0 238 return NS_OK;
michael@0 239 }
michael@0 240
michael@0 241 if (aValue.IsEmpty()) {
michael@0 242 // If the new value of the property is an empty string we remove the
michael@0 243 // property.
michael@0 244 // XXX this ignores the priority string, should it?
michael@0 245 if (propID == eCSSPropertyExtra_variable) {
michael@0 246 return RemoveCustomProperty(aPropertyName);
michael@0 247 }
michael@0 248 return RemoveProperty(propID);
michael@0 249 }
michael@0 250
michael@0 251 bool important;
michael@0 252 if (aPriority.IsEmpty()) {
michael@0 253 important = false;
michael@0 254 } else if (aPriority.EqualsLiteral("important")) {
michael@0 255 important = true;
michael@0 256 } else {
michael@0 257 // XXX silent failure?
michael@0 258 return NS_OK;
michael@0 259 }
michael@0 260
michael@0 261 if (propID == eCSSPropertyExtra_variable) {
michael@0 262 return ParseCustomPropertyValue(aPropertyName, aValue, important);
michael@0 263 }
michael@0 264 return ParsePropertyValue(propID, aValue, important);
michael@0 265 }
michael@0 266
michael@0 267 NS_IMETHODIMP
michael@0 268 nsDOMCSSDeclaration::RemoveProperty(const nsAString& aPropertyName,
michael@0 269 nsAString& aReturn)
michael@0 270 {
michael@0 271 const nsCSSProperty propID =
michael@0 272 nsCSSProps::LookupProperty(aPropertyName,
michael@0 273 nsCSSProps::eEnabledForAllContent);
michael@0 274 if (propID == eCSSProperty_UNKNOWN) {
michael@0 275 aReturn.Truncate();
michael@0 276 return NS_OK;
michael@0 277 }
michael@0 278
michael@0 279 if (propID == eCSSPropertyExtra_variable) {
michael@0 280 RemoveCustomProperty(aPropertyName);
michael@0 281 return NS_OK;
michael@0 282 }
michael@0 283
michael@0 284 nsresult rv = GetPropertyValue(propID, aReturn);
michael@0 285 NS_ENSURE_SUCCESS(rv, rv);
michael@0 286
michael@0 287 return RemoveProperty(propID);
michael@0 288 }
michael@0 289
michael@0 290 /* static */ void
michael@0 291 nsDOMCSSDeclaration::GetCSSParsingEnvironmentForRule(css::Rule* aRule,
michael@0 292 CSSParsingEnvironment& aCSSParseEnv)
michael@0 293 {
michael@0 294 nsIStyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
michael@0 295 nsRefPtr<nsCSSStyleSheet> cssSheet(do_QueryObject(sheet));
michael@0 296 if (!cssSheet) {
michael@0 297 aCSSParseEnv.mPrincipal = nullptr;
michael@0 298 return;
michael@0 299 }
michael@0 300
michael@0 301 nsIDocument* document = sheet->GetOwningDocument();
michael@0 302 aCSSParseEnv.mSheetURI = sheet->GetSheetURI();
michael@0 303 aCSSParseEnv.mBaseURI = sheet->GetBaseURI();
michael@0 304 aCSSParseEnv.mPrincipal = cssSheet->Principal();
michael@0 305 aCSSParseEnv.mCSSLoader = document ? document->CSSLoader() : nullptr;
michael@0 306 }
michael@0 307
michael@0 308 nsresult
michael@0 309 nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSProperty aPropID,
michael@0 310 const nsAString& aPropValue,
michael@0 311 bool aIsImportant)
michael@0 312 {
michael@0 313 css::Declaration* olddecl = GetCSSDeclaration(true);
michael@0 314 if (!olddecl) {
michael@0 315 return NS_ERROR_FAILURE;
michael@0 316 }
michael@0 317
michael@0 318 CSSParsingEnvironment env;
michael@0 319 GetCSSParsingEnvironment(env);
michael@0 320 if (!env.mPrincipal) {
michael@0 321 return NS_ERROR_NOT_AVAILABLE;
michael@0 322 }
michael@0 323
michael@0 324 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
michael@0 325 // Attribute setting code, which leads in turn to BeginUpdate. We
michael@0 326 // need to start the update now so that the old rule doesn't get used
michael@0 327 // between when we mutate the declaration and when we set the new
michael@0 328 // rule (see stack in bug 209575).
michael@0 329 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
michael@0 330 css::Declaration* decl = olddecl->EnsureMutable();
michael@0 331
michael@0 332 nsCSSParser cssParser(env.mCSSLoader);
michael@0 333 bool changed;
michael@0 334 nsresult result = cssParser.ParseProperty(aPropID, aPropValue, env.mSheetURI,
michael@0 335 env.mBaseURI, env.mPrincipal, decl,
michael@0 336 &changed, aIsImportant);
michael@0 337 if (NS_FAILED(result) || !changed) {
michael@0 338 if (decl != olddecl) {
michael@0 339 delete decl;
michael@0 340 }
michael@0 341 return result;
michael@0 342 }
michael@0 343
michael@0 344 return SetCSSDeclaration(decl);
michael@0 345 }
michael@0 346
michael@0 347 nsresult
michael@0 348 nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName,
michael@0 349 const nsAString& aPropValue,
michael@0 350 bool aIsImportant)
michael@0 351 {
michael@0 352 MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
michael@0 353
michael@0 354 css::Declaration* olddecl = GetCSSDeclaration(true);
michael@0 355 if (!olddecl) {
michael@0 356 return NS_ERROR_FAILURE;
michael@0 357 }
michael@0 358
michael@0 359 CSSParsingEnvironment env;
michael@0 360 GetCSSParsingEnvironment(env);
michael@0 361 if (!env.mPrincipal) {
michael@0 362 return NS_ERROR_NOT_AVAILABLE;
michael@0 363 }
michael@0 364
michael@0 365 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
michael@0 366 // Attribute setting code, which leads in turn to BeginUpdate. We
michael@0 367 // need to start the update now so that the old rule doesn't get used
michael@0 368 // between when we mutate the declaration and when we set the new
michael@0 369 // rule (see stack in bug 209575).
michael@0 370 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
michael@0 371 css::Declaration* decl = olddecl->EnsureMutable();
michael@0 372
michael@0 373 nsCSSParser cssParser(env.mCSSLoader);
michael@0 374 bool changed;
michael@0 375 nsresult result =
michael@0 376 cssParser.ParseVariable(Substring(aPropertyName,
michael@0 377 CSS_CUSTOM_NAME_PREFIX_LENGTH),
michael@0 378 aPropValue, env.mSheetURI,
michael@0 379 env.mBaseURI, env.mPrincipal, decl,
michael@0 380 &changed, aIsImportant);
michael@0 381 if (NS_FAILED(result) || !changed) {
michael@0 382 if (decl != olddecl) {
michael@0 383 delete decl;
michael@0 384 }
michael@0 385 return result;
michael@0 386 }
michael@0 387
michael@0 388 return SetCSSDeclaration(decl);
michael@0 389 }
michael@0 390
michael@0 391 nsresult
michael@0 392 nsDOMCSSDeclaration::RemoveProperty(const nsCSSProperty aPropID)
michael@0 393 {
michael@0 394 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 395 if (!decl) {
michael@0 396 return NS_OK; // no decl, so nothing to remove
michael@0 397 }
michael@0 398
michael@0 399 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
michael@0 400 // Attribute setting code, which leads in turn to BeginUpdate. We
michael@0 401 // need to start the update now so that the old rule doesn't get used
michael@0 402 // between when we mutate the declaration and when we set the new
michael@0 403 // rule (see stack in bug 209575).
michael@0 404 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
michael@0 405
michael@0 406 decl = decl->EnsureMutable();
michael@0 407 decl->RemoveProperty(aPropID);
michael@0 408 return SetCSSDeclaration(decl);
michael@0 409 }
michael@0 410
michael@0 411 nsresult
michael@0 412 nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName)
michael@0 413 {
michael@0 414 MOZ_ASSERT(Substring(aPropertyName, 0,
michael@0 415 CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
michael@0 416
michael@0 417 css::Declaration* decl = GetCSSDeclaration(false);
michael@0 418 if (!decl) {
michael@0 419 return NS_OK; // no decl, so nothing to remove
michael@0 420 }
michael@0 421
michael@0 422 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
michael@0 423 // Attribute setting code, which leads in turn to BeginUpdate. We
michael@0 424 // need to start the update now so that the old rule doesn't get used
michael@0 425 // between when we mutate the declaration and when we set the new
michael@0 426 // rule (see stack in bug 209575).
michael@0 427 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
michael@0 428
michael@0 429 decl = decl->EnsureMutable();
michael@0 430 decl->RemoveVariableDeclaration(Substring(aPropertyName,
michael@0 431 CSS_CUSTOM_NAME_PREFIX_LENGTH));
michael@0 432 return SetCSSDeclaration(decl);
michael@0 433 }
michael@0 434
michael@0 435 bool IsCSSPropertyExposedToJS(nsCSSProperty aProperty, JSContext* cx, JSObject* obj)
michael@0 436 {
michael@0 437 nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent;
michael@0 438
michael@0 439 // Optimization: we skip checking properties of the JSContext
michael@0 440 // in the majority case where the property does not have the
michael@0 441 // CSS_PROPERTY_ALWAYS_ENABLED_IN_PRIVILEGED_CONTENT flag.
michael@0 442 bool isEnabledInChromeOrCertifiedApp
michael@0 443 = nsCSSProps::PropHasFlags(aProperty,
michael@0 444 CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP);
michael@0 445
michael@0 446 if (isEnabledInChromeOrCertifiedApp) {
michael@0 447 if (dom::IsInCertifiedApp(cx, obj) ||
michael@0 448 nsContentUtils::ThreadsafeIsCallerChrome())
michael@0 449 {
michael@0 450 enabledState |= nsCSSProps::eEnabledInChromeOrCertifiedApp;
michael@0 451 }
michael@0 452 }
michael@0 453 return nsCSSProps::IsEnabled(aProperty, enabledState);
michael@0 454 }

mercurial