layout/style/nsCSSValue.cpp
author Xianzhu Wang <wangxianzhu@chromium.org>
Tue, 05 Mar 2019 12:18:01 +0000
changeset 464575 4325ba7b38576495a78289c2a8f4dff391922e66
parent 461895 e0fb4657355d4d8052bff3f1e8baac265b5efef7
child 474446 d7a7edbebd6a08f22d78b5c86b2f2d4573eb77dd
permissions -rw-r--r--
Bug 1529059 [wpt PR 15420] - [BlinkGenPropertyTrees] Initiailize double_sided of synthetic effect, a=testonly Automatic update from web-platform-tests [BlinkGenPropertyTrees] Initiailize double_sided of synthetic effect Previously synthetic effects always had double_sided==false, causing the layer disappear when the backface was facing forward. Bug: 928190 Change-Id: I35534b40346d5c5918bc99c00a4ca6b4e3b68796 Reviewed-on: https://chromium-review.googlesource.com/c/1475815 Reviewed-by: Philip Rogers <pdr@chromium.org> Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org> Cr-Commit-Position: refs/heads/master@{#632764} -- wpt-commits: a89467050deaf1dcbd9140a2f0670b1b85e518ee wpt-pr: 15420

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* representation of simple property values within CSS declarations */

#include "nsCSSValue.h"

#include "mozilla/CORSMode.h"
#include "mozilla/FontPropertyTypes.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/ServoTypes.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/ImageLoader.h"
#include "gfxFontConstants.h"
#include "imgIRequest.h"
#include "imgRequestProxy.h"
#include "mozilla/dom/Document.h"
#include "nsIURIMutator.h"
#include "nsCSSProps.h"
#include "nsNetUtil.h"
#include "nsPresContext.h"
#include "nsStyleUtil.h"
#include "nsDeviceContext.h"
#include "nsContentUtils.h"

using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::dom;

nsCSSValue::nsCSSValue(int32_t aValue, nsCSSUnit aUnit) : mUnit(aUnit) {
  MOZ_ASSERT(aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated,
             "not an int value");
  if (aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated) {
    mValue.mInt = aValue;
  } else {
    mUnit = eCSSUnit_Null;
    mValue.mInt = 0;
  }
}

nsCSSValue::nsCSSValue(float aValue, nsCSSUnit aUnit) : mUnit(aUnit) {
  MOZ_ASSERT(eCSSUnit_Percent <= aUnit, "not a float value");
  if (eCSSUnit_Percent <= aUnit) {
    mValue.mFloat = aValue;
    MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  } else {
    mUnit = eCSSUnit_Null;
    mValue.mInt = 0;
  }
}

nsCSSValue::nsCSSValue(const nsString& aValue, nsCSSUnit aUnit) : mUnit(aUnit) {
  MOZ_ASSERT(UnitHasStringValue(), "not a string value");
  if (UnitHasStringValue()) {
    mValue.mString = BufferFromString(aValue).take();
  } else {
    mUnit = eCSSUnit_Null;
    mValue.mInt = 0;
  }
}

nsCSSValue::nsCSSValue(nsCSSValue::Array* aValue, nsCSSUnit aUnit)
    : mUnit(aUnit) {
  MOZ_ASSERT(UnitHasArrayValue(), "bad unit");
  mValue.mArray = aValue;
  mValue.mArray->AddRef();
}

