layout/style/Declaration.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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

mercurial