CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / layout / style / Declaration.cpp
blob927b4acc4d03cf0baaf40d9ac0400c7704d7bdeb
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
13 * License.
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.
22 * Contributor(s):
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
44 * stylesheet
47 #include "mozilla/css/Declaration.h"
48 #include "nsPrintfCString.h"
50 namespace mozilla {
51 namespace css {
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),
68 mImmutable(PR_FALSE)
70 MOZ_COUNT_CTOR(mozilla::css::Declaration);
73 Declaration::~Declaration()
75 MOZ_COUNT_DTOR(mozilla::css::Declaration);
78 void
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);
90 void
91 Declaration::RemoveProperty(nsCSSProperty aProperty)
93 nsCSSExpandedDataBlock data;
94 ExpandTo(&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);
102 } else {
103 data.ClearLonghandProperty(aProperty);
104 mOrder.RemoveElement(aProperty);
107 CompressFrom(&data);
110 PRBool
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);
120 return !!val;
123 PRBool
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);
134 if (!val) {
135 return PR_FALSE;
138 val->AppendToString(aProperty, aResult);
139 return PR_TRUE;
142 void
143 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
145 aValue.Truncate(0);
147 // simple properties are easy.
148 if (!nsCSSProps::IsShorthand(aProperty)) {
149 AppendValueToString(aProperty, aValue);
150 return;
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
163 // string.
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
167 // those values.
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.
180 continue;
182 ++totalCount;
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) {
187 ++importantCount;
188 val = mImportantData->ValueFor(*p);
190 if (!val) {
191 // Case (1) above: some subproperties not specified.
192 return;
194 if (val->GetUnit() == eCSSUnit_Inherit) {
195 ++inheritCount;
196 } else if (val->GetUnit() == eCSSUnit_Initial) {
197 ++initialCount;
200 if (importantCount != 0 && importantCount != totalCount) {
201 // Case (3), no consistent importance.
202 return;
204 if (initialCount == totalCount) {
205 // Simplify serialization below by serializing initial up-front.
206 nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue);
207 return;
209 if (inheritCount == totalCount) {
210 // Simplify serialization below by serializing inherit up-front.
211 nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue);
212 return;
214 if (initialCount != 0 || inheritCount != 0) {
215 // Case (2): partially initial or inherit.
216 return;
219 nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
220 switch (aProperty) {
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);
259 break;
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) {
277 needY = PR_TRUE;
278 vals[i]->GetPairValue().mXValue.AppendToString(subprops[i], aValue);
279 } else {
280 vals[i]->AppendToString(subprops[i], aValue);
282 if (i < 3)
283 aValue.Append(PRUnichar(' '));
286 if (needY) {
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);
291 } else {
292 vals[i]->AppendToString(subprops[i], aValue);
294 if (i < 3)
295 aValue.Append(PRUnichar(' '));
298 break;
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)
317 match = PR_FALSE;
320 if (!match) {
321 // We can't express what we have in the border shorthand
322 break;
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)))) {
353 aValue.Truncate();
355 break;
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);
382 break;
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)->
394 GetListValue();
395 const nsCSSValueList *repeat =
396 data->ValueFor(eCSSProperty_background_repeat)->
397 GetListValue();
398 const nsCSSValueList *attachment =
399 data->ValueFor(eCSSProperty_background_attachment)->
400 GetListValue();
401 const nsCSSValuePairList *position =
402 data->ValueFor(eCSSProperty_background_position)->
403 GetPairListValue();
404 const nsCSSValueList *clip =
405 data->ValueFor(eCSSProperty_background_clip)->
406 GetListValue();
407 const nsCSSValueList *origin =
408 data->ValueFor(eCSSProperty_background_origin)->
409 GetListValue();
410 const nsCSSValuePairList *size =
411 data->ValueFor(eCSSProperty_background_size)->
412 GetPairListValue();
413 for (;;) {
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.
417 aValue.Truncate();
418 return;
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,
425 aValue);
426 aValue.Append(PRUnichar(' '));
427 position->mXValue.AppendToString(eCSSProperty_background_position,
428 aValue);
429 aValue.Append(PRUnichar(' '));
430 position->mYValue.AppendToString(eCSSProperty_background_position,
431 aValue);
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
447 // shorthand.
448 if (clip->mValue != origin->mValue) {
449 aValue.Truncate();
450 return;
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;
461 clip = clip->mNext;
462 origin = origin->mNext;
463 size = size->mNext;
465 if (!image) {
466 if (repeat || attachment || position || clip || origin || size) {
467 // Uneven length lists, so can't be serialized as shorthand.
468 aValue.Truncate();
469 return;
471 break;
473 if (!repeat || !attachment || !position || !clip || !origin || !size) {
474 // Uneven length lists, so can't be serialized as shorthand.
475 aValue.Truncate();
476 return;
478 aValue.Append(PRUnichar(','));
479 aValue.Append(PRUnichar(' '));
482 aValue.Append(PRUnichar(' '));
483 AppendValueToString(eCSSProperty_background_color, aValue);
484 break;
486 case eCSSProperty_cue: {
487 if (AppendValueToString(eCSSProperty_cue_before, aValue)) {
488 aValue.Append(PRUnichar(' '));
489 if (!AppendValueToString(eCSSProperty_cue_after, aValue))
490 aValue.Truncate();
492 break;
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);
520 if (systemFont &&
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.
534 return;
536 systemFont->AppendToString(eCSSProperty__x_system_font, aValue);
537 } else {
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) {
547 return;
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);
573 break;
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);
581 break;
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);
589 break;
591 case eCSSProperty_pause: {
592 if (AppendValueToString(eCSSProperty_pause_before, aValue)) {
593 aValue.Append(PRUnichar(' '));
594 if (!AppendValueToString(eCSSProperty_pause_after, aValue))
595 aValue.Truncate();
597 break;
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,
636 aValue);
637 aValue.Append(PRUnichar(' '));
638 del->mValue.AppendToString(eCSSProperty_transition_delay, aValue);
639 aValue.Append(PRUnichar(' '));
640 } else {
641 aValue.Truncate();
643 } else {
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();
649 for (;;) {
650 pro->mValue.AppendToString(eCSSProperty_transition_property,
651 aValue);
652 aValue.Append(PRUnichar(' '));
653 dur->mValue.AppendToString(eCSSProperty_transition_duration,
654 aValue);
655 aValue.Append(PRUnichar(' '));
656 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
657 aValue);
658 aValue.Append(PRUnichar(' '));
659 del->mValue.AppendToString(eCSSProperty_transition_delay,
660 aValue);
661 pro = pro->mNext;
662 dur = dur->mNext;
663 tim = tim->mNext;
664 del = del->mNext;
665 if (!pro || !dur || !tim || !del) {
666 break;
668 aValue.AppendLiteral(", ");
670 if (pro || dur || tim || del) {
671 // Lists not all the same length, can't use shorthand.
672 aValue.Truncate();
675 break;
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);
687 break;
689 default:
690 NS_ABORT_IF_FALSE(false, "no other shorthands");
691 break;
695 PRBool
696 Declaration::GetValueIsImportant(const nsAString& aProperty) const
698 nsCSSProperty propID = nsCSSProps::LookupProperty(aProperty);
699 if (propID == eCSSProperty_UNKNOWN) {
700 return PR_FALSE;
702 return GetValueIsImportant(propID);
705 PRBool
706 Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
708 if (!mImportantData)
709 return PR_FALSE;
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.
720 continue;
722 if (!mImportantData->ValueFor(*p)) {
723 return PR_FALSE;
726 return PR_TRUE;
729 void
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) ==
737 aValue.IsEmpty(),
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);
743 else
744 aResult.Append(aValue);
745 if (GetValueIsImportant(aProperty)) {
746 aResult.AppendLiteral(" ! important");
748 aResult.AppendLiteral("; ");
751 void
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.
756 SetImmutable();
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();
768 PRInt32 index;
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;
781 break;
784 if (doneProperty)
785 continue;
788 // Try to use this property in a shorthand.
789 nsAutoString value;
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;
805 break;
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);
818 value.Truncate();
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;
835 if (doneProperty)
836 continue;
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
843 // should remove
844 aString.Truncate(aString.Length() - 1);
848 #ifdef DEBUG
849 void
850 Declaration::List(FILE* out, PRInt32 aIndent) const
852 for (PRInt32 index = aIndent; --index >= 0; ) fputs(" ", out);
854 fputs("{ ", out);
855 nsAutoString s;
856 ToString(s);
857 fputs(NS_ConvertUTF16toUTF8(s).get(), out);
858 fputs("}", out);
860 #endif
862 void
863 Declaration::GetNthProperty(PRUint32 aIndex, nsAString& aReturn) const
865 aReturn.Truncate();
866 if (aIndex < mOrder.Length()) {
867 nsCSSProperty property = OrderValueAt(aIndex);
868 if (0 <= property) {
869 AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
874 void
875 Declaration::InitializeEmpty()
877 NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
878 mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
881 Declaration*
882 Declaration::EnsureMutable()
884 NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
885 if (!IsMutable()) {
886 return new Declaration(*this);
887 } else {
888 return this;
892 } // namespace mozilla::css
893 } // namespace mozilla