nsCSSValue::nsCSSValue(const nsCSSValue& aCopy) : mUnit(aCopy.mUnit) {
  if (mUnit <= eCSSUnit_DummyInherit) {
    // nothing to do, but put this important case first
  } else if (eCSSUnit_Percent <= mUnit) {
    mValue.mFloat = aCopy.mValue.mFloat;
    MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  } else if (UnitHasStringValue()) {
    mValue.mString = aCopy.mValue.mString;
    mValue.mString->AddRef();
  } else if (eCSSUnit_Integer <= mUnit && mUnit <= eCSSUnit_Enumerated) {
    mValue.mInt = aCopy.mValue.mInt;
  } else if (UnitHasArrayValue()) {
    mValue.mArray = aCopy.mValue.mArray;
    mValue.mArray->AddRef();
  } else if (eCSSUnit_Pair == mUnit) {
    mValue.mPair = aCopy.mValue.mPair;
    mValue.mPair->AddRef();
  } else if (eCSSUnit_List == mUnit) {
    mValue.mList = aCopy.mValue.mList;
    mValue.mList->AddRef();
  } else if (eCSSUnit_SharedList == mUnit) {
    mValue.mSharedList = aCopy.mValue.mSharedList;
    mValue.mSharedList->AddRef();
  } else if (eCSSUnit_PairList == mUnit) {
    mValue.mPairList = aCopy.mValue.mPairList;
    mValue.mPairList->AddRef();
  } else if (eCSSUnit_AtomIdent == mUnit) {
    mValue.mAtom = aCopy.mValue.mAtom;
    mValue.mAtom->AddRef();
  } else {
    MOZ_ASSERT(false, "unknown unit");
  }
}

nsCSSValue& nsCSSValue::operator=(const nsCSSValue& aCopy) {
  if (this != &aCopy) {
    Reset();
    new (this) nsCSSValue(aCopy);
  }
  return *this;
}

nsCSSValue& nsCSSValue::operator=(nsCSSValue&& aOther) {
  MOZ_ASSERT(this != &aOther, "Self assigment with rvalue reference");

  Reset();
  mUnit = aOther.mUnit;
  mValue = aOther.mValue;
  aOther.mUnit = eCSSUnit_Null;

  return *this;
}

bool nsCSSValue::operator==(const nsCSSValue& aOther) const {
  if (mUnit == aOther.mUnit) {
    if (mUnit <= eCSSUnit_DummyInherit) {
      return true;
    } else if (UnitHasStringValue()) {
      return (NS_strcmp(GetBufferValue(mValue.mString),
                        GetBufferValue(aOther.mValue.mString)) == 0);
    } else if ((eCSSUnit_Integer <= mUnit) && (mUnit <= eCSSUnit_Enumerated)) {
      return mValue.mInt == aOther.mValue.mInt;
    } else if (UnitHasArrayValue()) {
      return *mValue.mArray == *aOther.mValue.mArray;
    } else if (eCSSUnit_Pair == mUnit) {
      return *mValue.mPair == *aOther.mValue.mPair;
    } else if (eCSSUnit_List == mUnit) {
      return nsCSSValueList::Equal(mValue.mList, aOther.mValue.mList);
    } else if (eCSSUnit_SharedList == mUnit) {
      return *mValue.mSharedList == *aOther.mValue.mSharedList;
    } else if (eCSSUnit_PairList == mUnit) {
      return nsCSSValuePairList::Equal(mValue.mPairList,
                                       aOther.mValue.mPairList);
    } else if (eCSSUnit_AtomIdent == mUnit) {
      return mValue.mAtom == aOther.mValue.mAtom;
    } else {
      return mValue.mFloat == aOther.mValue.mFloat;
    }
  }
  return false;
}

double nsCSSValue::GetAngleValueInDegrees() const {
  // Note that this extends the value from float to double.
  return GetAngleValue();
}

double nsCSSValue::GetAngleValueInRadians() const {
  return GetAngleValueInDegrees() * M_PI / 180.0;
}

nscoord nsCSSValue::GetPixelLength() const {
  MOZ_ASSERT(IsPixelLengthUnit(), "not a fixed length unit");

  double scaleFactor;
  switch (mUnit) {
    case eCSSUnit_Pixel:
      return nsPresContext::CSSPixelsToAppUnits(mValue.mFloat);
    case eCSSUnit_Pica:
      scaleFactor = 16.0;
      break;
    case eCSSUnit_Point:
      scaleFactor = 4 / 3.0;
      break;
    case eCSSUnit_Inch:
      scaleFactor = 96.0;
      break;
    case eCSSUnit_Millimeter:
      scaleFactor = 96 / 25.4;
      break;
    case eCSSUnit_Centimeter:
      scaleFactor = 96 / 2.54;
      break;
    case eCSSUnit_Quarter:
      scaleFactor = 96 / 101.6;
      break;
    default:
      NS_ERROR("should never get here");
      return 0;
  }
  return nsPresContext::CSSPixelsToAppUnits(float(mValue.mFloat * scaleFactor));
}

