layout/style/Declaration.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /*
michael@0 7 * representation of a declaration block (or style attribute) in a CSS
michael@0 8 * stylesheet
michael@0 9 */
michael@0 10
michael@0 11 #include "mozilla/ArrayUtils.h"
michael@0 12 #include "mozilla/MemoryReporting.h"
michael@0 13
michael@0 14 #include "mozilla/css/Declaration.h"
michael@0 15 #include "nsPrintfCString.h"
michael@0 16 #include "gfxFontConstants.h"
michael@0 17 #include "nsStyleUtil.h"
michael@0 18
michael@0 19 namespace mozilla {
michael@0 20 namespace css {
michael@0 21
michael@0 22 Declaration::Declaration()
michael@0 23 : mImmutable(false)
michael@0 24 {
michael@0 25 MOZ_COUNT_CTOR(mozilla::css::Declaration);
michael@0 26 }
michael@0 27
michael@0 28 Declaration::Declaration(const Declaration& aCopy)
michael@0 29 : mOrder(aCopy.mOrder),
michael@0 30 mVariableOrder(aCopy.mVariableOrder),
michael@0 31 mData(aCopy.mData ? aCopy.mData->Clone() : nullptr),
michael@0 32 mImportantData(aCopy.mImportantData ?
michael@0 33 aCopy.mImportantData->Clone() : nullptr),
michael@0 34 mVariables(aCopy.mVariables ?
michael@0 35 new CSSVariableDeclarations(*aCopy.mVariables) :
michael@0 36 nullptr),
michael@0 37 mImportantVariables(aCopy.mImportantVariables ?
michael@0 38 new CSSVariableDeclarations(*aCopy.mImportantVariables) :
michael@0 39 nullptr),
michael@0 40 mImmutable(false)
michael@0 41 {
michael@0 42 MOZ_COUNT_CTOR(mozilla::css::Declaration);
michael@0 43 }
michael@0 44
michael@0 45 Declaration::~Declaration()
michael@0 46 {
michael@0 47 MOZ_COUNT_DTOR(mozilla::css::Declaration);
michael@0 48 }
michael@0 49
michael@0 50 void
michael@0 51 Declaration::ValueAppended(nsCSSProperty aProperty)
michael@0 52 {
michael@0 53 NS_ABORT_IF_FALSE(!mData && !mImportantData,
michael@0 54 "should only be called while expanded");
michael@0 55 NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
michael@0 56 "shorthands forbidden");
michael@0 57 // order IS important for CSS, so remove and add to the end
michael@0 58 mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
michael@0 59 mOrder.AppendElement(static_cast<uint32_t>(aProperty));
michael@0 60 }
michael@0 61
michael@0 62 void
michael@0 63 Declaration::RemoveProperty(nsCSSProperty aProperty)
michael@0 64 {
michael@0 65 MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT);
michael@0 66
michael@0 67 nsCSSExpandedDataBlock data;
michael@0 68 ExpandTo(&data);
michael@0 69 NS_ABORT_IF_FALSE(!mData && !mImportantData, "Expand didn't null things out");
michael@0 70
michael@0 71 if (nsCSSProps::IsShorthand(aProperty)) {
michael@0 72 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
michael@0 73 data.ClearLonghandProperty(*p);
michael@0 74 mOrder.RemoveElement(static_cast<uint32_t>(*p));
michael@0 75 }
michael@0 76 } else {
michael@0 77 data.ClearLonghandProperty(aProperty);
michael@0 78 mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
michael@0 79 }
michael@0 80
michael@0 81 CompressFrom(&data);
michael@0 82 }
michael@0 83
michael@0 84 bool
michael@0 85 Declaration::HasProperty(nsCSSProperty aProperty) const
michael@0 86 {
michael@0 87 NS_ABORT_IF_FALSE(0 <= aProperty &&
michael@0 88 aProperty < eCSSProperty_COUNT_no_shorthands,
michael@0 89 "property ID out of range");
michael@0 90
michael@0 91 nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
michael@0 92 ? mImportantData : mData;
michael@0 93 const nsCSSValue *val = data->ValueFor(aProperty);
michael@0 94 return !!val;
michael@0 95 }
michael@0 96
michael@0 97 bool
michael@0 98 Declaration::AppendValueToString(nsCSSProperty aProperty,
michael@0 99 nsAString& aResult,
michael@0 100 nsCSSValue::Serialization aSerialization) const
michael@0 101 {
michael@0 102 NS_ABORT_IF_FALSE(0 <= aProperty &&
michael@0 103 aProperty < eCSSProperty_COUNT_no_shorthands,
michael@0 104 "property ID out of range");
michael@0 105
michael@0 106 nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
michael@0 107 ? mImportantData : mData;
michael@0 108 const nsCSSValue *val = data->ValueFor(aProperty);
michael@0 109 if (!val) {
michael@0 110 return false;
michael@0 111 }
michael@0 112
michael@0 113 val->AppendToString(aProperty, aResult, aSerialization);
michael@0 114 return true;
michael@0 115 }
michael@0 116
michael@0 117 // Helper to append |aString| with the shorthand sides notation used in e.g.
michael@0 118 // 'padding'. |aProperties| and |aValues| are expected to have 4 elements.
michael@0 119 static void
michael@0 120 AppendSidesShorthandToString(const nsCSSProperty aProperties[],
michael@0 121 const nsCSSValue* aValues[],
michael@0 122 nsAString& aString,
michael@0 123 nsCSSValue::Serialization aSerialization)
michael@0 124 {
michael@0 125 const nsCSSValue& value1 = *aValues[0];
michael@0 126 const nsCSSValue& value2 = *aValues[1];
michael@0 127 const nsCSSValue& value3 = *aValues[2];
michael@0 128 const nsCSSValue& value4 = *aValues[3];
michael@0 129
michael@0 130 NS_ABORT_IF_FALSE(value1.GetUnit() != eCSSUnit_Null, "null value 1");
michael@0 131 value1.AppendToString(aProperties[0], aString, aSerialization);
michael@0 132 if (value1 != value2 || value1 != value3 || value1 != value4) {
michael@0 133 aString.Append(char16_t(' '));
michael@0 134 NS_ABORT_IF_FALSE(value2.GetUnit() != eCSSUnit_Null, "null value 2");
michael@0 135 value2.AppendToString(aProperties[1], aString, aSerialization);
michael@0 136 if (value1 != value3 || value2 != value4) {
michael@0 137 aString.Append(char16_t(' '));
michael@0 138 NS_ABORT_IF_FALSE(value3.GetUnit() != eCSSUnit_Null, "null value 3");
michael@0 139 value3.AppendToString(aProperties[2], aString, aSerialization);
michael@0 140 if (value2 != value4) {
michael@0 141 aString.Append(char16_t(' '));
michael@0 142 NS_ABORT_IF_FALSE(value4.GetUnit() != eCSSUnit_Null, "null value 4");
michael@0 143 value4.AppendToString(aProperties[3], aString, aSerialization);
michael@0 144 }
michael@0 145 }
michael@0 146 }
michael@0 147 }
michael@0 148
michael@0 149 void
michael@0 150 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
michael@0 151 {
michael@0 152 GetValue(aProperty, aValue, nsCSSValue::eNormalized);
michael@0 153 }
michael@0 154
michael@0 155 void
michael@0 156 Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
michael@0 157 {
michael@0 158 GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
michael@0 159 }
michael@0 160
michael@0 161 void
michael@0 162 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
michael@0 163 nsCSSValue::Serialization aSerialization) const
michael@0 164 {
michael@0 165 aValue.Truncate(0);
michael@0 166
michael@0 167 // simple properties are easy.
michael@0 168 if (!nsCSSProps::IsShorthand(aProperty)) {
michael@0 169 AppendValueToString(aProperty, aValue, aSerialization);
michael@0 170 return;
michael@0 171 }
michael@0 172
michael@0 173 // DOM Level 2 Style says (when describing CSS2Properties, although
michael@0 174 // not CSSStyleDeclaration.getPropertyValue):
michael@0 175 // However, if there is no shorthand declaration that could be added
michael@0 176 // to the ruleset without changing in any way the rules already
michael@0 177 // declared in the ruleset (i.e., by adding longhand rules that were
michael@0 178 // previously not declared in the ruleset), then the empty string
michael@0 179 // should be returned for the shorthand property.
michael@0 180 // This means we need to check a number of cases:
michael@0 181 // (1) Since a shorthand sets all sub-properties, if some of its
michael@0 182 // subproperties were not specified, we must return the empty
michael@0 183 // string.
michael@0 184 // (2) Since 'inherit', 'initial' and 'unset' can only be specified
michael@0 185 // as the values for entire properties, we need to return the
michael@0 186 // empty string if some but not all of the subproperties have one
michael@0 187 // of those values.
michael@0 188 // (3) Since a single value only makes sense with or without
michael@0 189 // !important, we return the empty string if some values are
michael@0 190 // !important and some are not.
michael@0 191 // Since we're doing this check for 'inherit' and 'initial' up front,
michael@0 192 // we can also simplify the property serialization code by serializing
michael@0 193 // those values up front as well.
michael@0 194 //
michael@0 195 // Additionally, if a shorthand property was set using a value with a
michael@0 196 // variable reference and none of its component longhand properties were
michael@0 197 // then overridden on the declaration, we return the token stream
michael@0 198 // assigned to the shorthand.
michael@0 199 const nsCSSValue* tokenStream = nullptr;
michael@0 200 uint32_t totalCount = 0, importantCount = 0,
michael@0 201 initialCount = 0, inheritCount = 0, unsetCount = 0,
michael@0 202 matchingTokenStreamCount = 0;
michael@0 203 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
michael@0 204 if (*p == eCSSProperty__x_system_font ||
michael@0 205 nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
michael@0 206 // The system-font subproperty and the *-source properties don't count.
michael@0 207 continue;
michael@0 208 }
michael@0 209 ++totalCount;
michael@0 210 const nsCSSValue *val = mData->ValueFor(*p);
michael@0 211 NS_ABORT_IF_FALSE(!val || !mImportantData || !mImportantData->ValueFor(*p),
michael@0 212 "can't be in both blocks");
michael@0 213 if (!val && mImportantData) {
michael@0 214 ++importantCount;
michael@0 215 val = mImportantData->ValueFor(*p);
michael@0 216 }
michael@0 217 if (!val) {
michael@0 218 // Case (1) above: some subproperties not specified.
michael@0 219 return;
michael@0 220 }
michael@0 221 if (val->GetUnit() == eCSSUnit_Inherit) {
michael@0 222 ++inheritCount;
michael@0 223 } else if (val->GetUnit() == eCSSUnit_Initial) {
michael@0 224 ++initialCount;
michael@0 225 } else if (val->GetUnit() == eCSSUnit_Unset) {
michael@0 226 ++unsetCount;
michael@0 227 } else if (val->GetUnit() == eCSSUnit_TokenStream &&
michael@0 228 val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
michael@0 229 tokenStream = val;
michael@0 230 ++matchingTokenStreamCount;
michael@0 231 }
michael@0 232 }
michael@0 233 if (importantCount != 0 && importantCount != totalCount) {
michael@0 234 // Case (3), no consistent importance.
michael@0 235 return;
michael@0 236 }
michael@0 237 if (initialCount == totalCount) {
michael@0 238 // Simplify serialization below by serializing initial up-front.
michael@0 239 nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue,
michael@0 240 nsCSSValue::eNormalized);
michael@0 241 return;
michael@0 242 }
michael@0 243 if (inheritCount == totalCount) {
michael@0 244 // Simplify serialization below by serializing inherit up-front.
michael@0 245 nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue,
michael@0 246 nsCSSValue::eNormalized);
michael@0 247 return;
michael@0 248 }
michael@0 249 if (unsetCount == totalCount) {
michael@0 250 // Simplify serialization below by serializing unset up-front.
michael@0 251 nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue,
michael@0 252 nsCSSValue::eNormalized);
michael@0 253 return;
michael@0 254 }
michael@0 255 if (initialCount != 0 || inheritCount != 0 || unsetCount != 0) {
michael@0 256 // Case (2): partially initial, inherit or unset.
michael@0 257 return;
michael@0 258 }
michael@0 259 if (tokenStream) {
michael@0 260 if (matchingTokenStreamCount == totalCount) {
michael@0 261 // Shorthand was specified using variable references and all of its
michael@0 262 // longhand components were set by the shorthand.
michael@0 263 aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream);
michael@0 264 } else {
michael@0 265 // In all other cases, serialize to the empty string.
michael@0 266 }
michael@0 267 return;
michael@0 268 }
michael@0 269
michael@0 270 nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
michael@0 271 switch (aProperty) {
michael@0 272 case eCSSProperty_margin:
michael@0 273 case eCSSProperty_padding:
michael@0 274 case eCSSProperty_border_color:
michael@0 275 case eCSSProperty_border_style:
michael@0 276 case eCSSProperty_border_width: {
michael@0 277 const nsCSSProperty* subprops =
michael@0 278 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 279 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
michael@0 280 kNotFound, "first subprop must be top");
michael@0 281 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
michael@0 282 kNotFound, "second subprop must be right");
michael@0 283 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
michael@0 284 kNotFound, "third subprop must be bottom");
michael@0 285 NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
michael@0 286 kNotFound, "fourth subprop must be left");
michael@0 287 const nsCSSValue* vals[4] = {
michael@0 288 data->ValueFor(subprops[0]),
michael@0 289 data->ValueFor(subprops[1]),
michael@0 290 data->ValueFor(subprops[2]),
michael@0 291 data->ValueFor(subprops[3])
michael@0 292 };
michael@0 293 AppendSidesShorthandToString(subprops, vals, aValue, aSerialization);
michael@0 294 break;
michael@0 295 }
michael@0 296 case eCSSProperty_border_radius:
michael@0 297 case eCSSProperty__moz_outline_radius: {
michael@0 298 const nsCSSProperty* subprops =
michael@0 299 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 300 const nsCSSValue* vals[4] = {
michael@0 301 data->ValueFor(subprops[0]),
michael@0 302 data->ValueFor(subprops[1]),
michael@0 303 data->ValueFor(subprops[2]),
michael@0 304 data->ValueFor(subprops[3])
michael@0 305 };
michael@0 306
michael@0 307 // For compatibility, only write a slash and the y-values
michael@0 308 // if they're not identical to the x-values.
michael@0 309 bool needY = false;
michael@0 310 const nsCSSValue* xVals[4];
michael@0 311 const nsCSSValue* yVals[4];
michael@0 312 for (int i = 0; i < 4; i++) {
michael@0 313 if (vals[i]->GetUnit() == eCSSUnit_Pair) {
michael@0 314 needY = true;
michael@0 315 xVals[i] = &vals[i]->GetPairValue().mXValue;
michael@0 316 yVals[i] = &vals[i]->GetPairValue().mYValue;
michael@0 317 } else {
michael@0 318 xVals[i] = yVals[i] = vals[i];
michael@0 319 }
michael@0 320 }
michael@0 321
michael@0 322 AppendSidesShorthandToString(subprops, xVals, aValue, aSerialization);
michael@0 323 if (needY) {
michael@0 324 aValue.AppendLiteral(" / ");
michael@0 325 AppendSidesShorthandToString(subprops, yVals, aValue, aSerialization);
michael@0 326 }
michael@0 327 break;
michael@0 328 }
michael@0 329 case eCSSProperty_border_image: {
michael@0 330 // Even though there are some cases where we could omit
michael@0 331 // 'border-image-source' (when it's none), it's probably not a
michael@0 332 // good idea since it's likely to be confusing. It would also
michael@0 333 // require adding the extra check that we serialize *something*.
michael@0 334 AppendValueToString(eCSSProperty_border_image_source, aValue,
michael@0 335 aSerialization);
michael@0 336
michael@0 337 bool sliceDefault = data->HasDefaultBorderImageSlice();
michael@0 338 bool widthDefault = data->HasDefaultBorderImageWidth();
michael@0 339 bool outsetDefault = data->HasDefaultBorderImageOutset();
michael@0 340
michael@0 341 if (!sliceDefault || !widthDefault || !outsetDefault) {
michael@0 342 aValue.Append(char16_t(' '));
michael@0 343 AppendValueToString(eCSSProperty_border_image_slice, aValue,
michael@0 344 aSerialization);
michael@0 345 if (!widthDefault || !outsetDefault) {
michael@0 346 aValue.Append(NS_LITERAL_STRING(" /"));
michael@0 347 if (!widthDefault) {
michael@0 348 aValue.Append(char16_t(' '));
michael@0 349 AppendValueToString(eCSSProperty_border_image_width, aValue,
michael@0 350 aSerialization);
michael@0 351 }
michael@0 352 if (!outsetDefault) {
michael@0 353 aValue.Append(NS_LITERAL_STRING(" / "));
michael@0 354 AppendValueToString(eCSSProperty_border_image_outset, aValue,
michael@0 355 aSerialization);
michael@0 356 }
michael@0 357 }
michael@0 358 }
michael@0 359
michael@0 360 bool repeatDefault = data->HasDefaultBorderImageRepeat();
michael@0 361 if (!repeatDefault) {
michael@0 362 aValue.Append(char16_t(' '));
michael@0 363 AppendValueToString(eCSSProperty_border_image_repeat, aValue,
michael@0 364 aSerialization);
michael@0 365 }
michael@0 366 break;
michael@0 367 }
michael@0 368 case eCSSProperty_border: {
michael@0 369 // If we have a non-default value for any of the properties that
michael@0 370 // this shorthand sets but cannot specify, we have to return the
michael@0 371 // empty string.
michael@0 372 if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() !=
michael@0 373 eCSSUnit_None ||
michael@0 374 !data->HasDefaultBorderImageSlice() ||
michael@0 375 !data->HasDefaultBorderImageWidth() ||
michael@0 376 !data->HasDefaultBorderImageOutset() ||
michael@0 377 !data->HasDefaultBorderImageRepeat() ||
michael@0 378 data->ValueFor(eCSSProperty_border_top_colors)->GetUnit() !=
michael@0 379 eCSSUnit_None ||
michael@0 380 data->ValueFor(eCSSProperty_border_right_colors)->GetUnit() !=
michael@0 381 eCSSUnit_None ||
michael@0 382 data->ValueFor(eCSSProperty_border_bottom_colors)->GetUnit() !=
michael@0 383 eCSSUnit_None ||
michael@0 384 data->ValueFor(eCSSProperty_border_left_colors)->GetUnit() !=
michael@0 385 eCSSUnit_None) {
michael@0 386 break;
michael@0 387 }
michael@0 388
michael@0 389 const nsCSSProperty* subproptables[3] = {
michael@0 390 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
michael@0 391 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
michael@0 392 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
michael@0 393 };
michael@0 394 bool match = true;
michael@0 395 for (const nsCSSProperty** subprops = subproptables,
michael@0 396 **subprops_end = ArrayEnd(subproptables);
michael@0 397 subprops < subprops_end; ++subprops) {
michael@0 398 // Check only the first four subprops in each table, since the
michael@0 399 // others are extras for dimensional box properties.
michael@0 400 const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
michael@0 401 for (int32_t side = 1; side < 4; ++side) {
michael@0 402 const nsCSSValue *otherSide =
michael@0 403 data->ValueFor((*subprops)[side]);
michael@0 404 if (*firstSide != *otherSide)
michael@0 405 match = false;
michael@0 406 }
michael@0 407 }
michael@0 408 if (!match) {
michael@0 409 // We can't express what we have in the border shorthand
michael@0 410 break;
michael@0 411 }
michael@0 412 // tweak aProperty and fall through
michael@0 413 aProperty = eCSSProperty_border_top;
michael@0 414 }
michael@0 415 case eCSSProperty_border_top:
michael@0 416 case eCSSProperty_border_right:
michael@0 417 case eCSSProperty_border_bottom:
michael@0 418 case eCSSProperty_border_left:
michael@0 419 case eCSSProperty_border_start:
michael@0 420 case eCSSProperty_border_end:
michael@0 421 case eCSSProperty__moz_column_rule:
michael@0 422 case eCSSProperty_outline: {
michael@0 423 const nsCSSProperty* subprops =
michael@0 424 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 425 NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
michael@0 426 NS_LITERAL_CSTRING("-color")) ||
michael@0 427 StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
michael@0 428 NS_LITERAL_CSTRING("-color-value")),
michael@0 429 "third subprop must be the color property");
michael@0 430 const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
michael@0 431 bool isMozUseTextColor =
michael@0 432 colorValue->GetUnit() == eCSSUnit_Enumerated &&
michael@0 433 colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
michael@0 434 if (!AppendValueToString(subprops[0], aValue, aSerialization) ||
michael@0 435 !(aValue.Append(char16_t(' ')),
michael@0 436 AppendValueToString(subprops[1], aValue, aSerialization)) ||
michael@0 437 // Don't output a third value when it's -moz-use-text-color.
michael@0 438 !(isMozUseTextColor ||
michael@0 439 (aValue.Append(char16_t(' ')),
michael@0 440 AppendValueToString(subprops[2], aValue, aSerialization)))) {
michael@0 441 aValue.Truncate();
michael@0 442 }
michael@0 443 break;
michael@0 444 }
michael@0 445 case eCSSProperty_margin_left:
michael@0 446 case eCSSProperty_margin_right:
michael@0 447 case eCSSProperty_margin_start:
michael@0 448 case eCSSProperty_margin_end:
michael@0 449 case eCSSProperty_padding_left:
michael@0 450 case eCSSProperty_padding_right:
michael@0 451 case eCSSProperty_padding_start:
michael@0 452 case eCSSProperty_padding_end:
michael@0 453 case eCSSProperty_border_left_color:
michael@0 454 case eCSSProperty_border_left_style:
michael@0 455 case eCSSProperty_border_left_width:
michael@0 456 case eCSSProperty_border_right_color:
michael@0 457 case eCSSProperty_border_right_style:
michael@0 458 case eCSSProperty_border_right_width:
michael@0 459 case eCSSProperty_border_start_color:
michael@0 460 case eCSSProperty_border_start_style:
michael@0 461 case eCSSProperty_border_start_width:
michael@0 462 case eCSSProperty_border_end_color:
michael@0 463 case eCSSProperty_border_end_style:
michael@0 464 case eCSSProperty_border_end_width: {
michael@0 465 const nsCSSProperty* subprops =
michael@0 466 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 467 NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
michael@0 468 "not box property with physical vs. logical cascading");
michael@0 469 AppendValueToString(subprops[0], aValue, aSerialization);
michael@0 470 break;
michael@0 471 }
michael@0 472 case eCSSProperty_background: {
michael@0 473 // We know from above that all subproperties were specified.
michael@0 474 // However, we still can't represent that in the shorthand unless
michael@0 475 // they're all lists of the same length. So if they're different
michael@0 476 // lengths, we need to bail out.
michael@0 477 // We also need to bail out if an item has background-clip and
michael@0 478 // background-origin that are different and not the default
michael@0 479 // values. (We omit them if they're both default.)
michael@0 480 const nsCSSValueList *image =
michael@0 481 data->ValueFor(eCSSProperty_background_image)->
michael@0 482 GetListValue();
michael@0 483 const nsCSSValuePairList *repeat =
michael@0 484 data->ValueFor(eCSSProperty_background_repeat)->
michael@0 485 GetPairListValue();
michael@0 486 const nsCSSValueList *attachment =
michael@0 487 data->ValueFor(eCSSProperty_background_attachment)->
michael@0 488 GetListValue();
michael@0 489 const nsCSSValueList *position =
michael@0 490 data->ValueFor(eCSSProperty_background_position)->
michael@0 491 GetListValue();
michael@0 492 const nsCSSValueList *clip =
michael@0 493 data->ValueFor(eCSSProperty_background_clip)->
michael@0 494 GetListValue();
michael@0 495 const nsCSSValueList *origin =
michael@0 496 data->ValueFor(eCSSProperty_background_origin)->
michael@0 497 GetListValue();
michael@0 498 const nsCSSValuePairList *size =
michael@0 499 data->ValueFor(eCSSProperty_background_size)->
michael@0 500 GetPairListValue();
michael@0 501 for (;;) {
michael@0 502 image->mValue.AppendToString(eCSSProperty_background_image, aValue,
michael@0 503 aSerialization);
michael@0 504 aValue.Append(char16_t(' '));
michael@0 505 repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue,
michael@0 506 aSerialization);
michael@0 507 if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
michael@0 508 repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue,
michael@0 509 aSerialization);
michael@0 510 }
michael@0 511 aValue.Append(char16_t(' '));
michael@0 512 attachment->mValue.AppendToString(eCSSProperty_background_attachment,
michael@0 513 aValue, aSerialization);
michael@0 514 aValue.Append(char16_t(' '));
michael@0 515 position->mValue.AppendToString(eCSSProperty_background_position,
michael@0 516 aValue, aSerialization);
michael@0 517
michael@0 518 if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
michael@0 519 size->mYValue.GetUnit() != eCSSUnit_Auto) {
michael@0 520 aValue.Append(char16_t(' '));
michael@0 521 aValue.Append(char16_t('/'));
michael@0 522 aValue.Append(char16_t(' '));
michael@0 523 size->mXValue.AppendToString(eCSSProperty_background_size, aValue,
michael@0 524 aSerialization);
michael@0 525 aValue.Append(char16_t(' '));
michael@0 526 size->mYValue.AppendToString(eCSSProperty_background_size, aValue,
michael@0 527 aSerialization);
michael@0 528 }
michael@0 529
michael@0 530 NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
michael@0 531 origin->mValue.GetUnit() == eCSSUnit_Enumerated,
michael@0 532 "should not have inherit/initial within list");
michael@0 533
michael@0 534 if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
michael@0 535 origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
michael@0 536 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
michael@0 537 eCSSProperty_background_origin] ==
michael@0 538 nsCSSProps::kBackgroundOriginKTable);
michael@0 539 MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
michael@0 540 eCSSProperty_background_clip] ==
michael@0 541 nsCSSProps::kBackgroundOriginKTable);
michael@0 542 static_assert(NS_STYLE_BG_CLIP_BORDER ==
michael@0 543 NS_STYLE_BG_ORIGIN_BORDER &&
michael@0 544 NS_STYLE_BG_CLIP_PADDING ==
michael@0 545 NS_STYLE_BG_ORIGIN_PADDING &&
michael@0 546 NS_STYLE_BG_CLIP_CONTENT ==
michael@0 547 NS_STYLE_BG_ORIGIN_CONTENT,
michael@0 548 "bg-clip and bg-origin style constants must agree");
michael@0 549 aValue.Append(char16_t(' '));
michael@0 550 origin->mValue.AppendToString(eCSSProperty_background_origin, aValue,
michael@0 551 aSerialization);
michael@0 552
michael@0 553 if (clip->mValue != origin->mValue) {
michael@0 554 aValue.Append(char16_t(' '));
michael@0 555 clip->mValue.AppendToString(eCSSProperty_background_clip, aValue,
michael@0 556 aSerialization);
michael@0 557 }
michael@0 558 }
michael@0 559
michael@0 560 image = image->mNext;
michael@0 561 repeat = repeat->mNext;
michael@0 562 attachment = attachment->mNext;
michael@0 563 position = position->mNext;
michael@0 564 clip = clip->mNext;
michael@0 565 origin = origin->mNext;
michael@0 566 size = size->mNext;
michael@0 567
michael@0 568 if (!image) {
michael@0 569 if (repeat || attachment || position || clip || origin || size) {
michael@0 570 // Uneven length lists, so can't be serialized as shorthand.
michael@0 571 aValue.Truncate();
michael@0 572 return;
michael@0 573 }
michael@0 574 break;
michael@0 575 }
michael@0 576 if (!repeat || !attachment || !position || !clip || !origin || !size) {
michael@0 577 // Uneven length lists, so can't be serialized as shorthand.
michael@0 578 aValue.Truncate();
michael@0 579 return;
michael@0 580 }
michael@0 581 aValue.Append(char16_t(','));
michael@0 582 aValue.Append(char16_t(' '));
michael@0 583 }
michael@0 584
michael@0 585 aValue.Append(char16_t(' '));
michael@0 586 AppendValueToString(eCSSProperty_background_color, aValue,
michael@0 587 aSerialization);
michael@0 588 break;
michael@0 589 }
michael@0 590 case eCSSProperty_font: {
michael@0 591 // systemFont might not be present; other values are guaranteed to be
michael@0 592 // available based on the shorthand check at the beginning of the
michael@0 593 // function, as long as the prop is enabled
michael@0 594 const nsCSSValue *systemFont =
michael@0 595 data->ValueFor(eCSSProperty__x_system_font);
michael@0 596 const nsCSSValue *style =
michael@0 597 data->ValueFor(eCSSProperty_font_style);
michael@0 598 const nsCSSValue *variant =
michael@0 599 data->ValueFor(eCSSProperty_font_variant);
michael@0 600 const nsCSSValue *weight =
michael@0 601 data->ValueFor(eCSSProperty_font_weight);
michael@0 602 const nsCSSValue *size =
michael@0 603 data->ValueFor(eCSSProperty_font_size);
michael@0 604 const nsCSSValue *lh =
michael@0 605 data->ValueFor(eCSSProperty_line_height);
michael@0 606 const nsCSSValue *family =
michael@0 607 data->ValueFor(eCSSProperty_font_family);
michael@0 608 const nsCSSValue *stretch =
michael@0 609 data->ValueFor(eCSSProperty_font_stretch);
michael@0 610 const nsCSSValue *sizeAdjust =
michael@0 611 data->ValueFor(eCSSProperty_font_size_adjust);
michael@0 612 const nsCSSValue *featureSettings =
michael@0 613 data->ValueFor(eCSSProperty_font_feature_settings);
michael@0 614 const nsCSSValue *languageOverride =
michael@0 615 data->ValueFor(eCSSProperty_font_language_override);
michael@0 616 const nsCSSValue *fontKerning =
michael@0 617 data->ValueFor(eCSSProperty_font_kerning);
michael@0 618 const nsCSSValue *fontSynthesis =
michael@0 619 data->ValueFor(eCSSProperty_font_synthesis);
michael@0 620 const nsCSSValue *fontVariantAlternates =
michael@0 621 data->ValueFor(eCSSProperty_font_variant_alternates);
michael@0 622 const nsCSSValue *fontVariantCaps =
michael@0 623 data->ValueFor(eCSSProperty_font_variant_caps);
michael@0 624 const nsCSSValue *fontVariantEastAsian =
michael@0 625 data->ValueFor(eCSSProperty_font_variant_east_asian);
michael@0 626 const nsCSSValue *fontVariantLigatures =
michael@0 627 data->ValueFor(eCSSProperty_font_variant_ligatures);
michael@0 628 const nsCSSValue *fontVariantNumeric =
michael@0 629 data->ValueFor(eCSSProperty_font_variant_numeric);
michael@0 630 const nsCSSValue *fontVariantPosition =
michael@0 631 data->ValueFor(eCSSProperty_font_variant_position);
michael@0 632
michael@0 633 // if font features are not enabled, pointers for fontVariant
michael@0 634 // values above may be null since the shorthand check ignores them
michael@0 635 // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
michael@0 636 bool fontFeaturesEnabled =
michael@0 637 nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
michael@0 638
michael@0 639 if (systemFont &&
michael@0 640 systemFont->GetUnit() != eCSSUnit_None &&
michael@0 641 systemFont->GetUnit() != eCSSUnit_Null) {
michael@0 642 if (style->GetUnit() != eCSSUnit_System_Font ||
michael@0 643 variant->GetUnit() != eCSSUnit_System_Font ||
michael@0 644 weight->GetUnit() != eCSSUnit_System_Font ||
michael@0 645 size->GetUnit() != eCSSUnit_System_Font ||
michael@0 646 lh->GetUnit() != eCSSUnit_System_Font ||
michael@0 647 family->GetUnit() != eCSSUnit_System_Font ||
michael@0 648 stretch->GetUnit() != eCSSUnit_System_Font ||
michael@0 649 sizeAdjust->GetUnit() != eCSSUnit_System_Font ||
michael@0 650 featureSettings->GetUnit() != eCSSUnit_System_Font ||
michael@0 651 languageOverride->GetUnit() != eCSSUnit_System_Font ||
michael@0 652 (fontFeaturesEnabled &&
michael@0 653 (fontKerning->GetUnit() != eCSSUnit_System_Font ||
michael@0 654 fontSynthesis->GetUnit() != eCSSUnit_System_Font ||
michael@0 655 fontVariantAlternates->GetUnit() != eCSSUnit_System_Font ||
michael@0 656 fontVariantCaps->GetUnit() != eCSSUnit_System_Font ||
michael@0 657 fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font ||
michael@0 658 fontVariantLigatures->GetUnit() != eCSSUnit_System_Font ||
michael@0 659 fontVariantNumeric->GetUnit() != eCSSUnit_System_Font ||
michael@0 660 fontVariantPosition->GetUnit() != eCSSUnit_System_Font))) {
michael@0 661 // This can't be represented as a shorthand.
michael@0 662 return;
michael@0 663 }
michael@0 664 systemFont->AppendToString(eCSSProperty__x_system_font, aValue,
michael@0 665 aSerialization);
michael@0 666 } else {
michael@0 667 // properties reset by this shorthand property to their
michael@0 668 // initial values but not represented in its syntax
michael@0 669 if (stretch->GetUnit() != eCSSUnit_Enumerated ||
michael@0 670 stretch->GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
michael@0 671 sizeAdjust->GetUnit() != eCSSUnit_None ||
michael@0 672 featureSettings->GetUnit() != eCSSUnit_Normal ||
michael@0 673 languageOverride->GetUnit() != eCSSUnit_Normal ||
michael@0 674 (fontFeaturesEnabled &&
michael@0 675 (fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
michael@0 676 fontSynthesis->GetUnit() != eCSSUnit_Enumerated ||
michael@0 677 fontSynthesis->GetIntValue() !=
michael@0 678 (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) ||
michael@0 679 fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
michael@0 680 fontVariantCaps->GetUnit() != eCSSUnit_Normal ||
michael@0 681 fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
michael@0 682 fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
michael@0 683 fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
michael@0 684 fontVariantPosition->GetUnit() != eCSSUnit_Normal))) {
michael@0 685 return;
michael@0 686 }
michael@0 687
michael@0 688 if (style->GetUnit() != eCSSUnit_Enumerated ||
michael@0 689 style->GetIntValue() != NS_FONT_STYLE_NORMAL) {
michael@0 690 style->AppendToString(eCSSProperty_font_style, aValue,
michael@0 691 aSerialization);
michael@0 692 aValue.Append(char16_t(' '));
michael@0 693 }
michael@0 694 if (variant->GetUnit() != eCSSUnit_Enumerated ||
michael@0 695 variant->GetIntValue() != NS_FONT_VARIANT_NORMAL) {
michael@0 696 variant->AppendToString(eCSSProperty_font_variant, aValue,
michael@0 697 aSerialization);
michael@0 698 aValue.Append(char16_t(' '));
michael@0 699 }
michael@0 700 if (weight->GetUnit() != eCSSUnit_Enumerated ||
michael@0 701 weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
michael@0 702 weight->AppendToString(eCSSProperty_font_weight, aValue,
michael@0 703 aSerialization);
michael@0 704 aValue.Append(char16_t(' '));
michael@0 705 }
michael@0 706 size->AppendToString(eCSSProperty_font_size, aValue, aSerialization);
michael@0 707 if (lh->GetUnit() != eCSSUnit_Normal) {
michael@0 708 aValue.Append(char16_t('/'));
michael@0 709 lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization);
michael@0 710 }
michael@0 711 aValue.Append(char16_t(' '));
michael@0 712 family->AppendToString(eCSSProperty_font_family, aValue,
michael@0 713 aSerialization);
michael@0 714 }
michael@0 715 break;
michael@0 716 }
michael@0 717 case eCSSProperty_list_style:
michael@0 718 if (AppendValueToString(eCSSProperty_list_style_type, aValue,
michael@0 719 aSerialization)) {
michael@0 720 aValue.Append(char16_t(' '));
michael@0 721 }
michael@0 722 if (AppendValueToString(eCSSProperty_list_style_position, aValue,
michael@0 723 aSerialization)) {
michael@0 724 aValue.Append(char16_t(' '));
michael@0 725 }
michael@0 726 AppendValueToString(eCSSProperty_list_style_image, aValue,
michael@0 727 aSerialization);
michael@0 728 break;
michael@0 729 case eCSSProperty_overflow: {
michael@0 730 const nsCSSValue &xValue =
michael@0 731 *data->ValueFor(eCSSProperty_overflow_x);
michael@0 732 const nsCSSValue &yValue =
michael@0 733 *data->ValueFor(eCSSProperty_overflow_y);
michael@0 734 if (xValue == yValue)
michael@0 735 xValue.AppendToString(eCSSProperty_overflow_x, aValue, aSerialization);
michael@0 736 break;
michael@0 737 }
michael@0 738 case eCSSProperty_text_decoration: {
michael@0 739 // If text-decoration-color or text-decoration-style isn't initial value,
michael@0 740 // we cannot serialize the text-decoration shorthand value.
michael@0 741 const nsCSSValue *decorationColor =
michael@0 742 data->ValueFor(eCSSProperty_text_decoration_color);
michael@0 743 const nsCSSValue *decorationStyle =
michael@0 744 data->ValueFor(eCSSProperty_text_decoration_style);
michael@0 745
michael@0 746 NS_ABORT_IF_FALSE(decorationStyle->GetUnit() == eCSSUnit_Enumerated,
michael@0 747 nsPrintfCString("bad text-decoration-style unit %d",
michael@0 748 decorationStyle->GetUnit()).get());
michael@0 749
michael@0 750 if (decorationColor->GetUnit() != eCSSUnit_Enumerated ||
michael@0 751 decorationColor->GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR ||
michael@0 752 decorationStyle->GetIntValue() !=
michael@0 753 NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
michael@0 754 return;
michael@0 755 }
michael@0 756
michael@0 757 AppendValueToString(eCSSProperty_text_decoration_line, aValue,
michael@0 758 aSerialization);
michael@0 759 break;
michael@0 760 }
michael@0 761 case eCSSProperty_transition: {
michael@0 762 const nsCSSValue *transProp =
michael@0 763 data->ValueFor(eCSSProperty_transition_property);
michael@0 764 const nsCSSValue *transDuration =
michael@0 765 data->ValueFor(eCSSProperty_transition_duration);
michael@0 766 const nsCSSValue *transTiming =
michael@0 767 data->ValueFor(eCSSProperty_transition_timing_function);
michael@0 768 const nsCSSValue *transDelay =
michael@0 769 data->ValueFor(eCSSProperty_transition_delay);
michael@0 770
michael@0 771 NS_ABORT_IF_FALSE(transDuration->GetUnit() == eCSSUnit_List ||
michael@0 772 transDuration->GetUnit() == eCSSUnit_ListDep,
michael@0 773 nsPrintfCString("bad t-duration unit %d",
michael@0 774 transDuration->GetUnit()).get());
michael@0 775 NS_ABORT_IF_FALSE(transTiming->GetUnit() == eCSSUnit_List ||
michael@0 776 transTiming->GetUnit() == eCSSUnit_ListDep,
michael@0 777 nsPrintfCString("bad t-timing unit %d",
michael@0 778 transTiming->GetUnit()).get());
michael@0 779 NS_ABORT_IF_FALSE(transDelay->GetUnit() == eCSSUnit_List ||
michael@0 780 transDelay->GetUnit() == eCSSUnit_ListDep,
michael@0 781 nsPrintfCString("bad t-delay unit %d",
michael@0 782 transDelay->GetUnit()).get());
michael@0 783
michael@0 784 const nsCSSValueList* dur = transDuration->GetListValue();
michael@0 785 const nsCSSValueList* tim = transTiming->GetListValue();
michael@0 786 const nsCSSValueList* del = transDelay->GetListValue();
michael@0 787
michael@0 788 if (transProp->GetUnit() == eCSSUnit_None ||
michael@0 789 transProp->GetUnit() == eCSSUnit_All) {
michael@0 790 // If any of the other three lists has more than one element,
michael@0 791 // we can't use the shorthand.
michael@0 792 if (!dur->mNext && !tim->mNext && !del->mNext) {
michael@0 793 transProp->AppendToString(eCSSProperty_transition_property, aValue,
michael@0 794 aSerialization);
michael@0 795 aValue.Append(char16_t(' '));
michael@0 796 dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue,
michael@0 797 aSerialization);
michael@0 798 aValue.Append(char16_t(' '));
michael@0 799 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
michael@0 800 aValue, aSerialization);
michael@0 801 aValue.Append(char16_t(' '));
michael@0 802 del->mValue.AppendToString(eCSSProperty_transition_delay, aValue,
michael@0 803 aSerialization);
michael@0 804 aValue.Append(char16_t(' '));
michael@0 805 } else {
michael@0 806 aValue.Truncate();
michael@0 807 }
michael@0 808 } else {
michael@0 809 NS_ABORT_IF_FALSE(transProp->GetUnit() == eCSSUnit_List ||
michael@0 810 transProp->GetUnit() == eCSSUnit_ListDep,
michael@0 811 nsPrintfCString("bad t-prop unit %d",
michael@0 812 transProp->GetUnit()).get());
michael@0 813 const nsCSSValueList* pro = transProp->GetListValue();
michael@0 814 for (;;) {
michael@0 815 pro->mValue.AppendToString(eCSSProperty_transition_property,
michael@0 816 aValue, aSerialization);
michael@0 817 aValue.Append(char16_t(' '));
michael@0 818 dur->mValue.AppendToString(eCSSProperty_transition_duration,
michael@0 819 aValue, aSerialization);
michael@0 820 aValue.Append(char16_t(' '));
michael@0 821 tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
michael@0 822 aValue, aSerialization);
michael@0 823 aValue.Append(char16_t(' '));
michael@0 824 del->mValue.AppendToString(eCSSProperty_transition_delay,
michael@0 825 aValue, aSerialization);
michael@0 826 pro = pro->mNext;
michael@0 827 dur = dur->mNext;
michael@0 828 tim = tim->mNext;
michael@0 829 del = del->mNext;
michael@0 830 if (!pro || !dur || !tim || !del) {
michael@0 831 break;
michael@0 832 }
michael@0 833 aValue.AppendLiteral(", ");
michael@0 834 }
michael@0 835 if (pro || dur || tim || del) {
michael@0 836 // Lists not all the same length, can't use shorthand.
michael@0 837 aValue.Truncate();
michael@0 838 }
michael@0 839 }
michael@0 840 break;
michael@0 841 }
michael@0 842 case eCSSProperty_animation: {
michael@0 843 const nsCSSProperty* subprops =
michael@0 844 nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
michael@0 845 static const size_t numProps = 7;
michael@0 846 NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
michael@0 847 "unexpected number of subproperties");
michael@0 848 const nsCSSValue* values[numProps];
michael@0 849 const nsCSSValueList* lists[numProps];
michael@0 850
michael@0 851 for (uint32_t i = 0; i < numProps; ++i) {
michael@0 852 values[i] = data->ValueFor(subprops[i]);
michael@0 853 NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
michael@0 854 values[i]->GetUnit() == eCSSUnit_ListDep,
michael@0 855 nsPrintfCString("bad a-duration unit %d",
michael@0 856 values[i]->GetUnit()).get());
michael@0 857 lists[i] = values[i]->GetListValue();
michael@0 858 }
michael@0 859
michael@0 860 for (;;) {
michael@0 861 // We must serialize 'animation-name' last in case it has
michael@0 862 // a value that conflicts with one of the other keyword properties.
michael@0 863 NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
michael@0 864 eCSSProperty_animation_name,
michael@0 865 "animation-name must be last");
michael@0 866 bool done = false;
michael@0 867 for (uint32_t i = 0;;) {
michael@0 868 lists[i]->mValue.AppendToString(subprops[i], aValue, aSerialization);
michael@0 869 lists[i] = lists[i]->mNext;
michael@0 870 if (!lists[i]) {
michael@0 871 done = true;
michael@0 872 }
michael@0 873 if (++i == numProps) {
michael@0 874 break;
michael@0 875 }
michael@0 876 aValue.Append(char16_t(' '));
michael@0 877 }
michael@0 878 if (done) {
michael@0 879 break;
michael@0 880 }
michael@0 881 aValue.AppendLiteral(", ");
michael@0 882 }
michael@0 883 for (uint32_t i = 0; i < numProps; ++i) {
michael@0 884 if (lists[i]) {
michael@0 885 // Lists not all the same length, can't use shorthand.
michael@0 886 aValue.Truncate();
michael@0 887 break;
michael@0 888 }
michael@0 889 }
michael@0 890 break;
michael@0 891 }
michael@0 892 case eCSSProperty_marker: {
michael@0 893 const nsCSSValue &endValue =
michael@0 894 *data->ValueFor(eCSSProperty_marker_end);
michael@0 895 const nsCSSValue &midValue =
michael@0 896 *data->ValueFor(eCSSProperty_marker_mid);
michael@0 897 const nsCSSValue &startValue =
michael@0 898 *data->ValueFor(eCSSProperty_marker_start);
michael@0 899 if (endValue == midValue && midValue == startValue)
michael@0 900 AppendValueToString(eCSSProperty_marker_end, aValue, aSerialization);
michael@0 901 break;
michael@0 902 }
michael@0 903 case eCSSProperty__moz_columns: {
michael@0 904 // Two values, column-count and column-width, separated by a space.
michael@0 905 const nsCSSProperty* subprops =
michael@0 906 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 907 AppendValueToString(subprops[0], aValue, aSerialization);
michael@0 908 aValue.Append(char16_t(' '));
michael@0 909 AppendValueToString(subprops[1], aValue, aSerialization);
michael@0 910 break;
michael@0 911 }
michael@0 912 case eCSSProperty_flex: {
michael@0 913 // flex-grow, flex-shrink, flex-basis, separated by single space
michael@0 914 const nsCSSProperty* subprops =
michael@0 915 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 916
michael@0 917 AppendValueToString(subprops[0], aValue, aSerialization);
michael@0 918 aValue.Append(char16_t(' '));
michael@0 919 AppendValueToString(subprops[1], aValue, aSerialization);
michael@0 920 aValue.Append(char16_t(' '));
michael@0 921 AppendValueToString(subprops[2], aValue, aSerialization);
michael@0 922 break;
michael@0 923 }
michael@0 924 case eCSSProperty_flex_flow: {
michael@0 925 // flex-direction, flex-wrap, separated by single space
michael@0 926 const nsCSSProperty* subprops =
michael@0 927 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 928 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
michael@0 929 "must have exactly two subproperties");
michael@0 930
michael@0 931 AppendValueToString(subprops[0], aValue, aSerialization);
michael@0 932 aValue.Append(char16_t(' '));
michael@0 933 AppendValueToString(subprops[1], aValue, aSerialization);
michael@0 934 break;
michael@0 935 }
michael@0 936 case eCSSProperty_grid_row:
michael@0 937 case eCSSProperty_grid_column: {
michael@0 938 // grid-{row,column}-start, grid-{row,column}-end, separated by a slash
michael@0 939 const nsCSSProperty* subprops =
michael@0 940 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 941 NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
michael@0 942 "must have exactly two subproperties");
michael@0 943
michael@0 944 // TODO: should we simplify when possible?
michael@0 945 AppendValueToString(subprops[0], aValue, aSerialization);
michael@0 946 aValue.AppendLiteral(" / ");
michael@0 947 AppendValueToString(subprops[1], aValue, aSerialization);
michael@0 948 break;
michael@0 949 }
michael@0 950 case eCSSProperty_grid_area: {
michael@0 951 const nsCSSProperty* subprops =
michael@0 952 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 953 NS_ABORT_IF_FALSE(subprops[4] == eCSSProperty_UNKNOWN,
michael@0 954 "must have exactly four subproperties");
michael@0 955
michael@0 956 // TODO: should we simplify when possible?
michael@0 957 AppendValueToString(subprops[0], aValue, aSerialization);
michael@0 958 aValue.AppendLiteral(" / ");
michael@0 959 AppendValueToString(subprops[1], aValue, aSerialization);
michael@0 960 aValue.AppendLiteral(" / ");
michael@0 961 AppendValueToString(subprops[2], aValue, aSerialization);
michael@0 962 aValue.AppendLiteral(" / ");
michael@0 963 AppendValueToString(subprops[3], aValue, aSerialization);
michael@0 964 break;
michael@0 965 }
michael@0 966
michael@0 967 // This can express either grid-template-{areas,columns,rows}
michael@0 968 // or grid-auto-{flow,columns,rows}, but not both.
michael@0 969 case eCSSProperty_grid: {
michael@0 970 const nsCSSValue& areasValue =
michael@0 971 *data->ValueFor(eCSSProperty_grid_template_areas);
michael@0 972 const nsCSSValue& columnsValue =
michael@0 973 *data->ValueFor(eCSSProperty_grid_template_columns);
michael@0 974 const nsCSSValue& rowsValue =
michael@0 975 *data->ValueFor(eCSSProperty_grid_template_rows);
michael@0 976
michael@0 977 const nsCSSValue& autoFlowValue =
michael@0 978 *data->ValueFor(eCSSProperty_grid_auto_flow);
michael@0 979 const nsCSSValue& autoColumnsValue =
michael@0 980 *data->ValueFor(eCSSProperty_grid_auto_columns);
michael@0 981 const nsCSSValue& autoRowsValue =
michael@0 982 *data->ValueFor(eCSSProperty_grid_auto_rows);
michael@0 983
michael@0 984 if (areasValue.GetUnit() == eCSSUnit_None &&
michael@0 985 columnsValue.GetUnit() == eCSSUnit_None &&
michael@0 986 rowsValue.GetUnit() == eCSSUnit_None) {
michael@0 987 AppendValueToString(eCSSProperty_grid_auto_flow,
michael@0 988 aValue, aSerialization);
michael@0 989 aValue.Append(char16_t(' '));
michael@0 990 AppendValueToString(eCSSProperty_grid_auto_columns,
michael@0 991 aValue, aSerialization);
michael@0 992 aValue.AppendLiteral(" / ");
michael@0 993 AppendValueToString(eCSSProperty_grid_auto_rows,
michael@0 994 aValue, aSerialization);
michael@0 995 break;
michael@0 996 } else if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
michael@0 997 autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_NONE &&
michael@0 998 autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
michael@0 999 autoRowsValue.GetUnit() == eCSSUnit_Auto)) {
michael@0 1000 // Not serializable, bail.
michael@0 1001 return;
michael@0 1002 }
michael@0 1003 // Fall through to eCSSProperty_grid_template
michael@0 1004 }
michael@0 1005 case eCSSProperty_grid_template: {
michael@0 1006 const nsCSSValue& areasValue =
michael@0 1007 *data->ValueFor(eCSSProperty_grid_template_areas);
michael@0 1008 const nsCSSValue& columnsValue =
michael@0 1009 *data->ValueFor(eCSSProperty_grid_template_columns);
michael@0 1010 const nsCSSValue& rowsValue =
michael@0 1011 *data->ValueFor(eCSSProperty_grid_template_rows);
michael@0 1012 if (areasValue.GetUnit() == eCSSUnit_None) {
michael@0 1013 AppendValueToString(eCSSProperty_grid_template_columns,
michael@0 1014 aValue, aSerialization);
michael@0 1015 aValue.AppendLiteral(" / ");
michael@0 1016 AppendValueToString(eCSSProperty_grid_template_rows,
michael@0 1017 aValue, aSerialization);
michael@0 1018 break;
michael@0 1019 }
michael@0 1020 if (columnsValue.GetUnit() == eCSSUnit_List ||
michael@0 1021 columnsValue.GetUnit() == eCSSUnit_ListDep) {
michael@0 1022 const nsCSSValueList* columnsItem = columnsValue.GetListValue();
michael@0 1023 if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
michael@0 1024 columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
michael@0 1025 // We have "grid-template-areas:[something]; grid-template-columns:subgrid"
michael@0 1026 // which isn't a value that the shorthand can express. Bail.
michael@0 1027 return;
michael@0 1028 }
michael@0 1029 }
michael@0 1030 if (rowsValue.GetUnit() != eCSSUnit_List &&
michael@0 1031 rowsValue.GetUnit() != eCSSUnit_ListDep) {
michael@0 1032 // We have "grid-template-areas:[something]; grid-template-rows:none"
michael@0 1033 // which isn't a value that the shorthand can express. Bail.
michael@0 1034 return;
michael@0 1035 }
michael@0 1036 const nsCSSValueList* rowsItem = rowsValue.GetListValue();
michael@0 1037 if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
michael@0 1038 rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
michael@0 1039 // We have "grid-template-areas:[something]; grid-template-rows:subgrid"
michael@0 1040 // which isn't a value that the shorthand can express. Bail.
michael@0 1041 return;
michael@0 1042 }
michael@0 1043 const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas();
michael@0 1044 uint32_t nRowItems = 0;
michael@0 1045 while (rowsItem) {
michael@0 1046 nRowItems++;
michael@0 1047 rowsItem = rowsItem->mNext;
michael@0 1048 }
michael@0 1049 MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items");
michael@0 1050 if ((nRowItems - 1) / 2 != areas->NRows()) {
michael@0 1051 // Not serializable, bail.
michael@0 1052 return;
michael@0 1053 }
michael@0 1054 if (columnsValue.GetUnit() != eCSSUnit_None) {
michael@0 1055 AppendValueToString(eCSSProperty_grid_template_columns,
michael@0 1056 aValue, aSerialization);
michael@0 1057 aValue.AppendLiteral(" / ");
michael@0 1058 }
michael@0 1059 rowsItem = rowsValue.GetListValue();
michael@0 1060 uint32_t row = 0;
michael@0 1061 for (;;) {
michael@0 1062 bool addSpaceSeparator = true;
michael@0 1063 nsCSSUnit unit = rowsItem->mValue.GetUnit();
michael@0 1064
michael@0 1065 if (unit == eCSSUnit_Null) {
michael@0 1066 // Empty or omitted <line-names>. Serializes to nothing.
michael@0 1067 addSpaceSeparator = false; // Avoid a double space.
michael@0 1068
michael@0 1069 } else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) {
michael@0 1070 // Non-empty <line-names>
michael@0 1071 aValue.AppendLiteral("(");
michael@0 1072 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
michael@0 1073 aValue, aSerialization);
michael@0 1074 aValue.AppendLiteral(")");
michael@0 1075
michael@0 1076 } else {
michael@0 1077 nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue);
michael@0 1078 aValue.Append(char16_t(' '));
michael@0 1079
michael@0 1080 // <track-size>
michael@0 1081 rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
michael@0 1082 aValue, aSerialization);
michael@0 1083 if (rowsItem->mNext &&
michael@0 1084 rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null &&
michael@0 1085 !rowsItem->mNext->mNext) {
michael@0 1086 // Break out of the loop early to avoid a trailing space.
michael@0 1087 break;
michael@0 1088 }
michael@0 1089 }
michael@0 1090
michael@0 1091 rowsItem = rowsItem->mNext;
michael@0 1092 if (!rowsItem) {
michael@0 1093 break;
michael@0 1094 }
michael@0 1095
michael@0 1096 if (addSpaceSeparator) {
michael@0 1097 aValue.Append(char16_t(' '));
michael@0 1098 }
michael@0 1099 }
michael@0 1100 break;
michael@0 1101 }
michael@0 1102 case eCSSProperty__moz_transform: {
michael@0 1103 // shorthands that are just aliases with different parsing rules
michael@0 1104 const nsCSSProperty* subprops =
michael@0 1105 nsCSSProps::SubpropertyEntryFor(aProperty);
michael@0 1106 NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
michael@0 1107 "must have exactly one subproperty");
michael@0 1108 AppendValueToString(subprops[0], aValue, aSerialization);
michael@0 1109 break;
michael@0 1110 }
michael@0 1111 case eCSSProperty_all:
michael@0 1112 // If we got here, then we didn't have all "inherit" or "initial" or
michael@0 1113 // "unset" values for all of the longhand property components of 'all'.
michael@0 1114 // There is no other possible value that is valid for all properties,
michael@0 1115 // so serialize as the empty string.
michael@0 1116 break;
michael@0 1117 default:
michael@0 1118 NS_ABORT_IF_FALSE(false, "no other shorthands");
michael@0 1119 break;
michael@0 1120 }
michael@0 1121 }
michael@0 1122
michael@0 1123 bool
michael@0 1124 Declaration::GetValueIsImportant(const nsAString& aProperty) const
michael@0 1125 {
michael@0 1126 nsCSSProperty propID =
michael@0 1127 nsCSSProps::LookupProperty(aProperty, nsCSSProps::eIgnoreEnabledState);
michael@0 1128 if (propID == eCSSProperty_UNKNOWN) {
michael@0 1129 return false;
michael@0 1130 }
michael@0 1131 if (propID == eCSSPropertyExtra_variable) {
michael@0 1132 const nsSubstring& variableName =
michael@0 1133 Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);
michael@0 1134 return GetVariableValueIsImportant(variableName);
michael@0 1135 }
michael@0 1136 return GetValueIsImportant(propID);
michael@0 1137 }
michael@0 1138
michael@0 1139 bool
michael@0 1140 Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
michael@0 1141 {
michael@0 1142 if (!mImportantData)
michael@0 1143 return false;
michael@0 1144
michael@0 1145 // Calling ValueFor is inefficient, but we can assume '!important' is rare.
michael@0 1146
michael@0 1147 if (!nsCSSProps::IsShorthand(aProperty)) {
michael@0 1148 return mImportantData->ValueFor(aProperty) != nullptr;
michael@0 1149 }
michael@0 1150
michael@0 1151 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
michael@0 1152 if (*p == eCSSProperty__x_system_font) {
michael@0 1153 // The system_font subproperty doesn't count.
michael@0 1154 continue;
michael@0 1155 }
michael@0 1156 if (!mImportantData->ValueFor(*p)) {
michael@0 1157 return false;
michael@0 1158 }
michael@0 1159 }
michael@0 1160 return true;
michael@0 1161 }
michael@0 1162
michael@0 1163 void
michael@0 1164 Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
michael@0 1165 nsAutoString& aValue,
michael@0 1166 nsAString& aResult) const
michael@0 1167 {
michael@0 1168 NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
michael@0 1169 "property enum out of range");
michael@0 1170 NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
michael@0 1171 aValue.IsEmpty(),
michael@0 1172 "aValue should be given for shorthands but not longhands");
michael@0 1173 AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
michael@0 1174 aResult.AppendLiteral(": ");
michael@0 1175 if (aValue.IsEmpty())
michael@0 1176 AppendValueToString(aProperty, aResult, nsCSSValue::eNormalized);
michael@0 1177 else
michael@0 1178 aResult.Append(aValue);
michael@0 1179 if (GetValueIsImportant(aProperty)) {
michael@0 1180 aResult.AppendLiteral(" ! important");
michael@0 1181 }
michael@0 1182 aResult.AppendLiteral("; ");
michael@0 1183 }
michael@0 1184
michael@0 1185 void
michael@0 1186 Declaration::AppendVariableAndValueToString(const nsAString& aName,
michael@0 1187 nsAString& aResult) const
michael@0 1188 {
michael@0 1189 aResult.AppendLiteral("--");
michael@0 1190 aResult.Append(aName);
michael@0 1191 CSSVariableDeclarations::Type type;
michael@0 1192 nsString value;
michael@0 1193 bool important;
michael@0 1194
michael@0 1195 if (mImportantVariables && mImportantVariables->Get(aName, type, value)) {
michael@0 1196 important = true;
michael@0 1197 } else {
michael@0 1198 MOZ_ASSERT(mVariables);
michael@0 1199 MOZ_ASSERT(mVariables->Has(aName));
michael@0 1200 mVariables->Get(aName, type, value);
michael@0 1201 important = false;
michael@0 1202 }
michael@0 1203
michael@0 1204 switch (type) {
michael@0 1205 case CSSVariableDeclarations::eTokenStream:
michael@0 1206 if (value.IsEmpty()) {
michael@0 1207 aResult.Append(':');
michael@0 1208 } else {
michael@0 1209 aResult.AppendLiteral(": ");
michael@0 1210 aResult.Append(value);
michael@0 1211 }
michael@0 1212 break;
michael@0 1213
michael@0 1214 case CSSVariableDeclarations::eInitial:
michael@0 1215 aResult.AppendLiteral("initial");
michael@0 1216 break;
michael@0 1217
michael@0 1218 case CSSVariableDeclarations::eInherit:
michael@0 1219 aResult.AppendLiteral("inherit");
michael@0 1220 break;
michael@0 1221
michael@0 1222 case CSSVariableDeclarations::eUnset:
michael@0 1223 aResult.AppendLiteral("unset");
michael@0 1224 break;
michael@0 1225
michael@0 1226 default:
michael@0 1227 MOZ_ASSERT(false, "unexpected variable value type");
michael@0 1228 }
michael@0 1229
michael@0 1230 if (important) {
michael@0 1231 aResult.AppendLiteral("! important");
michael@0 1232 }
michael@0 1233 aResult.AppendLiteral("; ");
michael@0 1234 }
michael@0 1235
michael@0 1236 void
michael@0 1237 Declaration::ToString(nsAString& aString) const
michael@0 1238 {
michael@0 1239 // Someone cares about this declaration's contents, so don't let it
michael@0 1240 // change from under them. See e.g. bug 338679.
michael@0 1241 SetImmutable();
michael@0 1242
michael@0 1243 nsCSSCompressedDataBlock *systemFontData =
michael@0 1244 GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
michael@0 1245 const nsCSSValue *systemFont =
michael@0 1246 systemFontData->ValueFor(eCSSProperty__x_system_font);
michael@0 1247 const bool haveSystemFont = systemFont &&
michael@0 1248 systemFont->GetUnit() != eCSSUnit_None &&
michael@0 1249 systemFont->GetUnit() != eCSSUnit_Null;
michael@0 1250 bool didSystemFont = false;
michael@0 1251
michael@0 1252 int32_t count = mOrder.Length();
michael@0 1253 int32_t index;
michael@0 1254 nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
michael@0 1255 for (index = 0; index < count; index++) {
michael@0 1256 nsCSSProperty property = GetPropertyAt(index);
michael@0 1257
michael@0 1258 if (property == eCSSPropertyExtra_variable) {
michael@0 1259 uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT;
michael@0 1260 AppendVariableAndValueToString(mVariableOrder[variableIndex], aString);
michael@0 1261 continue;
michael@0 1262 }
michael@0 1263
michael@0 1264 if (!nsCSSProps::IsEnabled(property)) {
michael@0 1265 continue;
michael@0 1266 }
michael@0 1267 bool doneProperty = false;
michael@0 1268
michael@0 1269 // If we already used this property in a shorthand, skip it.
michael@0 1270 if (shorthandsUsed.Length() > 0) {
michael@0 1271 for (const nsCSSProperty *shorthands =
michael@0 1272 nsCSSProps::ShorthandsContaining(property);
michael@0 1273 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
michael@0 1274 if (shorthandsUsed.Contains(*shorthands)) {
michael@0 1275 doneProperty = true;
michael@0 1276 break;
michael@0 1277 }
michael@0 1278 }
michael@0 1279 if (doneProperty)
michael@0 1280 continue;
michael@0 1281 }
michael@0 1282
michael@0 1283 // Try to use this property in a shorthand.
michael@0 1284 nsAutoString value;
michael@0 1285 for (const nsCSSProperty *shorthands =
michael@0 1286 nsCSSProps::ShorthandsContaining(property);
michael@0 1287 *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
michael@0 1288 // ShorthandsContaining returns the shorthands in order from those
michael@0 1289 // that contain the most subproperties to those that contain the
michael@0 1290 // least, which is exactly the order we want to test them.
michael@0 1291 nsCSSProperty shorthand = *shorthands;
michael@0 1292
michael@0 1293 // If GetValue gives us a non-empty string back, we can use that
michael@0 1294 // value; otherwise it's not possible to use this shorthand.
michael@0 1295 GetValue(shorthand, value);
michael@0 1296 if (!value.IsEmpty()) {
michael@0 1297 AppendPropertyAndValueToString(shorthand, value, aString);
michael@0 1298 shorthandsUsed.AppendElement(shorthand);
michael@0 1299 doneProperty = true;
michael@0 1300 break;
michael@0 1301 }
michael@0 1302
michael@0 1303 NS_ABORT_IF_FALSE(shorthand != eCSSProperty_font ||
michael@0 1304 *(shorthands + 1) == eCSSProperty_UNKNOWN,
michael@0 1305 "font should always be the only containing shorthand");
michael@0 1306 if (shorthand == eCSSProperty_font) {
michael@0 1307 if (haveSystemFont && !didSystemFont) {
michael@0 1308 // Output the shorthand font declaration that we will
michael@0 1309 // partially override later. But don't add it to
michael@0 1310 // |shorthandsUsed|, since we will have to override it.
michael@0 1311 systemFont->AppendToString(eCSSProperty__x_system_font, value,
michael@0 1312 nsCSSValue::eNormalized);
michael@0 1313 AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
michael@0 1314 value.Truncate();
michael@0 1315 didSystemFont = true;
michael@0 1316 }
michael@0 1317
michael@0 1318 // That we output the system font is enough for this property if:
michael@0 1319 // (1) it's the hidden system font subproperty (which either
michael@0 1320 // means we output it or we don't have it), or
michael@0 1321 // (2) its value is the hidden system font value and it matches
michael@0 1322 // the hidden system font subproperty in importance, and
michael@0 1323 // we output the system font subproperty.
michael@0 1324 const nsCSSValue *val = systemFontData->ValueFor(property);
michael@0 1325 if (property == eCSSProperty__x_system_font ||
michael@0 1326 (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
michael@0 1327 doneProperty = true;
michael@0 1328 }
michael@0 1329 }
michael@0 1330 }
michael@0 1331 if (doneProperty)
michael@0 1332 continue;
michael@0 1333
michael@0 1334 NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
michael@0 1335 AppendPropertyAndValueToString(property, value, aString);
michael@0 1336 }
michael@0 1337 if (! aString.IsEmpty()) {
michael@0 1338 // if the string is not empty, we have trailing whitespace we
michael@0 1339 // should remove
michael@0 1340 aString.Truncate(aString.Length() - 1);
michael@0 1341 }
michael@0 1342 }
michael@0 1343
michael@0 1344 #ifdef DEBUG
michael@0 1345 void
michael@0 1346 Declaration::List(FILE* out, int32_t aIndent) const
michael@0 1347 {
michael@0 1348 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
michael@0 1349
michael@0 1350 fputs("{ ", out);
michael@0 1351 nsAutoString s;
michael@0 1352 ToString(s);
michael@0 1353 fputs(NS_ConvertUTF16toUTF8(s).get(), out);
michael@0 1354 fputs("}", out);
michael@0 1355 }
michael@0 1356 #endif
michael@0 1357
michael@0 1358 bool
michael@0 1359 Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
michael@0 1360 {
michael@0 1361 aReturn.Truncate();
michael@0 1362 if (aIndex < mOrder.Length()) {
michael@0 1363 nsCSSProperty property = GetPropertyAt(aIndex);
michael@0 1364 if (property == eCSSPropertyExtra_variable) {
michael@0 1365 GetCustomPropertyNameAt(aIndex, aReturn);
michael@0 1366 return true;
michael@0 1367 }
michael@0 1368 if (0 <= property) {
michael@0 1369 AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
michael@0 1370 return true;
michael@0 1371 }
michael@0 1372 }
michael@0 1373 return false;
michael@0 1374 }
michael@0 1375
michael@0 1376 void
michael@0 1377 Declaration::InitializeEmpty()
michael@0 1378 {
michael@0 1379 NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
michael@0 1380 mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
michael@0 1381 }
michael@0 1382
michael@0 1383 Declaration*
michael@0 1384 Declaration::EnsureMutable()
michael@0 1385 {
michael@0 1386 NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
michael@0 1387 if (!IsMutable()) {
michael@0 1388 return new Declaration(*this);
michael@0 1389 } else {
michael@0 1390 return this;
michael@0 1391 }
michael@0 1392 }
michael@0 1393
michael@0 1394 size_t
michael@0 1395 Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
michael@0 1396 {
michael@0 1397 size_t n = aMallocSizeOf(this);
michael@0 1398 n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1399 n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0;
michael@0 1400 n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
michael@0 1401 if (mVariables) {
michael@0 1402 n += mVariables->SizeOfIncludingThis(aMallocSizeOf);
michael@0 1403 }
michael@0 1404 if (mImportantVariables) {
michael@0 1405 n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf);
michael@0 1406 }
michael@0 1407 return n;
michael@0 1408 }
michael@0 1409
michael@0 1410 bool
michael@0 1411 Declaration::HasVariableDeclaration(const nsAString& aName) const
michael@0 1412 {
michael@0 1413 return (mVariables && mVariables->Has(aName)) ||
michael@0 1414 (mImportantVariables && mImportantVariables->Has(aName));
michael@0 1415 }
michael@0 1416
michael@0 1417 void
michael@0 1418 Declaration::GetVariableDeclaration(const nsAString& aName,
michael@0 1419 nsAString& aValue) const
michael@0 1420 {
michael@0 1421 aValue.Truncate();
michael@0 1422
michael@0 1423 CSSVariableDeclarations::Type type;
michael@0 1424 nsString value;
michael@0 1425
michael@0 1426 if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) ||
michael@0 1427 (mVariables && mVariables->Get(aName, type, value))) {
michael@0 1428 switch (type) {
michael@0 1429 case CSSVariableDeclarations::eTokenStream:
michael@0 1430 aValue.Append(value);
michael@0 1431 break;
michael@0 1432
michael@0 1433 case CSSVariableDeclarations::eInitial:
michael@0 1434 aValue.AppendLiteral("initial");
michael@0 1435 break;
michael@0 1436
michael@0 1437 case CSSVariableDeclarations::eInherit:
michael@0 1438 aValue.AppendLiteral("inherit");
michael@0 1439 break;
michael@0 1440
michael@0 1441 case CSSVariableDeclarations::eUnset:
michael@0 1442 aValue.AppendLiteral("unset");
michael@0 1443 break;
michael@0 1444
michael@0 1445 default:
michael@0 1446 MOZ_ASSERT(false, "unexpected variable value type");
michael@0 1447 }
michael@0 1448 }
michael@0 1449 }
michael@0 1450
michael@0 1451 void
michael@0 1452 Declaration::AddVariableDeclaration(const nsAString& aName,
michael@0 1453 CSSVariableDeclarations::Type aType,
michael@0 1454 const nsString& aValue,
michael@0 1455 bool aIsImportant,
michael@0 1456 bool aOverrideImportant)
michael@0 1457 {
michael@0 1458 MOZ_ASSERT(IsMutable());
michael@0 1459
michael@0 1460 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
michael@0 1461 if (index == nsTArray<nsString>::NoIndex) {
michael@0 1462 index = mVariableOrder.Length();
michael@0 1463 mVariableOrder.AppendElement(aName);
michael@0 1464 }
michael@0 1465
michael@0 1466 if (!aIsImportant && !aOverrideImportant &&
michael@0 1467 mImportantVariables && mImportantVariables->Has(aName)) {
michael@0 1468 return;
michael@0 1469 }
michael@0 1470
michael@0 1471 CSSVariableDeclarations* variables;
michael@0 1472 if (aIsImportant) {
michael@0 1473 if (mVariables) {
michael@0 1474 mVariables->Remove(aName);
michael@0 1475 }
michael@0 1476 if (!mImportantVariables) {
michael@0 1477 mImportantVariables = new CSSVariableDeclarations;
michael@0 1478 }
michael@0 1479 variables = mImportantVariables;
michael@0 1480 } else {
michael@0 1481 if (mImportantVariables) {
michael@0 1482 mImportantVariables->Remove(aName);
michael@0 1483 }
michael@0 1484 if (!mVariables) {
michael@0 1485 mVariables = new CSSVariableDeclarations;
michael@0 1486 }
michael@0 1487 variables = mVariables;
michael@0 1488 }
michael@0 1489
michael@0 1490 switch (aType) {
michael@0 1491 case CSSVariableDeclarations::eTokenStream:
michael@0 1492 variables->PutTokenStream(aName, aValue);
michael@0 1493 break;
michael@0 1494
michael@0 1495 case CSSVariableDeclarations::eInitial:
michael@0 1496 MOZ_ASSERT(aValue.IsEmpty());
michael@0 1497 variables->PutInitial(aName);
michael@0 1498 break;
michael@0 1499
michael@0 1500 case CSSVariableDeclarations::eInherit:
michael@0 1501 MOZ_ASSERT(aValue.IsEmpty());
michael@0 1502 variables->PutInherit(aName);
michael@0 1503 break;
michael@0 1504
michael@0 1505 case CSSVariableDeclarations::eUnset:
michael@0 1506 MOZ_ASSERT(aValue.IsEmpty());
michael@0 1507 variables->PutUnset(aName);
michael@0 1508 break;
michael@0 1509
michael@0 1510 default:
michael@0 1511 MOZ_ASSERT(false, "unexpected aType value");
michael@0 1512 }
michael@0 1513
michael@0 1514 uint32_t propertyIndex = index + eCSSProperty_COUNT;
michael@0 1515 mOrder.RemoveElement(propertyIndex);
michael@0 1516 mOrder.AppendElement(propertyIndex);
michael@0 1517 }
michael@0 1518
michael@0 1519 void
michael@0 1520 Declaration::RemoveVariableDeclaration(const nsAString& aName)
michael@0 1521 {
michael@0 1522 if (mVariables) {
michael@0 1523 mVariables->Remove(aName);
michael@0 1524 }
michael@0 1525 if (mImportantVariables) {
michael@0 1526 mImportantVariables->Remove(aName);
michael@0 1527 }
michael@0 1528 nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
michael@0 1529 if (index != nsTArray<nsString>::NoIndex) {
michael@0 1530 mOrder.RemoveElement(index + eCSSProperty_COUNT);
michael@0 1531 }
michael@0 1532 }
michael@0 1533
michael@0 1534 bool
michael@0 1535 Declaration::GetVariableValueIsImportant(const nsAString& aName) const
michael@0 1536 {
michael@0 1537 return mImportantVariables && mImportantVariables->Has(aName);
michael@0 1538 }
michael@0 1539
michael@0 1540 } // namespace mozilla::css
michael@0 1541 } // namespace mozilla

mercurial