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