// Assert against resetting non-trivial CSS values from the parallel Servo
// traversal, since the refcounts aren't thread-safe.
// Note that the caller might be an OMTA thread, which is allowed to operate off
// main thread because it owns all of the corresponding nsCSSValues and any that
// they might be sharing members with. Since this can happen concurrently with
// the servo traversal, we have to use a more-precise (but slower) test.
#define DO_RELEASE(member)                                         \
  {                                                                \
    MOZ_ASSERT(!ServoStyleSet::IsCurrentThreadInServoTraversal()); \
    mValue.member->Release();                                      \
  }

void nsCSSValue::DoReset() {
  if (UnitHasStringValue()) {
    mValue.mString->Release();
  } else if (UnitHasArrayValue()) {
    DO_RELEASE(mArray);
  } else if (eCSSUnit_Pair == mUnit) {
    DO_RELEASE(mPair);
  } else if (eCSSUnit_List == mUnit) {
    DO_RELEASE(mList);
  } else if (eCSSUnit_SharedList == mUnit) {
    DO_RELEASE(mSharedList);
  } else if (eCSSUnit_PairList == mUnit) {
    DO_RELEASE(mPairList);
  } else if (eCSSUnit_AtomIdent == mUnit) {
    DO_RELEASE(mAtom);
  }
  mUnit = eCSSUnit_Null;
}

#undef DO_RELEASE

void nsCSSValue::SetIntValue(int32_t aValue, nsCSSUnit aUnit) {
  MOZ_ASSERT(aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated,
             "not an int value");
  Reset();
  if (aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated) {
    mUnit = aUnit;
    mValue.mInt = aValue;
  }
}

void nsCSSValue::SetPercentValue(float aValue) {
  Reset();
  mUnit = eCSSUnit_Percent;
  mValue.mFloat = aValue;
  MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
}

void nsCSSValue::SetFloatValue(float aValue, nsCSSUnit aUnit) {
  MOZ_ASSERT(IsFloatUnit(aUnit), "not a float value");
  Reset();
  if (IsFloatUnit(aUnit)) {
    mUnit = aUnit;
    mValue.mFloat = aValue;
    MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
  }
}

void nsCSSValue::SetStringValue(const nsString& aValue, nsCSSUnit aUnit) {
  Reset();
  mUnit = aUnit;
  MOZ_ASSERT(UnitHasStringValue(), "not a string unit");
  if (UnitHasStringValue()) {
    mValue.mString = BufferFromString(aValue).take();
  } else
    mUnit = eCSSUnit_Null;
}

void nsCSSValue::SetAtomIdentValue(already_AddRefed<nsAtom> aValue) {
  Reset();
  mUnit = eCSSUnit_AtomIdent;
  mValue.mAtom = aValue.take();
}

void nsCSSValue::SetIntegerCoordValue(nscoord aValue) {
  SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aValue),
                eCSSUnit_Pixel);
}

void nsCSSValue::SetArrayValue(nsCSSValue::Array* aValue, nsCSSUnit aUnit) {
  Reset();
  mUnit = aUnit;
  MOZ_ASSERT(UnitHasArrayValue(), "bad unit");
  mValue.mArray = aValue;
  mValue.mArray->AddRef();
}

