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