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/. */
5
6 /* base class for DOM objects for element.style and cssStyleRule.style */
7
8 #include "nsDOMCSSDeclaration.h"
9
10 #include "nsCSSParser.h"
11 #include "mozilla/DeclarationBlockInlines.h"
12 #include "mozilla/StyleSheetInlines.h"
13 #include "mozilla/css/Rule.h"
14 #include "mozilla/DeclarationBlockInlines.h"
15 #include "mozilla/dom/CSS2PropertiesBinding.h"
16 #include "nsCSSProps.h"
17 #include "nsCOMPtr.h"
18 #include "mozAutoDocUpdate.h"
19 #include "nsIURI.h"
20 #include "mozilla/dom/BindingUtils.h"
21 #include "nsContentUtils.h"
22 #include "nsQueryObject.h"
23 #include "mozilla/layers/ScrollLinkedEffectDetector.h"
24
25 using namespace mozilla;
26
~nsDOMCSSDeclaration()27 nsDOMCSSDeclaration::~nsDOMCSSDeclaration()
28 {
29 }
30
31 /* virtual */ JSObject*
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)32 nsDOMCSSDeclaration::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
33 {
34 return dom::CSS2PropertiesBinding::Wrap(aCx, this, aGivenProto);
35 }
36
37 NS_INTERFACE_TABLE_HEAD(nsDOMCSSDeclaration)
NS_INTERFACE_TABLE(nsDOMCSSDeclaration,nsICSSDeclaration,nsIDOMCSSStyleDeclaration)38 NS_INTERFACE_TABLE(nsDOMCSSDeclaration,
39 nsICSSDeclaration,
40 nsIDOMCSSStyleDeclaration)
41 NS_INTERFACE_TABLE_TO_MAP_SEGUE
42 NS_INTERFACE_MAP_END
43
44 NS_IMETHODIMP
45 nsDOMCSSDeclaration::GetPropertyValue(const nsCSSPropertyID aPropID,
46 nsAString& aValue)
47 {
48 NS_PRECONDITION(aPropID != eCSSProperty_UNKNOWN,
49 "Should never pass eCSSProperty_UNKNOWN around");
50
51 aValue.Truncate();
52 if (DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read)) {
53 decl->GetPropertyValueByID(aPropID, aValue);
54 }
55 return NS_OK;
56 }
57
58 NS_IMETHODIMP
SetPropertyValue(const nsCSSPropertyID aPropID,const nsAString & aValue)59 nsDOMCSSDeclaration::SetPropertyValue(const nsCSSPropertyID aPropID,
60 const nsAString& aValue)
61 {
62 switch (aPropID) {
63 case eCSSProperty_background_position:
64 case eCSSProperty_background_position_x:
65 case eCSSProperty_background_position_y:
66 case eCSSProperty_transform:
67 case eCSSProperty_top:
68 case eCSSProperty_left:
69 case eCSSProperty_bottom:
70 case eCSSProperty_right:
71 case eCSSProperty_margin:
72 case eCSSProperty_margin_top:
73 case eCSSProperty_margin_left:
74 case eCSSProperty_margin_bottom:
75 case eCSSProperty_margin_right:
76 case eCSSProperty_margin_inline_start:
77 case eCSSProperty_margin_inline_end:
78 case eCSSProperty_margin_block_start:
79 case eCSSProperty_margin_block_end:
80 mozilla::layers::ScrollLinkedEffectDetector::PositioningPropertyMutated();
81 break;
82 default:
83 break;
84 }
85
86 if (aValue.IsEmpty()) {
87 // If the new value of the property is an empty string we remove the
88 // property.
89 return RemovePropertyInternal(aPropID);
90 }
91
92 return ParsePropertyValue(aPropID, aValue, false);
93 }
94
95
96 NS_IMETHODIMP
GetCssText(nsAString & aCssText)97 nsDOMCSSDeclaration::GetCssText(nsAString& aCssText)
98 {
99 DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read);
100 aCssText.Truncate();
101
102 if (decl) {
103 decl->ToString(aCssText);
104 }
105
106 return NS_OK;
107 }
108
109 NS_IMETHODIMP
SetCssText(const nsAString & aCssText)110 nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText)
111 {
112 // We don't need to *do* anything with the old declaration, but we need
113 // to ensure that it exists, or else SetCSSDeclaration may crash.
114 DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_Modify);
115 if (!olddecl) {
116 return NS_ERROR_NOT_AVAILABLE;
117 }
118
119 CSSParsingEnvironment env;
120 GetCSSParsingEnvironment(env);
121 if (!env.mPrincipal) {
122 return NS_ERROR_NOT_AVAILABLE;
123 }
124
125 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
126 // Attribute setting code, which leads in turn to BeginUpdate. We
127 // need to start the update now so that the old rule doesn't get used
128 // between when we mutate the declaration and when we set the new
129 // rule (see stack in bug 209575).
130 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
131
132 RefPtr<DeclarationBlock> newdecl;
133 if (olddecl->IsServo()) {
134 newdecl = ServoDeclarationBlock::FromCssText(aCssText);
135 } else {
136 RefPtr<css::Declaration> decl(new css::Declaration());
137 decl->InitializeEmpty();
138 nsCSSParser cssParser(env.mCSSLoader);
139 bool changed;
140 nsresult result = cssParser.ParseDeclarations(aCssText, env.mSheetURI,
141 env.mBaseURI, env.mPrincipal,
142 decl, &changed);
143 if (NS_FAILED(result) || !changed) {
144 return result;
145 }
146 newdecl = decl.forget();
147 }
148
149 return SetCSSDeclaration(newdecl);
150 }
151
152 NS_IMETHODIMP
GetLength(uint32_t * aLength)153 nsDOMCSSDeclaration::GetLength(uint32_t* aLength)
154 {
155 DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read);
156
157 if (decl) {
158 *aLength = decl->Count();
159 } else {
160 *aLength = 0;
161 }
162
163 return NS_OK;
164 }
165
166 already_AddRefed<dom::CSSValue>
GetPropertyCSSValue(const nsAString & aPropertyName,ErrorResult & aRv)167 nsDOMCSSDeclaration::GetPropertyCSSValue(const nsAString& aPropertyName, ErrorResult& aRv)
168 {
169 // We don't support CSSValue yet so we'll just return null...
170
171 return nullptr;
172 }
173
174 void
IndexedGetter(uint32_t aIndex,bool & aFound,nsAString & aPropName)175 nsDOMCSSDeclaration::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName)
176 {
177 DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read);
178 aFound = decl && decl->GetNthProperty(aIndex, aPropName);
179 }
180
181 NS_IMETHODIMP
GetPropertyValue(const nsAString & aPropertyName,nsAString & aReturn)182 nsDOMCSSDeclaration::GetPropertyValue(const nsAString& aPropertyName,
183 nsAString& aReturn)
184 {
185 aReturn.Truncate();
186 if (DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read)) {
187 decl->GetPropertyValue(aPropertyName, aReturn);
188 }
189 return NS_OK;
190 }
191
192 NS_IMETHODIMP
GetAuthoredPropertyValue(const nsAString & aPropertyName,nsAString & aReturn)193 nsDOMCSSDeclaration::GetAuthoredPropertyValue(const nsAString& aPropertyName,
194 nsAString& aReturn)
195 {
196 if (DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read)) {
197 decl->GetAuthoredPropertyValue(aPropertyName, aReturn);
198 }
199 return NS_OK;
200 }
201
202 NS_IMETHODIMP
GetPropertyPriority(const nsAString & aPropertyName,nsAString & aReturn)203 nsDOMCSSDeclaration::GetPropertyPriority(const nsAString& aPropertyName,
204 nsAString& aReturn)
205 {
206 DeclarationBlock* decl = GetCSSDeclaration(eOperation_Read);
207
208 aReturn.Truncate();
209 if (decl && decl->GetPropertyIsImportant(aPropertyName)) {
210 aReturn.AssignLiteral("important");
211 }
212
213 return NS_OK;
214 }
215
216 NS_IMETHODIMP
SetProperty(const nsAString & aPropertyName,const nsAString & aValue,const nsAString & aPriority)217 nsDOMCSSDeclaration::SetProperty(const nsAString& aPropertyName,
218 const nsAString& aValue,
219 const nsAString& aPriority)
220 {
221 if (aValue.IsEmpty()) {
222 // If the new value of the property is an empty string we remove the
223 // property.
224 // XXX this ignores the priority string, should it?
225 return RemovePropertyInternal(aPropertyName);
226 }
227
228 // In the common (and fast) cases we can use the property id
229 nsCSSPropertyID propID =
230 nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent);
231 if (propID == eCSSProperty_UNKNOWN) {
232 return NS_OK;
233 }
234
235 bool important;
236 if (aPriority.IsEmpty()) {
237 important = false;
238 } else if (aPriority.EqualsLiteral("important")) {
239 important = true;
240 } else {
241 // XXX silent failure?
242 return NS_OK;
243 }
244
245 if (propID == eCSSPropertyExtra_variable) {
246 return ParseCustomPropertyValue(aPropertyName, aValue, important);
247 }
248 return ParsePropertyValue(propID, aValue, important);
249 }
250
251 NS_IMETHODIMP
RemoveProperty(const nsAString & aPropertyName,nsAString & aReturn)252 nsDOMCSSDeclaration::RemoveProperty(const nsAString& aPropertyName,
253 nsAString& aReturn)
254 {
255 nsresult rv = GetPropertyValue(aPropertyName, aReturn);
256 NS_ENSURE_SUCCESS(rv, rv);
257 return RemovePropertyInternal(aPropertyName);
258 }
259
260 /* static */ void
GetCSSParsingEnvironmentForRule(css::Rule * aRule,CSSParsingEnvironment & aCSSParseEnv)261 nsDOMCSSDeclaration::GetCSSParsingEnvironmentForRule(css::Rule* aRule,
262 CSSParsingEnvironment& aCSSParseEnv)
263 {
264 CSSStyleSheet* sheet = aRule ? aRule->GetStyleSheet() : nullptr;
265 if (!sheet) {
266 aCSSParseEnv.mPrincipal = nullptr;
267 return;
268 }
269
270 nsIDocument* document = sheet->GetOwningDocument();
271 aCSSParseEnv.mSheetURI = sheet->GetSheetURI();
272 aCSSParseEnv.mBaseURI = sheet->GetBaseURI();
273 aCSSParseEnv.mPrincipal = sheet->Principal();
274 aCSSParseEnv.mCSSLoader = document ? document->CSSLoader() : nullptr;
275 }
276
277 nsresult
ParsePropertyValue(const nsCSSPropertyID aPropID,const nsAString & aPropValue,bool aIsImportant)278 nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSPropertyID aPropID,
279 const nsAString& aPropValue,
280 bool aIsImportant)
281 {
282 DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_Modify);
283 if (!olddecl) {
284 return NS_ERROR_NOT_AVAILABLE;
285 }
286
287 CSSParsingEnvironment env;
288 GetCSSParsingEnvironment(env);
289 if (!env.mPrincipal) {
290 return NS_ERROR_NOT_AVAILABLE;
291 }
292
293 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
294 // Attribute setting code, which leads in turn to BeginUpdate. We
295 // need to start the update now so that the old rule doesn't get used
296 // between when we mutate the declaration and when we set the new
297 // rule (see stack in bug 209575).
298 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
299 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable();
300
301 bool changed;
302 if (decl->IsGecko()) {
303 nsCSSParser cssParser(env.mCSSLoader);
304 cssParser.ParseProperty(aPropID, aPropValue,
305 env.mSheetURI, env.mBaseURI, env.mPrincipal,
306 decl->AsGecko(), &changed, aIsImportant);
307 } else {
308 nsIAtom* atom = nsCSSProps::AtomForProperty(aPropID);
309 NS_ConvertUTF16toUTF8 value(aPropValue);
310 changed = Servo_DeclarationBlock_SetProperty(
311 decl->AsServo()->Raw(), atom, false, &value, aIsImportant);
312 }
313 if (!changed) {
314 // Parsing failed -- but we don't throw an exception for that.
315 return NS_OK;
316 }
317
318 return SetCSSDeclaration(decl);
319 }
320
321 nsresult
ParseCustomPropertyValue(const nsAString & aPropertyName,const nsAString & aPropValue,bool aIsImportant)322 nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName,
323 const nsAString& aPropValue,
324 bool aIsImportant)
325 {
326 MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
327
328 DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_Modify);
329 if (!olddecl) {
330 return NS_ERROR_NOT_AVAILABLE;
331 }
332
333 CSSParsingEnvironment env;
334 GetCSSParsingEnvironment(env);
335 if (!env.mPrincipal) {
336 return NS_ERROR_NOT_AVAILABLE;
337 }
338
339 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
340 // Attribute setting code, which leads in turn to BeginUpdate. We
341 // need to start the update now so that the old rule doesn't get used
342 // between when we mutate the declaration and when we set the new
343 // rule (see stack in bug 209575).
344 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
345 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable();
346
347 bool changed;
348 auto propName = Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
349 if (decl->IsGecko()) {
350 nsCSSParser cssParser(env.mCSSLoader);
351 cssParser.ParseVariable(propName, aPropValue, env.mSheetURI,
352 env.mBaseURI, env.mPrincipal, decl->AsGecko(),
353 &changed, aIsImportant);
354 } else {
355 RefPtr<nsIAtom> atom = NS_Atomize(propName);
356 NS_ConvertUTF16toUTF8 value(aPropValue);
357 changed = Servo_DeclarationBlock_SetProperty(
358 decl->AsServo()->Raw(), atom, true, &value, aIsImportant);
359 }
360 if (!changed) {
361 // Parsing failed -- but we don't throw an exception for that.
362 return NS_OK;
363 }
364
365 return SetCSSDeclaration(decl);
366 }
367
368 nsresult
RemovePropertyInternal(nsCSSPropertyID aPropID)369 nsDOMCSSDeclaration::RemovePropertyInternal(nsCSSPropertyID aPropID)
370 {
371 DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_RemoveProperty);
372 if (!olddecl) {
373 return NS_OK; // no decl, so nothing to remove
374 }
375
376 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
377 // Attribute setting code, which leads in turn to BeginUpdate. We
378 // need to start the update now so that the old rule doesn't get used
379 // between when we mutate the declaration and when we set the new
380 // rule (see stack in bug 209575).
381 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
382
383 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable();
384 decl->RemovePropertyByID(aPropID);
385 return SetCSSDeclaration(decl);
386 }
387
388 nsresult
RemovePropertyInternal(const nsAString & aPropertyName)389 nsDOMCSSDeclaration::RemovePropertyInternal(const nsAString& aPropertyName)
390 {
391 DeclarationBlock* olddecl = GetCSSDeclaration(eOperation_RemoveProperty);
392 if (!olddecl) {
393 return NS_OK; // no decl, so nothing to remove
394 }
395
396 // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
397 // Attribute setting code, which leads in turn to BeginUpdate. We
398 // need to start the update now so that the old rule doesn't get used
399 // between when we mutate the declaration and when we set the new
400 // rule (see stack in bug 209575).
401 mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
402
403 RefPtr<DeclarationBlock> decl = olddecl->EnsureMutable();
404 decl->RemoveProperty(aPropertyName);
405 return SetCSSDeclaration(decl);
406 }
407