void nsCSSValue::SetPairValue(const nsCSSValuePair* aValue) {
  // pairs should not be used for null/inherit/initial values
  MOZ_ASSERT(aValue && aValue->mXValue.GetUnit() != eCSSUnit_Null &&
                 aValue->mYValue.GetUnit() != eCSSUnit_Null &&
                 aValue->mXValue.GetUnit() != eCSSUnit_Inherit &&
                 aValue->mYValue.GetUnit() != eCSSUnit_Inherit &&
                 aValue->mXValue.GetUnit() != eCSSUnit_Initial &&
                 aValue->mYValue.GetUnit() != eCSSUnit_Initial &&
                 aValue->mXValue.GetUnit() != eCSSUnit_Unset &&
                 aValue->mYValue.GetUnit() != eCSSUnit_Unset,
             "missing or inappropriate pair value");
  Reset();
  mUnit = eCSSUnit_Pair;
  mValue.mPair = new nsCSSValuePair_heap(aValue->mXValue, aValue->mYValue);
  mValue.mPair->AddRef();
}

void nsCSSValue::SetPairValue(const nsCSSValue& xValue,
                              const nsCSSValue& yValue) {
  MOZ_ASSERT(xValue.GetUnit() != eCSSUnit_Null &&
                 yValue.GetUnit() != eCSSUnit_Null &&
                 xValue.GetUnit() != eCSSUnit_Inherit &&
                 yValue.GetUnit() != eCSSUnit_Inherit &&
                 xValue.GetUnit() != eCSSUnit_Initial &&
                 yValue.GetUnit() != eCSSUnit_Initial &&
                 xValue.GetUnit() != eCSSUnit_Unset &&
                 yValue.GetUnit() != eCSSUnit_Unset,
             "inappropriate pair value");
  Reset();
  mUnit = eCSSUnit_Pair;
  mValue.mPair = new nsCSSValuePair_heap(xValue, yValue);
  mValue.mPair->AddRef();
}

nsCSSValueList* nsCSSValue::SetListValue() {
  Reset();
  mUnit = eCSSUnit_List;
  mValue.mList = new nsCSSValueList_heap;
  mValue.mList->AddRef();
  return mValue.mList;
}

void nsCSSValue::SetSharedListValue(nsCSSValueSharedList* aList) {
  Reset();
  mUnit = eCSSUnit_SharedList;
  mValue.mSharedList = aList;
  mValue.mSharedList->AddRef();
}

nsCSSValuePairList* nsCSSValue::SetPairListValue() {
  Reset();
  mUnit = eCSSUnit_PairList;
  mValue.mPairList = new nsCSSValuePairList_heap;
  mValue.mPairList->AddRef();
  return mValue.mPairList;
}

void nsCSSValue::SetNoneValue() {
  Reset();
  mUnit = eCSSUnit_None;
}

void nsCSSValue::SetCalcValue(const nsStyleCoord::CalcValue& aCalc) {
  RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
  if (!aCalc.mHasPercent) {
    arr->Item(0).SetIntegerCoordValue(aCalc.mLength);
  } else {
    nsCSSValue::Array* arr2 = nsCSSValue::Array::Create(2);
    arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
    arr2->Item(0).SetIntegerCoordValue(aCalc.mLength);
    arr2->Item(1).SetPercentValue(aCalc.mPercent);
  }

  SetArrayValue(arr, eCSSUnit_Calc);
}

nsStyleCoord::CalcValue nsCSSValue::GetCalcValue() const {
  MOZ_ASSERT(mUnit == eCSSUnit_Calc, "The unit should be eCSSUnit_Calc");

  const nsCSSValue::Array* array = GetArrayValue();
  MOZ_ASSERT(array->Count() == 1, "There should be a 1-length array");

  const nsCSSValue& rootValue = array->Item(0);

  nsStyleCoord::CalcValue result;

  if (rootValue.GetUnit() == eCSSUnit_Pixel) {
    result.mLength = rootValue.GetPixelLength();
    result.mPercent = 0.0f;
    result.mHasPercent = false;
  } else {
    MOZ_ASSERT(rootValue.GetUnit() == eCSSUnit_Calc_Plus,
               "Calc unit should be eCSSUnit_Calc_Plus");

    const nsCSSValue::Array* calcPlusArray = rootValue.GetArrayValue();
    MOZ_ASSERT(calcPlusArray->Count() == 2,
               "eCSSUnit_Calc_Plus should have a 2-length array");

    const nsCSSValue& length = calcPlusArray->Item(0);
    const nsCSSValue& percent = calcPlusArray->Item(1);
    MOZ_ASSERT(length.GetUnit() == eCSSUnit_Pixel,
               "The first value should be eCSSUnit_Pixel");
    MOZ_ASSERT(percent.GetUnit() == eCSSUnit_Percent,
               "The first value should be eCSSUnit_Percent");
    result.mLength = length.GetPixelLength();
    result.mPercent = percent.GetPercentValue();
    result.mHasPercent = true;
  }

  return result;
}

