tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SMILCSSValueType.cpp (19853B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* representation of a value for a SMIL-animated CSS property */
      8 
      9 #include "SMILCSSValueType.h"
     10 
     11 #include "mozilla/DeclarationBlock.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/PresShellInlines.h"
     14 #include "mozilla/SMILParserUtils.h"
     15 #include "mozilla/SMILValue.h"
     16 #include "mozilla/ServoBindings.h"
     17 #include "mozilla/ServoCSSParser.h"
     18 #include "mozilla/ServoStyleSet.h"
     19 #include "mozilla/StyleAnimationValue.h"
     20 #include "mozilla/dom/BaseKeyframeTypesBinding.h"
     21 #include "mozilla/dom/Document.h"
     22 #include "mozilla/dom/Element.h"
     23 #include "nsCSSProps.h"
     24 #include "nsCSSValue.h"
     25 #include "nsColor.h"
     26 #include "nsComputedDOMStyle.h"
     27 #include "nsDebug.h"
     28 #include "nsPresContext.h"
     29 #include "nsPresContextInlines.h"
     30 #include "nsString.h"
     31 #include "nsStyleUtil.h"
     32 
     33 using namespace mozilla::dom;
     34 
     35 namespace mozilla {
     36 
     37 using ServoAnimationValues = CopyableAutoTArray<RefPtr<StyleAnimationValue>, 1>;
     38 
     39 /*static*/
     40 SMILCSSValueType SMILCSSValueType::sSingleton;
     41 
     42 struct ValueWrapper {
     43  ValueWrapper(NonCustomCSSPropertyId aPropId, const AnimationValue& aValue)
     44      : mPropId(aPropId) {
     45    MOZ_ASSERT(!aValue.IsNull());
     46    mServoValues.AppendElement(aValue.mServo);
     47  }
     48  ValueWrapper(NonCustomCSSPropertyId aPropId,
     49               const RefPtr<StyleAnimationValue>& aValue)
     50      : mPropId(aPropId), mServoValues{(aValue)} {}
     51  ValueWrapper(NonCustomCSSPropertyId aPropId, ServoAnimationValues&& aValues)
     52      : mPropId(aPropId), mServoValues{std::move(aValues)} {}
     53 
     54  bool operator==(const ValueWrapper& aOther) const {
     55    if (mPropId != aOther.mPropId) {
     56      return false;
     57    }
     58 
     59    MOZ_ASSERT(!mServoValues.IsEmpty());
     60    size_t len = mServoValues.Length();
     61    if (len != aOther.mServoValues.Length()) {
     62      return false;
     63    }
     64    for (size_t i = 0; i < len; i++) {
     65      if (!Servo_AnimationValue_DeepEqual(mServoValues[i],
     66                                          aOther.mServoValues[i])) {
     67        return false;
     68      }
     69    }
     70    return true;
     71  }
     72 
     73  bool operator!=(const ValueWrapper& aOther) const {
     74    return !(*this == aOther);
     75  }
     76 
     77  NonCustomCSSPropertyId mPropId;
     78  ServoAnimationValues mServoValues;
     79 };
     80 
     81 // Helper Methods
     82 // --------------
     83 
     84 // If one argument is null, this method updates it to point to "zero"
     85 // for the other argument's Unit (if applicable; otherwise, we return false).
     86 //
     87 // If neither argument is null, this method simply returns true.
     88 //
     89 // If both arguments are null, this method returns false.
     90 //
     91 // |aZeroValueStorage| should be a reference to a
     92 // RefPtr<StyleAnimationValue>. This is used where we may need to allocate a
     93 // new ServoAnimationValue to represent the appropriate zero value.
     94 //
     95 // Returns true on success, or otherwise.
     96 static bool FinalizeServoAnimationValues(
     97    const RefPtr<StyleAnimationValue>*& aValue1,
     98    const RefPtr<StyleAnimationValue>*& aValue2,
     99    RefPtr<StyleAnimationValue>& aZeroValueStorage) {
    100  if (!aValue1 && !aValue2) {
    101    return false;
    102  }
    103 
    104  // Are we missing either val? (If so, it's an implied 0 in other val's units)
    105 
    106  if (!aValue1) {
    107    aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue2).Consume();
    108    aValue1 = &aZeroValueStorage;
    109  } else if (!aValue2) {
    110    aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue1).Consume();
    111    aValue2 = &aZeroValueStorage;
    112  }
    113  return *aValue1 && *aValue2;
    114 }
    115 
    116 static ValueWrapper* ExtractValueWrapper(SMILValue& aValue) {
    117  return static_cast<ValueWrapper*>(aValue.mU.mPtr);
    118 }
    119 
    120 static const ValueWrapper* ExtractValueWrapper(const SMILValue& aValue) {
    121  return static_cast<const ValueWrapper*>(aValue.mU.mPtr);
    122 }
    123 
    124 // Class methods
    125 // -------------
    126 void SMILCSSValueType::InitValue(SMILValue& aValue) const {
    127  MOZ_ASSERT(aValue.IsNull(), "Unexpected SMIL value type");
    128 
    129  aValue.mU.mPtr = nullptr;
    130  aValue.mType = this;
    131 }
    132 
    133 void SMILCSSValueType::DestroyValue(SMILValue& aValue) const {
    134  MOZ_ASSERT(aValue.mType == this, "Unexpected SMIL value type");
    135  delete static_cast<ValueWrapper*>(aValue.mU.mPtr);
    136  aValue.mType = SMILNullType::Singleton();
    137 }
    138 
    139 nsresult SMILCSSValueType::Assign(SMILValue& aDest,
    140                                  const SMILValue& aSrc) const {
    141  MOZ_ASSERT(aDest.mType == aSrc.mType, "Incompatible SMIL types");
    142  MOZ_ASSERT(aDest.mType == this, "Unexpected SMIL value type");
    143  const ValueWrapper* srcWrapper = ExtractValueWrapper(aSrc);
    144  ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
    145 
    146  if (srcWrapper) {
    147    if (!destWrapper) {
    148      // barely-initialized dest -- need to alloc & copy
    149      aDest.mU.mPtr = new ValueWrapper(*srcWrapper);
    150    } else {
    151      // both already fully-initialized -- just copy straight across
    152      *destWrapper = *srcWrapper;
    153    }
    154  } else if (destWrapper) {
    155    // fully-initialized dest, barely-initialized src -- clear dest
    156    delete destWrapper;
    157    aDest.mU.mPtr = destWrapper = nullptr;
    158  }  // else, both are barely-initialized -- nothing to do.
    159 
    160  return NS_OK;
    161 }
    162 
    163 bool SMILCSSValueType::IsEqual(const SMILValue& aLeft,
    164                               const SMILValue& aRight) const {
    165  MOZ_ASSERT(aLeft.mType == aRight.mType, "Incompatible SMIL types");
    166  MOZ_ASSERT(aLeft.mType == this, "Unexpected SMIL value");
    167  const ValueWrapper* leftWrapper = ExtractValueWrapper(aLeft);
    168  const ValueWrapper* rightWrapper = ExtractValueWrapper(aRight);
    169 
    170  if (leftWrapper) {
    171    if (rightWrapper) {
    172      // Both non-null
    173      NS_WARNING_ASSERTION(leftWrapper != rightWrapper,
    174                           "Two SMILValues with matching ValueWrapper ptr");
    175      return *leftWrapper == *rightWrapper;
    176    }
    177    // Left non-null, right null
    178    return false;
    179  }
    180  if (rightWrapper) {
    181    // Left null, right non-null
    182    return false;
    183  }
    184  // Both null
    185  return true;
    186 }
    187 
    188 static bool AddOrAccumulate(SMILValue& aDest, const SMILValue& aValueToAdd,
    189                            CompositeOperation aCompositeOp, uint64_t aCount) {
    190  MOZ_ASSERT(aValueToAdd.mType == aDest.mType,
    191             "Trying to add mismatching types");
    192  MOZ_ASSERT(aValueToAdd.mType == &SMILCSSValueType::sSingleton,
    193             "Unexpected SMIL value type");
    194  MOZ_ASSERT(aCompositeOp == CompositeOperation::Add ||
    195                 aCompositeOp == CompositeOperation::Accumulate,
    196             "Composite operation should be add or accumulate");
    197  MOZ_ASSERT(aCompositeOp != CompositeOperation::Add || aCount == 1,
    198             "Count should be 1 if composite operation is add");
    199 
    200  ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
    201  const ValueWrapper* valueToAddWrapper = ExtractValueWrapper(aValueToAdd);
    202 
    203  // If both of the values are empty just fail. This can happen in rare cases
    204  // such as when the underlying animation produced an empty value.
    205  //
    206  // Technically, it doesn't matter what we return here since in either case it
    207  // will produce the same result: an empty value.
    208  if (!destWrapper && !valueToAddWrapper) {
    209    return false;
    210  }
    211 
    212  NonCustomCSSPropertyId property =
    213      valueToAddWrapper ? valueToAddWrapper->mPropId : destWrapper->mPropId;
    214  // Special case: font-size-adjust and stroke-dasharray are explicitly
    215  // non-additive (even though StyleAnimationValue *could* support adding them)
    216  if (property == eCSSProperty_font_size_adjust ||
    217      property == eCSSProperty_stroke_dasharray) {
    218    return false;
    219  }
    220  // Skip font shorthand since it includes font-size-adjust.
    221  if (property == eCSSProperty_font) {
    222    return false;
    223  }
    224 
    225  size_t len = valueToAddWrapper ? valueToAddWrapper->mServoValues.Length()
    226                                 : destWrapper->mServoValues.Length();
    227 
    228  MOZ_ASSERT(!valueToAddWrapper || !destWrapper ||
    229                 valueToAddWrapper->mServoValues.Length() ==
    230                     destWrapper->mServoValues.Length(),
    231             "Both of values' length in the wrappers should be the same if "
    232             "both of them exist");
    233 
    234  for (size_t i = 0; i < len; i++) {
    235    const RefPtr<StyleAnimationValue>* valueToAdd =
    236        valueToAddWrapper ? &valueToAddWrapper->mServoValues[i] : nullptr;
    237    const RefPtr<StyleAnimationValue>* destValue =
    238        destWrapper ? &destWrapper->mServoValues[i] : nullptr;
    239    RefPtr<StyleAnimationValue> zeroValueStorage;
    240    if (!FinalizeServoAnimationValues(valueToAdd, destValue,
    241                                      zeroValueStorage)) {
    242      return false;
    243    }
    244 
    245    // FinalizeServoAnimationValues may have updated destValue so we should make
    246    // sure the aDest and aDestWrapper outparams are up-to-date.
    247    if (destWrapper) {
    248      destWrapper->mServoValues[i] = *destValue;
    249    } else {
    250      // aDest may be a barely-initialized "zero" destination.
    251      aDest.mU.mPtr = destWrapper = new ValueWrapper(property, *destValue);
    252      destWrapper->mServoValues.SetLength(len);
    253    }
    254 
    255    RefPtr<StyleAnimationValue> result;
    256    if (aCompositeOp == CompositeOperation::Add) {
    257      result = Servo_AnimationValues_Add(*destValue, *valueToAdd).Consume();
    258    } else {
    259      result = Servo_AnimationValues_Accumulate(*destValue, *valueToAdd, aCount)
    260                   .Consume();
    261    }
    262 
    263    if (!result) {
    264      return false;
    265    }
    266    destWrapper->mServoValues[i] = result;
    267  }
    268 
    269  return true;
    270 }
    271 
    272 nsresult SMILCSSValueType::SandwichAdd(SMILValue& aDest,
    273                                       const SMILValue& aValueToAdd) const {
    274  return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Add, 1)
    275             ? NS_OK
    276             : NS_ERROR_FAILURE;
    277 }
    278 
    279 nsresult SMILCSSValueType::Add(SMILValue& aDest, const SMILValue& aValueToAdd,
    280                               uint32_t aCount) const {
    281  return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Accumulate,
    282                         aCount)
    283             ? NS_OK
    284             : NS_ERROR_FAILURE;
    285 }
    286 
    287 nsresult SMILCSSValueType::ComputeDistance(const SMILValue& aFrom,
    288                                           const SMILValue& aTo,
    289                                           double& aDistance) const {
    290  MOZ_ASSERT(aFrom.mType == aTo.mType, "Trying to compare different types");
    291  MOZ_ASSERT(aFrom.mType == this, "Unexpected source type");
    292 
    293  const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
    294  const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
    295  MOZ_ASSERT(toWrapper, "expecting non-null endpoint");
    296 
    297  size_t len = toWrapper->mServoValues.Length();
    298  MOZ_ASSERT(!fromWrapper || fromWrapper->mServoValues.Length() == len,
    299             "From and to values length should be the same if "
    300             "The start value exists");
    301 
    302  double squareDistance = 0;
    303 
    304  for (size_t i = 0; i < len; i++) {
    305    const RefPtr<StyleAnimationValue>* fromValue =
    306        fromWrapper ? &fromWrapper->mServoValues[i] : nullptr;
    307    const RefPtr<StyleAnimationValue>* toValue = &toWrapper->mServoValues[i];
    308    RefPtr<StyleAnimationValue> zeroValueStorage;
    309    if (!FinalizeServoAnimationValues(fromValue, toValue, zeroValueStorage)) {
    310      return NS_ERROR_FAILURE;
    311    }
    312 
    313    double distance =
    314        Servo_AnimationValues_ComputeDistance(*fromValue, *toValue);
    315    if (distance < 0.0) {
    316      return NS_ERROR_FAILURE;
    317    }
    318 
    319    if (len == 1) {
    320      aDistance = distance;
    321      return NS_OK;
    322    }
    323    squareDistance += distance * distance;
    324  }
    325 
    326  aDistance = sqrt(squareDistance);
    327 
    328  return NS_OK;
    329 }
    330 
    331 nsresult SMILCSSValueType::Interpolate(const SMILValue& aStartVal,
    332                                       const SMILValue& aEndVal,
    333                                       double aUnitDistance,
    334                                       SMILValue& aResult) const {
    335  MOZ_ASSERT(aStartVal.mType == aEndVal.mType,
    336             "Trying to interpolate different types");
    337  MOZ_ASSERT(aStartVal.mType == this, "Unexpected types for interpolation");
    338  MOZ_ASSERT(aResult.mType == this, "Unexpected result type");
    339  MOZ_ASSERT(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
    340             "unit distance value out of bounds");
    341  MOZ_ASSERT(!aResult.mU.mPtr, "expecting barely-initialized outparam");
    342 
    343  const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
    344  const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
    345  MOZ_ASSERT(endWrapper, "expecting non-null endpoint");
    346 
    347  // For discretely-animated properties Servo_AnimationValues_Interpolate will
    348  // perform the discrete animation (i.e. 50% flip) and return a success result.
    349  // However, SMIL has its own special discrete animation behavior that it uses
    350  // when keyTimes are specified, but we won't run that unless that this method
    351  // returns a failure to indicate that the property cannot be smoothly
    352  // interpolated, i.e. that we need to use a discrete calcMode.
    353  //
    354  // For shorthands, Servo_Property_IsDiscreteAnimatable will always return
    355  // false. That's fine since most shorthands (like 'font' and
    356  // 'text-decoration') include non-discrete components. If authors want to
    357  // treat all components as discrete then they should use calcMode="discrete".
    358  if (Servo_Property_IsDiscreteAnimatable(endWrapper->mPropId)) {
    359    return NS_ERROR_FAILURE;
    360  }
    361 
    362  ServoAnimationValues results;
    363  size_t len = endWrapper->mServoValues.Length();
    364  results.SetCapacity(len);
    365  MOZ_ASSERT(!startWrapper || startWrapper->mServoValues.Length() == len,
    366             "Start and end values length should be the same if "
    367             "the start value exists");
    368  for (size_t i = 0; i < len; i++) {
    369    const RefPtr<StyleAnimationValue>* startValue =
    370        startWrapper ? &startWrapper->mServoValues[i] : nullptr;
    371    const RefPtr<StyleAnimationValue>* endValue = &endWrapper->mServoValues[i];
    372    RefPtr<StyleAnimationValue> zeroValueStorage;
    373    if (!FinalizeServoAnimationValues(startValue, endValue, zeroValueStorage)) {
    374      return NS_ERROR_FAILURE;
    375    }
    376 
    377    RefPtr<StyleAnimationValue> result =
    378        Servo_AnimationValues_Interpolate(*startValue, *endValue, aUnitDistance)
    379            .Consume();
    380    if (!result) {
    381      return NS_ERROR_FAILURE;
    382    }
    383    results.AppendElement(result);
    384  }
    385  aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropId, std::move(results));
    386 
    387  return NS_OK;
    388 }
    389 
    390 static ServoAnimationValues ValueFromStringHelper(
    391    NonCustomCSSPropertyId aPropId, Element* aTargetElement,
    392    nsPresContext* aPresContext, const ComputedStyle* aComputedStyle,
    393    const nsAString& aString) {
    394  ServoAnimationValues result;
    395 
    396  Document* doc = aTargetElement->GetComposedDoc();
    397  if (!doc) {
    398    return result;
    399  }
    400 
    401  // Parse property
    402  ServoCSSParser::ParsingEnvironment env =
    403      ServoCSSParser::GetParsingEnvironment(doc);
    404  RefPtr<StyleLockedDeclarationBlock> servoDeclarationBlock =
    405      ServoCSSParser::ParseProperty(
    406          aPropId, NS_ConvertUTF16toUTF8(aString), env,
    407          StyleParsingMode::ALLOW_UNITLESS_LENGTH |
    408              StyleParsingMode::ALLOW_ALL_NUMERIC_VALUES);
    409  if (!servoDeclarationBlock) {
    410    return result;
    411  }
    412 
    413  // Compute value
    414  aPresContext->StyleSet()->GetAnimationValues(
    415      servoDeclarationBlock, aTargetElement, aComputedStyle, result);
    416 
    417  return result;
    418 }
    419 
    420 // static
    421 void SMILCSSValueType::ValueFromString(NonCustomCSSPropertyId aPropId,
    422                                       Element* aTargetElement,
    423                                       const nsAString& aString,
    424                                       SMILValue& aValue,
    425                                       bool* aIsContextSensitive) {
    426  MOZ_ASSERT(aValue.IsNull(), "Outparam should be null-typed");
    427  nsPresContext* presContext =
    428      nsContentUtils::GetContextForContent(aTargetElement);
    429  if (!presContext) {
    430    NS_WARNING("Not parsing animation value; unable to get PresContext");
    431    return;
    432  }
    433 
    434  Document* doc = aTargetElement->GetComposedDoc();
    435  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc, nullptr, 0, 1,
    436                                                aString, nullptr)) {
    437    return;
    438  }
    439 
    440  RefPtr<const ComputedStyle> computedStyle =
    441      nsComputedDOMStyle::GetComputedStyle(aTargetElement);
    442  if (!computedStyle) {
    443    return;
    444  }
    445 
    446  ServoAnimationValues parsedValues = ValueFromStringHelper(
    447      aPropId, aTargetElement, presContext, computedStyle, aString);
    448  if (aIsContextSensitive) {
    449    // FIXME: Bug 1358955 - detect context-sensitive values and set this value
    450    // appropriately.
    451    *aIsContextSensitive = false;
    452  }
    453 
    454  if (!parsedValues.IsEmpty()) {
    455    sSingleton.InitValue(aValue);
    456    aValue.mU.mPtr = new ValueWrapper(aPropId, std::move(parsedValues));
    457  }
    458 }
    459 
    460 // static
    461 SMILValue SMILCSSValueType::ValueFromAnimationValue(
    462    NonCustomCSSPropertyId aPropId, Element* aTargetElement,
    463    const AnimationValue& aValue) {
    464  SMILValue result;
    465 
    466  Document* doc = aTargetElement->GetComposedDoc();
    467  // We'd like to avoid serializing |aValue| if possible, and since the
    468  // string passed to CSPAllowsInlineStyle is only used for reporting violations
    469  // and an intermediate CSS value is not likely to be particularly useful
    470  // in that case, we just use a generic placeholder string instead.
    471  static const nsLiteralString kPlaceholderText = u"[SVG animation of CSS]"_ns;
    472  if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc, nullptr, 0, 1,
    473                                                kPlaceholderText, nullptr)) {
    474    return result;
    475  }
    476 
    477  sSingleton.InitValue(result);
    478  result.mU.mPtr = new ValueWrapper(aPropId, aValue);
    479 
    480  return result;
    481 }
    482 
    483 // static
    484 bool SMILCSSValueType::SetPropertyValues(NonCustomCSSPropertyId aPropertyId,
    485                                         const SMILValue& aValue,
    486                                         DeclarationBlock& aDecl) {
    487  MOZ_ASSERT(aValue.mType == &SMILCSSValueType::sSingleton,
    488             "Unexpected SMIL value type");
    489  const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
    490  if (!wrapper) {
    491    return Servo_DeclarationBlock_RemovePropertyById(aDecl.Raw(), aPropertyId,
    492                                                     {});
    493  }
    494 
    495  bool changed = false;
    496  for (const auto& value : wrapper->mServoValues) {
    497    changed |= Servo_DeclarationBlock_SetPropertyToAnimationValue(aDecl.Raw(),
    498                                                                  value, {});
    499  }
    500 
    501  return changed;
    502 }
    503 
    504 // static
    505 NonCustomCSSPropertyId SMILCSSValueType::PropertyFromValue(
    506    const SMILValue& aValue) {
    507  if (aValue.mType != &SMILCSSValueType::sSingleton) {
    508    return eCSSProperty_UNKNOWN;
    509  }
    510 
    511  const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
    512  if (!wrapper) {
    513    return eCSSProperty_UNKNOWN;
    514  }
    515 
    516  return wrapper->mPropId;
    517 }
    518 
    519 // static
    520 void SMILCSSValueType::FinalizeValue(SMILValue& aValue,
    521                                     const SMILValue& aValueToMatch) {
    522  MOZ_ASSERT(aValue.mType == aValueToMatch.mType, "Incompatible SMIL types");
    523  MOZ_ASSERT(aValue.mType == &SMILCSSValueType::sSingleton,
    524             "Unexpected SMIL value type");
    525 
    526  ValueWrapper* valueWrapper = ExtractValueWrapper(aValue);
    527  // If |aValue| already has a value, there's nothing to do here.
    528  if (valueWrapper) {
    529    return;
    530  }
    531 
    532  const ValueWrapper* valueToMatchWrapper = ExtractValueWrapper(aValueToMatch);
    533  if (!valueToMatchWrapper) {
    534    MOZ_ASSERT_UNREACHABLE("Value to match is empty");
    535    return;
    536  }
    537 
    538  ServoAnimationValues zeroValues;
    539  zeroValues.SetCapacity(valueToMatchWrapper->mServoValues.Length());
    540 
    541  for (const auto& valueToMatch : valueToMatchWrapper->mServoValues) {
    542    RefPtr<StyleAnimationValue> zeroValue =
    543        Servo_AnimationValues_GetZeroValue(valueToMatch).Consume();
    544    if (!zeroValue) {
    545      return;
    546    }
    547    zeroValues.AppendElement(std::move(zeroValue));
    548  }
    549  aValue.mU.mPtr =
    550      new ValueWrapper(valueToMatchWrapper->mPropId, std::move(zeroValues));
    551 }
    552 
    553 }  // namespace mozilla