layout/style/Declaration.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/style/Declaration.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1541 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/*
    1.10 + * representation of a declaration block (or style attribute) in a CSS
    1.11 + * stylesheet
    1.12 + */
    1.13 +
    1.14 +#include "mozilla/ArrayUtils.h"
    1.15 +#include "mozilla/MemoryReporting.h"
    1.16 +
    1.17 +#include "mozilla/css/Declaration.h"
    1.18 +#include "nsPrintfCString.h"
    1.19 +#include "gfxFontConstants.h"
    1.20 +#include "nsStyleUtil.h"
    1.21 +
    1.22 +namespace mozilla {
    1.23 +namespace css {
    1.24 +
    1.25 +Declaration::Declaration()
    1.26 +  : mImmutable(false)
    1.27 +{
    1.28 +  MOZ_COUNT_CTOR(mozilla::css::Declaration);
    1.29 +}
    1.30 +
    1.31 +Declaration::Declaration(const Declaration& aCopy)
    1.32 +  : mOrder(aCopy.mOrder),
    1.33 +    mVariableOrder(aCopy.mVariableOrder),
    1.34 +    mData(aCopy.mData ? aCopy.mData->Clone() : nullptr),
    1.35 +    mImportantData(aCopy.mImportantData ?
    1.36 +                     aCopy.mImportantData->Clone() : nullptr),
    1.37 +    mVariables(aCopy.mVariables ?
    1.38 +        new CSSVariableDeclarations(*aCopy.mVariables) :
    1.39 +        nullptr),
    1.40 +    mImportantVariables(aCopy.mImportantVariables ?
    1.41 +        new CSSVariableDeclarations(*aCopy.mImportantVariables) :
    1.42 +        nullptr),
    1.43 +    mImmutable(false)
    1.44 +{
    1.45 +  MOZ_COUNT_CTOR(mozilla::css::Declaration);
    1.46 +}
    1.47 +
    1.48 +Declaration::~Declaration()
    1.49 +{
    1.50 +  MOZ_COUNT_DTOR(mozilla::css::Declaration);
    1.51 +}
    1.52 +
    1.53 +void
    1.54 +Declaration::ValueAppended(nsCSSProperty aProperty)
    1.55 +{
    1.56 +  NS_ABORT_IF_FALSE(!mData && !mImportantData,
    1.57 +                    "should only be called while expanded");
    1.58 +  NS_ABORT_IF_FALSE(!nsCSSProps::IsShorthand(aProperty),
    1.59 +                    "shorthands forbidden");
    1.60 +  // order IS important for CSS, so remove and add to the end
    1.61 +  mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
    1.62 +  mOrder.AppendElement(static_cast<uint32_t>(aProperty));
    1.63 +}
    1.64 +
    1.65 +void
    1.66 +Declaration::RemoveProperty(nsCSSProperty aProperty)
    1.67 +{
    1.68 +  MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT);
    1.69 +
    1.70 +  nsCSSExpandedDataBlock data;
    1.71 +  ExpandTo(&data);
    1.72 +  NS_ABORT_IF_FALSE(!mData && !mImportantData, "Expand didn't null things out");
    1.73 +
    1.74 +  if (nsCSSProps::IsShorthand(aProperty)) {
    1.75 +    CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
    1.76 +      data.ClearLonghandProperty(*p);
    1.77 +      mOrder.RemoveElement(static_cast<uint32_t>(*p));
    1.78 +    }
    1.79 +  } else {
    1.80 +    data.ClearLonghandProperty(aProperty);
    1.81 +    mOrder.RemoveElement(static_cast<uint32_t>(aProperty));
    1.82 +  }
    1.83 +
    1.84 +  CompressFrom(&data);
    1.85 +}
    1.86 +
    1.87 +bool
    1.88 +Declaration::HasProperty(nsCSSProperty aProperty) const
    1.89 +{
    1.90 +  NS_ABORT_IF_FALSE(0 <= aProperty &&
    1.91 +                    aProperty < eCSSProperty_COUNT_no_shorthands,
    1.92 +                    "property ID out of range");
    1.93 +
    1.94 +  nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
    1.95 +                                      ? mImportantData : mData;
    1.96 +  const nsCSSValue *val = data->ValueFor(aProperty);
    1.97 +  return !!val;
    1.98 +}
    1.99 +
   1.100 +bool
   1.101 +Declaration::AppendValueToString(nsCSSProperty aProperty,
   1.102 +                                 nsAString& aResult,
   1.103 +                                 nsCSSValue::Serialization aSerialization) const
   1.104 +{
   1.105 +  NS_ABORT_IF_FALSE(0 <= aProperty &&
   1.106 +                    aProperty < eCSSProperty_COUNT_no_shorthands,
   1.107 +                    "property ID out of range");
   1.108 +
   1.109 +  nsCSSCompressedDataBlock *data = GetValueIsImportant(aProperty)
   1.110 +                                      ? mImportantData : mData;
   1.111 +  const nsCSSValue *val = data->ValueFor(aProperty);
   1.112 +  if (!val) {
   1.113 +    return false;
   1.114 +  }
   1.115 +
   1.116 +  val->AppendToString(aProperty, aResult, aSerialization);
   1.117 +  return true;
   1.118 +}
   1.119 +
   1.120 +// Helper to append |aString| with the shorthand sides notation used in e.g.
   1.121 +// 'padding'. |aProperties| and |aValues| are expected to have 4 elements.
   1.122 +static void
   1.123 +AppendSidesShorthandToString(const nsCSSProperty aProperties[],
   1.124 +                             const nsCSSValue* aValues[],
   1.125 +                             nsAString& aString,
   1.126 +                             nsCSSValue::Serialization aSerialization)
   1.127 +{
   1.128 +  const nsCSSValue& value1 = *aValues[0];
   1.129 +  const nsCSSValue& value2 = *aValues[1];
   1.130 +  const nsCSSValue& value3 = *aValues[2];
   1.131 +  const nsCSSValue& value4 = *aValues[3];
   1.132 +
   1.133 +  NS_ABORT_IF_FALSE(value1.GetUnit() != eCSSUnit_Null, "null value 1");
   1.134 +  value1.AppendToString(aProperties[0], aString, aSerialization);
   1.135 +  if (value1 != value2 || value1 != value3 || value1 != value4) {
   1.136 +    aString.Append(char16_t(' '));
   1.137 +    NS_ABORT_IF_FALSE(value2.GetUnit() != eCSSUnit_Null, "null value 2");
   1.138 +    value2.AppendToString(aProperties[1], aString, aSerialization);
   1.139 +    if (value1 != value3 || value2 != value4) {
   1.140 +      aString.Append(char16_t(' '));
   1.141 +      NS_ABORT_IF_FALSE(value3.GetUnit() != eCSSUnit_Null, "null value 3");
   1.142 +      value3.AppendToString(aProperties[2], aString, aSerialization);
   1.143 +      if (value2 != value4) {
   1.144 +        aString.Append(char16_t(' '));
   1.145 +        NS_ABORT_IF_FALSE(value4.GetUnit() != eCSSUnit_Null, "null value 4");
   1.146 +        value4.AppendToString(aProperties[3], aString, aSerialization);
   1.147 +      }
   1.148 +    }
   1.149 +  }
   1.150 +}
   1.151 +
   1.152 +void
   1.153 +Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
   1.154 +{
   1.155 +  GetValue(aProperty, aValue, nsCSSValue::eNormalized);
   1.156 +}
   1.157 +
   1.158 +void
   1.159 +Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
   1.160 +{
   1.161 +  GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
   1.162 +}
   1.163 +
   1.164 +void
   1.165 +Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
   1.166 +                      nsCSSValue::Serialization aSerialization) const
   1.167 +{
   1.168 +  aValue.Truncate(0);
   1.169 +
   1.170 +  // simple properties are easy.
   1.171 +  if (!nsCSSProps::IsShorthand(aProperty)) {
   1.172 +    AppendValueToString(aProperty, aValue, aSerialization);
   1.173 +    return;
   1.174 +  }
   1.175 +
   1.176 +  // DOM Level 2 Style says (when describing CSS2Properties, although
   1.177 +  // not CSSStyleDeclaration.getPropertyValue):
   1.178 +  //   However, if there is no shorthand declaration that could be added
   1.179 +  //   to the ruleset without changing in any way the rules already
   1.180 +  //   declared in the ruleset (i.e., by adding longhand rules that were
   1.181 +  //   previously not declared in the ruleset), then the empty string
   1.182 +  //   should be returned for the shorthand property.
   1.183 +  // This means we need to check a number of cases:
   1.184 +  //   (1) Since a shorthand sets all sub-properties, if some of its
   1.185 +  //       subproperties were not specified, we must return the empty
   1.186 +  //       string.
   1.187 +  //   (2) Since 'inherit', 'initial' and 'unset' can only be specified
   1.188 +  //       as the values for entire properties, we need to return the
   1.189 +  //       empty string if some but not all of the subproperties have one
   1.190 +  //       of those values.
   1.191 +  //   (3) Since a single value only makes sense with or without
   1.192 +  //       !important, we return the empty string if some values are
   1.193 +  //       !important and some are not.
   1.194 +  // Since we're doing this check for 'inherit' and 'initial' up front,
   1.195 +  // we can also simplify the property serialization code by serializing
   1.196 +  // those values up front as well.
   1.197 +  //
   1.198 +  // Additionally, if a shorthand property was set using a value with a
   1.199 +  // variable reference and none of its component longhand properties were
   1.200 +  // then overridden on the declaration, we return the token stream
   1.201 +  // assigned to the shorthand.
   1.202 +  const nsCSSValue* tokenStream = nullptr;
   1.203 +  uint32_t totalCount = 0, importantCount = 0,
   1.204 +           initialCount = 0, inheritCount = 0, unsetCount = 0,
   1.205 +           matchingTokenStreamCount = 0;
   1.206 +  CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
   1.207 +    if (*p == eCSSProperty__x_system_font ||
   1.208 +         nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
   1.209 +      // The system-font subproperty and the *-source properties don't count.
   1.210 +      continue;
   1.211 +    }
   1.212 +    ++totalCount;
   1.213 +    const nsCSSValue *val = mData->ValueFor(*p);
   1.214 +    NS_ABORT_IF_FALSE(!val || !mImportantData || !mImportantData->ValueFor(*p),
   1.215 +                      "can't be in both blocks");
   1.216 +    if (!val && mImportantData) {
   1.217 +      ++importantCount;
   1.218 +      val = mImportantData->ValueFor(*p);
   1.219 +    }
   1.220 +    if (!val) {
   1.221 +      // Case (1) above: some subproperties not specified.
   1.222 +      return;
   1.223 +    }
   1.224 +    if (val->GetUnit() == eCSSUnit_Inherit) {
   1.225 +      ++inheritCount;
   1.226 +    } else if (val->GetUnit() == eCSSUnit_Initial) {
   1.227 +      ++initialCount;
   1.228 +    } else if (val->GetUnit() == eCSSUnit_Unset) {
   1.229 +      ++unsetCount;
   1.230 +    } else if (val->GetUnit() == eCSSUnit_TokenStream &&
   1.231 +               val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) {
   1.232 +      tokenStream = val;
   1.233 +      ++matchingTokenStreamCount;
   1.234 +    }
   1.235 +  }
   1.236 +  if (importantCount != 0 && importantCount != totalCount) {
   1.237 +    // Case (3), no consistent importance.
   1.238 +    return;
   1.239 +  }
   1.240 +  if (initialCount == totalCount) {
   1.241 +    // Simplify serialization below by serializing initial up-front.
   1.242 +    nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue,
   1.243 +                                                nsCSSValue::eNormalized);
   1.244 +    return;
   1.245 +  }
   1.246 +  if (inheritCount == totalCount) {
   1.247 +    // Simplify serialization below by serializing inherit up-front.
   1.248 +    nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue,
   1.249 +                                                nsCSSValue::eNormalized);
   1.250 +    return;
   1.251 +  }
   1.252 +  if (unsetCount == totalCount) {
   1.253 +    // Simplify serialization below by serializing unset up-front.
   1.254 +    nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue,
   1.255 +                                              nsCSSValue::eNormalized);
   1.256 +    return;
   1.257 +  }
   1.258 +  if (initialCount != 0 || inheritCount != 0 || unsetCount != 0) {
   1.259 +    // Case (2): partially initial, inherit or unset.
   1.260 +    return;
   1.261 +  }
   1.262 +  if (tokenStream) {
   1.263 +    if (matchingTokenStreamCount == totalCount) {
   1.264 +      // Shorthand was specified using variable references and all of its
   1.265 +      // longhand components were set by the shorthand.
   1.266 +      aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream);
   1.267 +    } else {
   1.268 +      // In all other cases, serialize to the empty string.
   1.269 +    }
   1.270 +    return;
   1.271 +  }
   1.272 +
   1.273 +  nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
   1.274 +  switch (aProperty) {
   1.275 +    case eCSSProperty_margin: 
   1.276 +    case eCSSProperty_padding: 
   1.277 +    case eCSSProperty_border_color: 
   1.278 +    case eCSSProperty_border_style: 
   1.279 +    case eCSSProperty_border_width: {
   1.280 +      const nsCSSProperty* subprops =
   1.281 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.282 +      NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
   1.283 +                        kNotFound, "first subprop must be top");
   1.284 +      NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
   1.285 +                        kNotFound, "second subprop must be right");
   1.286 +      NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
   1.287 +                        kNotFound, "third subprop must be bottom");
   1.288 +      NS_ABORT_IF_FALSE(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
   1.289 +                        kNotFound, "fourth subprop must be left");
   1.290 +      const nsCSSValue* vals[4] = {
   1.291 +        data->ValueFor(subprops[0]),
   1.292 +        data->ValueFor(subprops[1]),
   1.293 +        data->ValueFor(subprops[2]),
   1.294 +        data->ValueFor(subprops[3])
   1.295 +      };
   1.296 +      AppendSidesShorthandToString(subprops, vals, aValue, aSerialization);
   1.297 +      break;
   1.298 +    }
   1.299 +    case eCSSProperty_border_radius:
   1.300 +    case eCSSProperty__moz_outline_radius: {
   1.301 +      const nsCSSProperty* subprops =
   1.302 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.303 +      const nsCSSValue* vals[4] = {
   1.304 +        data->ValueFor(subprops[0]),
   1.305 +        data->ValueFor(subprops[1]),
   1.306 +        data->ValueFor(subprops[2]),
   1.307 +        data->ValueFor(subprops[3])
   1.308 +      };
   1.309 +
   1.310 +      // For compatibility, only write a slash and the y-values
   1.311 +      // if they're not identical to the x-values.
   1.312 +      bool needY = false;
   1.313 +      const nsCSSValue* xVals[4];
   1.314 +      const nsCSSValue* yVals[4];
   1.315 +      for (int i = 0; i < 4; i++) {
   1.316 +        if (vals[i]->GetUnit() == eCSSUnit_Pair) {
   1.317 +          needY = true;
   1.318 +          xVals[i] = &vals[i]->GetPairValue().mXValue;
   1.319 +          yVals[i] = &vals[i]->GetPairValue().mYValue;
   1.320 +        } else {
   1.321 +          xVals[i] = yVals[i] = vals[i];
   1.322 +        }
   1.323 +      }
   1.324 +
   1.325 +      AppendSidesShorthandToString(subprops, xVals, aValue, aSerialization);
   1.326 +      if (needY) {
   1.327 +        aValue.AppendLiteral(" / ");
   1.328 +        AppendSidesShorthandToString(subprops, yVals, aValue, aSerialization);
   1.329 +      }
   1.330 +      break;
   1.331 +    }
   1.332 +    case eCSSProperty_border_image: {
   1.333 +      // Even though there are some cases where we could omit
   1.334 +      // 'border-image-source' (when it's none), it's probably not a
   1.335 +      // good idea since it's likely to be confusing.  It would also
   1.336 +      // require adding the extra check that we serialize *something*.
   1.337 +      AppendValueToString(eCSSProperty_border_image_source, aValue,
   1.338 +                          aSerialization);
   1.339 +
   1.340 +      bool sliceDefault = data->HasDefaultBorderImageSlice();
   1.341 +      bool widthDefault = data->HasDefaultBorderImageWidth();
   1.342 +      bool outsetDefault = data->HasDefaultBorderImageOutset();
   1.343 +
   1.344 +      if (!sliceDefault || !widthDefault || !outsetDefault) {
   1.345 +        aValue.Append(char16_t(' '));
   1.346 +        AppendValueToString(eCSSProperty_border_image_slice, aValue,
   1.347 +                            aSerialization);
   1.348 +        if (!widthDefault || !outsetDefault) {
   1.349 +          aValue.Append(NS_LITERAL_STRING(" /"));
   1.350 +          if (!widthDefault) {
   1.351 +            aValue.Append(char16_t(' '));
   1.352 +            AppendValueToString(eCSSProperty_border_image_width, aValue,
   1.353 +                                aSerialization);
   1.354 +          }
   1.355 +          if (!outsetDefault) {
   1.356 +            aValue.Append(NS_LITERAL_STRING(" / "));
   1.357 +            AppendValueToString(eCSSProperty_border_image_outset, aValue,
   1.358 +                                aSerialization);
   1.359 +          }
   1.360 +        }
   1.361 +      }
   1.362 +
   1.363 +      bool repeatDefault = data->HasDefaultBorderImageRepeat();
   1.364 +      if (!repeatDefault) {
   1.365 +        aValue.Append(char16_t(' '));
   1.366 +        AppendValueToString(eCSSProperty_border_image_repeat, aValue,
   1.367 +                            aSerialization);
   1.368 +      }
   1.369 +      break;
   1.370 +    }
   1.371 +    case eCSSProperty_border: {
   1.372 +      // If we have a non-default value for any of the properties that
   1.373 +      // this shorthand sets but cannot specify, we have to return the
   1.374 +      // empty string.
   1.375 +      if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() !=
   1.376 +            eCSSUnit_None ||
   1.377 +          !data->HasDefaultBorderImageSlice() ||
   1.378 +          !data->HasDefaultBorderImageWidth() ||
   1.379 +          !data->HasDefaultBorderImageOutset() ||
   1.380 +          !data->HasDefaultBorderImageRepeat() ||
   1.381 +          data->ValueFor(eCSSProperty_border_top_colors)->GetUnit() !=
   1.382 +            eCSSUnit_None ||
   1.383 +          data->ValueFor(eCSSProperty_border_right_colors)->GetUnit() !=
   1.384 +            eCSSUnit_None ||
   1.385 +          data->ValueFor(eCSSProperty_border_bottom_colors)->GetUnit() !=
   1.386 +            eCSSUnit_None ||
   1.387 +          data->ValueFor(eCSSProperty_border_left_colors)->GetUnit() !=
   1.388 +            eCSSUnit_None) {
   1.389 +        break;
   1.390 +      }
   1.391 +
   1.392 +      const nsCSSProperty* subproptables[3] = {
   1.393 +        nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
   1.394 +        nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
   1.395 +        nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
   1.396 +      };
   1.397 +      bool match = true;
   1.398 +      for (const nsCSSProperty** subprops = subproptables,
   1.399 +               **subprops_end = ArrayEnd(subproptables);
   1.400 +           subprops < subprops_end; ++subprops) {
   1.401 +        // Check only the first four subprops in each table, since the
   1.402 +        // others are extras for dimensional box properties.
   1.403 +        const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]);
   1.404 +        for (int32_t side = 1; side < 4; ++side) {
   1.405 +          const nsCSSValue *otherSide =
   1.406 +            data->ValueFor((*subprops)[side]);
   1.407 +          if (*firstSide != *otherSide)
   1.408 +            match = false;
   1.409 +        }
   1.410 +      }
   1.411 +      if (!match) {
   1.412 +        // We can't express what we have in the border shorthand
   1.413 +        break;
   1.414 +      }
   1.415 +      // tweak aProperty and fall through
   1.416 +      aProperty = eCSSProperty_border_top;
   1.417 +    }
   1.418 +    case eCSSProperty_border_top:
   1.419 +    case eCSSProperty_border_right:
   1.420 +    case eCSSProperty_border_bottom:
   1.421 +    case eCSSProperty_border_left:
   1.422 +    case eCSSProperty_border_start:
   1.423 +    case eCSSProperty_border_end:
   1.424 +    case eCSSProperty__moz_column_rule:
   1.425 +    case eCSSProperty_outline: {
   1.426 +      const nsCSSProperty* subprops =
   1.427 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.428 +      NS_ABORT_IF_FALSE(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
   1.429 +                                       NS_LITERAL_CSTRING("-color")) ||
   1.430 +                        StringEndsWith(nsCSSProps::GetStringValue(subprops[2]),
   1.431 +                                       NS_LITERAL_CSTRING("-color-value")),
   1.432 +                        "third subprop must be the color property");
   1.433 +      const nsCSSValue *colorValue = data->ValueFor(subprops[2]);
   1.434 +      bool isMozUseTextColor =
   1.435 +        colorValue->GetUnit() == eCSSUnit_Enumerated &&
   1.436 +        colorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR;
   1.437 +      if (!AppendValueToString(subprops[0], aValue, aSerialization) ||
   1.438 +          !(aValue.Append(char16_t(' ')),
   1.439 +            AppendValueToString(subprops[1], aValue, aSerialization)) ||
   1.440 +          // Don't output a third value when it's -moz-use-text-color.
   1.441 +          !(isMozUseTextColor ||
   1.442 +            (aValue.Append(char16_t(' ')),
   1.443 +             AppendValueToString(subprops[2], aValue, aSerialization)))) {
   1.444 +        aValue.Truncate();
   1.445 +      }
   1.446 +      break;
   1.447 +    }
   1.448 +    case eCSSProperty_margin_left:
   1.449 +    case eCSSProperty_margin_right:
   1.450 +    case eCSSProperty_margin_start:
   1.451 +    case eCSSProperty_margin_end:
   1.452 +    case eCSSProperty_padding_left:
   1.453 +    case eCSSProperty_padding_right:
   1.454 +    case eCSSProperty_padding_start:
   1.455 +    case eCSSProperty_padding_end:
   1.456 +    case eCSSProperty_border_left_color:
   1.457 +    case eCSSProperty_border_left_style:
   1.458 +    case eCSSProperty_border_left_width:
   1.459 +    case eCSSProperty_border_right_color:
   1.460 +    case eCSSProperty_border_right_style:
   1.461 +    case eCSSProperty_border_right_width:
   1.462 +    case eCSSProperty_border_start_color:
   1.463 +    case eCSSProperty_border_start_style:
   1.464 +    case eCSSProperty_border_start_width:
   1.465 +    case eCSSProperty_border_end_color:
   1.466 +    case eCSSProperty_border_end_style:
   1.467 +    case eCSSProperty_border_end_width: {
   1.468 +      const nsCSSProperty* subprops =
   1.469 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.470 +      NS_ABORT_IF_FALSE(subprops[3] == eCSSProperty_UNKNOWN,
   1.471 +                        "not box property with physical vs. logical cascading");
   1.472 +      AppendValueToString(subprops[0], aValue, aSerialization);
   1.473 +      break;
   1.474 +    }
   1.475 +    case eCSSProperty_background: {
   1.476 +      // We know from above that all subproperties were specified.
   1.477 +      // However, we still can't represent that in the shorthand unless
   1.478 +      // they're all lists of the same length.  So if they're different
   1.479 +      // lengths, we need to bail out.
   1.480 +      // We also need to bail out if an item has background-clip and
   1.481 +      // background-origin that are different and not the default
   1.482 +      // values.  (We omit them if they're both default.)
   1.483 +      const nsCSSValueList *image =
   1.484 +        data->ValueFor(eCSSProperty_background_image)->
   1.485 +        GetListValue();
   1.486 +      const nsCSSValuePairList *repeat =
   1.487 +        data->ValueFor(eCSSProperty_background_repeat)->
   1.488 +        GetPairListValue();
   1.489 +      const nsCSSValueList *attachment =
   1.490 +        data->ValueFor(eCSSProperty_background_attachment)->
   1.491 +        GetListValue();
   1.492 +      const nsCSSValueList *position =
   1.493 +        data->ValueFor(eCSSProperty_background_position)->
   1.494 +        GetListValue();
   1.495 +      const nsCSSValueList *clip =
   1.496 +        data->ValueFor(eCSSProperty_background_clip)->
   1.497 +        GetListValue();
   1.498 +      const nsCSSValueList *origin =
   1.499 +        data->ValueFor(eCSSProperty_background_origin)->
   1.500 +        GetListValue();
   1.501 +      const nsCSSValuePairList *size =
   1.502 +        data->ValueFor(eCSSProperty_background_size)->
   1.503 +        GetPairListValue();
   1.504 +      for (;;) {
   1.505 +        image->mValue.AppendToString(eCSSProperty_background_image, aValue,
   1.506 +                                     aSerialization);
   1.507 +        aValue.Append(char16_t(' '));
   1.508 +        repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue,
   1.509 +                                       aSerialization);
   1.510 +        if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
   1.511 +          repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue,
   1.512 +                                         aSerialization);
   1.513 +        }
   1.514 +        aValue.Append(char16_t(' '));
   1.515 +        attachment->mValue.AppendToString(eCSSProperty_background_attachment,
   1.516 +                                          aValue, aSerialization);
   1.517 +        aValue.Append(char16_t(' '));
   1.518 +        position->mValue.AppendToString(eCSSProperty_background_position,
   1.519 +                                        aValue, aSerialization);
   1.520 +        
   1.521 +        if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
   1.522 +            size->mYValue.GetUnit() != eCSSUnit_Auto) {
   1.523 +          aValue.Append(char16_t(' '));
   1.524 +          aValue.Append(char16_t('/'));
   1.525 +          aValue.Append(char16_t(' '));
   1.526 +          size->mXValue.AppendToString(eCSSProperty_background_size, aValue,
   1.527 +                                       aSerialization);
   1.528 +          aValue.Append(char16_t(' '));
   1.529 +          size->mYValue.AppendToString(eCSSProperty_background_size, aValue,
   1.530 +                                       aSerialization);
   1.531 +        }
   1.532 +
   1.533 +        NS_ABORT_IF_FALSE(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
   1.534 +                          origin->mValue.GetUnit() == eCSSUnit_Enumerated,
   1.535 +                          "should not have inherit/initial within list");
   1.536 +
   1.537 +        if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
   1.538 +            origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
   1.539 +          MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
   1.540 +                       eCSSProperty_background_origin] ==
   1.541 +                     nsCSSProps::kBackgroundOriginKTable);
   1.542 +          MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
   1.543 +                       eCSSProperty_background_clip] ==
   1.544 +                     nsCSSProps::kBackgroundOriginKTable);
   1.545 +          static_assert(NS_STYLE_BG_CLIP_BORDER ==
   1.546 +                        NS_STYLE_BG_ORIGIN_BORDER &&
   1.547 +                        NS_STYLE_BG_CLIP_PADDING ==
   1.548 +                        NS_STYLE_BG_ORIGIN_PADDING &&
   1.549 +                        NS_STYLE_BG_CLIP_CONTENT ==
   1.550 +                        NS_STYLE_BG_ORIGIN_CONTENT,
   1.551 +                        "bg-clip and bg-origin style constants must agree");
   1.552 +          aValue.Append(char16_t(' '));
   1.553 +          origin->mValue.AppendToString(eCSSProperty_background_origin, aValue,
   1.554 +                                        aSerialization);
   1.555 +
   1.556 +          if (clip->mValue != origin->mValue) {
   1.557 +            aValue.Append(char16_t(' '));
   1.558 +            clip->mValue.AppendToString(eCSSProperty_background_clip, aValue,
   1.559 +                                        aSerialization);
   1.560 +          }
   1.561 +        }
   1.562 +
   1.563 +        image = image->mNext;
   1.564 +        repeat = repeat->mNext;
   1.565 +        attachment = attachment->mNext;
   1.566 +        position = position->mNext;
   1.567 +        clip = clip->mNext;
   1.568 +        origin = origin->mNext;
   1.569 +        size = size->mNext;
   1.570 +
   1.571 +        if (!image) {
   1.572 +          if (repeat || attachment || position || clip || origin || size) {
   1.573 +            // Uneven length lists, so can't be serialized as shorthand.
   1.574 +            aValue.Truncate();
   1.575 +            return;
   1.576 +          }
   1.577 +          break;
   1.578 +        }
   1.579 +        if (!repeat || !attachment || !position || !clip || !origin || !size) {
   1.580 +          // Uneven length lists, so can't be serialized as shorthand.
   1.581 +          aValue.Truncate();
   1.582 +          return;
   1.583 +        }
   1.584 +        aValue.Append(char16_t(','));
   1.585 +        aValue.Append(char16_t(' '));
   1.586 +      }
   1.587 +
   1.588 +      aValue.Append(char16_t(' '));
   1.589 +      AppendValueToString(eCSSProperty_background_color, aValue,
   1.590 +                          aSerialization);
   1.591 +      break;
   1.592 +    }
   1.593 +    case eCSSProperty_font: {
   1.594 +      // systemFont might not be present; other values are guaranteed to be
   1.595 +      // available based on the shorthand check at the beginning of the
   1.596 +      // function, as long as the prop is enabled
   1.597 +      const nsCSSValue *systemFont =
   1.598 +        data->ValueFor(eCSSProperty__x_system_font);
   1.599 +      const nsCSSValue *style =
   1.600 +        data->ValueFor(eCSSProperty_font_style);
   1.601 +      const nsCSSValue *variant =
   1.602 +        data->ValueFor(eCSSProperty_font_variant);
   1.603 +      const nsCSSValue *weight =
   1.604 +        data->ValueFor(eCSSProperty_font_weight);
   1.605 +      const nsCSSValue *size =
   1.606 +        data->ValueFor(eCSSProperty_font_size);
   1.607 +      const nsCSSValue *lh =
   1.608 +        data->ValueFor(eCSSProperty_line_height);
   1.609 +      const nsCSSValue *family =
   1.610 +        data->ValueFor(eCSSProperty_font_family);
   1.611 +      const nsCSSValue *stretch =
   1.612 +        data->ValueFor(eCSSProperty_font_stretch);
   1.613 +      const nsCSSValue *sizeAdjust =
   1.614 +        data->ValueFor(eCSSProperty_font_size_adjust);
   1.615 +      const nsCSSValue *featureSettings =
   1.616 +        data->ValueFor(eCSSProperty_font_feature_settings);
   1.617 +      const nsCSSValue *languageOverride =
   1.618 +        data->ValueFor(eCSSProperty_font_language_override);
   1.619 +      const nsCSSValue *fontKerning =
   1.620 +        data->ValueFor(eCSSProperty_font_kerning);
   1.621 +      const nsCSSValue *fontSynthesis =
   1.622 +        data->ValueFor(eCSSProperty_font_synthesis);
   1.623 +      const nsCSSValue *fontVariantAlternates =
   1.624 +        data->ValueFor(eCSSProperty_font_variant_alternates);
   1.625 +      const nsCSSValue *fontVariantCaps =
   1.626 +        data->ValueFor(eCSSProperty_font_variant_caps);
   1.627 +      const nsCSSValue *fontVariantEastAsian =
   1.628 +        data->ValueFor(eCSSProperty_font_variant_east_asian);
   1.629 +      const nsCSSValue *fontVariantLigatures =
   1.630 +        data->ValueFor(eCSSProperty_font_variant_ligatures);
   1.631 +      const nsCSSValue *fontVariantNumeric =
   1.632 +        data->ValueFor(eCSSProperty_font_variant_numeric);
   1.633 +      const nsCSSValue *fontVariantPosition =
   1.634 +        data->ValueFor(eCSSProperty_font_variant_position);
   1.635 +
   1.636 +      // if font features are not enabled, pointers for fontVariant
   1.637 +      // values above may be null since the shorthand check ignores them
   1.638 +      // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
   1.639 +      bool fontFeaturesEnabled =
   1.640 +        nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
   1.641 +
   1.642 +      if (systemFont &&
   1.643 +          systemFont->GetUnit() != eCSSUnit_None &&
   1.644 +          systemFont->GetUnit() != eCSSUnit_Null) {
   1.645 +        if (style->GetUnit() != eCSSUnit_System_Font ||
   1.646 +            variant->GetUnit() != eCSSUnit_System_Font ||
   1.647 +            weight->GetUnit() != eCSSUnit_System_Font ||
   1.648 +            size->GetUnit() != eCSSUnit_System_Font ||
   1.649 +            lh->GetUnit() != eCSSUnit_System_Font ||
   1.650 +            family->GetUnit() != eCSSUnit_System_Font ||
   1.651 +            stretch->GetUnit() != eCSSUnit_System_Font ||
   1.652 +            sizeAdjust->GetUnit() != eCSSUnit_System_Font ||
   1.653 +            featureSettings->GetUnit() != eCSSUnit_System_Font ||
   1.654 +            languageOverride->GetUnit() != eCSSUnit_System_Font ||
   1.655 +            (fontFeaturesEnabled &&
   1.656 +             (fontKerning->GetUnit() != eCSSUnit_System_Font ||
   1.657 +              fontSynthesis->GetUnit() != eCSSUnit_System_Font ||
   1.658 +              fontVariantAlternates->GetUnit() != eCSSUnit_System_Font ||
   1.659 +              fontVariantCaps->GetUnit() != eCSSUnit_System_Font ||
   1.660 +              fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font ||
   1.661 +              fontVariantLigatures->GetUnit() != eCSSUnit_System_Font ||
   1.662 +              fontVariantNumeric->GetUnit() != eCSSUnit_System_Font ||
   1.663 +              fontVariantPosition->GetUnit() != eCSSUnit_System_Font))) {
   1.664 +          // This can't be represented as a shorthand.
   1.665 +          return;
   1.666 +        }
   1.667 +        systemFont->AppendToString(eCSSProperty__x_system_font, aValue,
   1.668 +                                   aSerialization);
   1.669 +      } else {
   1.670 +        // properties reset by this shorthand property to their
   1.671 +        // initial values but not represented in its syntax
   1.672 +        if (stretch->GetUnit() != eCSSUnit_Enumerated ||
   1.673 +            stretch->GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
   1.674 +            sizeAdjust->GetUnit() != eCSSUnit_None ||
   1.675 +            featureSettings->GetUnit() != eCSSUnit_Normal ||
   1.676 +            languageOverride->GetUnit() != eCSSUnit_Normal ||
   1.677 +            (fontFeaturesEnabled &&
   1.678 +             (fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO ||
   1.679 +              fontSynthesis->GetUnit() != eCSSUnit_Enumerated ||
   1.680 +              fontSynthesis->GetIntValue() !=
   1.681 +                (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) ||
   1.682 +              fontVariantAlternates->GetUnit() != eCSSUnit_Normal ||
   1.683 +              fontVariantCaps->GetUnit() != eCSSUnit_Normal ||
   1.684 +              fontVariantEastAsian->GetUnit() != eCSSUnit_Normal ||
   1.685 +              fontVariantLigatures->GetUnit() != eCSSUnit_Normal ||
   1.686 +              fontVariantNumeric->GetUnit() != eCSSUnit_Normal ||
   1.687 +              fontVariantPosition->GetUnit() != eCSSUnit_Normal))) {
   1.688 +          return;
   1.689 +        }
   1.690 +
   1.691 +        if (style->GetUnit() != eCSSUnit_Enumerated ||
   1.692 +            style->GetIntValue() != NS_FONT_STYLE_NORMAL) {
   1.693 +          style->AppendToString(eCSSProperty_font_style, aValue,
   1.694 +                                aSerialization);
   1.695 +          aValue.Append(char16_t(' '));
   1.696 +        }
   1.697 +        if (variant->GetUnit() != eCSSUnit_Enumerated ||
   1.698 +            variant->GetIntValue() != NS_FONT_VARIANT_NORMAL) {
   1.699 +          variant->AppendToString(eCSSProperty_font_variant, aValue,
   1.700 +                                  aSerialization);
   1.701 +          aValue.Append(char16_t(' '));
   1.702 +        }
   1.703 +        if (weight->GetUnit() != eCSSUnit_Enumerated ||
   1.704 +            weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
   1.705 +          weight->AppendToString(eCSSProperty_font_weight, aValue,
   1.706 +                                 aSerialization);
   1.707 +          aValue.Append(char16_t(' '));
   1.708 +        }
   1.709 +        size->AppendToString(eCSSProperty_font_size, aValue, aSerialization);
   1.710 +        if (lh->GetUnit() != eCSSUnit_Normal) {
   1.711 +          aValue.Append(char16_t('/'));
   1.712 +          lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization);
   1.713 +        }
   1.714 +        aValue.Append(char16_t(' '));
   1.715 +        family->AppendToString(eCSSProperty_font_family, aValue,
   1.716 +                               aSerialization);
   1.717 +      }
   1.718 +      break;
   1.719 +    }
   1.720 +    case eCSSProperty_list_style:
   1.721 +      if (AppendValueToString(eCSSProperty_list_style_type, aValue,
   1.722 +                              aSerialization)) {
   1.723 +        aValue.Append(char16_t(' '));
   1.724 +      }
   1.725 +      if (AppendValueToString(eCSSProperty_list_style_position, aValue,
   1.726 +                              aSerialization)) {
   1.727 +        aValue.Append(char16_t(' '));
   1.728 +      }
   1.729 +      AppendValueToString(eCSSProperty_list_style_image, aValue,
   1.730 +                          aSerialization);
   1.731 +      break;
   1.732 +    case eCSSProperty_overflow: {
   1.733 +      const nsCSSValue &xValue =
   1.734 +        *data->ValueFor(eCSSProperty_overflow_x);
   1.735 +      const nsCSSValue &yValue =
   1.736 +        *data->ValueFor(eCSSProperty_overflow_y);
   1.737 +      if (xValue == yValue)
   1.738 +        xValue.AppendToString(eCSSProperty_overflow_x, aValue, aSerialization);
   1.739 +      break;
   1.740 +    }
   1.741 +    case eCSSProperty_text_decoration: {
   1.742 +      // If text-decoration-color or text-decoration-style isn't initial value,
   1.743 +      // we cannot serialize the text-decoration shorthand value.
   1.744 +      const nsCSSValue *decorationColor =
   1.745 +        data->ValueFor(eCSSProperty_text_decoration_color);
   1.746 +      const nsCSSValue *decorationStyle =
   1.747 +        data->ValueFor(eCSSProperty_text_decoration_style);
   1.748 +
   1.749 +      NS_ABORT_IF_FALSE(decorationStyle->GetUnit() == eCSSUnit_Enumerated,
   1.750 +                        nsPrintfCString("bad text-decoration-style unit %d",
   1.751 +                                        decorationStyle->GetUnit()).get());
   1.752 +
   1.753 +      if (decorationColor->GetUnit() != eCSSUnit_Enumerated ||
   1.754 +          decorationColor->GetIntValue() != NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR ||
   1.755 +          decorationStyle->GetIntValue() !=
   1.756 +            NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
   1.757 +        return;
   1.758 +      }
   1.759 +
   1.760 +      AppendValueToString(eCSSProperty_text_decoration_line, aValue,
   1.761 +                          aSerialization);
   1.762 +      break;
   1.763 +    }
   1.764 +    case eCSSProperty_transition: {
   1.765 +      const nsCSSValue *transProp =
   1.766 +        data->ValueFor(eCSSProperty_transition_property);
   1.767 +      const nsCSSValue *transDuration =
   1.768 +        data->ValueFor(eCSSProperty_transition_duration);
   1.769 +      const nsCSSValue *transTiming =
   1.770 +        data->ValueFor(eCSSProperty_transition_timing_function);
   1.771 +      const nsCSSValue *transDelay =
   1.772 +        data->ValueFor(eCSSProperty_transition_delay);
   1.773 +
   1.774 +      NS_ABORT_IF_FALSE(transDuration->GetUnit() == eCSSUnit_List ||
   1.775 +                        transDuration->GetUnit() == eCSSUnit_ListDep,
   1.776 +                        nsPrintfCString("bad t-duration unit %d",
   1.777 +                                        transDuration->GetUnit()).get());
   1.778 +      NS_ABORT_IF_FALSE(transTiming->GetUnit() == eCSSUnit_List ||
   1.779 +                        transTiming->GetUnit() == eCSSUnit_ListDep,
   1.780 +                        nsPrintfCString("bad t-timing unit %d",
   1.781 +                                        transTiming->GetUnit()).get());
   1.782 +      NS_ABORT_IF_FALSE(transDelay->GetUnit() == eCSSUnit_List ||
   1.783 +                        transDelay->GetUnit() == eCSSUnit_ListDep,
   1.784 +                        nsPrintfCString("bad t-delay unit %d",
   1.785 +                                        transDelay->GetUnit()).get());
   1.786 +
   1.787 +      const nsCSSValueList* dur = transDuration->GetListValue();
   1.788 +      const nsCSSValueList* tim = transTiming->GetListValue();
   1.789 +      const nsCSSValueList* del = transDelay->GetListValue();
   1.790 +
   1.791 +      if (transProp->GetUnit() == eCSSUnit_None ||
   1.792 +          transProp->GetUnit() == eCSSUnit_All) {
   1.793 +        // If any of the other three lists has more than one element,
   1.794 +        // we can't use the shorthand.
   1.795 +        if (!dur->mNext && !tim->mNext && !del->mNext) {
   1.796 +          transProp->AppendToString(eCSSProperty_transition_property, aValue,
   1.797 +                                    aSerialization);
   1.798 +          aValue.Append(char16_t(' '));
   1.799 +          dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue,
   1.800 +                                     aSerialization);
   1.801 +          aValue.Append(char16_t(' '));
   1.802 +          tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
   1.803 +                                     aValue, aSerialization);
   1.804 +          aValue.Append(char16_t(' '));
   1.805 +          del->mValue.AppendToString(eCSSProperty_transition_delay, aValue,
   1.806 +                                     aSerialization);
   1.807 +          aValue.Append(char16_t(' '));
   1.808 +        } else {
   1.809 +          aValue.Truncate();
   1.810 +        }
   1.811 +      } else {
   1.812 +        NS_ABORT_IF_FALSE(transProp->GetUnit() == eCSSUnit_List ||
   1.813 +                          transProp->GetUnit() == eCSSUnit_ListDep,
   1.814 +                          nsPrintfCString("bad t-prop unit %d",
   1.815 +                                          transProp->GetUnit()).get());
   1.816 +        const nsCSSValueList* pro = transProp->GetListValue();
   1.817 +        for (;;) {
   1.818 +          pro->mValue.AppendToString(eCSSProperty_transition_property,
   1.819 +                                        aValue, aSerialization);
   1.820 +          aValue.Append(char16_t(' '));
   1.821 +          dur->mValue.AppendToString(eCSSProperty_transition_duration,
   1.822 +                                        aValue, aSerialization);
   1.823 +          aValue.Append(char16_t(' '));
   1.824 +          tim->mValue.AppendToString(eCSSProperty_transition_timing_function,
   1.825 +                                        aValue, aSerialization);
   1.826 +          aValue.Append(char16_t(' '));
   1.827 +          del->mValue.AppendToString(eCSSProperty_transition_delay,
   1.828 +                                        aValue, aSerialization);
   1.829 +          pro = pro->mNext;
   1.830 +          dur = dur->mNext;
   1.831 +          tim = tim->mNext;
   1.832 +          del = del->mNext;
   1.833 +          if (!pro || !dur || !tim || !del) {
   1.834 +            break;
   1.835 +          }
   1.836 +          aValue.AppendLiteral(", ");
   1.837 +        }
   1.838 +        if (pro || dur || tim || del) {
   1.839 +          // Lists not all the same length, can't use shorthand.
   1.840 +          aValue.Truncate();
   1.841 +        }
   1.842 +      }
   1.843 +      break;
   1.844 +    }
   1.845 +    case eCSSProperty_animation: {
   1.846 +      const nsCSSProperty* subprops =
   1.847 +        nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation);
   1.848 +      static const size_t numProps = 7;
   1.849 +      NS_ABORT_IF_FALSE(subprops[numProps] == eCSSProperty_UNKNOWN,
   1.850 +                        "unexpected number of subproperties");
   1.851 +      const nsCSSValue* values[numProps];
   1.852 +      const nsCSSValueList* lists[numProps];
   1.853 +
   1.854 +      for (uint32_t i = 0; i < numProps; ++i) {
   1.855 +        values[i] = data->ValueFor(subprops[i]);
   1.856 +        NS_ABORT_IF_FALSE(values[i]->GetUnit() == eCSSUnit_List ||
   1.857 +                          values[i]->GetUnit() == eCSSUnit_ListDep,
   1.858 +                          nsPrintfCString("bad a-duration unit %d",
   1.859 +                                          values[i]->GetUnit()).get());
   1.860 +        lists[i] = values[i]->GetListValue();
   1.861 +      }
   1.862 +
   1.863 +      for (;;) {
   1.864 +        // We must serialize 'animation-name' last in case it has
   1.865 +        // a value that conflicts with one of the other keyword properties.
   1.866 +        NS_ABORT_IF_FALSE(subprops[numProps - 1] ==
   1.867 +                            eCSSProperty_animation_name,
   1.868 +                          "animation-name must be last");
   1.869 +        bool done = false;
   1.870 +        for (uint32_t i = 0;;) {
   1.871 +          lists[i]->mValue.AppendToString(subprops[i], aValue, aSerialization);
   1.872 +          lists[i] = lists[i]->mNext;
   1.873 +          if (!lists[i]) {
   1.874 +            done = true;
   1.875 +          }
   1.876 +          if (++i == numProps) {
   1.877 +            break;
   1.878 +          }
   1.879 +          aValue.Append(char16_t(' '));
   1.880 +        }
   1.881 +        if (done) {
   1.882 +          break;
   1.883 +        }
   1.884 +        aValue.AppendLiteral(", ");
   1.885 +      }
   1.886 +      for (uint32_t i = 0; i < numProps; ++i) {
   1.887 +        if (lists[i]) {
   1.888 +          // Lists not all the same length, can't use shorthand.
   1.889 +          aValue.Truncate();
   1.890 +          break;
   1.891 +        }
   1.892 +      }
   1.893 +      break;
   1.894 +    }
   1.895 +    case eCSSProperty_marker: {
   1.896 +      const nsCSSValue &endValue =
   1.897 +        *data->ValueFor(eCSSProperty_marker_end);
   1.898 +      const nsCSSValue &midValue =
   1.899 +        *data->ValueFor(eCSSProperty_marker_mid);
   1.900 +      const nsCSSValue &startValue =
   1.901 +        *data->ValueFor(eCSSProperty_marker_start);
   1.902 +      if (endValue == midValue && midValue == startValue)
   1.903 +        AppendValueToString(eCSSProperty_marker_end, aValue, aSerialization);
   1.904 +      break;
   1.905 +    }
   1.906 +    case eCSSProperty__moz_columns: {
   1.907 +      // Two values, column-count and column-width, separated by a space.
   1.908 +      const nsCSSProperty* subprops =
   1.909 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.910 +      AppendValueToString(subprops[0], aValue, aSerialization);
   1.911 +      aValue.Append(char16_t(' '));
   1.912 +      AppendValueToString(subprops[1], aValue, aSerialization);
   1.913 +      break;
   1.914 +    }
   1.915 +    case eCSSProperty_flex: {
   1.916 +      // flex-grow, flex-shrink, flex-basis, separated by single space
   1.917 +      const nsCSSProperty* subprops =
   1.918 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.919 +
   1.920 +      AppendValueToString(subprops[0], aValue, aSerialization);
   1.921 +      aValue.Append(char16_t(' '));
   1.922 +      AppendValueToString(subprops[1], aValue, aSerialization);
   1.923 +      aValue.Append(char16_t(' '));
   1.924 +      AppendValueToString(subprops[2], aValue, aSerialization);
   1.925 +      break;
   1.926 +    }
   1.927 +    case eCSSProperty_flex_flow: {
   1.928 +      // flex-direction, flex-wrap, separated by single space
   1.929 +      const nsCSSProperty* subprops =
   1.930 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.931 +      NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
   1.932 +                        "must have exactly two subproperties");
   1.933 +
   1.934 +      AppendValueToString(subprops[0], aValue, aSerialization);
   1.935 +      aValue.Append(char16_t(' '));
   1.936 +      AppendValueToString(subprops[1], aValue, aSerialization);
   1.937 +      break;
   1.938 +    }
   1.939 +    case eCSSProperty_grid_row:
   1.940 +    case eCSSProperty_grid_column: {
   1.941 +      // grid-{row,column}-start, grid-{row,column}-end, separated by a slash
   1.942 +      const nsCSSProperty* subprops =
   1.943 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.944 +      NS_ABORT_IF_FALSE(subprops[2] == eCSSProperty_UNKNOWN,
   1.945 +                        "must have exactly two subproperties");
   1.946 +
   1.947 +      // TODO: should we simplify when possible?
   1.948 +      AppendValueToString(subprops[0], aValue, aSerialization);
   1.949 +      aValue.AppendLiteral(" / ");
   1.950 +      AppendValueToString(subprops[1], aValue, aSerialization);
   1.951 +      break;
   1.952 +    }
   1.953 +    case eCSSProperty_grid_area: {
   1.954 +      const nsCSSProperty* subprops =
   1.955 +        nsCSSProps::SubpropertyEntryFor(aProperty);
   1.956 +      NS_ABORT_IF_FALSE(subprops[4] == eCSSProperty_UNKNOWN,
   1.957 +                        "must have exactly four subproperties");
   1.958 +
   1.959 +      // TODO: should we simplify when possible?
   1.960 +      AppendValueToString(subprops[0], aValue, aSerialization);
   1.961 +      aValue.AppendLiteral(" / ");
   1.962 +      AppendValueToString(subprops[1], aValue, aSerialization);
   1.963 +      aValue.AppendLiteral(" / ");
   1.964 +      AppendValueToString(subprops[2], aValue, aSerialization);
   1.965 +      aValue.AppendLiteral(" / ");
   1.966 +      AppendValueToString(subprops[3], aValue, aSerialization);
   1.967 +      break;
   1.968 +    }
   1.969 +
   1.970 +    // This can express either grid-template-{areas,columns,rows}
   1.971 +    // or grid-auto-{flow,columns,rows}, but not both.
   1.972 +    case eCSSProperty_grid: {
   1.973 +      const nsCSSValue& areasValue =
   1.974 +        *data->ValueFor(eCSSProperty_grid_template_areas);
   1.975 +      const nsCSSValue& columnsValue =
   1.976 +        *data->ValueFor(eCSSProperty_grid_template_columns);
   1.977 +      const nsCSSValue& rowsValue =
   1.978 +        *data->ValueFor(eCSSProperty_grid_template_rows);
   1.979 +
   1.980 +      const nsCSSValue& autoFlowValue =
   1.981 +        *data->ValueFor(eCSSProperty_grid_auto_flow);
   1.982 +      const nsCSSValue& autoColumnsValue =
   1.983 +        *data->ValueFor(eCSSProperty_grid_auto_columns);
   1.984 +      const nsCSSValue& autoRowsValue =
   1.985 +        *data->ValueFor(eCSSProperty_grid_auto_rows);
   1.986 +
   1.987 +      if (areasValue.GetUnit() == eCSSUnit_None &&
   1.988 +          columnsValue.GetUnit() == eCSSUnit_None &&
   1.989 +          rowsValue.GetUnit() == eCSSUnit_None) {
   1.990 +        AppendValueToString(eCSSProperty_grid_auto_flow,
   1.991 +                            aValue, aSerialization);
   1.992 +        aValue.Append(char16_t(' '));
   1.993 +        AppendValueToString(eCSSProperty_grid_auto_columns,
   1.994 +                            aValue, aSerialization);
   1.995 +        aValue.AppendLiteral(" / ");
   1.996 +        AppendValueToString(eCSSProperty_grid_auto_rows,
   1.997 +                            aValue, aSerialization);
   1.998 +        break;
   1.999 +      } else if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated &&
  1.1000 +                   autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_NONE &&
  1.1001 +                   autoColumnsValue.GetUnit() == eCSSUnit_Auto &&
  1.1002 +                   autoRowsValue.GetUnit() == eCSSUnit_Auto)) {
  1.1003 +        // Not serializable, bail.
  1.1004 +        return;
  1.1005 +      }
  1.1006 +      // Fall through to eCSSProperty_grid_template
  1.1007 +    }
  1.1008 +    case eCSSProperty_grid_template: {
  1.1009 +      const nsCSSValue& areasValue =
  1.1010 +        *data->ValueFor(eCSSProperty_grid_template_areas);
  1.1011 +      const nsCSSValue& columnsValue =
  1.1012 +        *data->ValueFor(eCSSProperty_grid_template_columns);
  1.1013 +      const nsCSSValue& rowsValue =
  1.1014 +        *data->ValueFor(eCSSProperty_grid_template_rows);
  1.1015 +      if (areasValue.GetUnit() == eCSSUnit_None) {
  1.1016 +        AppendValueToString(eCSSProperty_grid_template_columns,
  1.1017 +                            aValue, aSerialization);
  1.1018 +        aValue.AppendLiteral(" / ");
  1.1019 +        AppendValueToString(eCSSProperty_grid_template_rows,
  1.1020 +                            aValue, aSerialization);
  1.1021 +        break;
  1.1022 +      }
  1.1023 +      if (columnsValue.GetUnit() == eCSSUnit_List ||
  1.1024 +          columnsValue.GetUnit() == eCSSUnit_ListDep) {
  1.1025 +        const nsCSSValueList* columnsItem = columnsValue.GetListValue();
  1.1026 +        if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
  1.1027 +            columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
  1.1028 +          // We have "grid-template-areas:[something]; grid-template-columns:subgrid"
  1.1029 +          // which isn't a value that the shorthand can express. Bail.
  1.1030 +          return;
  1.1031 +        }
  1.1032 +      }
  1.1033 +      if (rowsValue.GetUnit() != eCSSUnit_List &&
  1.1034 +          rowsValue.GetUnit() != eCSSUnit_ListDep) {
  1.1035 +        // We have "grid-template-areas:[something]; grid-template-rows:none"
  1.1036 +        // which isn't a value that the shorthand can express. Bail.
  1.1037 +        return;
  1.1038 +      }
  1.1039 +      const nsCSSValueList* rowsItem = rowsValue.GetListValue();
  1.1040 +      if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated &&
  1.1041 +          rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
  1.1042 +        // We have "grid-template-areas:[something]; grid-template-rows:subgrid"
  1.1043 +        // which isn't a value that the shorthand can express. Bail.
  1.1044 +        return;
  1.1045 +      }
  1.1046 +      const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas();
  1.1047 +      uint32_t nRowItems = 0;
  1.1048 +      while (rowsItem) {
  1.1049 +        nRowItems++;
  1.1050 +        rowsItem = rowsItem->mNext;
  1.1051 +      }
  1.1052 +      MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items");
  1.1053 +      if ((nRowItems - 1) / 2 != areas->NRows()) {
  1.1054 +        // Not serializable, bail.
  1.1055 +        return;
  1.1056 +      }
  1.1057 +      if (columnsValue.GetUnit() != eCSSUnit_None) {
  1.1058 +        AppendValueToString(eCSSProperty_grid_template_columns,
  1.1059 +                            aValue, aSerialization);
  1.1060 +        aValue.AppendLiteral(" / ");
  1.1061 +      }
  1.1062 +      rowsItem = rowsValue.GetListValue();
  1.1063 +      uint32_t row = 0;
  1.1064 +      for (;;) {
  1.1065 +        bool addSpaceSeparator = true;
  1.1066 +        nsCSSUnit unit = rowsItem->mValue.GetUnit();
  1.1067 +
  1.1068 +        if (unit == eCSSUnit_Null) {
  1.1069 +          // Empty or omitted <line-names>. Serializes to nothing.
  1.1070 +          addSpaceSeparator = false;  // Avoid a double space.
  1.1071 +
  1.1072 +        } else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) {
  1.1073 +          // Non-empty <line-names>
  1.1074 +          aValue.AppendLiteral("(");
  1.1075 +          rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
  1.1076 +                                          aValue, aSerialization);
  1.1077 +          aValue.AppendLiteral(")");
  1.1078 +
  1.1079 +        } else {
  1.1080 +          nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue);
  1.1081 +          aValue.Append(char16_t(' '));
  1.1082 +
  1.1083 +          // <track-size>
  1.1084 +          rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows,
  1.1085 +                                          aValue, aSerialization);
  1.1086 +          if (rowsItem->mNext &&
  1.1087 +              rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null &&
  1.1088 +              !rowsItem->mNext->mNext) {
  1.1089 +            // Break out of the loop early to avoid a trailing space.
  1.1090 +            break;
  1.1091 +          }
  1.1092 +        }
  1.1093 +
  1.1094 +        rowsItem = rowsItem->mNext;
  1.1095 +        if (!rowsItem) {
  1.1096 +          break;
  1.1097 +        }
  1.1098 +
  1.1099 +        if (addSpaceSeparator) {
  1.1100 +          aValue.Append(char16_t(' '));
  1.1101 +        }
  1.1102 +      }
  1.1103 +      break;
  1.1104 +    }
  1.1105 +    case eCSSProperty__moz_transform: {
  1.1106 +      // shorthands that are just aliases with different parsing rules
  1.1107 +      const nsCSSProperty* subprops =
  1.1108 +        nsCSSProps::SubpropertyEntryFor(aProperty);
  1.1109 +      NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
  1.1110 +                        "must have exactly one subproperty");
  1.1111 +      AppendValueToString(subprops[0], aValue, aSerialization);
  1.1112 +      break;
  1.1113 +    }
  1.1114 +    case eCSSProperty_all:
  1.1115 +      // If we got here, then we didn't have all "inherit" or "initial" or
  1.1116 +      // "unset" values for all of the longhand property components of 'all'.
  1.1117 +      // There is no other possible value that is valid for all properties,
  1.1118 +      // so serialize as the empty string.
  1.1119 +      break;
  1.1120 +    default:
  1.1121 +      NS_ABORT_IF_FALSE(false, "no other shorthands");
  1.1122 +      break;
  1.1123 +  }
  1.1124 +}
  1.1125 +
  1.1126 +bool
  1.1127 +Declaration::GetValueIsImportant(const nsAString& aProperty) const
  1.1128 +{
  1.1129 +  nsCSSProperty propID =
  1.1130 +    nsCSSProps::LookupProperty(aProperty, nsCSSProps::eIgnoreEnabledState);
  1.1131 +  if (propID == eCSSProperty_UNKNOWN) {
  1.1132 +    return false;
  1.1133 +  }
  1.1134 +  if (propID == eCSSPropertyExtra_variable) {
  1.1135 +    const nsSubstring& variableName =
  1.1136 +      Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH);
  1.1137 +    return GetVariableValueIsImportant(variableName);
  1.1138 +  }
  1.1139 +  return GetValueIsImportant(propID);
  1.1140 +}
  1.1141 +
  1.1142 +bool
  1.1143 +Declaration::GetValueIsImportant(nsCSSProperty aProperty) const
  1.1144 +{
  1.1145 +  if (!mImportantData)
  1.1146 +    return false;
  1.1147 +
  1.1148 +  // Calling ValueFor is inefficient, but we can assume '!important' is rare.
  1.1149 +
  1.1150 +  if (!nsCSSProps::IsShorthand(aProperty)) {
  1.1151 +    return mImportantData->ValueFor(aProperty) != nullptr;
  1.1152 +  }
  1.1153 +
  1.1154 +  CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
  1.1155 +    if (*p == eCSSProperty__x_system_font) {
  1.1156 +      // The system_font subproperty doesn't count.
  1.1157 +      continue;
  1.1158 +    }
  1.1159 +    if (!mImportantData->ValueFor(*p)) {
  1.1160 +      return false;
  1.1161 +    }
  1.1162 +  }
  1.1163 +  return true;
  1.1164 +}
  1.1165 +
  1.1166 +void
  1.1167 +Declaration::AppendPropertyAndValueToString(nsCSSProperty aProperty,
  1.1168 +                                            nsAutoString& aValue,
  1.1169 +                                            nsAString& aResult) const
  1.1170 +{
  1.1171 +  NS_ABORT_IF_FALSE(0 <= aProperty && aProperty < eCSSProperty_COUNT,
  1.1172 +                    "property enum out of range");
  1.1173 +  NS_ABORT_IF_FALSE((aProperty < eCSSProperty_COUNT_no_shorthands) ==
  1.1174 +                    aValue.IsEmpty(),
  1.1175 +                    "aValue should be given for shorthands but not longhands");
  1.1176 +  AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult);
  1.1177 +  aResult.AppendLiteral(": ");
  1.1178 +  if (aValue.IsEmpty())
  1.1179 +    AppendValueToString(aProperty, aResult, nsCSSValue::eNormalized);
  1.1180 +  else
  1.1181 +    aResult.Append(aValue);
  1.1182 +  if (GetValueIsImportant(aProperty)) {
  1.1183 +    aResult.AppendLiteral(" ! important");
  1.1184 +  }
  1.1185 +  aResult.AppendLiteral("; ");
  1.1186 +}
  1.1187 +
  1.1188 +void
  1.1189 +Declaration::AppendVariableAndValueToString(const nsAString& aName,
  1.1190 +                                            nsAString& aResult) const
  1.1191 +{
  1.1192 +  aResult.AppendLiteral("--");
  1.1193 +  aResult.Append(aName);
  1.1194 +  CSSVariableDeclarations::Type type;
  1.1195 +  nsString value;
  1.1196 +  bool important;
  1.1197 +
  1.1198 +  if (mImportantVariables && mImportantVariables->Get(aName, type, value)) {
  1.1199 +    important = true;
  1.1200 +  } else {
  1.1201 +    MOZ_ASSERT(mVariables);
  1.1202 +    MOZ_ASSERT(mVariables->Has(aName));
  1.1203 +    mVariables->Get(aName, type, value);
  1.1204 +    important = false;
  1.1205 +  }
  1.1206 +
  1.1207 +  switch (type) {
  1.1208 +    case CSSVariableDeclarations::eTokenStream:
  1.1209 +      if (value.IsEmpty()) {
  1.1210 +        aResult.Append(':');
  1.1211 +      } else {
  1.1212 +        aResult.AppendLiteral(": ");
  1.1213 +        aResult.Append(value);
  1.1214 +      }
  1.1215 +      break;
  1.1216 +
  1.1217 +    case CSSVariableDeclarations::eInitial:
  1.1218 +      aResult.AppendLiteral("initial");
  1.1219 +      break;
  1.1220 +
  1.1221 +    case CSSVariableDeclarations::eInherit:
  1.1222 +      aResult.AppendLiteral("inherit");
  1.1223 +      break;
  1.1224 +
  1.1225 +    case CSSVariableDeclarations::eUnset:
  1.1226 +      aResult.AppendLiteral("unset");
  1.1227 +      break;
  1.1228 +
  1.1229 +    default:
  1.1230 +      MOZ_ASSERT(false, "unexpected variable value type");
  1.1231 +  }
  1.1232 +
  1.1233 +  if (important) {
  1.1234 +    aResult.AppendLiteral("! important");
  1.1235 +  }
  1.1236 +  aResult.AppendLiteral("; ");
  1.1237 +}
  1.1238 +
  1.1239 +void
  1.1240 +Declaration::ToString(nsAString& aString) const
  1.1241 +{
  1.1242 +  // Someone cares about this declaration's contents, so don't let it
  1.1243 +  // change from under them.  See e.g. bug 338679.
  1.1244 +  SetImmutable();
  1.1245 +
  1.1246 +  nsCSSCompressedDataBlock *systemFontData =
  1.1247 +    GetValueIsImportant(eCSSProperty__x_system_font) ? mImportantData : mData;
  1.1248 +  const nsCSSValue *systemFont =
  1.1249 +    systemFontData->ValueFor(eCSSProperty__x_system_font);
  1.1250 +  const bool haveSystemFont = systemFont &&
  1.1251 +                                systemFont->GetUnit() != eCSSUnit_None &&
  1.1252 +                                systemFont->GetUnit() != eCSSUnit_Null;
  1.1253 +  bool didSystemFont = false;
  1.1254 +
  1.1255 +  int32_t count = mOrder.Length();
  1.1256 +  int32_t index;
  1.1257 +  nsAutoTArray<nsCSSProperty, 16> shorthandsUsed;
  1.1258 +  for (index = 0; index < count; index++) {
  1.1259 +    nsCSSProperty property = GetPropertyAt(index);
  1.1260 +
  1.1261 +    if (property == eCSSPropertyExtra_variable) {
  1.1262 +      uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT;
  1.1263 +      AppendVariableAndValueToString(mVariableOrder[variableIndex], aString);
  1.1264 +      continue;
  1.1265 +    }
  1.1266 +
  1.1267 +    if (!nsCSSProps::IsEnabled(property)) {
  1.1268 +      continue;
  1.1269 +    }
  1.1270 +    bool doneProperty = false;
  1.1271 +
  1.1272 +    // If we already used this property in a shorthand, skip it.
  1.1273 +    if (shorthandsUsed.Length() > 0) {
  1.1274 +      for (const nsCSSProperty *shorthands =
  1.1275 +             nsCSSProps::ShorthandsContaining(property);
  1.1276 +           *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
  1.1277 +        if (shorthandsUsed.Contains(*shorthands)) {
  1.1278 +          doneProperty = true;
  1.1279 +          break;
  1.1280 +        }
  1.1281 +      }
  1.1282 +      if (doneProperty)
  1.1283 +        continue;
  1.1284 +    }
  1.1285 +
  1.1286 +    // Try to use this property in a shorthand.
  1.1287 +    nsAutoString value;
  1.1288 +    for (const nsCSSProperty *shorthands =
  1.1289 +           nsCSSProps::ShorthandsContaining(property);
  1.1290 +         *shorthands != eCSSProperty_UNKNOWN; ++shorthands) {
  1.1291 +      // ShorthandsContaining returns the shorthands in order from those
  1.1292 +      // that contain the most subproperties to those that contain the
  1.1293 +      // least, which is exactly the order we want to test them.
  1.1294 +      nsCSSProperty shorthand = *shorthands;
  1.1295 +
  1.1296 +      // If GetValue gives us a non-empty string back, we can use that
  1.1297 +      // value; otherwise it's not possible to use this shorthand.
  1.1298 +      GetValue(shorthand, value);
  1.1299 +      if (!value.IsEmpty()) {
  1.1300 +        AppendPropertyAndValueToString(shorthand, value, aString);
  1.1301 +        shorthandsUsed.AppendElement(shorthand);
  1.1302 +        doneProperty = true;
  1.1303 +        break;
  1.1304 +      }
  1.1305 +
  1.1306 +      NS_ABORT_IF_FALSE(shorthand != eCSSProperty_font ||
  1.1307 +                        *(shorthands + 1) == eCSSProperty_UNKNOWN,
  1.1308 +                        "font should always be the only containing shorthand");
  1.1309 +      if (shorthand == eCSSProperty_font) {
  1.1310 +        if (haveSystemFont && !didSystemFont) {
  1.1311 +          // Output the shorthand font declaration that we will
  1.1312 +          // partially override later.  But don't add it to
  1.1313 +          // |shorthandsUsed|, since we will have to override it.
  1.1314 +          systemFont->AppendToString(eCSSProperty__x_system_font, value,
  1.1315 +                                     nsCSSValue::eNormalized);
  1.1316 +          AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
  1.1317 +          value.Truncate();
  1.1318 +          didSystemFont = true;
  1.1319 +        }
  1.1320 +
  1.1321 +        // That we output the system font is enough for this property if:
  1.1322 +        //   (1) it's the hidden system font subproperty (which either
  1.1323 +        //       means we output it or we don't have it), or
  1.1324 +        //   (2) its value is the hidden system font value and it matches
  1.1325 +        //       the hidden system font subproperty in importance, and
  1.1326 +        //       we output the system font subproperty.
  1.1327 +        const nsCSSValue *val = systemFontData->ValueFor(property);
  1.1328 +        if (property == eCSSProperty__x_system_font ||
  1.1329 +            (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) {
  1.1330 +          doneProperty = true;
  1.1331 +        }
  1.1332 +      }
  1.1333 +    }
  1.1334 +    if (doneProperty)
  1.1335 +      continue;
  1.1336 +
  1.1337 +    NS_ABORT_IF_FALSE(value.IsEmpty(), "value should be empty now");
  1.1338 +    AppendPropertyAndValueToString(property, value, aString);
  1.1339 +  }
  1.1340 +  if (! aString.IsEmpty()) {
  1.1341 +    // if the string is not empty, we have trailing whitespace we
  1.1342 +    // should remove
  1.1343 +    aString.Truncate(aString.Length() - 1);
  1.1344 +  }
  1.1345 +}
  1.1346 +
  1.1347 +#ifdef DEBUG
  1.1348 +void
  1.1349 +Declaration::List(FILE* out, int32_t aIndent) const
  1.1350 +{
  1.1351 +  for (int32_t index = aIndent; --index >= 0; ) fputs("  ", out);
  1.1352 +
  1.1353 +  fputs("{ ", out);
  1.1354 +  nsAutoString s;
  1.1355 +  ToString(s);
  1.1356 +  fputs(NS_ConvertUTF16toUTF8(s).get(), out);
  1.1357 +  fputs("}", out);
  1.1358 +}
  1.1359 +#endif
  1.1360 +
  1.1361 +bool
  1.1362 +Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const
  1.1363 +{
  1.1364 +  aReturn.Truncate();
  1.1365 +  if (aIndex < mOrder.Length()) {
  1.1366 +    nsCSSProperty property = GetPropertyAt(aIndex);
  1.1367 +    if (property == eCSSPropertyExtra_variable) {
  1.1368 +      GetCustomPropertyNameAt(aIndex, aReturn);
  1.1369 +      return true;
  1.1370 +    }
  1.1371 +    if (0 <= property) {
  1.1372 +      AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn);
  1.1373 +      return true;
  1.1374 +    }
  1.1375 +  }
  1.1376 +  return false;
  1.1377 +}
  1.1378 +
  1.1379 +void
  1.1380 +Declaration::InitializeEmpty()
  1.1381 +{
  1.1382 +  NS_ABORT_IF_FALSE(!mData && !mImportantData, "already initialized");
  1.1383 +  mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
  1.1384 +}
  1.1385 +
  1.1386 +Declaration*
  1.1387 +Declaration::EnsureMutable()
  1.1388 +{
  1.1389 +  NS_ABORT_IF_FALSE(mData, "should only be called when not expanded");
  1.1390 +  if (!IsMutable()) {
  1.1391 +    return new Declaration(*this);
  1.1392 +  } else {
  1.1393 +    return this;
  1.1394 +  }
  1.1395 +}
  1.1396 +
  1.1397 +size_t
  1.1398 +Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
  1.1399 +{
  1.1400 +  size_t n = aMallocSizeOf(this);
  1.1401 +  n += mOrder.SizeOfExcludingThis(aMallocSizeOf);
  1.1402 +  n += mData          ? mData         ->SizeOfIncludingThis(aMallocSizeOf) : 0;
  1.1403 +  n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0;
  1.1404 +  if (mVariables) {
  1.1405 +    n += mVariables->SizeOfIncludingThis(aMallocSizeOf);
  1.1406 +  }
  1.1407 +  if (mImportantVariables) {
  1.1408 +    n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf);
  1.1409 +  }
  1.1410 +  return n;
  1.1411 +}
  1.1412 +
  1.1413 +bool
  1.1414 +Declaration::HasVariableDeclaration(const nsAString& aName) const
  1.1415 +{
  1.1416 +  return (mVariables && mVariables->Has(aName)) ||
  1.1417 +         (mImportantVariables && mImportantVariables->Has(aName));
  1.1418 +}
  1.1419 +
  1.1420 +void
  1.1421 +Declaration::GetVariableDeclaration(const nsAString& aName,
  1.1422 +                                    nsAString& aValue) const
  1.1423 +{
  1.1424 +  aValue.Truncate();
  1.1425 +
  1.1426 +  CSSVariableDeclarations::Type type;
  1.1427 +  nsString value;
  1.1428 +
  1.1429 +  if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) ||
  1.1430 +      (mVariables && mVariables->Get(aName, type, value))) {
  1.1431 +    switch (type) {
  1.1432 +      case CSSVariableDeclarations::eTokenStream:
  1.1433 +        aValue.Append(value);
  1.1434 +        break;
  1.1435 +
  1.1436 +      case CSSVariableDeclarations::eInitial:
  1.1437 +        aValue.AppendLiteral("initial");
  1.1438 +        break;
  1.1439 +
  1.1440 +      case CSSVariableDeclarations::eInherit:
  1.1441 +        aValue.AppendLiteral("inherit");
  1.1442 +        break;
  1.1443 +
  1.1444 +      case CSSVariableDeclarations::eUnset:
  1.1445 +        aValue.AppendLiteral("unset");
  1.1446 +        break;
  1.1447 +
  1.1448 +      default:
  1.1449 +        MOZ_ASSERT(false, "unexpected variable value type");
  1.1450 +    }
  1.1451 +  }
  1.1452 +}
  1.1453 +
  1.1454 +void
  1.1455 +Declaration::AddVariableDeclaration(const nsAString& aName,
  1.1456 +                                    CSSVariableDeclarations::Type aType,
  1.1457 +                                    const nsString& aValue,
  1.1458 +                                    bool aIsImportant,
  1.1459 +                                    bool aOverrideImportant)
  1.1460 +{
  1.1461 +  MOZ_ASSERT(IsMutable());
  1.1462 +
  1.1463 +  nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
  1.1464 +  if (index == nsTArray<nsString>::NoIndex) {
  1.1465 +    index = mVariableOrder.Length();
  1.1466 +    mVariableOrder.AppendElement(aName);
  1.1467 +  }
  1.1468 +
  1.1469 +  if (!aIsImportant && !aOverrideImportant &&
  1.1470 +      mImportantVariables && mImportantVariables->Has(aName)) {
  1.1471 +    return;
  1.1472 +  }
  1.1473 +
  1.1474 +  CSSVariableDeclarations* variables;
  1.1475 +  if (aIsImportant) {
  1.1476 +    if (mVariables) {
  1.1477 +      mVariables->Remove(aName);
  1.1478 +    }
  1.1479 +    if (!mImportantVariables) {
  1.1480 +      mImportantVariables = new CSSVariableDeclarations;
  1.1481 +    }
  1.1482 +    variables = mImportantVariables;
  1.1483 +  } else {
  1.1484 +    if (mImportantVariables) {
  1.1485 +      mImportantVariables->Remove(aName);
  1.1486 +    }
  1.1487 +    if (!mVariables) {
  1.1488 +      mVariables = new CSSVariableDeclarations;
  1.1489 +    }
  1.1490 +    variables = mVariables;
  1.1491 +  }
  1.1492 +
  1.1493 +  switch (aType) {
  1.1494 +    case CSSVariableDeclarations::eTokenStream:
  1.1495 +      variables->PutTokenStream(aName, aValue);
  1.1496 +      break;
  1.1497 +
  1.1498 +    case CSSVariableDeclarations::eInitial:
  1.1499 +      MOZ_ASSERT(aValue.IsEmpty());
  1.1500 +      variables->PutInitial(aName);
  1.1501 +      break;
  1.1502 +
  1.1503 +    case CSSVariableDeclarations::eInherit:
  1.1504 +      MOZ_ASSERT(aValue.IsEmpty());
  1.1505 +      variables->PutInherit(aName);
  1.1506 +      break;
  1.1507 +
  1.1508 +    case CSSVariableDeclarations::eUnset:
  1.1509 +      MOZ_ASSERT(aValue.IsEmpty());
  1.1510 +      variables->PutUnset(aName);
  1.1511 +      break;
  1.1512 +
  1.1513 +    default:
  1.1514 +      MOZ_ASSERT(false, "unexpected aType value");
  1.1515 +  }
  1.1516 +
  1.1517 +  uint32_t propertyIndex = index + eCSSProperty_COUNT;
  1.1518 +  mOrder.RemoveElement(propertyIndex);
  1.1519 +  mOrder.AppendElement(propertyIndex);
  1.1520 +}
  1.1521 +
  1.1522 +void
  1.1523 +Declaration::RemoveVariableDeclaration(const nsAString& aName)
  1.1524 +{
  1.1525 +  if (mVariables) {
  1.1526 +    mVariables->Remove(aName);
  1.1527 +  }
  1.1528 +  if (mImportantVariables) {
  1.1529 +    mImportantVariables->Remove(aName);
  1.1530 +  }
  1.1531 +  nsTArray<nsString>::index_type index = mVariableOrder.IndexOf(aName);
  1.1532 +  if (index != nsTArray<nsString>::NoIndex) {
  1.1533 +    mOrder.RemoveElement(index + eCSSProperty_COUNT);
  1.1534 +  }
  1.1535 +}
  1.1536 +
  1.1537 +bool
  1.1538 +Declaration::GetVariableValueIsImportant(const nsAString& aName) const
  1.1539 +{
  1.1540 +  return mImportantVariables && mImportantVariables->Has(aName);
  1.1541 +}
  1.1542 +
  1.1543 +} // namespace mozilla::css
  1.1544 +} // namespace mozilla

mercurial