// static
already_AddRefed<nsStringBuffer> nsCSSValue::BufferFromString(
    const nsString& aValue) {
  RefPtr<nsStringBuffer> buffer = nsStringBuffer::FromString(aValue);
  if (buffer) {
    return buffer.forget();
  }

  nsString::size_type length = aValue.Length();

  // NOTE: Alloc prouduces a new, already-addref'd (refcnt = 1) buffer.
  // NOTE: String buffer allocation is currently fallible.
  size_t sz = (length + 1) * sizeof(char16_t);
  buffer = nsStringBuffer::Alloc(sz);
  if (MOZ_UNLIKELY(!buffer)) {
    NS_ABORT_OOM(sz);
  }

  char16_t* data = static_cast<char16_t*>(buffer->Data());
  nsCharTraits<char16_t>::copy(data, aValue.get(), length);
  // Null-terminate.
  data[length] = 0;
  return buffer.forget();
}

size_t nsCSSValue::SizeOfExcludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  size_t n = 0;

  switch (GetUnit()) {
    // No value: nothing extra to measure.
    case eCSSUnit_Null:
    case eCSSUnit_Auto:
    case eCSSUnit_Inherit:
    case eCSSUnit_Initial:
    case eCSSUnit_Unset:
    case eCSSUnit_None:
    case eCSSUnit_Normal:
    case eCSSUnit_System_Font:
    case eCSSUnit_All:
    case eCSSUnit_Dummy:
    case eCSSUnit_DummyInherit:
      break;

    // String
    case eCSSUnit_String:
    case eCSSUnit_Ident:
    case eCSSUnit_Attr:
    case eCSSUnit_Element:
      n += mValue.mString->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
      break;

    // Array
    case eCSSUnit_Array:
    case eCSSUnit_Counter:
    case eCSSUnit_Counters:
    case eCSSUnit_Cubic_Bezier:
    case eCSSUnit_Steps:
    case eCSSUnit_Symbols:
    case eCSSUnit_Function:
    case eCSSUnit_Calc:
    case eCSSUnit_Calc_Plus:
      break;

    // Pair
    case eCSSUnit_Pair:
      n += mValue.mPair->SizeOfIncludingThis(aMallocSizeOf);
      break;

    // List
    case eCSSUnit_List:
      n += mValue.mList->SizeOfIncludingThis(aMallocSizeOf);
      break;

    // SharedList
    case eCSSUnit_SharedList:
      // Makes more sense not to measure, since it most cases the list
      // will be shared.
      break;

    // PairList
    case eCSSUnit_PairList:
      n += mValue.mPairList->SizeOfIncludingThis(aMallocSizeOf);
      break;

    // Atom is always shared, and thus should not be counted.
    case eCSSUnit_AtomIdent:
      break;

    // Int: nothing extra to measure.
    case eCSSUnit_Integer:
    case eCSSUnit_Enumerated:
      break;

    // Float: nothing extra to measure.
    case eCSSUnit_Percent:
    case eCSSUnit_Number:
    case eCSSUnit_ViewportWidth:
    case eCSSUnit_ViewportHeight:
    case eCSSUnit_ViewportMin:
    case eCSSUnit_ViewportMax:
    case eCSSUnit_EM:
    case eCSSUnit_XHeight:
    case eCSSUnit_Char:
    case eCSSUnit_RootEM:
    case eCSSUnit_Point:
    case eCSSUnit_Inch:
    case eCSSUnit_Millimeter:
    case eCSSUnit_Centimeter:
    case eCSSUnit_Pica:
    case eCSSUnit_Pixel:
    case eCSSUnit_Quarter:
    case eCSSUnit_Degree:
    case eCSSUnit_Hertz:
    case eCSSUnit_Kilohertz:
    case eCSSUnit_Seconds:
    case eCSSUnit_Milliseconds:
    case eCSSUnit_FlexFraction:
      break;

    default:
      MOZ_ASSERT(false, "bad nsCSSUnit");
      break;
  }

  return n;
}

