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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
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
27 : *
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.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : /*
43 : * representation of a declaration block (or style attribute) in a CSS
44 : * stylesheet
45 : */
46 :
47 : #include "mozilla/Util.h"
48 :
49 : #include "mozilla/css/Declaration.h"
50 : #include "nsPrintfCString.h"
51 :
52 : namespace mozilla {
53 : namespace css {
54 :
55 : // check that we can fit all the CSS properties into a PRUint8
56 : // for the mOrder array - if not, might need to use PRUint16!
57 : MOZ_STATIC_ASSERT(eCSSProperty_COUNT_no_shorthands - 1 <= PR_UINT8_MAX,
58 : "CSS longhand property numbers no longer fit in a PRUint8");
59 :
60 0 : Declaration::Declaration()
61 0 : : mImmutable(false)
62 : {
63 0 : MOZ_COUNT_CTOR(mozilla::css::Declaration);
64 0 : }
65 :
66 0 : Declaration::Declaration(const Declaration& aCopy)
67 : : mOrder(aCopy.mOrder),
68 0 : mData(aCopy.mData ? aCopy.mData->Clone() : nsnull),
69 : mImportantData(aCopy.mImportantData
70 0 : ? aCopy.mImportantData->Clone() : nsnull),
71 0 : mImmutable(false)
72 : {
73 0 : MOZ_COUNT_CTOR(mozilla::css::Declaration);
74 0 : }
75 :
76 0 : Declaration::~Declaration()
77 : {
78 0 : MOZ_COUNT_DTOR(mozilla::css::Declaration);
79 0 : }
80 :
81 : void
82 0 : Declaration::ValueAppended(nsCSSProperty aProperty)
83 : {
84 0 : NS_ABORT_IF_FALSE(!mData && !mImportantData,
85 : "should only be called while expanded");
86 0 : NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
87 : "shorthands forbidden");
88 : // order IS important for CSS, so remove and add to the end
89 0 : mOrder.RemoveElement(aProperty);
90 0 : mOrder.AppendElement(aProperty);
91 0 : }
92 :
93 : void
94 0 : Declaration::RemoveProperty(nsCSSProperty aProperty)
95 : {
96 0 : nsCSSExpandedDataBlock data;
97 0 : ExpandTo(&data);
98 0 : NS_ABORT_IF_FALSE(!mData && !mImportantData, "Expand didn't null things out");
99 :
100 0 : if (nsCSSProps::IsShorthand(aProperty)) {
101 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
102 0 : data.ClearLonghandProperty(*p);
103 0 : mOrder.RemoveElement(*p);
104 : }
105 : } else {
106 0 : data.ClearLonghandProperty(aProperty);
107 0 : mOrder.RemoveElement(aProperty);
108 : }
109 :
110 0 : CompressFrom(&data);
111 0 : }
112 :
113 : bool
114 0 : Declaration::HasProperty(nsCSSProperty aProperty) const
115 : {
116 0 : NS_ABORT_IF_FALSE(0 <= aProperty &&
117 : aProperty < eCSSProperty_COUNT_no_shorthands,
118 : "property ID out of range");
119 :
120 0 : nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
121 0 : ? mImportantData : mData;
122 0 : const nsCSSValue *val = data->ValueFor(aProperty);
123 0 : return !!val;
124 : }
125 :
126 : bool
127 0 : Declaration::AppendValueToString(nsCSSProperty aProperty,
128 : nsAString& aResult) const
129 : {
130 0 : NS_ABORT_IF_FALSE(0 <= aProperty &&
131 : aProperty < eCSSProperty_COUNT_no_shorthands,
132 : "property ID out of range");
133 :
134 0 : nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
135 0 : ? mImportantData : mData;
136 0 : const nsCSSValue *val = data->ValueFor(aProperty);
137 0 : if (!val) {
138 0 : return false;
139 : }
140 :
141 0 : val->AppendToString(aProperty, aResult);
142 0 : return true;
143 : }
144 :
145 : void
146 0 : Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
147 : {
148 0 : aValue.Truncate(0);
149 :
150 : // simple properties are easy.
151 0 : if (!nsCSSProps::IsShorthand(aProperty)) {
152 0 : AppendValueToString(aProperty, aValue);
153 0 : return;
154 : }
155 :
156 : // DOM Level 2 Style says (when describing CSS2Properties, although
157 : // not CSSStyleDeclaration.getPropertyValue):
158 : // However, if there is no shorthand declaration that could be added
159 : // to the ruleset without changing in any way the rules already
160 : // declared in the ruleset (i.e., by adding longhand rules that were
161 : // previously not declared in the ruleset), then the empty string
162 : // should be returned for the shorthand property.
163 : // This means we need to check a number of cases:
164 : // (1) Since a shorthand sets all sub-properties, if some of its
165 : // subproperties were not specified, we must return the empty
166 : // string.
167 : // (2) Since 'inherit' and 'initial' can only be specified as the
168 : // values for entire properties, we need to return the empty
169 : // string if some but not all of the subproperties have one of
170 : // those values.
171 : // (3) Since a single value only makes sense with or without
172 : // !important, we return the empty string if some values are
173 : // !important and some are not.
174 : // Since we're doing this check for 'inherit' and 'initial' up front,
175 : // we can also simplify the property serialization code by serializing
176 : // those values up front as well.
177 0 : PRUint32 totalCount = 0, importantCount = 0,
178 0 : initialCount = 0, inheritCount = 0;
179 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
180 0 : if (*p == eCSSProperty__x_system_font ||
181 0 : nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
182 : // The system-font subproperty and the *-source properties don't count.
183 0 : continue;
184 : }
185 0 : ++totalCount;
186 0 : const nsCSSValue *val = mData->ValueFor(*p);
187 0 : NS_ABORT_IF_FALSE(!val || !mImportantData || !mImportantData->ValueFor(*p),
188 : "can't be in both blocks");
189 0 : if (!val && mImportantData) {
190 0 : ++importantCount;
191 0 : val = mImportantData->ValueFor(*p);
192 : }
193 0 : if (!val) {
194 : // Case (1) above: some subproperties not specified.
195 0 : return;
196 : }
197 0 : if (val->GetUnit() == eCSSUnit_Inherit) {
198 0 : ++inheritCount;
199 0 : } else if (val->GetUnit() == eCSSUnit_Initial) {
200 0 : ++initialCount;
201 : }
202 : }
203 0 : if (importantCount != 0 && importantCount != totalCount) {
204 : // Case (3), no consistent importance.
205 0 : return;
206 : }
207 0 : if (initialCount == totalCount) {
208 : // Simplify serialization below by serializing initial up-front.
209 0 : nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue);
210 0 : return;
211 : }
212 0 : if (inheritCount == totalCount) {
213 : // Simplify serialization below by serializing inherit up-front.
214 0 : nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue);
215 0 : return;
216 : }
217 0 : if (initialCount != 0 || inheritCount != 0) {
218 : // Case (2): partially initial or inherit.
219 0 : return;
220 : }
221 :
222 0 : nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
223 0 : switch (aProperty) {
224 : case eCSSProperty_margin:
225 : case eCSSProperty_padding:
226 : case eCSSProperty_border_color:
227 : case eCSSProperty_border_style:
228 : case eCSSProperty_border_width: {
229 : const nsCSSProperty* subprops =
230 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
231 0 : NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
232 : kNotFound, "first subprop must be top");
233 0 : NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
234 : kNotFound, "second subprop must be right");
235 0 : NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
236 : kNotFound, "third subprop must be bottom");
237 0 : NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
238 : kNotFound, "fourth subprop must be left");
239 0 : const nsCSSValue &topValue = *data->ValueFor(subprops[0]);
240 0 : const nsCSSValue &rightValue = *data->ValueFor(subprops[1]);
241 0 : const nsCSSValue &bottomValue = *data->ValueFor(subprops[2]);
242 0 : const nsCSSValue &leftValue = *data->ValueFor(subprops[3]);
243 :
244 0 : NS_ABORT_IF_FALSE(topValue.GetUnit() != eCSSUnit_Null, "null top");
245 0 : topValue.AppendToString(subprops[0], aValue);
246 0 : if (topValue != rightValue || topValue != leftValue ||
247 0 : topValue != bottomValue) {
248 0 : aValue.Append(PRUnichar(' '));
249 0 : NS_ABORT_IF_FALSE(rightValue.GetUnit() != eCSSUnit_Null, "null right");
250 0 : rightValue.AppendToString(subprops[1], aValue);
251 0 : if (topValue != bottomValue || rightValue != leftValue) {
252 0 : aValue.Append(PRUnichar(' '));
253 0 : NS_ABORT_IF_FALSE(bottomValue.GetUnit() != eCSSUnit_Null, "null bottom");
254 0 : bottomValue.AppendToString(subprops[2], aValue);
255 0 : if (rightValue != leftValue) {
256 0 : aValue.Append(PRUnichar(' '));
257 0 : NS_ABORT_IF_FALSE(leftValue.GetUnit() != eCSSUnit_Null, "null left");
258 0 : leftValue.AppendToString(subprops[3], aValue);
259 : }
260 : }
261 : }
262 0 : break;
263 : }
264 : case eCSSProperty_border_radius:
265 : case eCSSProperty__moz_outline_radius: {
266 : const nsCSSProperty* subprops =
267 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
268 : const nsCSSValue* vals[4] = {
269 0 : data->ValueFor(subprops[0]),
270 0 : data->ValueFor(subprops[1]),
271 0 : data->ValueFor(subprops[2]),
272 0 : data->ValueFor(subprops[3])
273 0 : };
274 :
275 : // For compatibility, only write a slash and the y-values
276 : // if they're not identical to the x-values.
277 0 : bool needY = false;
278 0 : for (int i = 0; i < 4; i++) {
279 0 : if (vals[i]->GetUnit() == eCSSUnit_Pair) {
280 0 : needY = true;
281 0 : vals[i]->GetPairValue().mXValue.AppendToString(subprops[i], aValue);
282 : } else {
283 0 : vals[i]->AppendToString(subprops[i], aValue);
284 : }
285 0 : if (i < 3)
286 0 : aValue.Append(PRUnichar(' '));
287 : }
288 :
289 0 : if (needY) {
290 0 : aValue.AppendLiteral(" / ");
291 0 : for (int i = 0; i < 4; i++) {
292 0 : if (vals[i]->GetUnit() == eCSSUnit_Pair) {
293 0 : vals[i]->GetPairValue().mYValue.AppendToString(subprops[i], aValue);
294 : } else {
295 0 : vals[i]->AppendToString(subprops[i], aValue);
296 : }
297 0 : if (i < 3)
298 0 : aValue.Append(PRUnichar(' '));
299 : }
300 : }
301 0 : break;
302 : }
303 : case eCSSProperty_border: {
304 : const nsCSSProperty* subproptables[3] = {
305 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
306 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
307 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
308 0 : };
309 0 : bool match = true;
310 0 : for (const nsCSSProperty** subprops = subproptables,
311 0 : **subprops_end = ArrayEnd(subproptables);
312 : subprops < subprops_end; ++subprops) {
313 : // Check only the first four subprops in each table, since the
314 : // others are extras for dimensional box properties.
315 0 : const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
316 0 : for (PRInt32 side = 1; side < 4; ++side) {
317 : const nsCSSValue *otherSide =
318 0 : data->ValueFor((*subprops)[side]);
319 0 : if (*firstSide != *otherSide)
320 0 : match = false;
321 : }
322 : }
323 0 : if (!match) {
324 : // We can't express what we have in the border shorthand
325 0 : break;
326 : }
327 : // tweak aProperty and fall through
328 0 : aProperty = eCSSProperty_border_top;
329 : }
330 : case eCSSProperty_border_top:
331 : case eCSSProperty_border_right:
332 : case eCSSProperty_border_bottom:
333 : case eCSSProperty_border_left:
334 : case eCSSProperty_border_start:
335 : case eCSSProperty_border_end:
336 : case eCSSProperty__moz_column_rule:
337 : case eCSSProperty_outline: {
338 : const nsCSSProperty* subprops =
339 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
340 0 : NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
341 : NS_LITERAL_CSTRING("-color")) ||
342 : StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
343 : NS_LITERAL_CSTRING("-color-value")),
344 : "third subprop must be the color property");
345 0 : const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
346 : bool isMozUseTextColor =
347 0 : colorValue->GetUnit() == eCSSUnit_Enumerated &&
348 0 : colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
349 0 : if (!AppendValueToString(subprops[0], aValue) ||
350 0 : !(aValue.Append(PRUnichar(' ')),
351 0 : AppendValueToString(subprops[1], aValue)) ||
352 : // Don't output a third value when it's -moz-use-text-color.
353 : !(isMozUseTextColor ||
354 0 : (aValue.Append(PRUnichar(' ')),
355 0 : AppendValueToString(subprops[2], aValue)))) {
356 0 : aValue.Truncate();
357 : }
358 0 : break;
359 : }
360 : case eCSSProperty_margin_left:
361 : case eCSSProperty_margin_right:
362 : case eCSSProperty_margin_start:
363 : case eCSSProperty_margin_end:
364 : case eCSSProperty_padding_left:
365 : case eCSSProperty_padding_right:
366 : case eCSSProperty_padding_start:
367 : case eCSSProperty_padding_end:
368 : case eCSSProperty_border_left_color:
369 : case eCSSProperty_border_left_style:
370 : case eCSSProperty_border_left_width:
371 : case eCSSProperty_border_right_color:
372 : case eCSSProperty_border_right_style:
373 : case eCSSProperty_border_right_width:
374 : case eCSSProperty_border_start_color:
375 : case eCSSProperty_border_start_style:
376 : case eCSSProperty_border_start_width:
377 : case eCSSProperty_border_end_color:
378 : case eCSSProperty_border_end_style:
379 : case eCSSProperty_border_end_width: {
380 : const nsCSSProperty* subprops =
381 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
382 0 : NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
383 : "not box property with physical vs. logical cascading");
384 0 : AppendValueToString(subprops[0], aValue);
385 0 : break;
386 : }
387 : case eCSSProperty_background: {
388 : // We know from above that all subproperties were specified.
389 : // However, we still can't represent that in the shorthand unless
390 : // they're all lists of the same length. So if they're different
391 : // lengths, we need to bail out.
392 : // We also need to bail out if an item has background-clip and
393 : // background-origin that are different and not the default
394 : // values. (We omit them if they're both default.)
395 : const nsCSSValueList *image =
396 : data->ValueFor(eCSSProperty_background_image)->
397 0 : GetListValue();
398 : const nsCSSValuePairList *repeat =
399 : data->ValueFor(eCSSProperty_background_repeat)->
400 0 : GetPairListValue();
401 : const nsCSSValueList *attachment =
402 : data->ValueFor(eCSSProperty_background_attachment)->
403 0 : GetListValue();
404 : const nsCSSValueList *position =
405 : data->ValueFor(eCSSProperty_background_position)->
406 0 : GetListValue();
407 : const nsCSSValueList *clip =
408 : data->ValueFor(eCSSProperty_background_clip)->
409 0 : GetListValue();
410 : const nsCSSValueList *origin =
411 : data->ValueFor(eCSSProperty_background_origin)->
412 0 : GetListValue();
413 : const nsCSSValuePairList *size =
414 : data->ValueFor(eCSSProperty_background_size)->
415 0 : GetPairListValue();
416 0 : for (;;) {
417 0 : if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
418 0 : size->mYValue.GetUnit() != eCSSUnit_Auto) {
419 : // Non-default background-size, so can't be serialized as shorthand.
420 0 : aValue.Truncate();
421 0 : return;
422 : }
423 0 : image->mValue.AppendToString(eCSSProperty_background_image, aValue);
424 0 : aValue.Append(PRUnichar(' '));
425 0 : repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue);
426 0 : if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
427 0 : repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue);
428 : }
429 0 : aValue.Append(PRUnichar(' '));
430 : attachment->mValue.AppendToString(eCSSProperty_background_attachment,
431 0 : aValue);
432 0 : aValue.Append(PRUnichar(' '));
433 : position->mValue.AppendToString(eCSSProperty_background_position,
434 0 : aValue);
435 :
436 0 : NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
437 : origin->mValue.GetUnit() == eCSSUnit_Enumerated,
438 : "should not have inherit/initial within list");
439 :
440 0 : if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
441 0 : origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
442 : // The shorthand only has a single clip/origin value which sets
443 : // both properties. So if they're different (and non-default),
444 : // we can't represent the state using the shorthand.
445 : MOZ_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER ==
446 : NS_STYLE_BG_ORIGIN_BORDER &&
447 : NS_STYLE_BG_CLIP_PADDING ==
448 : NS_STYLE_BG_ORIGIN_PADDING &&
449 : NS_STYLE_BG_CLIP_CONTENT ==
450 : NS_STYLE_BG_ORIGIN_CONTENT,
451 : "bg-clip and bg-origin style constants must agree");
452 0 : if (clip->mValue != origin->mValue) {
453 0 : aValue.Truncate();
454 0 : return;
455 : }
456 :
457 0 : aValue.Append(PRUnichar(' '));
458 0 : clip->mValue.AppendToString(eCSSProperty_background_clip, aValue);
459 : }
460 :
461 0 : image = image->mNext;
462 0 : repeat = repeat->mNext;
463 0 : attachment = attachment->mNext;
464 0 : position = position->mNext;
465 0 : clip = clip->mNext;
466 0 : origin = origin->mNext;
467 0 : size = size->mNext;
468 :
469 0 : if (!image) {
470 0 : if (repeat || attachment || position || clip || origin || size) {
471 : // Uneven length lists, so can't be serialized as shorthand.
472 0 : aValue.Truncate();
473 0 : return;
474 : }
475 : break;
476 : }
477 0 : if (!repeat || !attachment || !position || !clip || !origin || !size) {
478 : // Uneven length lists, so can't be serialized as shorthand.
479 0 : aValue.Truncate();
480 0 : return;
481 : }
482 0 : aValue.Append(PRUnichar(','));
483 0 : aValue.Append(PRUnichar(' '));
484 : }
485 :
486 0 : aValue.Append(PRUnichar(' '));
487 0 : AppendValueToString(eCSSProperty_background_color, aValue);
488 0 : break;
489 : }
490 : case eCSSProperty_font: {
491 : // systemFont might not be present; the others are guaranteed to be
492 : // based on the shorthand check at the beginning of the function
493 : const nsCSSValue *systemFont =
494 0 : data->ValueFor(eCSSProperty__x_system_font);
495 : const nsCSSValue &style =
496 0 : *data->ValueFor(eCSSProperty_font_style);
497 : const nsCSSValue &variant =
498 0 : *data->ValueFor(eCSSProperty_font_variant);
499 : const nsCSSValue &weight =
500 0 : *data->ValueFor(eCSSProperty_font_weight);
501 : const nsCSSValue &size =
502 0 : *data->ValueFor(eCSSProperty_font_size);
503 : const nsCSSValue &lh =
504 0 : *data->ValueFor(eCSSProperty_line_height);
505 : const nsCSSValue &family =
506 0 : *data->ValueFor(eCSSProperty_font_family);
507 : const nsCSSValue &stretch =
508 0 : *data->ValueFor(eCSSProperty_font_stretch);
509 : const nsCSSValue &sizeAdjust =
510 0 : *data->ValueFor(eCSSProperty_font_size_adjust);
511 : const nsCSSValue &featureSettings =
512 0 : *data->ValueFor(eCSSProperty_font_feature_settings);
513 : const nsCSSValue &languageOverride =
514 0 : *data->ValueFor(eCSSProperty_font_language_override);
515 :
516 0 : if (systemFont &&
517 0 : systemFont->GetUnit() != eCSSUnit_None &&
518 0 : systemFont->GetUnit() != eCSSUnit_Null) {
519 0 : if (style.GetUnit() != eCSSUnit_System_Font ||
520 0 : variant.GetUnit() != eCSSUnit_System_Font ||
521 0 : weight.GetUnit() != eCSSUnit_System_Font ||
522 0 : size.GetUnit() != eCSSUnit_System_Font ||
523 0 : lh.GetUnit() != eCSSUnit_System_Font ||
524 0 : family.GetUnit() != eCSSUnit_System_Font ||
525 0 : stretch.GetUnit() != eCSSUnit_System_Font ||
526 0 : sizeAdjust.GetUnit() != eCSSUnit_System_Font ||
527 0 : featureSettings.GetUnit() != eCSSUnit_System_Font ||
528 0 : languageOverride.GetUnit() != eCSSUnit_System_Font) {
529 : // This can't be represented as a shorthand.
530 0 : return;
531 : }
532 0 : systemFont->AppendToString(eCSSProperty__x_system_font, aValue);
533 : } else {
534 : // The font-stretch, font-size-adjust,
535 : // -moz-font-feature-settings, and -moz-font-language-override
536 : // properties are reset by this shorthand property to their
537 : // initial values, but can't be represented in its syntax.
538 0 : if (stretch.GetUnit() != eCSSUnit_Enumerated ||
539 0 : stretch.GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
540 0 : sizeAdjust.GetUnit() != eCSSUnit_None ||
541 0 : featureSettings.GetUnit() != eCSSUnit_Normal ||
542 0 : languageOverride.GetUnit() != eCSSUnit_Normal) {
543 0 : return;
544 : }
545 :
546 0 : if (style.GetUnit() != eCSSUnit_Enumerated ||
547 0 : style.GetIntValue() != NS_FONT_STYLE_NORMAL) {
548 0 : style.AppendToString(eCSSProperty_font_style, aValue);
549 0 : aValue.Append(PRUnichar(' '));
550 : }
551 0 : if (variant.GetUnit() != eCSSUnit_Enumerated ||
552 0 : variant.GetIntValue() != NS_FONT_VARIANT_NORMAL) {
553 0 : variant.AppendToString(eCSSProperty_font_variant, aValue);
554 0 : aValue.Append(PRUnichar(' '));
555 : }
556 0 : if (weight.GetUnit() != eCSSUnit_Enumerated ||
557 0 : weight.GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
558 0 : weight.AppendToString(eCSSProperty_font_weight, aValue);
559 0 : aValue.Append(PRUnichar(' '));
560 : }
561 0 : size.AppendToString(eCSSProperty_font_size, aValue);
562 0 : if (lh.GetUnit() != eCSSUnit_Normal) {
563 0 : aValue.Append(PRUnichar('/'));
564 0 : lh.AppendToString(eCSSProperty_line_height, aValue);
565 : }
566 0 : aValue.Append(PRUnichar(' '));
567 0 : family.AppendToString(eCSSProperty_font_family, aValue);
568 : }
569 0 : break;
570 : }
571 : case eCSSProperty_list_style:
572 0 : if (AppendValueToString(eCSSProperty_list_style_type, aValue))
573 0 : aValue.Append(PRUnichar(' '));
574 0 : if (AppendValueToString(eCSSProperty_list_style_position, aValue))
575 0 : aValue.Append(PRUnichar(' '));
576 0 : AppendValueToString(eCSSProperty_list_style_image, aValue);
577 0 : break;
578 : case eCSSProperty_overflow: {
579 : const nsCSSValue &xValue =
580 0 : *data->ValueFor(eCSSProperty_overflow_x);
581 : const nsCSSValue &yValue =
582 0 : *data->ValueFor(eCSSProperty_overflow_y);
583 0 : if (xValue == yValue)
584 0 : xValue.AppendToString(eCSSProperty_overflow_x, aValue);
585 0 : break;
586 : }
587 : case eCSSProperty_text_decoration: {
588 : // If text-decoration-color or text-decoration-style isn't initial value,
589 : // we cannot serialize the text-decoration shorthand value.
590 : const nsCSSValue &decorationColor =
591 0 : *data->ValueFor(eCSSProperty_text_decoration_color);
592 : const nsCSSValue &decorationStyle =
593 0 : *data->ValueFor(eCSSProperty_text_decoration_style);
594 :
595 0 : NS_ABORT_IF_FALSE(decorationStyle.GetUnit() == eCSSUnit_Enumerated,
596 : nsPrintfCString(32, "bad text-decoration-style unit %d",
597 : decorationStyle.GetUnit()).get());
598 :
599 0 : if (decorationColor.GetUnit() != eCSSUnit_Enumerated ||
600 0 : decorationColor.GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR ||
601 0 : decorationStyle.GetIntValue() !=
602 : NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
603 0 : return;
604 : }
605 :
606 : const nsCSSValue &textBlink =
607 0 : *data->ValueFor(eCSSProperty_text_blink);
608 : const nsCSSValue &decorationLine =
609 0 : *data->ValueFor(eCSSProperty_text_decoration_line);
610 :
611 0 : NS_ABORT_IF_FALSE(textBlink.GetUnit() == eCSSUnit_Enumerated,
612 : nsPrintfCString(32, "bad text-blink unit %d",
613 : textBlink.GetUnit()).get());
614 0 : NS_ABORT_IF_FALSE(decorationLine.GetUnit() == eCSSUnit_Enumerated,
615 : nsPrintfCString(32, "bad text-decoration-line unit %d",
616 : decorationLine.GetUnit()).get());
617 :
618 0 : bool blinkNone = (textBlink.GetIntValue() == NS_STYLE_TEXT_BLINK_NONE);
619 : bool lineNone =
620 0 : (decorationLine.GetIntValue() == NS_STYLE_TEXT_DECORATION_LINE_NONE);
621 :
622 0 : if (blinkNone && lineNone) {
623 0 : AppendValueToString(eCSSProperty_text_decoration_line, aValue);
624 : } else {
625 0 : if (!blinkNone) {
626 0 : AppendValueToString(eCSSProperty_text_blink, aValue);
627 : }
628 0 : if (!lineNone) {
629 0 : if (!aValue.IsEmpty()) {
630 0 : aValue.Append(PRUnichar(' '));
631 : }
632 0 : AppendValueToString(eCSSProperty_text_decoration_line, aValue);
633 : }
634 : }
635 0 : break;
636 : }
637 : case eCSSProperty_transition: {
638 : const nsCSSValue &transProp =
639 0 : *data->ValueFor(eCSSProperty_transition_property);
640 : const nsCSSValue &transDuration =
641 0 : *data->ValueFor(eCSSProperty_transition_duration);
642 : const nsCSSValue &transTiming =
643 0 : *data->ValueFor(eCSSProperty_transition_timing_function);
644 : const nsCSSValue &transDelay =
645 0 : *data->ValueFor(eCSSProperty_transition_delay);
646 :
647 0 : NS_ABORT_IF_FALSE(transDuration.GetUnit() == eCSSUnit_List ||
648 : transDuration.GetUnit() == eCSSUnit_ListDep,
649 : nsPrintfCString(32, "bad t-duration unit %d",
650 : transDuration.GetUnit()).get());
651 0 : NS_ABORT_IF_FALSE(transTiming.GetUnit() == eCSSUnit_List ||
652 : transTiming.GetUnit() == eCSSUnit_ListDep,
653 : nsPrintfCString(32, "bad t-timing unit %d",
654 : transTiming.GetUnit()).get());
655 0 : NS_ABORT_IF_FALSE(transDelay.GetUnit() == eCSSUnit_List ||
656 : transDelay.GetUnit() == eCSSUnit_ListDep,
657 : nsPrintfCString(32, "bad t-delay unit %d",
658 : transDelay.GetUnit()).get());
659 :
660 0 : const nsCSSValueList* dur = transDuration.GetListValue();
661 0 : const nsCSSValueList* tim = transTiming.GetListValue();
662 0 : const nsCSSValueList* del = transDelay.GetListValue();
663 :
664 0 : if (transProp.GetUnit() == eCSSUnit_None ||
665 0 : transProp.GetUnit() == eCSSUnit_All) {
666 : // If any of the other three lists has more than one element,
667 : // we can't use the shorthand.
668 0 : if (!dur->mNext && !tim->mNext && !del->mNext) {
669 0 : transProp.AppendToString(eCSSProperty_transition_property, aValue);
670 0 : aValue.Append(PRUnichar(' '));
671 0 : dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue);
672 0 : aValue.Append(PRUnichar(' '));
673 : tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
674 0 : aValue);
675 0 : aValue.Append(PRUnichar(' '));
676 0 : del->mValue.AppendToString(eCSSProperty_transition_delay, aValue);
677 0 : aValue.Append(PRUnichar(' '));
678 : } else {
679 0 : aValue.Truncate();
680 : }
681 : } else {
682 0 : NS_ABORT_IF_FALSE(transProp.GetUnit() == eCSSUnit_List ||
683 : transProp.GetUnit() == eCSSUnit_ListDep,
684 : nsPrintfCString(32, "bad t-prop unit %d",
685 : transProp.GetUnit()).get());
686 0 : const nsCSSValueList* pro = transProp.GetListValue();
687 0 : for (;;) {
688 : pro->mValue.AppendToString(eCSSProperty_transition_property,
689 0 : aValue);
690 0 : aValue.Append(PRUnichar(' '));
691 : dur->mValue.AppendToString(eCSSProperty_transition_duration,
692 0 : aValue);
693 0 : aValue.Append(PRUnichar(' '));
694 : tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
695 0 : aValue);
696 0 : aValue.Append(PRUnichar(' '));
697 : del->mValue.AppendToString(eCSSProperty_transition_delay,
698 0 : aValue);
699 0 : pro = pro->mNext;
700 0 : dur = dur->mNext;
701 0 : tim = tim->mNext;
702 0 : del = del->mNext;
703 0 : if (!pro || !dur || !tim || !del) {
704 : break;
705 : }
706 0 : aValue.AppendLiteral(", ");
707 : }
708 0 : if (pro || dur || tim || del) {
709 : // Lists not all the same length, can't use shorthand.
710 0 : aValue.Truncate();
711 : }
712 : }
713 0 : break;
714 : }
715 : case eCSSProperty_animation: {
716 : const nsCSSProperty* subprops =
717 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
718 : static const size_t numProps = 7;
719 0 : NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
720 : "unexpected number of subproperties");
721 : const nsCSSValue* values[numProps];
722 : const nsCSSValueList* lists[numProps];
723 :
724 0 : for (PRUint32 i = 0; i < numProps; ++i) {
725 0 : values[i] = data->ValueFor(subprops[i]);
726 0 : NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
727 : values[i]->GetUnit() == eCSSUnit_ListDep,
728 : nsPrintfCString(32, "bad a-duration unit %d",
729 : values[i]->GetUnit()).get());
730 0 : lists[i] = values[i]->GetListValue();
731 : }
732 :
733 0 : for (;;) {
734 : // We must serialize 'animation-name' last in case it has
735 : // a value that conflicts with one of the other keyword properties.
736 0 : NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
737 : eCSSProperty_animation_name,
738 : "animation-name must be last");
739 0 : bool done = false;
740 0 : for (PRUint32 i = 0;;) {
741 0 : lists[i]->mValue.AppendToString(subprops[i], aValue);
742 0 : lists[i] = lists[i]->mNext;
743 0 : if (!lists[i]) {
744 0 : done = true;
745 : }
746 0 : if (++i == numProps) {
747 : break;
748 : }
749 0 : aValue.Append(PRUnichar(' '));
750 : }
751 0 : if (done) {
752 : break;
753 : }
754 0 : aValue.AppendLiteral(", ");
755 : }
756 0 : for (PRUint32 i = 0; i < numProps; ++i) {
757 0 : if (lists[i]) {
758 : // Lists not all the same length, can't use shorthand.
759 0 : aValue.Truncate();
760 0 : break;
761 : }
762 : }
763 0 : break;
764 : }
765 : case eCSSProperty_marker: {
766 : const nsCSSValue &endValue =
767 0 : *data->ValueFor(eCSSProperty_marker_end);
768 : const nsCSSValue &midValue =
769 0 : *data->ValueFor(eCSSProperty_marker_mid);
770 : const nsCSSValue &startValue =
771 0 : *data->ValueFor(eCSSProperty_marker_start);
772 0 : if (endValue == midValue && midValue == startValue)
773 0 : AppendValueToString(eCSSProperty_marker_end, aValue);
774 0 : break;
775 : }
776 : case eCSSProperty__moz_columns: {
777 : // Two values, column-count and column-width, separated by a space.
778 : const nsCSSProperty* subprops =
779 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
780 0 : AppendValueToString(subprops[0], aValue);
781 0 : aValue.Append(PRUnichar(' '));
782 0 : AppendValueToString(subprops[1], aValue);
783 0 : break;
784 : }
785 : default:
786 0 : NS_ABORT_IF_FALSE(false, "no other shorthands");
787 0 : break;
788 : }
789 : }
790 :
791 : bool
792 0 : Declaration::GetValueIsImportant(const nsAString& aProperty) const
793 : {
794 0 : nsCSSProperty propID = nsCSSProps::LookupProperty(aProperty);
795 0 : if (propID == eCSSProperty_UNKNOWN) {
796 0 : return false;
797 : }
798 0 : return GetValueIsImportant(propID);
799 : }
800 :
801 : bool
802 0 : Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
803 : {
804 0 : if (!mImportantData)
805 0 : return false;
806 :
807 : // Calling ValueFor is inefficient, but we can assume '!important' is rare.
808 :
809 0 : if (!nsCSSProps::IsShorthand(aProperty)) {
810 0 : return mImportantData->ValueFor(aProperty) != nsnull;
811 : }
812 :
813 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
814 0 : if (*p == eCSSProperty__x_system_font) {
815 : // The system_font subproperty doesn't count.
816 0 : continue;
817 : }
818 0 : if (!mImportantData->ValueFor(*p)) {
819 0 : return false;
820 : }
821 : }
822 0 : return true;
823 : }
824 :
825 : void
826 0 : Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
827 : nsAutoString& aValue,
828 : nsAString& aResult) const
829 : {
830 0 : NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
831 : "property enum out of range");
832 0 : NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
833 : aValue.IsEmpty(),
834 : "aValue should be given for shorthands but not longhands");
835 0 : AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
836 0 : aResult.AppendLiteral(": ");
837 0 : if (aValue.IsEmpty())
838 0 : AppendValueToString(aProperty, aResult);
839 : else
840 0 : aResult.Append(aValue);
841 0 : if (GetValueIsImportant(aProperty)) {
842 0 : aResult.AppendLiteral(" ! important");
843 : }
844 0 : aResult.AppendLiteral("; ");
845 0 : }
846 :
847 : void
848 0 : Declaration::ToString(nsAString& aString) const
849 : {
850 : // Someone cares about this declaration's contents, so don't let it
851 : // change from under them. See e.g. bug 338679.
852 0 : SetImmutable();
853 :
854 : nsCSSCompressedDataBlock *systemFontData =
855 0 : GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
856 : const nsCSSValue *systemFont =
857 0 : systemFontData->ValueFor(eCSSProperty__x_system_font);
858 : const bool haveSystemFont = systemFont &&
859 0 : systemFont->GetUnit() != eCSSUnit_None &&
860 0 : systemFont->GetUnit() != eCSSUnit_Null;
861 0 : bool didSystemFont = false;
862 :
863 0 : PRInt32 count = mOrder.Length();
864 : PRInt32 index;
865 0 : nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
866 0 : for (index = 0; index < count; index++) {
867 0 : nsCSSProperty property = OrderValueAt(index);
868 0 : bool doneProperty = false;
869 :
870 : // If we already used this property in a shorthand, skip it.
871 0 : if (shorthandsUsed.Length() > 0) {
872 0 : for (const nsCSSProperty *shorthands =
873 0 : nsCSSProps::ShorthandsContaining(property);
874 : *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
875 0 : if (shorthandsUsed.Contains(*shorthands)) {
876 0 : doneProperty = true;
877 0 : break;
878 : }
879 : }
880 0 : if (doneProperty)
881 0 : continue;
882 : }
883 :
884 : // Try to use this property in a shorthand.
885 0 : nsAutoString value;
886 0 : for (const nsCSSProperty *shorthands =
887 0 : nsCSSProps::ShorthandsContaining(property);
888 : *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
889 : // ShorthandsContaining returns the shorthands in order from those
890 : // that contain the most subproperties to those that contain the
891 : // least, which is exactly the order we want to test them.
892 0 : nsCSSProperty shorthand = *shorthands;
893 :
894 : // If GetValue gives us a non-empty string back, we can use that
895 : // value; otherwise it's not possible to use this shorthand.
896 0 : GetValue(shorthand, value);
897 0 : if (!value.IsEmpty()) {
898 0 : AppendPropertyAndValueToString(shorthand, value, aString);
899 0 : shorthandsUsed.AppendElement(shorthand);
900 0 : doneProperty = true;
901 0 : break;
902 : }
903 :
904 0 : NS_ABORT_IF_FALSE(shorthand != eCSSProperty_font ||
905 : *(shorthands + 1) == eCSSProperty_UNKNOWN,
906 : "font should always be the only containing shorthand");
907 0 : if (shorthand == eCSSProperty_font) {
908 0 : if (haveSystemFont && !didSystemFont) {
909 : // Output the shorthand font declaration that we will
910 : // partially override later. But don't add it to
911 : // |shorthandsUsed|, since we will have to override it.
912 0 : systemFont->AppendToString(eCSSProperty__x_system_font, value);
913 0 : AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
914 0 : value.Truncate();
915 0 : didSystemFont = true;
916 : }
917 :
918 : // That we output the system font is enough for this property if:
919 : // (1) it's the hidden system font subproperty (which either
920 : // means we output it or we don't have it), or
921 : // (2) its value is the hidden system font value and it matches
922 : // the hidden system font subproperty in importance, and
923 : // we output the system font subproperty.
924 0 : const nsCSSValue *val = systemFontData->ValueFor(property);
925 0 : if (property == eCSSProperty__x_system_font ||
926 0 : (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
927 0 : doneProperty = true;
928 : }
929 : }
930 : }
931 0 : if (doneProperty)
932 0 : continue;
933 :
934 0 : NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
935 0 : AppendPropertyAndValueToString(property, value, aString);
936 : }
937 0 : if (! aString.IsEmpty()) {
938 : // if the string is not empty, we have trailing whitespace we
939 : // should remove
940 0 : aString.Truncate(aString.Length() - 1);
941 : }
942 0 : }
943 :
944 : #ifdef DEBUG
945 : void
946 0 : Declaration::List(FILE* out, PRInt32 aIndent) const
947 : {
948 0 : for (PRInt32 index = aIndent; --index >= 0; ) fputs(" ", out);
949 :
950 0 : fputs("{ ", out);
951 0 : nsAutoString s;
952 0 : ToString(s);
953 0 : fputs(NS_ConvertUTF16toUTF8(s).get(), out);
954 0 : fputs("}", out);
955 0 : }
956 : #endif
957 :
958 : void
959 0 : Declaration::GetNthProperty(PRUint32 aIndex, nsAString& aReturn) const
960 : {
961 0 : aReturn.Truncate();
962 0 : if (aIndex < mOrder.Length()) {
963 0 : nsCSSProperty property = OrderValueAt(aIndex);
964 0 : if (0 <= property) {
965 0 : AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
966 : }
967 : }
968 0 : }
969 :
970 : void
971 0 : Declaration::InitializeEmpty()
972 : {
973 0 : NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
974 0 : mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
975 0 : }
976 :
977 : Declaration*
978 0 : Declaration::EnsureMutable()
979 : {
980 0 : NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
981 0 : if (!IsMutable()) {
982 0 : return new Declaration(*this);
983 : } else {
984 0 : return this;
985 : }
986 : }
987 :
988 : size_t
989 0 : Declaration::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
990 : {
991 0 : size_t n = aMallocSizeOf(this);
992 0 : n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
993 0 : n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
994 0 : n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
995 0 : return n;
996 : }
997 :
998 : } // namespace mozilla::css
999 : } // namespace mozilla
|