1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Daniel Glazman <glazman@netscape.com>
24 * Mats Palmgren <mats.palmgren@bredband.net>
25 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>, Collabora Ltd.
26 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
43 * representation of a declaration block (or style attribute) in a CSS
47 #include "mozilla/css/Declaration.h"
48 #include "nsPrintfCString.h"
53 // check that we can fit all the CSS properties into a PRUint8
54 // for the mOrder array - if not, might need to use PRUint16!
55 PR_STATIC_ASSERT(eCSSProperty_COUNT_no_shorthands
- 1 <= PR_UINT8_MAX
);
57 Declaration::Declaration()
58 : mImmutable(PR_FALSE
)
60 MOZ_COUNT_CTOR(mozilla::css::Declaration
);
63 Declaration::Declaration(const Declaration
& aCopy
)
64 : mOrder(aCopy
.mOrder
),
65 mData(aCopy
.mData
? aCopy
.mData
->Clone() : nsnull
),
66 mImportantData(aCopy
.mImportantData
67 ? aCopy
.mImportantData
->Clone() : nsnull
),
70 MOZ_COUNT_CTOR(mozilla::css::Declaration
);
73 Declaration::~Declaration()
75 MOZ_COUNT_DTOR(mozilla::css::Declaration
);
79 Declaration::ValueAppended(nsCSSProperty aProperty
)
81 NS_ABORT_IF_FALSE(!mData
&& !mImportantData
,
82 "should only be called while expanded");
83 NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty
),
84 "shorthands forbidden");
85 // order IS important for CSS, so remove and add to the end
86 mOrder
.RemoveElement(aProperty
);
87 mOrder
.AppendElement(aProperty
);
91 Declaration::RemoveProperty(nsCSSProperty aProperty
)
93 nsCSSExpandedDataBlock data
;
95 NS_ABORT_IF_FALSE(!mData
&& !mImportantData
, "Expand didn't null things out");
97 if (nsCSSProps::IsShorthand(aProperty
)) {
98 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p
, aProperty
) {
99 data
.ClearLonghandProperty(*p
);
100 mOrder
.RemoveElement(*p
);
103 data
.ClearLonghandProperty(aProperty
);
104 mOrder
.RemoveElement(aProperty
);
111 Declaration::HasProperty(nsCSSProperty aProperty
) const
113 NS_ABORT_IF_FALSE(0 <= aProperty
&&
114 aProperty
< eCSSProperty_COUNT_no_shorthands
,
115 "property ID out of range");
117 nsCSSCompressedDataBlock
*data
= GetValueIsImportant(aProperty
)
118 ? mImportantData
: mData
;
119 const nsCSSValue
*val
= data
->ValueFor(aProperty
);
124 Declaration::AppendValueToString(nsCSSProperty aProperty
,
125 nsAString
& aResult
) const
127 NS_ABORT_IF_FALSE(0 <= aProperty
&&
128 aProperty
< eCSSProperty_COUNT_no_shorthands
,
129 "property ID out of range");
131 nsCSSCompressedDataBlock
*data
= GetValueIsImportant(aProperty
)
132 ? mImportantData
: mData
;
133 const nsCSSValue
*val
= data
->ValueFor(aProperty
);
138 val
->AppendToString(aProperty
, aResult
);
143 Declaration::GetValue(nsCSSProperty aProperty
, nsAString
& aValue
) const
147 // simple properties are easy.
148 if (!nsCSSProps::IsShorthand(aProperty
)) {
149 AppendValueToString(aProperty
, aValue
);
153 // DOM Level 2 Style says (when describing CSS2Properties, although
154 // not CSSStyleDeclaration.getPropertyValue):
155 // However, if there is no shorthand declaration that could be added
156 // to the ruleset without changing in any way the rules already
157 // declared in the ruleset (i.e., by adding longhand rules that were
158 // previously not declared in the ruleset), then the empty string
159 // should be returned for the shorthand property.
160 // This means we need to check a number of cases:
161 // (1) Since a shorthand sets all sub-properties, if some of its
162 // subproperties were not specified, we must return the empty
164 // (2) Since 'inherit' and 'initial' can only be specified as the
165 // values for entire properties, we need to return the empty
166 // string if some but not all of the subproperties have one of
168 // (3) Since a single value only makes sense with or without
169 // !important, we return the empty string if some values are
170 // !important and some are not.
171 // Since we're doing this check for 'inherit' and 'initial' up front,
172 // we can also simplify the property serialization code by serializing
173 // those values up front as well.
174 PRUint32 totalCount
= 0, importantCount
= 0,
175 initialCount
= 0, inheritCount
= 0;
176 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p
, aProperty
) {
177 if (*p
== eCSSProperty__x_system_font
||
178 nsCSSProps::PropHasFlags(*p
, CSS_PROPERTY_DIRECTIONAL_SOURCE
)) {
179 // The system-font subproperty and the *-source properties don't count.
183 const nsCSSValue
*val
= mData
->ValueFor(*p
);
184 NS_ABORT_IF_FALSE(!val
|| !mImportantData
|| !mImportantData
->ValueFor(*p
),
185 "can't be in both blocks");
186 if (!val
&& mImportantData
) {
188 val
= mImportantData
->ValueFor(*p
);
191 // Case (1) above: some subproperties not specified.
194 if (val
->GetUnit() == eCSSUnit_Inherit
) {
196 } else if (val
->GetUnit() == eCSSUnit_Initial
) {
200 if (importantCount
!= 0 && importantCount
!= totalCount
) {
201 // Case (3), no consistent importance.
204 if (initialCount
== totalCount
) {
205 // Simplify serialization below by serializing initial up-front.
206 nsCSSValue(eCSSUnit_Initial
).AppendToString(eCSSProperty_UNKNOWN
, aValue
);
209 if (inheritCount
== totalCount
) {
210 // Simplify serialization below by serializing inherit up-front.
211 nsCSSValue(eCSSUnit_Inherit
).AppendToString(eCSSProperty_UNKNOWN
, aValue
);
214 if (initialCount
!= 0 || inheritCount
!= 0) {
215 // Case (2): partially initial or inherit.
219 nsCSSCompressedDataBlock
*data
= importantCount
? mImportantData
: mData
;
221 case eCSSProperty_margin
:
222 case eCSSProperty_padding
:
223 case eCSSProperty_border_color
:
224 case eCSSProperty_border_style
:
225 case eCSSProperty_border_width
: {
226 const nsCSSProperty
* subprops
=
227 nsCSSProps::SubpropertyEntryFor(aProperty
);
228 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops
[0]).Find("-top") !=
229 kNotFound
, "first subprop must be top");
230 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops
[1]).Find("-right") !=
231 kNotFound
, "second subprop must be right");
232 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops
[2]).Find("-bottom") !=
233 kNotFound
, "third subprop must be bottom");
234 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops
[3]).Find("-left") !=
235 kNotFound
, "fourth subprop must be left");
236 const nsCSSValue
&topValue
= *data
->ValueFor(subprops
[0]);
237 const nsCSSValue
&rightValue
= *data
->ValueFor(subprops
[1]);
238 const nsCSSValue
&bottomValue
= *data
->ValueFor(subprops
[2]);
239 const nsCSSValue
&leftValue
= *data
->ValueFor(subprops
[3]);
241 NS_ABORT_IF_FALSE(topValue
.GetUnit() != eCSSUnit_Null
, "null top");
242 topValue
.AppendToString(subprops
[0], aValue
);
243 if (topValue
!= rightValue
|| topValue
!= leftValue
||
244 topValue
!= bottomValue
) {
245 aValue
.Append(PRUnichar(' '));
246 NS_ABORT_IF_FALSE(rightValue
.GetUnit() != eCSSUnit_Null
, "null right");
247 rightValue
.AppendToString(subprops
[1], aValue
);
248 if (topValue
!= bottomValue
|| rightValue
!= leftValue
) {
249 aValue
.Append(PRUnichar(' '));
250 NS_ABORT_IF_FALSE(bottomValue
.GetUnit() != eCSSUnit_Null
, "null bottom");
251 bottomValue
.AppendToString(subprops
[2], aValue
);
252 if (rightValue
!= leftValue
) {
253 aValue
.Append(PRUnichar(' '));
254 NS_ABORT_IF_FALSE(leftValue
.GetUnit() != eCSSUnit_Null
, "null left");
255 leftValue
.AppendToString(subprops
[3], aValue
);
261 case eCSSProperty_border_radius
:
262 case eCSSProperty__moz_outline_radius
: {
263 const nsCSSProperty
* subprops
=
264 nsCSSProps::SubpropertyEntryFor(aProperty
);
265 const nsCSSValue
* vals
[4] = {
266 data
->ValueFor(subprops
[0]),
267 data
->ValueFor(subprops
[1]),
268 data
->ValueFor(subprops
[2]),
269 data
->ValueFor(subprops
[3])
272 // For compatibility, only write a slash and the y-values
273 // if they're not identical to the x-values.
274 PRBool needY
= PR_FALSE
;
275 for (int i
= 0; i
< 4; i
++) {
276 if (vals
[i
]->GetUnit() == eCSSUnit_Pair
) {
278 vals
[i
]->GetPairValue().mXValue
.AppendToString(subprops
[i
], aValue
);
280 vals
[i
]->AppendToString(subprops
[i
], aValue
);
283 aValue
.Append(PRUnichar(' '));
287 aValue
.AppendLiteral(" / ");
288 for (int i
= 0; i
< 4; i
++) {
289 if (vals
[i
]->GetUnit() == eCSSUnit_Pair
) {
290 vals
[i
]->GetPairValue().mYValue
.AppendToString(subprops
[i
], aValue
);
292 vals
[i
]->AppendToString(subprops
[i
], aValue
);
295 aValue
.Append(PRUnichar(' '));
300 case eCSSProperty_border
: {
301 const nsCSSProperty
* subproptables
[3] = {
302 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color
),
303 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style
),
304 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width
)
306 PRBool match
= PR_TRUE
;
307 for (const nsCSSProperty
** subprops
= subproptables
,
308 **subprops_end
= subproptables
+ NS_ARRAY_LENGTH(subproptables
);
309 subprops
< subprops_end
; ++subprops
) {
310 // Check only the first four subprops in each table, since the
311 // others are extras for dimensional box properties.
312 const nsCSSValue
*firstSide
= data
->ValueFor((*subprops
)[0]);
313 for (PRInt32 side
= 1; side
< 4; ++side
) {
314 const nsCSSValue
*otherSide
=
315 data
->ValueFor((*subprops
)[side
]);
316 if (*firstSide
!= *otherSide
)
321 // We can't express what we have in the border shorthand
324 // tweak aProperty and fall through
325 aProperty
= eCSSProperty_border_top
;
327 case eCSSProperty_border_top
:
328 case eCSSProperty_border_right
:
329 case eCSSProperty_border_bottom
:
330 case eCSSProperty_border_left
:
331 case eCSSProperty_border_start
:
332 case eCSSProperty_border_end
:
333 case eCSSProperty__moz_column_rule
:
334 case eCSSProperty_outline
: {
335 const nsCSSProperty
* subprops
=
336 nsCSSProps::SubpropertyEntryFor(aProperty
);
337 NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops
[2]),
338 NS_LITERAL_CSTRING("-color")) ||
339 StringEndsWith(nsCSSProps::GetStringValue(subprops
[2]),
340 NS_LITERAL_CSTRING("-color-value")),
341 "third subprop must be the color property");
342 const nsCSSValue
*colorValue
= data
->ValueFor(subprops
[2]);
343 PRBool isMozUseTextColor
=
344 colorValue
->GetUnit() == eCSSUnit_Enumerated
&&
345 colorValue
->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR
;
346 if (!AppendValueToString(subprops
[0], aValue
) ||
347 !(aValue
.Append(PRUnichar(' ')),
348 AppendValueToString(subprops
[1], aValue
)) ||
349 // Don't output a third value when it's -moz-use-text-color.
350 !(isMozUseTextColor
||
351 (aValue
.Append(PRUnichar(' ')),
352 AppendValueToString(subprops
[2], aValue
)))) {
357 case eCSSProperty_margin_left
:
358 case eCSSProperty_margin_right
:
359 case eCSSProperty_margin_start
:
360 case eCSSProperty_margin_end
:
361 case eCSSProperty_padding_left
:
362 case eCSSProperty_padding_right
:
363 case eCSSProperty_padding_start
:
364 case eCSSProperty_padding_end
:
365 case eCSSProperty_border_left_color
:
366 case eCSSProperty_border_left_style
:
367 case eCSSProperty_border_left_width
:
368 case eCSSProperty_border_right_color
:
369 case eCSSProperty_border_right_style
:
370 case eCSSProperty_border_right_width
:
371 case eCSSProperty_border_start_color
:
372 case eCSSProperty_border_start_style
:
373 case eCSSProperty_border_start_width
:
374 case eCSSProperty_border_end_color
:
375 case eCSSProperty_border_end_style
:
376 case eCSSProperty_border_end_width
: {
377 const nsCSSProperty
* subprops
=
378 nsCSSProps::SubpropertyEntryFor(aProperty
);
379 NS_ABORT_IF_FALSE(subprops
[3] == eCSSProperty_UNKNOWN
,
380 "not box property with physical vs. logical cascading");
381 AppendValueToString(subprops
[0], aValue
);
384 case eCSSProperty_background
: {
385 // We know from above that all subproperties were specified.
386 // However, we still can't represent that in the shorthand unless
387 // they're all lists of the same length. So if they're different
388 // lengths, we need to bail out.
389 // We also need to bail out if an item has background-clip and
390 // background-origin that are different and not the default
391 // values. (We omit them if they're both default.)
392 const nsCSSValueList
*image
=
393 data
->ValueFor(eCSSProperty_background_image
)->
395 const nsCSSValueList
*repeat
=
396 data
->ValueFor(eCSSProperty_background_repeat
)->
398 const nsCSSValueList
*attachment
=
399 data
->ValueFor(eCSSProperty_background_attachment
)->
401 const nsCSSValuePairList
*position
=
402 data
->ValueFor(eCSSProperty_background_position
)->
404 const nsCSSValueList
*clip
=
405 data
->ValueFor(eCSSProperty_background_clip
)->
407 const nsCSSValueList
*origin
=
408 data
->ValueFor(eCSSProperty_background_origin
)->
410 const nsCSSValuePairList
*size
=
411 data
->ValueFor(eCSSProperty_background_size
)->
414 if (size
->mXValue
.GetUnit() != eCSSUnit_Auto
||
415 size
->mYValue
.GetUnit() != eCSSUnit_Auto
) {
416 // Non-default background-size, so can't be serialized as shorthand.
420 image
->mValue
.AppendToString(eCSSProperty_background_image
, aValue
);
421 aValue
.Append(PRUnichar(' '));
422 repeat
->mValue
.AppendToString(eCSSProperty_background_repeat
, aValue
);
423 aValue
.Append(PRUnichar(' '));
424 attachment
->mValue
.AppendToString(eCSSProperty_background_attachment
,
426 aValue
.Append(PRUnichar(' '));
427 position
->mXValue
.AppendToString(eCSSProperty_background_position
,
429 aValue
.Append(PRUnichar(' '));
430 position
->mYValue
.AppendToString(eCSSProperty_background_position
,
432 NS_ABORT_IF_FALSE(clip
->mValue
.GetUnit() == eCSSUnit_Enumerated
&&
433 origin
->mValue
.GetUnit() == eCSSUnit_Enumerated
,
434 "should not be inherit/initial within list and "
435 "should have returned early for real inherit/initial");
436 if (clip
->mValue
.GetIntValue() != NS_STYLE_BG_CLIP_BORDER
||
437 origin
->mValue
.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING
) {
438 PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER
==
439 NS_STYLE_BG_ORIGIN_BORDER
);
440 PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_PADDING
==
441 NS_STYLE_BG_ORIGIN_PADDING
);
442 PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_CONTENT
==
443 NS_STYLE_BG_ORIGIN_CONTENT
);
444 // The shorthand only has a single clip/origin value which
445 // sets both properties. So if they're different (and
446 // non-default), we can't represent the state using the
448 if (clip
->mValue
!= origin
->mValue
) {
453 aValue
.Append(PRUnichar(' '));
454 clip
->mValue
.AppendToString(eCSSProperty_background_clip
, aValue
);
457 image
= image
->mNext
;
458 repeat
= repeat
->mNext
;
459 attachment
= attachment
->mNext
;
460 position
= position
->mNext
;
462 origin
= origin
->mNext
;
466 if (repeat
|| attachment
|| position
|| clip
|| origin
|| size
) {
467 // Uneven length lists, so can't be serialized as shorthand.
473 if (!repeat
|| !attachment
|| !position
|| !clip
|| !origin
|| !size
) {
474 // Uneven length lists, so can't be serialized as shorthand.
478 aValue
.Append(PRUnichar(','));
479 aValue
.Append(PRUnichar(' '));
482 aValue
.Append(PRUnichar(' '));
483 AppendValueToString(eCSSProperty_background_color
, aValue
);
486 case eCSSProperty_cue
: {
487 if (AppendValueToString(eCSSProperty_cue_before
, aValue
)) {
488 aValue
.Append(PRUnichar(' '));
489 if (!AppendValueToString(eCSSProperty_cue_after
, aValue
))
494 case eCSSProperty_font
: {
495 // systemFont might not be present; the others are guaranteed to be
496 // based on the shorthand check at the beginning of the function
497 const nsCSSValue
*systemFont
=
498 data
->ValueFor(eCSSProperty__x_system_font
);
499 const nsCSSValue
&style
=
500 *data
->ValueFor(eCSSProperty_font_style
);
501 const nsCSSValue
&variant
=
502 *data
->ValueFor(eCSSProperty_font_variant
);
503 const nsCSSValue
&weight
=
504 *data
->ValueFor(eCSSProperty_font_weight
);
505 const nsCSSValue
&size
=
506 *data
->ValueFor(eCSSProperty_font_size
);
507 const nsCSSValue
&lh
=
508 *data
->ValueFor(eCSSProperty_line_height
);
509 const nsCSSValue
&family
=
510 *data
->ValueFor(eCSSProperty_font_family
);
511 const nsCSSValue
&stretch
=
512 *data
->ValueFor(eCSSProperty_font_stretch
);
513 const nsCSSValue
&sizeAdjust
=
514 *data
->ValueFor(eCSSProperty_font_size_adjust
);
515 const nsCSSValue
&featureSettings
=
516 *data
->ValueFor(eCSSProperty_font_feature_settings
);
517 const nsCSSValue
&languageOverride
=
518 *data
->ValueFor(eCSSProperty_font_language_override
);
521 systemFont
->GetUnit() != eCSSUnit_None
&&
522 systemFont
->GetUnit() != eCSSUnit_Null
) {
523 if (style
.GetUnit() != eCSSUnit_System_Font
||
524 variant
.GetUnit() != eCSSUnit_System_Font
||
525 weight
.GetUnit() != eCSSUnit_System_Font
||
526 size
.GetUnit() != eCSSUnit_System_Font
||
527 lh
.GetUnit() != eCSSUnit_System_Font
||
528 family
.GetUnit() != eCSSUnit_System_Font
||
529 stretch
.GetUnit() != eCSSUnit_System_Font
||
530 sizeAdjust
.GetUnit() != eCSSUnit_System_Font
||
531 featureSettings
.GetUnit() != eCSSUnit_System_Font
||
532 languageOverride
.GetUnit() != eCSSUnit_System_Font
) {
533 // This can't be represented as a shorthand.
536 systemFont
->AppendToString(eCSSProperty__x_system_font
, aValue
);
538 // The font-stretch, font-size-adjust,
539 // -moz-font-feature-settings, and -moz-font-language-override
540 // properties are reset by this shorthand property to their
541 // initial values, but can't be represented in its syntax.
542 if (stretch
.GetUnit() != eCSSUnit_Enumerated
||
543 stretch
.GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL
||
544 sizeAdjust
.GetUnit() != eCSSUnit_None
||
545 featureSettings
.GetUnit() != eCSSUnit_Normal
||
546 languageOverride
.GetUnit() != eCSSUnit_Normal
) {
550 if (style
.GetUnit() != eCSSUnit_Enumerated
||
551 style
.GetIntValue() != NS_FONT_STYLE_NORMAL
) {
552 style
.AppendToString(eCSSProperty_font_style
, aValue
);
553 aValue
.Append(PRUnichar(' '));
555 if (variant
.GetUnit() != eCSSUnit_Enumerated
||
556 variant
.GetIntValue() != NS_FONT_VARIANT_NORMAL
) {
557 variant
.AppendToString(eCSSProperty_font_variant
, aValue
);
558 aValue
.Append(PRUnichar(' '));
560 if (weight
.GetUnit() != eCSSUnit_Enumerated
||
561 weight
.GetIntValue() != NS_FONT_WEIGHT_NORMAL
) {
562 weight
.AppendToString(eCSSProperty_font_weight
, aValue
);
563 aValue
.Append(PRUnichar(' '));
565 size
.AppendToString(eCSSProperty_font_size
, aValue
);
566 if (lh
.GetUnit() != eCSSUnit_Normal
) {
567 aValue
.Append(PRUnichar('/'));
568 lh
.AppendToString(eCSSProperty_line_height
, aValue
);
570 aValue
.Append(PRUnichar(' '));
571 family
.AppendToString(eCSSProperty_font_family
, aValue
);
575 case eCSSProperty_list_style
:
576 if (AppendValueToString(eCSSProperty_list_style_type
, aValue
))
577 aValue
.Append(PRUnichar(' '));
578 if (AppendValueToString(eCSSProperty_list_style_position
, aValue
))
579 aValue
.Append(PRUnichar(' '));
580 AppendValueToString(eCSSProperty_list_style_image
, aValue
);
582 case eCSSProperty_overflow
: {
583 const nsCSSValue
&xValue
=
584 *data
->ValueFor(eCSSProperty_overflow_x
);
585 const nsCSSValue
&yValue
=
586 *data
->ValueFor(eCSSProperty_overflow_y
);
587 if (xValue
== yValue
)
588 xValue
.AppendToString(eCSSProperty_overflow_x
, aValue
);
591 case eCSSProperty_pause
: {
592 if (AppendValueToString(eCSSProperty_pause_before
, aValue
)) {
593 aValue
.Append(PRUnichar(' '));
594 if (!AppendValueToString(eCSSProperty_pause_after
, aValue
))
599 case eCSSProperty_transition
: {
600 const nsCSSValue
&transProp
=
601 *data
->ValueFor(eCSSProperty_transition_property
);
602 const nsCSSValue
&transDuration
=
603 *data
->ValueFor(eCSSProperty_transition_duration
);
604 const nsCSSValue
&transTiming
=
605 *data
->ValueFor(eCSSProperty_transition_timing_function
);
606 const nsCSSValue
&transDelay
=
607 *data
->ValueFor(eCSSProperty_transition_delay
);
609 NS_ABORT_IF_FALSE(transDuration
.GetUnit() == eCSSUnit_List
||
610 transDuration
.GetUnit() == eCSSUnit_ListDep
,
611 nsPrintfCString(32, "bad t-duration unit %d",
612 transDuration
.GetUnit()).get());
613 NS_ABORT_IF_FALSE(transTiming
.GetUnit() == eCSSUnit_List
||
614 transTiming
.GetUnit() == eCSSUnit_ListDep
,
615 nsPrintfCString(32, "bad t-timing unit %d",
616 transTiming
.GetUnit()).get());
617 NS_ABORT_IF_FALSE(transDelay
.GetUnit() == eCSSUnit_List
||
618 transDelay
.GetUnit() == eCSSUnit_ListDep
,
619 nsPrintfCString(32, "bad t-delay unit %d",
620 transDelay
.GetUnit()).get());
622 const nsCSSValueList
* dur
= transDuration
.GetListValue();
623 const nsCSSValueList
* tim
= transTiming
.GetListValue();
624 const nsCSSValueList
* del
= transDelay
.GetListValue();
626 if (transProp
.GetUnit() == eCSSUnit_None
||
627 transProp
.GetUnit() == eCSSUnit_All
) {
628 // If any of the other three lists has more than one element,
629 // we can't use the shorthand.
630 if (!dur
->mNext
&& !tim
->mNext
&& !del
->mNext
) {
631 transProp
.AppendToString(eCSSProperty_transition_property
, aValue
);
632 aValue
.Append(PRUnichar(' '));
633 dur
->mValue
.AppendToString(eCSSProperty_transition_duration
,aValue
);
634 aValue
.Append(PRUnichar(' '));
635 tim
->mValue
.AppendToString(eCSSProperty_transition_timing_function
,
637 aValue
.Append(PRUnichar(' '));
638 del
->mValue
.AppendToString(eCSSProperty_transition_delay
, aValue
);
639 aValue
.Append(PRUnichar(' '));
644 NS_ABORT_IF_FALSE(transProp
.GetUnit() == eCSSUnit_List
||
645 transProp
.GetUnit() == eCSSUnit_ListDep
,
646 nsPrintfCString(32, "bad t-prop unit %d",
647 transProp
.GetUnit()).get());
648 const nsCSSValueList
* pro
= transProp
.GetListValue();
650 pro
->mValue
.AppendToString(eCSSProperty_transition_property
,
652 aValue
.Append(PRUnichar(' '));
653 dur
->mValue
.AppendToString(eCSSProperty_transition_duration
,
655 aValue
.Append(PRUnichar(' '));
656 tim
->mValue
.AppendToString(eCSSProperty_transition_timing_function
,
658 aValue
.Append(PRUnichar(' '));
659 del
->mValue
.AppendToString(eCSSProperty_transition_delay
,
665 if (!pro
|| !dur
|| !tim
|| !del
) {
668 aValue
.AppendLiteral(", ");
670 if (pro
|| dur
|| tim
|| del
) {
671 // Lists not all the same length, can't use shorthand.
678 case eCSSProperty_marker
: {
679 const nsCSSValue
&endValue
=
680 *data
->ValueFor(eCSSProperty_marker_end
);
681 const nsCSSValue
&midValue
=
682 *data
->ValueFor(eCSSProperty_marker_mid
);
683 const nsCSSValue
&startValue
=
684 *data
->ValueFor(eCSSProperty_marker_start
);
685 if (endValue
== midValue
&& midValue
== startValue
)
686 AppendValueToString(eCSSProperty_marker_end
, aValue
);
690 NS_ABORT_IF_FALSE(false, "no other shorthands");
696 Declaration::GetValueIsImportant(const nsAString
& aProperty
) const
698 nsCSSProperty propID
= nsCSSProps::LookupProperty(aProperty
);
699 if (propID
== eCSSProperty_UNKNOWN
) {
702 return GetValueIsImportant(propID
);
706 Declaration::GetValueIsImportant(nsCSSProperty aProperty
) const
711 // Calling ValueFor is inefficient, but we can assume '!important' is rare.
713 if (!nsCSSProps::IsShorthand(aProperty
)) {
714 return mImportantData
->ValueFor(aProperty
) != nsnull
;
717 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p
, aProperty
) {
718 if (*p
== eCSSProperty__x_system_font
) {
719 // The system_font subproperty doesn't count.
722 if (!mImportantData
->ValueFor(*p
)) {
730 Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty
,
731 nsAutoString
& aValue
,
732 nsAString
& aResult
) const
734 NS_ABORT_IF_FALSE(0 <= aProperty
&& aProperty
< eCSSProperty_COUNT
,
735 "property enum out of range");
736 NS_ABORT_IF_FALSE((aProperty
< eCSSProperty_COUNT_no_shorthands
) ==
738 "aValue should be given for shorthands but not longhands");
739 AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty
), aResult
);
740 aResult
.AppendLiteral(": ");
741 if (aValue
.IsEmpty())
742 AppendValueToString(aProperty
, aResult
);
744 aResult
.Append(aValue
);
745 if (GetValueIsImportant(aProperty
)) {
746 aResult
.AppendLiteral(" ! important");
748 aResult
.AppendLiteral("; ");
752 Declaration::ToString(nsAString
& aString
) const
754 // Someone cares about this declaration's contents, so don't let it
755 // change from under them. See e.g. bug 338679.
758 nsCSSCompressedDataBlock
*systemFontData
=
759 GetValueIsImportant(eCSSProperty__x_system_font
) ? mImportantData
: mData
;
760 const nsCSSValue
*systemFont
=
761 systemFontData
->ValueFor(eCSSProperty__x_system_font
);
762 const PRBool haveSystemFont
= systemFont
&&
763 systemFont
->GetUnit() != eCSSUnit_None
&&
764 systemFont
->GetUnit() != eCSSUnit_Null
;
765 PRBool didSystemFont
= PR_FALSE
;
767 PRInt32 count
= mOrder
.Length();
769 nsAutoTArray
<nsCSSProperty
, 16> shorthandsUsed
;
770 for (index
= 0; index
< count
; index
++) {
771 nsCSSProperty property
= OrderValueAt(index
);
772 PRBool doneProperty
= PR_FALSE
;
774 // If we already used this property in a shorthand, skip it.
775 if (shorthandsUsed
.Length() > 0) {
776 for (const nsCSSProperty
*shorthands
=
777 nsCSSProps::ShorthandsContaining(property
);
778 *shorthands
!= eCSSProperty_UNKNOWN
; ++shorthands
) {
779 if (shorthandsUsed
.Contains(*shorthands
)) {
780 doneProperty
= PR_TRUE
;
788 // Try to use this property in a shorthand.
790 for (const nsCSSProperty
*shorthands
=
791 nsCSSProps::ShorthandsContaining(property
);
792 *shorthands
!= eCSSProperty_UNKNOWN
; ++shorthands
) {
793 // ShorthandsContaining returns the shorthands in order from those
794 // that contain the most subproperties to those that contain the
795 // least, which is exactly the order we want to test them.
796 nsCSSProperty shorthand
= *shorthands
;
798 // If GetValue gives us a non-empty string back, we can use that
799 // value; otherwise it's not possible to use this shorthand.
800 GetValue(shorthand
, value
);
801 if (!value
.IsEmpty()) {
802 AppendPropertyAndValueToString(shorthand
, value
, aString
);
803 shorthandsUsed
.AppendElement(shorthand
);
804 doneProperty
= PR_TRUE
;
808 NS_ABORT_IF_FALSE(shorthand
!= eCSSProperty_font
||
809 *(shorthands
+ 1) == eCSSProperty_UNKNOWN
,
810 "font should always be the only containing shorthand");
811 if (shorthand
== eCSSProperty_font
) {
812 if (haveSystemFont
&& !didSystemFont
) {
813 // Output the shorthand font declaration that we will
814 // partially override later. But don't add it to
815 // |shorthandsUsed|, since we will have to override it.
816 systemFont
->AppendToString(eCSSProperty__x_system_font
, value
);
817 AppendPropertyAndValueToString(eCSSProperty_font
, value
, aString
);
819 didSystemFont
= PR_TRUE
;
822 // That we output the system font is enough for this property if:
823 // (1) it's the hidden system font subproperty (which either
824 // means we output it or we don't have it), or
825 // (2) its value is the hidden system font value and it matches
826 // the hidden system font subproperty in importance, and
827 // we output the system font subproperty.
828 const nsCSSValue
*val
= systemFontData
->ValueFor(property
);
829 if (property
== eCSSProperty__x_system_font
||
830 (haveSystemFont
&& val
&& val
->GetUnit() == eCSSUnit_System_Font
)) {
831 doneProperty
= PR_TRUE
;
838 NS_ABORT_IF_FALSE(value
.IsEmpty(), "value should be empty now");
839 AppendPropertyAndValueToString(property
, value
, aString
);
841 if (! aString
.IsEmpty()) {
842 // if the string is not empty, we have trailing whitespace we
844 aString
.Truncate(aString
.Length() - 1);
850 Declaration::List(FILE* out
, PRInt32 aIndent
) const
852 for (PRInt32 index
= aIndent
; --index
>= 0; ) fputs(" ", out
);
857 fputs(NS_ConvertUTF16toUTF8(s
).get(), out
);
863 Declaration::GetNthProperty(PRUint32 aIndex
, nsAString
& aReturn
) const
866 if (aIndex
< mOrder
.Length()) {
867 nsCSSProperty property
= OrderValueAt(aIndex
);
869 AppendASCIItoUTF16(nsCSSProps::GetStringValue(property
), aReturn
);
875 Declaration::InitializeEmpty()
877 NS_ABORT_IF_FALSE(!mData
&& !mImportantData
, "already initialized");
878 mData
= nsCSSCompressedDataBlock::CreateEmptyBlock();
882 Declaration::EnsureMutable()
884 NS_ABORT_IF_FALSE(mData
, "should only be called when not expanded");
886 return new Declaration(*this);
892 } // namespace mozilla::css
893 } // namespace mozilla