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