// --- nsCSSValueList -----------------

nsCSSValueList::~nsCSSValueList() {
  MOZ_COUNT_DTOR(nsCSSValueList);
  NS_CSS_DELETE_LIST_MEMBER(nsCSSValueList, this, mNext);
}

nsCSSValueList* nsCSSValueList::Clone() const {
  nsCSSValueList* result = new nsCSSValueList(*this);
  nsCSSValueList* dest = result;
  const nsCSSValueList* src = this->mNext;
  while (src) {
    dest->mNext = new nsCSSValueList(*src);
    dest = dest->mNext;
    src = src->mNext;
  }

  MOZ_ASSERT(result, "shouldn't return null; supposed to be infallible");
  return result;
}

void nsCSSValueList::CloneInto(nsCSSValueList* aList) const {
  NS_ASSERTION(!aList->mNext, "Must be an empty list!");
  aList->mValue = mValue;
  aList->mNext = mNext ? mNext->Clone() : nullptr;
}

/* static */
bool nsCSSValueList::Equal(const nsCSSValueList* aList1,
                           const nsCSSValueList* aList2) {
  if (aList1 == aList2) {
    return true;
  }

  const nsCSSValueList *p1 = aList1, *p2 = aList2;
  for (; p1 && p2; p1 = p1->mNext, p2 = p2->mNext) {
    if (p1->mValue != p2->mValue) return false;
  }
  return !p1 && !p2;  // true if same length, false otherwise
}

size_t nsCSSValueList::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  size_t n = 0;
  const nsCSSValueList* v = this;
  while (v) {
    n += aMallocSizeOf(v);
    n += v->mValue.SizeOfExcludingThis(aMallocSizeOf);
    v = v->mNext;
  }
  return n;
}

size_t nsCSSValueList_heap::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  // Only measure it if it's unshared, to avoid double-counting.
  size_t n = 0;
  if (mRefCnt <= 1) {
    n += aMallocSizeOf(this);
    n += mValue.SizeOfExcludingThis(aMallocSizeOf);
    n += mNext ? mNext->SizeOfIncludingThis(aMallocSizeOf) : 0;
  }
  return n;
}

// --- nsCSSValueSharedList -----------------

nsCSSValueSharedList::~nsCSSValueSharedList() {
  if (mHead) {
    NS_CSS_DELETE_LIST_MEMBER(nsCSSValueList, mHead, mNext);
    delete mHead;
  }
}

bool nsCSSValueSharedList::operator==(
    const nsCSSValueSharedList& aOther) const {
  return nsCSSValueList::Equal(mHead, aOther.mHead);
}

size_t nsCSSValueSharedList::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  // Only measure it if it's unshared, to avoid double-counting.
  size_t n = 0;
  if (mRefCnt <= 1) {
    n += aMallocSizeOf(this);
    n += mHead->SizeOfIncludingThis(aMallocSizeOf);
  }
  return n;
}

// --- nsCSSValuePair -----------------

size_t nsCSSValuePair::SizeOfExcludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  size_t n = 0;
  n += mXValue.SizeOfExcludingThis(aMallocSizeOf);
  n += mYValue.SizeOfExcludingThis(aMallocSizeOf);
  return n;
}

size_t nsCSSValuePair_heap::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  // Only measure it if it's unshared, to avoid double-counting.
  size_t n = 0;
  if (mRefCnt <= 1) {
    n += aMallocSizeOf(this);
    n += mXValue.SizeOfExcludingThis(aMallocSizeOf);
    n += mYValue.SizeOfExcludingThis(aMallocSizeOf);
  }
  return n;
}

