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_image:
304 0 : AppendValueToString(eCSSProperty_border_image_source, aValue);
305 0 : aValue.Append(PRUnichar(' '));
306 0 : AppendValueToString(eCSSProperty_border_image_slice, aValue);
307 0 : aValue.Append(NS_LITERAL_STRING(" / "));
308 0 : AppendValueToString(eCSSProperty_border_image_width, aValue);
309 0 : aValue.Append(NS_LITERAL_STRING(" / "));
310 0 : AppendValueToString(eCSSProperty_border_image_outset, aValue);
311 0 : aValue.Append(PRUnichar(' '));
312 0 : AppendValueToString(eCSSProperty_border_image_repeat, aValue);
313 0 : break;
314 : case eCSSProperty_border: {
315 : const nsCSSProperty* subproptables[3] = {
316 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
317 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
318 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
319 0 : };
320 0 : bool match = true;
321 0 : for (const nsCSSProperty** subprops = subproptables,
322 0 : **subprops_end = ArrayEnd(subproptables);
323 : subprops < subprops_end; ++subprops) {
324 : // Check only the first four subprops in each table, since the
325 : // others are extras for dimensional box properties.
326 0 : const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
327 0 : for (PRInt32 side = 1; side < 4; ++side) {
328 : const nsCSSValue *otherSide =
329 0 : data->ValueFor((*subprops)[side]);
330 0 : if (*firstSide != *otherSide)
331 0 : match = false;
332 : }
333 : }
334 0 : if (!match) {
335 : // We can't express what we have in the border shorthand
336 0 : break;
337 : }
338 : // tweak aProperty and fall through
339 0 : aProperty = eCSSProperty_border_top;
340 : }
341 : case eCSSProperty_border_top:
342 : case eCSSProperty_border_right:
343 : case eCSSProperty_border_bottom:
344 : case eCSSProperty_border_left:
345 : case eCSSProperty_border_start:
346 : case eCSSProperty_border_end:
347 : case eCSSProperty__moz_column_rule:
348 : case eCSSProperty_outline: {
349 : const nsCSSProperty* subprops =
350 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
351 0 : NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
352 : NS_LITERAL_CSTRING("-color")) ||
353 : StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
354 : NS_LITERAL_CSTRING("-color-value")),
355 : "third subprop must be the color property");
356 0 : const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
357 : bool isMozUseTextColor =
358 0 : colorValue->GetUnit() == eCSSUnit_Enumerated &&
359 0 : colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
360 0 : if (!AppendValueToString(subprops[0], aValue) ||
361 0 : !(aValue.Append(PRUnichar(' ')),
362 0 : AppendValueToString(subprops[1], aValue)) ||
363 : // Don't output a third value when it's -moz-use-text-color.
364 : !(isMozUseTextColor ||
365 0 : (aValue.Append(PRUnichar(' ')),
366 0 : AppendValueToString(subprops[2], aValue)))) {
367 0 : aValue.Truncate();
368 : }
369 0 : break;
370 : }
371 : case eCSSProperty_margin_left:
372 : case eCSSProperty_margin_right:
373 : case eCSSProperty_margin_start:
374 : case eCSSProperty_margin_end:
375 : case eCSSProperty_padding_left:
376 : case eCSSProperty_padding_right:
377 : case eCSSProperty_padding_start:
378 : case eCSSProperty_padding_end:
379 : case eCSSProperty_border_left_color:
380 : case eCSSProperty_border_left_style:
381 : case eCSSProperty_border_left_width:
382 : case eCSSProperty_border_right_color:
383 : case eCSSProperty_border_right_style:
384 : case eCSSProperty_border_right_width:
385 : case eCSSProperty_border_start_color:
386 : case eCSSProperty_border_start_style:
387 : case eCSSProperty_border_start_width:
388 : case eCSSProperty_border_end_color:
389 : case eCSSProperty_border_end_style:
390 : case eCSSProperty_border_end_width: {
391 : const nsCSSProperty* subprops =
392 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
393 0 : NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
394 : "not box property with physical vs. logical cascading");
395 0 : AppendValueToString(subprops[0], aValue);
396 0 : break;
397 : }
398 : case eCSSProperty_background: {
399 : // We know from above that all subproperties were specified.
400 : // However, we still can't represent that in the shorthand unless
401 : // they're all lists of the same length. So if they're different
402 : // lengths, we need to bail out.
403 : // We also need to bail out if an item has background-clip and
404 : // background-origin that are different and not the default
405 : // values. (We omit them if they're both default.)
406 : const nsCSSValueList *image =
407 : data->ValueFor(eCSSProperty_background_image)->
408 0 : GetListValue();
409 : const nsCSSValuePairList *repeat =
410 : data->ValueFor(eCSSProperty_background_repeat)->
411 0 : GetPairListValue();
412 : const nsCSSValueList *attachment =
413 : data->ValueFor(eCSSProperty_background_attachment)->
414 0 : GetListValue();
415 : const nsCSSValueList *position =
416 : data->ValueFor(eCSSProperty_background_position)->
417 0 : GetListValue();
418 : const nsCSSValueList *clip =
419 : data->ValueFor(eCSSProperty_background_clip)->
420 0 : GetListValue();
421 : const nsCSSValueList *origin =
422 : data->ValueFor(eCSSProperty_background_origin)->
423 0 : GetListValue();
424 : const nsCSSValuePairList *size =
425 : data->ValueFor(eCSSProperty_background_size)->
426 0 : GetPairListValue();
427 0 : for (;;) {
428 0 : if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
429 0 : size->mYValue.GetUnit() != eCSSUnit_Auto) {
430 : // Non-default background-size, so can't be serialized as shorthand.
431 0 : aValue.Truncate();
432 0 : return;
433 : }
434 0 : image->mValue.AppendToString(eCSSProperty_background_image, aValue);
435 0 : aValue.Append(PRUnichar(' '));
436 0 : repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue);
437 0 : if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
438 0 : repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue);
439 : }
440 0 : aValue.Append(PRUnichar(' '));
441 : attachment->mValue.AppendToString(eCSSProperty_background_attachment,
442 0 : aValue);
443 0 : aValue.Append(PRUnichar(' '));
444 : position->mValue.AppendToString(eCSSProperty_background_position,
445 0 : aValue);
446 :
447 0 : NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
448 : origin->mValue.GetUnit() == eCSSUnit_Enumerated,
449 : "should not have inherit/initial within list");
450 :
451 0 : if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
452 0 : origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
453 : // The shorthand only has a single clip/origin value which sets
454 : // both properties. So if they're different (and non-default),
455 : // we can't represent the state using the shorthand.
456 : MOZ_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER ==
457 : NS_STYLE_BG_ORIGIN_BORDER &&
458 : NS_STYLE_BG_CLIP_PADDING ==
459 : NS_STYLE_BG_ORIGIN_PADDING &&
460 : NS_STYLE_BG_CLIP_CONTENT ==
461 : NS_STYLE_BG_ORIGIN_CONTENT,
462 : "bg-clip and bg-origin style constants must agree");
463 0 : if (clip->mValue != origin->mValue) {
464 0 : aValue.Truncate();
465 0 : return;
466 : }
467 :
468 0 : aValue.Append(PRUnichar(' '));
469 0 : clip->mValue.AppendToString(eCSSProperty_background_clip, aValue);
470 : }
471 :
472 0 : image = image->mNext;
473 0 : repeat = repeat->mNext;
474 0 : attachment = attachment->mNext;
475 0 : position = position->mNext;
476 0 : clip = clip->mNext;
477 0 : origin = origin->mNext;
478 0 : size = size->mNext;
479 :
480 0 : if (!image) {
481 0 : if (repeat || attachment || position || clip || origin || size) {
482 : // Uneven length lists, so can't be serialized as shorthand.
483 0 : aValue.Truncate();
484 0 : return;
485 : }
486 : break;
487 : }
488 0 : if (!repeat || !attachment || !position || !clip || !origin || !size) {
489 : // Uneven length lists, so can't be serialized as shorthand.
490 0 : aValue.Truncate();
491 0 : return;
492 : }
493 0 : aValue.Append(PRUnichar(','));
494 0 : aValue.Append(PRUnichar(' '));
495 : }
496 :
497 0 : aValue.Append(PRUnichar(' '));
498 0 : AppendValueToString(eCSSProperty_background_color, aValue);
499 0 : break;
500 : }
501 : case eCSSProperty_font: {
502 : // systemFont might not be present; the others are guaranteed to be
503 : // based on the shorthand check at the beginning of the function
504 : const nsCSSValue *systemFont =
505 0 : data->ValueFor(eCSSProperty__x_system_font);
506 : const nsCSSValue &style =
507 0 : *data->ValueFor(eCSSProperty_font_style);
508 : const nsCSSValue &variant =
509 0 : *data->ValueFor(eCSSProperty_font_variant);
510 : const nsCSSValue &weight =
511 0 : *data->ValueFor(eCSSProperty_font_weight);
512 : const nsCSSValue &size =
513 0 : *data->ValueFor(eCSSProperty_font_size);
514 : const nsCSSValue &lh =
515 0 : *data->ValueFor(eCSSProperty_line_height);
516 : const nsCSSValue &family =
517 0 : *data->ValueFor(eCSSProperty_font_family);
518 : const nsCSSValue &stretch =
519 0 : *data->ValueFor(eCSSProperty_font_stretch);
520 : const nsCSSValue &sizeAdjust =
521 0 : *data->ValueFor(eCSSProperty_font_size_adjust);
522 : const nsCSSValue &featureSettings =
523 0 : *data->ValueFor(eCSSProperty_font_feature_settings);
524 : const nsCSSValue &languageOverride =
525 0 : *data->ValueFor(eCSSProperty_font_language_override);
526 :
527 0 : if (systemFont &&
528 0 : systemFont->GetUnit() != eCSSUnit_None &&
529 0 : systemFont->GetUnit() != eCSSUnit_Null) {
530 0 : if (style.GetUnit() != eCSSUnit_System_Font ||
531 0 : variant.GetUnit() != eCSSUnit_System_Font ||
532 0 : weight.GetUnit() != eCSSUnit_System_Font ||
533 0 : size.GetUnit() != eCSSUnit_System_Font ||
534 0 : lh.GetUnit() != eCSSUnit_System_Font ||
535 0 : family.GetUnit() != eCSSUnit_System_Font ||
536 0 : stretch.GetUnit() != eCSSUnit_System_Font ||
537 0 : sizeAdjust.GetUnit() != eCSSUnit_System_Font ||
538 0 : featureSettings.GetUnit() != eCSSUnit_System_Font ||
539 0 : languageOverride.GetUnit() != eCSSUnit_System_Font) {
540 : // This can't be represented as a shorthand.
541 0 : return;
542 : }
543 0 : systemFont->AppendToString(eCSSProperty__x_system_font, aValue);
544 : } else {
545 : // The font-stretch, font-size-adjust,
546 : // -moz-font-feature-settings, and -moz-font-language-override
547 : // properties are reset by this shorthand property to their
548 : // initial values, but can't be represented in its syntax.
549 0 : if (stretch.GetUnit() != eCSSUnit_Enumerated ||
550 0 : stretch.GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
551 0 : sizeAdjust.GetUnit() != eCSSUnit_None ||
552 0 : featureSettings.GetUnit() != eCSSUnit_Normal ||
553 0 : languageOverride.GetUnit() != eCSSUnit_Normal) {
554 0 : return;
555 : }
556 :
557 0 : if (style.GetUnit() != eCSSUnit_Enumerated ||
558 0 : style.GetIntValue() != NS_FONT_STYLE_NORMAL) {
559 0 : style.AppendToString(eCSSProperty_font_style, aValue);
560 0 : aValue.Append(PRUnichar(' '));
561 : }
562 0 : if (variant.GetUnit() != eCSSUnit_Enumerated ||
563 0 : variant.GetIntValue() != NS_FONT_VARIANT_NORMAL) {
564 0 : variant.AppendToString(eCSSProperty_font_variant, aValue);
565 0 : aValue.Append(PRUnichar(' '));
566 : }
567 0 : if (weight.GetUnit() != eCSSUnit_Enumerated ||
568 0 : weight.GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
569 0 : weight.AppendToString(eCSSProperty_font_weight, aValue);
570 0 : aValue.Append(PRUnichar(' '));
571 : }
572 0 : size.AppendToString(eCSSProperty_font_size, aValue);
573 0 : if (lh.GetUnit() != eCSSUnit_Normal) {
574 0 : aValue.Append(PRUnichar('/'));
575 0 : lh.AppendToString(eCSSProperty_line_height, aValue);
576 : }
577 0 : aValue.Append(PRUnichar(' '));
578 0 : family.AppendToString(eCSSProperty_font_family, aValue);
579 : }
580 0 : break;
581 : }
582 : case eCSSProperty_list_style:
583 0 : if (AppendValueToString(eCSSProperty_list_style_type, aValue))
584 0 : aValue.Append(PRUnichar(' '));
585 0 : if (AppendValueToString(eCSSProperty_list_style_position, aValue))
586 0 : aValue.Append(PRUnichar(' '));
587 0 : AppendValueToString(eCSSProperty_list_style_image, aValue);
588 0 : break;
589 : case eCSSProperty_overflow: {
590 : const nsCSSValue &xValue =
591 0 : *data->ValueFor(eCSSProperty_overflow_x);
592 : const nsCSSValue &yValue =
593 0 : *data->ValueFor(eCSSProperty_overflow_y);
594 0 : if (xValue == yValue)
595 0 : xValue.AppendToString(eCSSProperty_overflow_x, aValue);
596 0 : break;
597 : }
598 : case eCSSProperty_text_decoration: {
599 : // If text-decoration-color or text-decoration-style isn't initial value,
600 : // we cannot serialize the text-decoration shorthand value.
601 : const nsCSSValue &decorationColor =
602 0 : *data->ValueFor(eCSSProperty_text_decoration_color);
603 : const nsCSSValue &decorationStyle =
604 0 : *data->ValueFor(eCSSProperty_text_decoration_style);
605 :
606 0 : NS_ABORT_IF_FALSE(decorationStyle.GetUnit() == eCSSUnit_Enumerated,
607 : nsPrintfCString(32, "bad text-decoration-style unit %d",
608 : decorationStyle.GetUnit()).get());
609 :
610 0 : if (decorationColor.GetUnit() != eCSSUnit_Enumerated ||
611 0 : decorationColor.GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR ||
612 0 : decorationStyle.GetIntValue() !=
613 : NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
614 0 : return;
615 : }
616 :
617 : const nsCSSValue &textBlink =
618 0 : *data->ValueFor(eCSSProperty_text_blink);
619 : const nsCSSValue &decorationLine =
620 0 : *data->ValueFor(eCSSProperty_text_decoration_line);
621 :
622 0 : NS_ABORT_IF_FALSE(textBlink.GetUnit() == eCSSUnit_Enumerated,
623 : nsPrintfCString(32, "bad text-blink unit %d",
624 : textBlink.GetUnit()).get());
625 0 : NS_ABORT_IF_FALSE(decorationLine.GetUnit() == eCSSUnit_Enumerated,
626 : nsPrintfCString(32, "bad text-decoration-line unit %d",
627 : decorationLine.GetUnit()).get());
628 :
629 0 : bool blinkNone = (textBlink.GetIntValue() == NS_STYLE_TEXT_BLINK_NONE);
630 : bool lineNone =
631 0 : (decorationLine.GetIntValue() == NS_STYLE_TEXT_DECORATION_LINE_NONE);
632 :
633 0 : if (blinkNone && lineNone) {
634 0 : AppendValueToString(eCSSProperty_text_decoration_line, aValue);
635 : } else {
636 0 : if (!blinkNone) {
637 0 : AppendValueToString(eCSSProperty_text_blink, aValue);
638 : }
639 0 : if (!lineNone) {
640 0 : if (!aValue.IsEmpty()) {
641 0 : aValue.Append(PRUnichar(' '));
642 : }
643 0 : AppendValueToString(eCSSProperty_text_decoration_line, aValue);
644 : }
645 : }
646 0 : break;
647 : }
648 : case eCSSProperty_transition: {
649 : const nsCSSValue &transProp =
650 0 : *data->ValueFor(eCSSProperty_transition_property);
651 : const nsCSSValue &transDuration =
652 0 : *data->ValueFor(eCSSProperty_transition_duration);
653 : const nsCSSValue &transTiming =
654 0 : *data->ValueFor(eCSSProperty_transition_timing_function);
655 : const nsCSSValue &transDelay =
656 0 : *data->ValueFor(eCSSProperty_transition_delay);
657 :
658 0 : NS_ABORT_IF_FALSE(transDuration.GetUnit() == eCSSUnit_List ||
659 : transDuration.GetUnit() == eCSSUnit_ListDep,
660 : nsPrintfCString(32, "bad t-duration unit %d",
661 : transDuration.GetUnit()).get());
662 0 : NS_ABORT_IF_FALSE(transTiming.GetUnit() == eCSSUnit_List ||
663 : transTiming.GetUnit() == eCSSUnit_ListDep,
664 : nsPrintfCString(32, "bad t-timing unit %d",
665 : transTiming.GetUnit()).get());
666 0 : NS_ABORT_IF_FALSE(transDelay.GetUnit() == eCSSUnit_List ||
667 : transDelay.GetUnit() == eCSSUnit_ListDep,
668 : nsPrintfCString(32, "bad t-delay unit %d",
669 : transDelay.GetUnit()).get());
670 :
671 0 : const nsCSSValueList* dur = transDuration.GetListValue();
672 0 : const nsCSSValueList* tim = transTiming.GetListValue();
673 0 : const nsCSSValueList* del = transDelay.GetListValue();
674 :
675 0 : if (transProp.GetUnit() == eCSSUnit_None ||
676 0 : transProp.GetUnit() == eCSSUnit_All) {
677 : // If any of the other three lists has more than one element,
678 : // we can't use the shorthand.
679 0 : if (!dur->mNext && !tim->mNext && !del->mNext) {
680 0 : transProp.AppendToString(eCSSProperty_transition_property, aValue);
681 0 : aValue.Append(PRUnichar(' '));
682 0 : dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue);
683 0 : aValue.Append(PRUnichar(' '));
684 : tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
685 0 : aValue);
686 0 : aValue.Append(PRUnichar(' '));
687 0 : del->mValue.AppendToString(eCSSProperty_transition_delay, aValue);
688 0 : aValue.Append(PRUnichar(' '));
689 : } else {
690 0 : aValue.Truncate();
691 : }
692 : } else {
693 0 : NS_ABORT_IF_FALSE(transProp.GetUnit() == eCSSUnit_List ||
694 : transProp.GetUnit() == eCSSUnit_ListDep,
695 : nsPrintfCString(32, "bad t-prop unit %d",
696 : transProp.GetUnit()).get());
697 0 : const nsCSSValueList* pro = transProp.GetListValue();
698 0 : for (;;) {
699 : pro->mValue.AppendToString(eCSSProperty_transition_property,
700 0 : aValue);
701 0 : aValue.Append(PRUnichar(' '));
702 : dur->mValue.AppendToString(eCSSProperty_transition_duration,
703 0 : aValue);
704 0 : aValue.Append(PRUnichar(' '));
705 : tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
706 0 : aValue);
707 0 : aValue.Append(PRUnichar(' '));
708 : del->mValue.AppendToString(eCSSProperty_transition_delay,
709 0 : aValue);
710 0 : pro = pro->mNext;
711 0 : dur = dur->mNext;
712 0 : tim = tim->mNext;
713 0 : del = del->mNext;
714 0 : if (!pro || !dur || !tim || !del) {
715 : break;
716 : }
717 0 : aValue.AppendLiteral(", ");
718 : }
719 0 : if (pro || dur || tim || del) {
720 : // Lists not all the same length, can't use shorthand.
721 0 : aValue.Truncate();
722 : }
723 : }
724 0 : break;
725 : }
726 : case eCSSProperty_animation: {
727 : const nsCSSProperty* subprops =
728 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
729 : static const size_t numProps = 7;
730 0 : NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
731 : "unexpected number of subproperties");
732 : const nsCSSValue* values[numProps];
733 : const nsCSSValueList* lists[numProps];
734 :
735 0 : for (PRUint32 i = 0; i < numProps; ++i) {
736 0 : values[i] = data->ValueFor(subprops[i]);
737 0 : NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
738 : values[i]->GetUnit() == eCSSUnit_ListDep,
739 : nsPrintfCString(32, "bad a-duration unit %d",
740 : values[i]->GetUnit()).get());
741 0 : lists[i] = values[i]->GetListValue();
742 : }
743 :
744 0 : for (;;) {
745 : // We must serialize 'animation-name' last in case it has
746 : // a value that conflicts with one of the other keyword properties.
747 0 : NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
748 : eCSSProperty_animation_name,
749 : "animation-name must be last");
750 0 : bool done = false;
751 0 : for (PRUint32 i = 0;;) {
752 0 : lists[i]->mValue.AppendToString(subprops[i], aValue);
753 0 : lists[i] = lists[i]->mNext;
754 0 : if (!lists[i]) {
755 0 : done = true;
756 : }
757 0 : if (++i == numProps) {
758 : break;
759 : }
760 0 : aValue.Append(PRUnichar(' '));
761 : }
762 0 : if (done) {
763 : break;
764 : }
765 0 : aValue.AppendLiteral(", ");
766 : }
767 0 : for (PRUint32 i = 0; i < numProps; ++i) {
768 0 : if (lists[i]) {
769 : // Lists not all the same length, can't use shorthand.
770 0 : aValue.Truncate();
771 0 : break;
772 : }
773 : }
774 0 : break;
775 : }
776 : case eCSSProperty_marker: {
777 : const nsCSSValue &endValue =
778 0 : *data->ValueFor(eCSSProperty_marker_end);
779 : const nsCSSValue &midValue =
780 0 : *data->ValueFor(eCSSProperty_marker_mid);
781 : const nsCSSValue &startValue =
782 0 : *data->ValueFor(eCSSProperty_marker_start);
783 0 : if (endValue == midValue && midValue == startValue)
784 0 : AppendValueToString(eCSSProperty_marker_end, aValue);
785 0 : break;
786 : }
787 : case eCSSProperty__moz_columns: {
788 : // Two values, column-count and column-width, separated by a space.
789 : const nsCSSProperty* subprops =
790 0 : nsCSSProps::SubpropertyEntryFor(aProperty);
791 0 : AppendValueToString(subprops[0], aValue);
792 0 : aValue.Append(PRUnichar(' '));
793 0 : AppendValueToString(subprops[1], aValue);
794 0 : break;
795 : }
796 : default:
797 0 : NS_ABORT_IF_FALSE(false, "no other shorthands");
798 0 : break;
799 : }
800 : }
801 :
802 : bool
803 0 : Declaration::GetValueIsImportant(const nsAString& aProperty) const
804 : {
805 0 : nsCSSProperty propID = nsCSSProps::LookupProperty(aProperty);
806 0 : if (propID == eCSSProperty_UNKNOWN) {
807 0 : return false;
808 : }
809 0 : return GetValueIsImportant(propID);
810 : }
811 :
812 : bool
813 0 : Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
814 : {
815 0 : if (!mImportantData)
816 0 : return false;
817 :
818 : // Calling ValueFor is inefficient, but we can assume '!important' is rare.
819 :
820 0 : if (!nsCSSProps::IsShorthand(aProperty)) {
821 0 : return mImportantData->ValueFor(aProperty) != nsnull;
822 : }
823 :
824 0 : CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
825 0 : if (*p == eCSSProperty__x_system_font) {
826 : // The system_font subproperty doesn't count.
827 0 : continue;
828 : }
829 0 : if (!mImportantData->ValueFor(*p)) {
830 0 : return false;
831 : }
832 : }
833 0 : return true;
834 : }
835 :
836 : void
837 0 : Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
838 : nsAutoString& aValue,
839 : nsAString& aResult) const
840 : {
841 0 : NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
842 : "property enum out of range");
843 0 : NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
844 : aValue.IsEmpty(),
845 : "aValue should be given for shorthands but not longhands");
846 0 : AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
847 0 : aResult.AppendLiteral(": ");
848 0 : if (aValue.IsEmpty())
849 0 : AppendValueToString(aProperty, aResult);
850 : else
851 0 : aResult.Append(aValue);
852 0 : if (GetValueIsImportant(aProperty)) {
853 0 : aResult.AppendLiteral(" ! important");
854 : }
855 0 : aResult.AppendLiteral("; ");
856 0 : }
857 :
858 : void
859 0 : Declaration::ToString(nsAString& aString) const
860 : {
861 : // Someone cares about this declaration's contents, so don't let it
862 : // change from under them. See e.g. bug 338679.
863 0 : SetImmutable();
864 :
865 : nsCSSCompressedDataBlock *systemFontData =
866 0 : GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
867 : const nsCSSValue *systemFont =
868 0 : systemFontData->ValueFor(eCSSProperty__x_system_font);
869 : const bool haveSystemFont = systemFont &&
870 0 : systemFont->GetUnit() != eCSSUnit_None &&
871 0 : systemFont->GetUnit() != eCSSUnit_Null;
872 0 : bool didSystemFont = false;
873 :
874 0 : PRInt32 count = mOrder.Length();
875 : PRInt32 index;
876 0 : nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
877 0 : for (index = 0; index < count; index++) {
878 0 : nsCSSProperty property = OrderValueAt(index);
879 0 : bool doneProperty = false;
880 :
881 : // If we already used this property in a shorthand, skip it.
882 0 : if (shorthandsUsed.Length() > 0) {
883 0 : for (const nsCSSProperty *shorthands =
884 0 : nsCSSProps::ShorthandsContaining(property);
885 : *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
886 0 : if (shorthandsUsed.Contains(*shorthands)) {
887 0 : doneProperty = true;
888 0 : break;
889 : }
890 : }
891 0 : if (doneProperty)
892 0 : continue;
893 : }
894 :
895 : // Try to use this property in a shorthand.
896 0 : nsAutoString value;
897 0 : for (const nsCSSProperty *shorthands =
898 0 : nsCSSProps::ShorthandsContaining(property);
899 : *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
900 : // ShorthandsContaining returns the shorthands in order from those
901 : // that contain the most subproperties to those that contain the
902 : // least, which is exactly the order we want to test them.
903 0 : nsCSSProperty shorthand = *shorthands;
904 :
905 : // If GetValue gives us a non-empty string back, we can use that
906 : // value; otherwise it's not possible to use this shorthand.
907 0 : GetValue(shorthand, value);
908 0 : if (!value.IsEmpty()) {
909 0 : AppendPropertyAndValueToString(shorthand, value, aString);
910 0 : shorthandsUsed.AppendElement(shorthand);
911 0 : doneProperty = true;
912 0 : break;
913 : }
914 :
915 0 : NS_ABORT_IF_FALSE(shorthand != eCSSProperty_font ||
916 : *(shorthands + 1) == eCSSProperty_UNKNOWN,
917 : "font should always be the only containing shorthand");
918 0 : if (shorthand == eCSSProperty_font) {
919 0 : if (haveSystemFont && !didSystemFont) {
920 : // Output the shorthand font declaration that we will
921 : // partially override later. But don't add it to
922 : // |shorthandsUsed|, since we will have to override it.
923 0 : systemFont->AppendToString(eCSSProperty__x_system_font, value);
924 0 : AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
925 0 : value.Truncate();
926 0 : didSystemFont = true;
927 : }
928 :
929 : // That we output the system font is enough for this property if:
930 : // (1) it's the hidden system font subproperty (which either
931 : // means we output it or we don't have it), or
932 : // (2) its value is the hidden system font value and it matches
933 : // the hidden system font subproperty in importance, and
934 : // we output the system font subproperty.
935 0 : const nsCSSValue *val = systemFontData->ValueFor(property);
936 0 : if (property == eCSSProperty__x_system_font ||
937 0 : (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
938 0 : doneProperty = true;
939 : }
940 : }
941 : }
942 0 : if (doneProperty)
943 0 : continue;
944 :
945 0 : NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
946 0 : AppendPropertyAndValueToString(property, value, aString);
947 : }
948 0 : if (! aString.IsEmpty()) {
949 : // if the string is not empty, we have trailing whitespace we
950 : // should remove
951 0 : aString.Truncate(aString.Length() - 1);
952 : }
953 0 : }
954 :
955 : #ifdef DEBUG
956 : void
957 0 : Declaration::List(FILE* out, PRInt32 aIndent) const
958 : {
959 0 : for (PRInt32 index = aIndent; --index >= 0; ) fputs(" ", out);
960 :
961 0 : fputs("{ ", out);
962 0 : nsAutoString s;
963 0 : ToString(s);
964 0 : fputs(NS_ConvertUTF16toUTF8(s).get(), out);
965 0 : fputs("}", out);
966 0 : }
967 : #endif
968 :
969 : void
970 0 : Declaration::GetNthProperty(PRUint32 aIndex, nsAString& aReturn) const
971 : {
972 0 : aReturn.Truncate();
973 0 : if (aIndex < mOrder.Length()) {
974 0 : nsCSSProperty property = OrderValueAt(aIndex);
975 0 : if (0 <= property) {
976 0 : AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
977 : }
978 : }
979 0 : }
980 :
981 : void
982 0 : Declaration::InitializeEmpty()
983 : {
984 0 : NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
985 0 : mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
986 0 : }
987 :
988 : Declaration*
989 0 : Declaration::EnsureMutable()
990 : {
991 0 : NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
992 0 : if (!IsMutable()) {
993 0 : return new Declaration(*this);
994 : } else {
995 0 : return this;
996 : }
997 : }
998 :
999 : size_t
1000 0 : Declaration::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
1001 : {
1002 0 : size_t n = aMallocSizeOf(this);
1003 0 : n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
1004 0 : n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
1005 0 : n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
1006 0 : return n;
1007 : }
1008 :
1009 : } // namespace mozilla::css
1010 : } // namespace mozilla
|