// --- nsCSSValuePairList -----------------

nsCSSValuePairList::~nsCSSValuePairList() {
  MOZ_COUNT_DTOR(nsCSSValuePairList);
  NS_CSS_DELETE_LIST_MEMBER(nsCSSValuePairList, this, mNext);
}

nsCSSValuePairList* nsCSSValuePairList::Clone() const {
  nsCSSValuePairList* result = new nsCSSValuePairList(*this);
  nsCSSValuePairList* dest = result;
  const nsCSSValuePairList* src = this->mNext;
  while (src) {
    dest->mNext = new nsCSSValuePairList(*src);
    dest = dest->mNext;
    src = src->mNext;
  }

  MOZ_ASSERT(result, "shouldn't return null; supposed to be infallible");
  return result;
}

/* static */
bool nsCSSValuePairList::Equal(const nsCSSValuePairList* aList1,
                               const nsCSSValuePairList* aList2) {
  if (aList1 == aList2) {
    return true;
  }

  const nsCSSValuePairList *p1 = aList1, *p2 = aList2;
  for (; p1 && p2; p1 = p1->mNext, p2 = p2->mNext) {
    if (p1->mXValue != p2->mXValue || p1->mYValue != p2->mYValue) return false;
  }
  return !p1 && !p2;  // true if same length, false otherwise
}

size_t nsCSSValuePairList::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  size_t n = 0;
  const nsCSSValuePairList* v = this;
  while (v) {
    n += aMallocSizeOf(v);
    n += v->mXValue.SizeOfExcludingThis(aMallocSizeOf);
    n += v->mYValue.SizeOfExcludingThis(aMallocSizeOf);
    v = v->mNext;
  }
  return n;
}

size_t nsCSSValuePairList_heap::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  // Only measure it if it's unshared, to avoid double-counting.
  size_t n = 0;
  if (mRefCnt <= 1) {
    n += aMallocSizeOf(this);
    n += mXValue.SizeOfExcludingThis(aMallocSizeOf);
    n += mYValue.SizeOfExcludingThis(aMallocSizeOf);
    n += mNext ? mNext->SizeOfIncludingThis(aMallocSizeOf) : 0;
  }
  return n;
}

size_t nsCSSValue::Array::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  size_t n = aMallocSizeOf(this);
  for (size_t i = 0; i < mCount; i++) {
    n += mArray[i].SizeOfExcludingThis(aMallocSizeOf);
  }
  return n;
}

css::URLValue::~URLValue() {
  if (mLoadID != 0) {
    ImageLoader::DeregisterCSSImageFromAllLoaders(this);
  }
}

bool css::URLValue::Equals(const URLValue& aOther) const {
  MOZ_ASSERT(NS_IsMainThread());

  bool eq;
  const URLExtraData* self = ExtraData();
  const URLExtraData* other = aOther.ExtraData();
  return GetString() == aOther.GetString() &&
         (GetURI() == aOther.GetURI() ||  // handles null == null
          (mURI && aOther.mURI &&
           NS_SUCCEEDED(mURI->Equals(aOther.mURI, &eq)) && eq)) &&
         (self->BaseURI() == other->BaseURI() ||
          (NS_SUCCEEDED(self->BaseURI()->Equals(other->BaseURI(), &eq)) &&
           eq)) &&
         self->Principal()->Equals(other->Principal()) &&
         IsLocalRef() == aOther.IsLocalRef();
}

bool css::URLValue::DefinitelyEqualURIs(const URLValue& aOther) const {
  if (ExtraData()->BaseURI() != aOther.ExtraData()->BaseURI()) {
    return false;
  }
  return GetString() == aOther.GetString();
}

bool css::URLValue::DefinitelyEqualURIsAndPrincipal(
    const URLValue& aOther) const {
  return ExtraData()->Principal() == aOther.ExtraData()->Principal() &&
         DefinitelyEqualURIs(aOther);
}

nsDependentCSubstring css::URLValue::GetString() const {
  const uint8_t* chars;
  uint32_t len;
  Servo_CssUrlData_GetSerialization(mCssUrl, &chars, &len);
  return nsDependentCSubstring(reinterpret_cast<const char*>(chars), len);
}

nsIURI* css::URLValue::GetURI() const {
  MOZ_ASSERT(NS_IsMainThread());

  if (!mURIResolved) {
    MOZ_ASSERT(!mURI);
    nsCOMPtr<nsIURI> newURI;
    NS_NewURI(getter_AddRefs(newURI), GetString(), nullptr,
              ExtraData()->BaseURI());
    mURI = newURI.forget();
    mURIResolved = true;
  }

  return mURI;
}

bool css::URLValue::HasRef() const {
  if (IsLocalRef()) {
    return true;
  }

  if (nsIURI* uri = GetURI()) {
    nsAutoCString ref;
    nsresult rv = uri->GetRef(ref);
    return NS_SUCCEEDED(rv) && !ref.IsEmpty();
  }

  return false;
}

already_AddRefed<nsIURI> css::URLValue::ResolveLocalRef(nsIURI* aURI) const {
  nsCOMPtr<nsIURI> result = GetURI();

  if (result && IsLocalRef()) {
    nsCString ref;
    mURI->GetRef(ref);

    nsresult rv = NS_MutateURI(aURI).SetRef(ref).Finalize(result);

    if (NS_FAILED(rv)) {
      // If setting the ref failed, just return the original URI.
      result = aURI;
    }
  }

  return result.forget();
}

already_AddRefed<nsIURI> css::URLValue::ResolveLocalRef(
    nsIContent* aContent) const {
  nsCOMPtr<nsIURI> url = aContent->GetBaseURI();
  return ResolveLocalRef(url);
}

void css::URLValue::GetSourceString(nsString& aRef) const {
  nsIURI* uri = GetURI();
  if (!uri) {
    aRef.Truncate();
    return;
  }

  nsCString cref;
  if (IsLocalRef()) {
    // XXXheycam It's possible we can just return mString in this case, since
    // it should be the "#fragment" string the URLValue was created with.
    uri->GetRef(cref);
    cref.Insert('#', 0);
  } else {
    // It's not entirely clear how to best handle failure here. Ensuring the
    // string is empty seems safest.
    nsresult rv = uri->GetSpec(cref);
    if (NS_FAILED(rv)) {
      cref.Truncate();
    }
  }

  aRef = NS_ConvertUTF8toUTF16(cref);
}

size_t css::URLValue::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  // Measurement of the following members may be added later if DMD finds it
  // is worthwhile:
  // - mURI
  // - mString

  // Only measure it if it's unshared, to avoid double-counting.
  size_t n = 0;
  if (mRefCnt <= 1) {
    n += aMallocSizeOf(this);
  }
  return n;
}

imgRequestProxy* css::URLValue::LoadImage(Document* aDocument) {
  MOZ_ASSERT(NS_IsMainThread());

  static uint64_t sNextLoadID = 1;

  if (mLoadID == 0) {
    mLoadID = sNextLoadID++;
  }

  // NB: If aDocument is not the original document, we may not be able to load
  // images from aDocument.  Instead we do the image load from the original doc
  // and clone it to aDocument.
  Document* loadingDoc = aDocument->GetOriginalDocument();
  if (!loadingDoc) {
    loadingDoc = aDocument;
  }

  // Kick off the load in the loading document.
  ImageLoader::LoadImage(this, loadingDoc);

  // Register the image in the document that's using it.
  return aDocument->StyleImageLoader()->RegisterCSSImage(this);
}

size_t mozilla::css::GridTemplateAreasValue::SizeOfIncludingThis(
    mozilla::MallocSizeOf aMallocSizeOf) const {
  // Only measure it if it's unshared, to avoid double-counting.
  size_t n = 0;
  if (mRefCnt <= 1) {
    n += aMallocSizeOf(this);
    n += mNamedAreas.ShallowSizeOfExcludingThis(aMallocSizeOf);
    n += mTemplates.ShallowSizeOfExcludingThis(aMallocSizeOf);
  }
  return n;
}