/* -*- 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 CSS style rules (selectors+declaration), CSS
 * selectors, and DOM objects for style rules, selectors, and
 * declarations
 */

#include "mozilla/css/StyleRule.h"

#include "mozilla/DeclarationBlockInlines.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/GroupRule.h"
#include "mozilla/css/Declaration.h"
#include "mozilla/dom/CSSStyleRuleBinding.h"
#include "nsIDocument.h"
#include "nsAtom.h"
#include "nsString.h"
#include "nsStyleUtil.h"
#include "nsDOMCSSDeclaration.h"
#include "nsNameSpaceManager.h"
#include "nsXMLNameSpaceMap.h"
#include "nsCSSPseudoClasses.h"
#include "nsCSSAnonBoxes.h"
#include "nsTArray.h"
#include "nsContentUtils.h"
#include "nsError.h"
#include "mozAutoDocUpdate.h"
#include "nsRuleProcessorData.h"

using namespace mozilla;

#define NS_IF_CLONE(member_)                                                  \
  PR_BEGIN_MACRO                                                              \
    if (member_) {                                                            \
      result->member_ = member_->Clone();                                     \
      if (!result->member_) {                                                 \
        delete result;                                                        \
        return nullptr;                                                        \
      }                                                                       \
    }                                                                         \
  PR_END_MACRO

#define NS_IF_DELETE(ptr)                                                     \
  PR_BEGIN_MACRO                                                              \
    delete ptr;                                                               \
    ptr = nullptr;                                                             \
  PR_END_MACRO

/* ************************************************************************** */

nsAtomList::nsAtomList(nsAtom* aAtom)
  : mAtom(aAtom),
    mNext(nullptr)
{
  MOZ_COUNT_CTOR(nsAtomList);
}

nsAtomList::nsAtomList(const nsString& aAtomValue)
  : mAtom(nullptr),
    mNext(nullptr)
{
  MOZ_COUNT_CTOR(nsAtomList);
  mAtom = NS_Atomize(aAtomValue);
}

nsAtomList*
nsAtomList::Clone(bool aDeep) const
{
  nsAtomList *result = new nsAtomList(mAtom);
  if (!result)
    return nullptr;

  if (aDeep)
    NS_CSS_CLONE_LIST_MEMBER(nsAtomList, this, mNext, result, (false));
  return result;
}

size_t
nsAtomList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
  size_t n = 0;
  const nsAtomList* a = this;
  while (a) {
    n += aMallocSizeOf(a);

    // The following members aren't measured:
    // - a->mAtom, because it may be shared

    a = a->mNext;
  }
  return n;
}

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

nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType)
  : mType(aType),
    mNext(nullptr)
{
  NS_ASSERTION(!nsCSSPseudoClasses::HasStringArg(aType) &&
               !nsCSSPseudoClasses::HasNthPairArg(aType),
               "unexpected pseudo-class");
  MOZ_COUNT_CTOR(nsPseudoClassList);
  u.mMemory = nullptr;
}

nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
                                     const char16_t* aString)
  : mType(aType),
    mNext(nullptr)
{
  NS_ASSERTION(nsCSSPseudoClasses::HasStringArg(aType),
               "unexpected pseudo-class");
  NS_ASSERTION(aString, "string expected");
  MOZ_COUNT_CTOR(nsPseudoClassList);
  u.mString = NS_strdup(aString);
}

nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
                                     const int32_t* aIntPair)
  : mType(aType),
    mNext(nullptr)
{
  NS_ASSERTION(nsCSSPseudoClasses::HasNthPairArg(aType),
               "unexpected pseudo-class");
  NS_ASSERTION(aIntPair, "integer pair expected");
  MOZ_COUNT_CTOR(nsPseudoClassList);
  u.mNumbers =
    static_cast<int32_t*>(nsMemory::Clone(aIntPair, sizeof(int32_t) * 2));
}

// adopts aSelectorList
nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
                                     nsCSSSelectorList* aSelectorList)
  : mType(aType),
    mNext(nullptr)
{
  NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType),
               "unexpected pseudo-class");
  NS_ASSERTION(aSelectorList, "selector list expected");
  MOZ_COUNT_CTOR(nsPseudoClassList);
  u.mSelectors = aSelectorList;
}

nsPseudoClassList*
nsPseudoClassList::Clone(bool aDeep) const
{
  nsPseudoClassList *result;
  if (!u.mMemory) {
    result = new nsPseudoClassList(mType);
  } else if (nsCSSPseudoClasses::HasStringArg(mType)) {
    result = new nsPseudoClassList(mType, u.mString);
  } else if (nsCSSPseudoClasses::HasNthPairArg(mType)) {
    result = new nsPseudoClassList(mType, u.mNumbers);
  } else {
    NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(mType),
                 "unexpected pseudo-class");
    // This constructor adopts its selector list argument.
    result = new nsPseudoClassList(mType, u.mSelectors->Clone());
  }

  if (aDeep)
    NS_CSS_CLONE_LIST_MEMBER(nsPseudoClassList, this, mNext, result,
                             (false));

  return result;
}

size_t
nsPseudoClassList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
  size_t n = 0;
  const nsPseudoClassList* p = this;
  while (p) {
    n += aMallocSizeOf(p);
    if (!p->u.mMemory) {
      // do nothing

    } else if (nsCSSPseudoClasses::HasStringArg(p->mType)) {
      n += aMallocSizeOf(p->u.mString);

    } else if (nsCSSPseudoClasses::HasNthPairArg(p->mType)) {
      n += aMallocSizeOf(p->u.mNumbers);

    } else {
      NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType),
                   "unexpected pseudo-class");
      n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf);
    }
    p = p->mNext;
  }
  return n;
}

nsPseudoClassList::~nsPseudoClassList(void)
{
  MOZ_COUNT_DTOR(nsPseudoClassList);
  if (nsCSSPseudoClasses::HasSelectorListArg(mType)) {
    delete u.mSelectors;
  } else if (u.mMemory) {
    free(u.mMemory);
  }
  NS_CSS_DELETE_LIST_MEMBER(nsPseudoClassList, this, mNext);
}

nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr)
  : mValue(),
    mNext(nullptr),
    mLowercaseAttr(nullptr),
    mCasedAttr(nullptr),
    mNameSpace(aNameSpace),
    mFunction(NS_ATTR_FUNC_SET),
    // mValueCaseSensitivity doesn't matter; we have no value.
    mValueCaseSensitivity(ValueCaseSensitivity::CaseSensitive)
{
  MOZ_COUNT_CTOR(nsAttrSelector);

  nsAutoString lowercase;
  nsContentUtils::ASCIIToLower(aAttr, lowercase);

  mCasedAttr = NS_Atomize(aAttr);
  mLowercaseAttr = NS_Atomize(lowercase);
}

nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction,
                               const nsString& aValue,
                               ValueCaseSensitivity aValueCaseSensitivity)
  : mValue(aValue),
    mNext(nullptr),
    mLowercaseAttr(nullptr),
    mCasedAttr(nullptr),
    mNameSpace(aNameSpace),
    mFunction(aFunction),
    mValueCaseSensitivity(aValueCaseSensitivity)
{
  MOZ_COUNT_CTOR(nsAttrSelector);

  nsAutoString lowercase;
  nsContentUtils::ASCIIToLower(aAttr, lowercase);

  mCasedAttr = NS_Atomize(aAttr);
  mLowercaseAttr = NS_Atomize(lowercase);
}

nsAttrSelector::nsAttrSelector(int32_t aNameSpace,  nsAtom* aLowercaseAttr,
                               nsAtom* aCasedAttr, uint8_t aFunction,
                               const nsString& aValue,
                               ValueCaseSensitivity aValueCaseSensitivity)
  : mValue(aValue),
    mNext(nullptr),
    mLowercaseAttr(aLowercaseAttr),
    mCasedAttr(aCasedAttr),
    mNameSpace(aNameSpace),
    mFunction(aFunction),
    mValueCaseSensitivity(aValueCaseSensitivity)
{
  MOZ_COUNT_CTOR(nsAttrSelector);
}

nsAttrSelector*
nsAttrSelector::Clone(bool aDeep) const
{
  nsAttrSelector *result =
    new nsAttrSelector(mNameSpace, mLowercaseAttr, mCasedAttr,
                       mFunction, mValue, mValueCaseSensitivity);

  if (aDeep)
    NS_CSS_CLONE_LIST_MEMBER(nsAttrSelector, this, mNext, result, (false));

  return result;
}

nsAttrSelector::~nsAttrSelector(void)
{
  MOZ_COUNT_DTOR(nsAttrSelector);

  NS_CSS_DELETE_LIST_MEMBER(nsAttrSelector, this, mNext);
}

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

// -- nsCSSSelector -------------------------------

nsCSSSelector::nsCSSSelector(void)
  : mLowercaseTag(nullptr),
    mCasedTag(nullptr),
    mIDList(nullptr),
    mClassList(nullptr),
    mPseudoClassList(nullptr),
    mAttrList(nullptr),
    mNegations(nullptr),
    mNext(nullptr),
    mNameSpace(kNameSpaceID_Unknown),
    mOperator(0),
    mPseudoType(CSSPseudoElementType::NotPseudo)
{
  MOZ_COUNT_CTOR(nsCSSSelector);
}

nsCSSSelector*
nsCSSSelector::Clone(bool aDeepNext, bool aDeepNegations) const
{
  nsCSSSelector *result = new nsCSSSelector();
  if (!result)
    return nullptr;

  result->mNameSpace = mNameSpace;
  result->mLowercaseTag = mLowercaseTag;
  result->mCasedTag = mCasedTag;
  result->mOperator = mOperator;
  result->mPseudoType = mPseudoType;

  NS_IF_CLONE(mIDList);
  NS_IF_CLONE(mClassList);
  NS_IF_CLONE(mPseudoClassList);
  NS_IF_CLONE(mAttrList);

  // No need to worry about multiple levels of recursion since an
  // mNegations can't have an mNext.
  NS_ASSERTION(!mNegations || !mNegations->mNext,
               "mNegations can't have non-null mNext");
  if (aDeepNegations) {
    NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNegations, result,
                             (true, false));
  }

  if (aDeepNext) {
    NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNext, result,
                             (false, true));
  }

  return result;
}

nsCSSSelector::~nsCSSSelector(void)
{
  MOZ_COUNT_DTOR(nsCSSSelector);
  Reset();
  // No need to worry about multiple levels of recursion since an
  // mNegations can't have an mNext.
  NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNext);
}

void nsCSSSelector::Reset(void)
{
  mNameSpace = kNameSpaceID_Unknown;
  mLowercaseTag = nullptr;
  mCasedTag = nullptr;
  NS_IF_DELETE(mIDList);
  NS_IF_DELETE(mClassList);
  NS_IF_DELETE(mPseudoClassList);
  NS_IF_DELETE(mAttrList);
  // No need to worry about multiple levels of recursion since an
  // mNegations can't have an mNext.
  NS_ASSERTION(!mNegations || !mNegations->mNext,
               "mNegations can't have non-null mNext");
  NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNegations);
  mOperator = char16_t(0);
}

void nsCSSSelector::SetNameSpace(int32_t aNameSpace)
{
  mNameSpace = aNameSpace;
}

void nsCSSSelector::SetTag(const nsString& aTag)
{
  if (aTag.IsEmpty()) {
    mLowercaseTag = mCasedTag =  nullptr;
    return;
  }

  mCasedTag = NS_Atomize(aTag);

  nsAutoString lowercase;
  nsContentUtils::ASCIIToLower(aTag, lowercase);
  mLowercaseTag = NS_Atomize(lowercase);
}

void nsCSSSelector::AddID(const nsString& aID)
{
  if (!aID.IsEmpty()) {
    nsAtomList** list = &mIDList;
    while (nullptr != *list) {
      list = &((*list)->mNext);
    }
    *list = new nsAtomList(aID);
  }
}

void nsCSSSelector::AddClass(const nsString& aClass)
{
  if (!aClass.IsEmpty()) {
    nsAtomList** list = &mClassList;
    while (nullptr != *list) {
      list = &((*list)->mNext);
    }
    *list = new nsAtomList(aClass);
  }
}

void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType)
{
  AddPseudoClassInternal(new nsPseudoClassList(aType));
}

void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
                                   const char16_t* aString)
{
  AddPseudoClassInternal(new nsPseudoClassList(aType, aString));
}

void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
                                   const int32_t* aIntPair)
{
  AddPseudoClassInternal(new nsPseudoClassList(aType, aIntPair));
}

void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
                                   nsCSSSelectorList* aSelectorList)
{
  // Take ownership of nsCSSSelectorList instead of copying.
  AddPseudoClassInternal(new nsPseudoClassList(aType, aSelectorList));
}

void nsCSSSelector::AddPseudoClassInternal(nsPseudoClassList *aPseudoClass)
{
  nsPseudoClassList** list = &mPseudoClassList;
  while (nullptr != *list) {
    list = &((*list)->mNext);
  }
  *list = aPseudoClass;
}

void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr)
{
  if (!aAttr.IsEmpty()) {
    nsAttrSelector** list = &mAttrList;
    while (nullptr != *list) {
      list = &((*list)->mNext);
    }
    *list = new nsAttrSelector(aNameSpace, aAttr);
  }
}

void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc,
                                 const nsString& aValue,
                                 nsAttrSelector::ValueCaseSensitivity aCaseSensitivity)
{
  if (!aAttr.IsEmpty()) {
    nsAttrSelector** list = &mAttrList;
    while (nullptr != *list) {
      list = &((*list)->mNext);
    }
    *list = new nsAttrSelector(aNameSpace, aAttr, aFunc, aValue, aCaseSensitivity);
  }
}

void nsCSSSelector::SetOperator(char16_t aOperator)
{
  mOperator = aOperator;
}

int32_t nsCSSSelector::CalcWeightWithoutNegations() const
{
  int32_t weight = 0;

#ifdef MOZ_XUL
  MOZ_ASSERT(!(IsPseudoElement() &&
               PseudoType() != CSSPseudoElementType::XULTree &&
               mClassList),
             "If non-XUL-tree pseudo-elements can have class selectors "
             "after them, specificity calculation must be updated");
#else
  MOZ_ASSERT(!(IsPseudoElement() && mClassList),
             "If pseudo-elements can have class selectors "
             "after them, specificity calculation must be updated");
#endif
  MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)),
             "If pseudo-elements can have id or attribute selectors "
             "after them, specificity calculation must be updated");

  if (IsPseudoElement()) {
    weight += 1;
  }
  if (nullptr != mCasedTag) {
    weight += 1;
  }
  nsAtomList* list = mIDList;
  while (nullptr != list) {
    weight += 1 << 20;
    list = list->mNext;
  }
  list = mClassList;
#ifdef MOZ_XUL
  // XUL tree pseudo-elements abuse mClassList to store some private
  // data; ignore that.
  if (PseudoType() == CSSPseudoElementType::XULTree) {
    list = nullptr;
  }
#endif
  while (nullptr != list) {
    weight += 1 << 10;
    list = list->mNext;
  }
  // FIXME (bug 561154):  This is incorrect for :-moz-any(), which isn't
  // really a pseudo-class.  In order to handle :-moz-any() correctly,
  // we need to compute specificity after we match, based on which
  // option we matched with (and thus also need to try the
  // highest-specificity options first).
  nsPseudoClassList *plist = mPseudoClassList;
  while (nullptr != plist) {
    weight += 1 << 10;
    plist = plist->mNext;
  }
  nsAttrSelector* attr = mAttrList;
  while (nullptr != attr) {
    weight += 1 << 10;
    attr = attr->mNext;
  }
  return weight;
}

int32_t nsCSSSelector::CalcWeight() const
{
  // Loop over this selector and all its negations.
  int32_t weight = 0;
  for (const nsCSSSelector *n = this; n; n = n->mNegations) {
    weight += n->CalcWeightWithoutNegations();
  }
  return weight;
}

//
// Builds the textual representation of a selector. Called by DOM 2 CSS
// StyleRule:selectorText
//
void
nsCSSSelector::ToString(nsAString& aString, CSSStyleSheet* aSheet,
                        bool aAppend) const
{
  if (!aAppend)
   aString.Truncate();

  // selectors are linked from right-to-left, so the next selector in
  // the linked list actually precedes this one in the resulting string
  AutoTArray<const nsCSSSelector*, 8> stack;
  for (const nsCSSSelector *s = this; s; s = s->mNext) {
    stack.AppendElement(s);
  }

  while (!stack.IsEmpty()) {
    uint32_t index = stack.Length() - 1;
    const nsCSSSelector *s = stack.ElementAt(index);
    stack.RemoveElementAt(index);

    s->AppendToStringWithoutCombinators(aString, aSheet, false);

    // Append the combinator, if needed.
    if (!stack.IsEmpty()) {
      const nsCSSSelector *next = stack.ElementAt(index - 1);
      char16_t oper = s->mOperator;
      if (next->IsPseudoElement()) {
        NS_ASSERTION(oper == char16_t(':'),
                     "improperly chained pseudo element");
      } else {
        NS_ASSERTION(oper != char16_t(0),
                     "compound selector without combinator");

        aString.Append(char16_t(' '));
        if (oper != char16_t(' ')) {
          aString.Append(oper);
          aString.Append(char16_t(' '));
        }
      }
    }
  }
}

void
nsCSSSelector::AppendToStringWithoutCombinators(
    nsAString& aString,
    CSSStyleSheet* aSheet,
    bool aUseStandardNamespacePrefixes) const
{
  AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false,
                                              aUseStandardNamespacePrefixes);

  for (const nsCSSSelector* negation = mNegations; negation;
       negation = negation->mNegations) {
    aString.AppendLiteral(":not(");
    negation->AppendToStringWithoutCombinatorsOrNegations(
        aString, aSheet, true, aUseStandardNamespacePrefixes);
    aString.Append(char16_t(')'));
  }
}

#ifdef DEBUG
nsCString
nsCSSSelector::RestrictedSelectorToString() const
{
  MOZ_ASSERT(IsRestrictedSelector());

  nsString result;
  AppendToStringWithoutCombinators(result, nullptr, true);
  return NS_ConvertUTF16toUTF8(result);
}

static bool
AppendStandardNamespacePrefixToString(nsAString& aString, int32_t aNameSpace)
{
  if (aNameSpace == kNameSpaceID_Unknown) {
    // Wildcard namespace; no prefix to write.
    return false;
  }
  switch (aNameSpace) {
    case kNameSpaceID_None:
      break;
    case kNameSpaceID_XML:
      aString.AppendLiteral("xml");
      break;
    case kNameSpaceID_XHTML:
      aString.AppendLiteral("html");
      break;
    case kNameSpaceID_XLink:
      aString.AppendLiteral("xlink");
      break;
    case kNameSpaceID_XSLT:
      aString.AppendLiteral("xsl");
      break;
    case kNameSpaceID_XBL:
      aString.AppendLiteral("xbl");
      break;
    case kNameSpaceID_MathML:
      aString.AppendLiteral("math");
      break;
    case kNameSpaceID_RDF:
      aString.AppendLiteral("rdf");
      break;
    case kNameSpaceID_XUL:
      aString.AppendLiteral("xul");
      break;
    case kNameSpaceID_SVG:
      aString.AppendLiteral("svg");
      break;
    default:
      aString.AppendLiteral("ns");
      aString.AppendInt(aNameSpace);
      break;
  }
  return true;
}
#endif

void
nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
                   (nsAString& aString, CSSStyleSheet* aSheet,
                   bool aIsNegated,
                   bool aUseStandardNamespacePrefixes) const
{
  nsAutoString temp;
  bool isPseudoElement = IsPseudoElement();

  // For non-pseudo-element selectors or for lone pseudo-elements, deal with
  // namespace prefixes.
  bool wroteNamespace = false;
  if (!isPseudoElement || !mNext) {
    // append the namespace prefix if needed
    nsXMLNameSpaceMap *sheetNS = aSheet ? aSheet->GetNameSpaceMap() : nullptr;

    // sheetNS is non-null if and only if we had an @namespace rule.  If it's
    // null, that means that the only namespaces we could have are the
    // wildcard namespace (which can be implicit in this case) and the "none"
    // namespace, which then needs to be explicitly specified.
    if (aUseStandardNamespacePrefixes) {
#ifdef DEBUG
      // We have no sheet to look up prefix information from.  This is
      // only for debugging, so use some "standard" prefixes that
      // are recognizable.
      wroteNamespace =
        AppendStandardNamespacePrefixToString(aString, mNameSpace);
      if (wroteNamespace) {
        aString.Append(char16_t('|'));
      }
#endif
    } else if (!sheetNS) {
      NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
                   mNameSpace == kNameSpaceID_None,
                   "How did we get this namespace?");
      if (mNameSpace == kNameSpaceID_None) {
        aString.Append(char16_t('|'));
        wroteNamespace = true;
      }
    } else if (sheetNS->FindNameSpaceID(nullptr) == mNameSpace) {
      // We have the default namespace (possibly including the wildcard
      // namespace).  Do nothing.
      NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
                   CanBeNamespaced(aIsNegated),
                   "How did we end up with this namespace?");
    } else if (mNameSpace == kNameSpaceID_None) {
      NS_ASSERTION(CanBeNamespaced(aIsNegated),
                   "How did we end up with this namespace?");
      aString.Append(char16_t('|'));
      wroteNamespace = true;
    } else if (mNameSpace != kNameSpaceID_Unknown) {
      NS_ASSERTION(CanBeNamespaced(aIsNegated),
                   "How did we end up with this namespace?");
      nsAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace);
      NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace "
                   "without a prefix?");
      nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(prefixAtom),
                                         aString);
      aString.Append(char16_t('|'));
      wroteNamespace = true;
    } else {
      // A selector for an element in any namespace, while the default
      // namespace is something else.  :not() is special in that the default
      // namespace is not implied for non-type selectors, so if this is a
      // negated non-type selector we don't need to output an explicit wildcard
      // namespace here, since those default to a wildcard namespace.
      if (CanBeNamespaced(aIsNegated)) {
        aString.AppendLiteral("*|");
        wroteNamespace = true;
      }
    }
  }

  if (!mLowercaseTag) {
    // Universal selector:  avoid writing the universal selector when we
    // can avoid it, especially since we're required to avoid it for the
    // inside of :not()
    if (wroteNamespace ||
        (!mIDList && !mClassList && !mPseudoClassList && !mAttrList &&
         (aIsNegated || !mNegations))) {
      aString.Append(char16_t('*'));
    }
  } else {
    // Append the tag name
    nsAutoString tag;
    (isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag);
    if (isPseudoElement) {
      if (!mNext) {
        // Lone pseudo-element selector -- toss in a wildcard type selector
        // XXXldb Why?
        aString.Append(char16_t('*'));
      }
      // While our atoms use one colon, most pseudo-elements require two
      // colons (those not in CSS level 2) and all pseudo-elements allow
      // two colons. So serialize to the non-deprecated two colon syntax.
      aString.Append(char16_t(':'));
      // This should not be escaped since (a) the pseudo-element string
      // has a ":" that can't be escaped and (b) all pseudo-elements at
      // this point are known, and therefore we know they don't need
      // escaping.
      aString.Append(tag);
    } else {
      nsStyleUtil::AppendEscapedCSSIdent(tag, aString);
    }
  }

  // Append the id, if there is one
  if (mIDList) {
    nsAtomList* list = mIDList;
    while (list != nullptr) {
      list->mAtom->ToString(temp);
      aString.Append(char16_t('#'));
      nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
      list = list->mNext;
    }
  }

  // Append each class in the linked list
  if (mClassList) {
    if (isPseudoElement) {
#ifdef MOZ_XUL
      MOZ_ASSERT(nsCSSAnonBoxes::IsTreePseudoElement(mLowercaseTag),
                 "must be tree pseudo-element");

      aString.Append(char16_t('('));
      for (nsAtomList* list = mClassList; list; list = list->mNext) {
        nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(list->mAtom), aString);
        aString.Append(char16_t(','));
      }
      // replace the final comma with a close-paren
      aString.Replace(aString.Length() - 1, 1, char16_t(')'));
#else
      NS_ERROR("Can't happen");
#endif
    } else {
      nsAtomList* list = mClassList;
      while (list != nullptr) {
        list->mAtom->ToString(temp);
        aString.Append(char16_t('.'));
        nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
        list = list->mNext;
      }
    }
  }

  // Append each attribute selector in the linked list
  if (mAttrList) {
    nsAttrSelector* list = mAttrList;
    while (list != nullptr) {
      aString.Append(char16_t('['));
      // Append the namespace prefix
      if (list->mNameSpace == kNameSpaceID_Unknown) {
        aString.Append(char16_t('*'));
        aString.Append(char16_t('|'));
      } else if (list->mNameSpace != kNameSpaceID_None) {
        if (aUseStandardNamespacePrefixes) {
#ifdef DEBUG
          AppendStandardNamespacePrefixToString(aString, list->mNameSpace);
          aString.Append(char16_t('|'));
#endif
        } else if (aSheet) {
          nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap();
          nsAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace);
          // Default namespaces don't apply to attribute selectors, so
          // we must have a useful prefix.
          NS_ASSERTION(prefixAtom,
                       "How did we end up with a namespace if the prefix "
                       "is unknown?");
          nsAutoString prefix;
          prefixAtom->ToString(prefix);
          nsStyleUtil::AppendEscapedCSSIdent(prefix, aString);
          aString.Append(char16_t('|'));
        }
      }
      // Append the attribute name
      list->mCasedAttr->ToString(temp);
      nsStyleUtil::AppendEscapedCSSIdent(temp, aString);

      if (list->mFunction != NS_ATTR_FUNC_SET) {
        // Append the function
        if (list->mFunction == NS_ATTR_FUNC_INCLUDES)
          aString.Append(char16_t('~'));
        else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH)
          aString.Append(char16_t('|'));
        else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH)
          aString.Append(char16_t('^'));
        else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH)
          aString.Append(char16_t('$'));
        else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH)
          aString.Append(char16_t('*'));

        aString.Append(char16_t('='));

        // Append the value
        nsStyleUtil::AppendEscapedCSSString(list->mValue, aString);

        if (list->mValueCaseSensitivity ==
              nsAttrSelector::ValueCaseSensitivity::CaseInsensitive) {
          aString.AppendLiteral(u" i");
        }
      }

      aString.Append(char16_t(']'));

      list = list->mNext;
    }
  }

  // Append each pseudo-class in the linked list
  for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) {
    nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp);
    // This should not be escaped since (a) the pseudo-class string
    // has a ":" that can't be escaped and (b) all pseudo-classes at
    // this point are known, and therefore we know they don't need
    // escaping.
    aString.Append(temp);
    if (list->u.mMemory) {
      aString.Append(char16_t('('));
      if (nsCSSPseudoClasses::HasStringArg(list->mType)) {
        nsStyleUtil::AppendEscapedCSSIdent(
          nsDependentString(list->u.mString), aString);
      } else if (nsCSSPseudoClasses::HasNthPairArg(list->mType)) {
        int32_t a = list->u.mNumbers[0],
                b = list->u.mNumbers[1];
        temp.Truncate();
        if (a != 0) {
          if (a == -1) {
            temp.Append(char16_t('-'));
          } else if (a != 1) {
            temp.AppendInt(a);
          }
          temp.Append(char16_t('n'));
        }
        if (b != 0 || a == 0) {
          if (b >= 0 && a != 0) // check a != 0 for whether we printed above
            temp.Append(char16_t('+'));
          temp.AppendInt(b);
        }
        aString.Append(temp);
      } else {
        NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType),
                     "unexpected pseudo-class");
        nsString tmp;
        list->u.mSelectors->ToString(tmp, aSheet);
        aString.Append(tmp);
      }
      aString.Append(char16_t(')'));
    }
  }
}

bool
nsCSSSelector::CanBeNamespaced(bool aIsNegated) const
{
  return !aIsNegated ||
         (!mIDList && !mClassList && !mPseudoClassList && !mAttrList);
}

size_t
nsCSSSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
  size_t n = 0;
  const nsCSSSelector* s = this;
  while (s) {
    n += aMallocSizeOf(s);

    #define MEASURE(x)   n += x ? x->SizeOfIncludingThis(aMallocSizeOf) : 0;

    MEASURE(s->mIDList);
    MEASURE(s->mClassList);
    MEASURE(s->mPseudoClassList);
    MEASURE(s->mNegations);
    MEASURE(s->mAttrList);

    // The following members aren't measured:
    // - s->mLowercaseTag, because it's an atom and therefore shared
    // - s->mCasedTag, because it's an atom and therefore shared

    s = s->mNext;
  }
  return n;
}

// -- nsCSSSelectorList -------------------------------

nsCSSSelectorList::nsCSSSelectorList(void)
  : mSelectors(nullptr),
    mWeight(0),
    mNext(nullptr)
{
  MOZ_COUNT_CTOR(nsCSSSelectorList);
}

nsCSSSelectorList::~nsCSSSelectorList()
{
  MOZ_COUNT_DTOR(nsCSSSelectorList);
  delete mSelectors;
  NS_CSS_DELETE_LIST_MEMBER(nsCSSSelectorList, this, mNext);
}

nsCSSSelector*
nsCSSSelectorList::AddSelector(char16_t aOperator)
{
  nsCSSSelector* newSel = new nsCSSSelector();

  if (mSelectors) {
    NS_ASSERTION(aOperator != char16_t(0), "chaining without combinator");
    mSelectors->SetOperator(aOperator);
  } else {
    NS_ASSERTION(aOperator == char16_t(0), "combinator without chaining");
  }

  newSel->mNext = mSelectors;
  mSelectors = newSel;
  return newSel;
}

void
nsCSSSelectorList::RemoveRightmostSelector()
{
  nsCSSSelector* current = mSelectors;
  mSelectors = mSelectors->mNext;
  MOZ_ASSERT(mSelectors,
             "Rightmost selector has been removed, but now "
             "mSelectors is null");
  mSelectors->SetOperator(char16_t(0));

  // Make sure that deleting current won't delete the whole list.
  current->mNext = nullptr;
  delete current;
}

void
nsCSSSelectorList::ToString(nsAString& aResult, CSSStyleSheet* aSheet)
{
  aResult.Truncate();
  nsCSSSelectorList *p = this;
  for (;;) {
    p->mSelectors->ToString(aResult, aSheet, true);
    p = p->mNext;
    if (!p)
      break;
    aResult.AppendLiteral(", ");
  }
}

nsCSSSelectorList*
nsCSSSelectorList::Clone(bool aDeep) const
{
  nsCSSSelectorList *result = new nsCSSSelectorList();
  result->mWeight = mWeight;
  NS_IF_CLONE(mSelectors);

  if (aDeep) {
    NS_CSS_CLONE_LIST_MEMBER(nsCSSSelectorList, this, mNext, result,
                             (false));
  }
  return result;
}

size_t
nsCSSSelectorList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
  size_t n = 0;
  const nsCSSSelectorList* s = this;
  while (s) {
    n += aMallocSizeOf(s);
    n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0;
    s = s->mNext;
  }
  return n;
}

// --------------------------------------------------------

class DOMCSSDeclarationImpl : public nsDOMCSSDeclaration
{
protected:
  // Needs to be protected so we can use NS_IMPL_ADDREF_USING_AGGREGATOR.
  virtual ~DOMCSSDeclarationImpl(void);

  // But we need to allow UniquePtr to delete us.
  friend class mozilla::DefaultDelete<DOMCSSDeclarationImpl>;

public:
  explicit DOMCSSDeclarationImpl(css::StyleRule *aRule);

  css::Rule* GetParentRule() final override { return mRule; }
  virtual DeclarationBlock* GetCSSDeclaration(Operation aOperation) override;
  virtual nsresult SetCSSDeclaration(DeclarationBlock* aDecl) override;
  virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv,
                                        nsIPrincipal* aSubjectPrincipal) override;
  nsDOMCSSDeclaration::ServoCSSParsingEnvironment
  GetServoCSSParsingEnvironment(nsIPrincipal* aSubjectPrincipal) const final override;
  virtual nsIDocument* DocToUpdate() override;

  // Override |AddRef| and |Release| for being owned by StyleRule.  Also, we
  // need to forward QI for cycle collection things to StyleRule.
  NS_DECL_ISUPPORTS_INHERITED

  virtual nsINode *GetParentObject() override
  {
    return mRule ? mRule->GetDocument() : nullptr;
  }

  virtual DocGroup* GetDocGroup() const override
  {
    if (!mRule) {
      return nullptr;
    }

    nsIDocument* document = mRule->GetDocument();
    return document ? document->GetDocGroup() : nullptr;
  }

protected:
  // This reference is not reference-counted. The rule object owns us and we go
  // away when it does.
  css::StyleRule *mRule;
};

DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule)
  : mRule(aRule)
{
}

DOMCSSDeclarationImpl::~DOMCSSDeclarationImpl(void)
{
}

NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)
NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)

NS_INTERFACE_MAP_BEGIN(DOMCSSDeclarationImpl)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  // We forward the cycle collection interfaces to mRule, which is
  // never null.
  if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
      aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
    return mRule->QueryInterface(aIID, aInstancePtr);
  }
  else
NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration)

DeclarationBlock*
DOMCSSDeclarationImpl::GetCSSDeclaration(Operation aOperation)
{
  if (aOperation != eOperation_Read) {
    RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
    if (sheet) {
      sheet->WillDirty();
    }
  }
  return mRule->GetDeclaration();
}

void
DOMCSSDeclarationImpl::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv,
                                                nsIPrincipal* aSubjectPrincipal)
{
  GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv);
}

nsDOMCSSDeclaration::ServoCSSParsingEnvironment
DOMCSSDeclarationImpl::GetServoCSSParsingEnvironment(
  nsIPrincipal* aSubjectPrincipal) const
{
  MOZ_CRASH("GetURLData shouldn't be calling on a Gecko rule");
}

nsresult
DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl)
{
  NS_PRECONDITION(mRule,
         "can only be called when |GetCSSDeclaration| returned a declaration");

  nsCOMPtr<nsIDocument> doc;
  RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
  if (sheet) {
    doc = sheet->GetAssociatedDocument();
  }

  mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true);

  mRule->SetDeclaration(aDecl->AsGecko());

  if (sheet) {
    sheet->RuleChanged(mRule);
  }
  return NS_OK;
}

nsIDocument*
DOMCSSDeclarationImpl::DocToUpdate()
{
  return nullptr;
}

// -- StyleRule ------------------------------------

namespace mozilla {
namespace css {

uint16_t
StyleRule::Type() const
{
  return CSSRuleBinding::STYLE_RULE;
}

nsICSSDeclaration*
StyleRule::Style()
{
  if (!mDOMDeclaration) {
    mDOMDeclaration.reset(new DOMCSSDeclarationImpl(this));
  }
  return mDOMDeclaration.get();
}

StyleRule::StyleRule(nsCSSSelectorList* aSelector,
                     Declaration* aDeclaration,
                     uint32_t aLineNumber,
                     uint32_t aColumnNumber)
  : BindingStyleRule(aLineNumber, aColumnNumber),
    mSelector(aSelector),
    mDeclaration(aDeclaration)
{
  NS_PRECONDITION(aDeclaration, "must have a declaration");

  mDeclaration->SetOwningRule(this);
}

// for |Clone|
StyleRule::StyleRule(const StyleRule& aCopy)
  : BindingStyleRule(aCopy),
    mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr),
    mDeclaration(new Declaration(*aCopy.mDeclaration))
{
  mDeclaration->SetOwningRule(this);
  // rest is constructed lazily on existing data
}

StyleRule::~StyleRule()
{
  delete mSelector;
  DropReferences();
}

void
StyleRule::DropReferences()
{
  if (mDeclaration) {
    mDeclaration->SetOwningRule(nullptr);
  }
}

// QueryInterface implementation for StyleRule
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleRule)
  if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) {
    *aInstancePtr = this;
    NS_ADDREF_THIS();
    return NS_OK;
  }
  else
NS_INTERFACE_MAP_END_INHERITING(Rule)

NS_IMPL_ADDREF_INHERITED(StyleRule, Rule)
NS_IMPL_RELEASE_INHERITED(StyleRule, Rule)

NS_IMPL_CYCLE_COLLECTION_CLASS(StyleRule)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(StyleRule, Rule)
  // Keep this in sync with IsCCLeaf.
  // Trace the wrapper for our declaration.  This just expands out
  // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
  // directly because the wrapper is on the declaration, not on us.
  if (tmp->mDOMDeclaration) {
    tmp->mDOMDeclaration->TraceWrapper(aCallbacks, aClosure);
  }
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(StyleRule, Rule)
  // Unlink the wrapper for our declaraton.  This just expands out
  // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
  // directly because the wrapper is on the declaration, not on us.
  if (tmp->mDOMDeclaration) {
    tmp->mDOMDeclaration->ReleaseWrapper(static_cast<nsISupports*>(p));
  }
  tmp->DropReferences();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(StyleRule, Rule)
  // Keep this in sync with IsCCLeaf.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

bool
StyleRule::IsCCLeaf() const
{
  if (!Rule::IsCCLeaf()) {
    return false;
  }

  return !mDOMDeclaration || !mDOMDeclaration->PreservingWrapper();
}

/* virtual */ int32_t
StyleRule::GetType() const
{
  return Rule::STYLE_RULE;
}

/* virtual */ already_AddRefed<Rule>
StyleRule::Clone() const
{
  RefPtr<Rule> clone = new StyleRule(*this);
  return clone.forget();
}

void
StyleRule::SetDeclaration(Declaration* aDecl)
{
  if (aDecl == mDeclaration) {
    return;
  }
  mDeclaration->SetOwningRule(nullptr);
  mDeclaration = aDecl;
  mDeclaration->SetOwningRule(this);
}

#ifdef DEBUG
/* virtual */ void
StyleRule::List(FILE* out, int32_t aIndent) const
{
  nsAutoCString str;
  // Indent
  for (int32_t index = aIndent; --index >= 0; ) {
    str.AppendLiteral("  ");
  }

  if (mSelector) {
    nsAutoString buffer;
    mSelector->ToString(buffer, GetStyleSheet());
    AppendUTF16toUTF8(buffer, str);
    str.Append(' ');
  }

  if (nullptr != mDeclaration) {
    nsAutoString buffer;
    str.AppendLiteral("{ ");
    mDeclaration->ToString(buffer);
    AppendUTF16toUTF8(buffer, str);
    str.Append('}');
    CSSStyleSheet* sheet = GetStyleSheet();
    if (sheet) {
      nsIURI* uri = sheet->GetSheetURI();
      if (uri) {
        str.AppendLiteral(" /* ");
        str.Append(uri->GetSpecOrDefault());
        str.Append(':');
        str.AppendInt(mLineNumber);
        str.AppendLiteral(" */");
      }
    }
  }
  else {
    str.AppendLiteral("{ null declaration }");
  }
  str.Append('\n');
  fprintf_stderr(out, "%s", str.get());
}
#endif

void
StyleRule::GetCssTextImpl(nsAString& aCssText) const
{
  if (mSelector) {
    mSelector->ToString(aCssText, GetStyleSheet());
    aCssText.Append(char16_t(' '));
  }
  aCssText.Append(char16_t('{'));
  aCssText.Append(char16_t(' '));
  if (mDeclaration)
  {
    nsAutoString   tempString;
    mDeclaration->ToString( tempString );
    aCssText.Append( tempString );
  }
  aCssText.Append(char16_t(' '));
  aCssText.Append(char16_t('}'));
}

void
StyleRule::GetSelectorText(nsAString& aSelectorText)
{
  if (mSelector)
    mSelector->ToString(aSelectorText, GetStyleSheet());
  else
    aSelectorText.Truncate();
}

void
StyleRule::SetSelectorText(const nsAString& aSelectorText)
{
  // XXX TBI - get a parser and re-parse the selectors,
  // XXX then need to re-compute the cascade
  // XXX and dirty sheet
}

/* virtual */ size_t
StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
  size_t n = aMallocSizeOf(this);
  n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0;
  n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0;

  // Measurement of the following members may be added later if DMD finds it is
  // worthwhile:
  // - mDOMRule;

  return n;
}

nsCSSSelectorList*
StyleRule::GetSelectorAtIndex(uint32_t aIndex, ErrorResult& rv)
{

  for (nsCSSSelectorList* sel = mSelector; sel;
       sel = sel->mNext, --aIndex) {
    if (aIndex == 0) {
      return sel;
    }
  }

  // Ran out of selectors
  rv.Throw(NS_ERROR_INVALID_ARG);
  return nullptr;
}

uint32_t
StyleRule::GetSelectorCount()
{
  uint32_t count = 0;
  for (nsCSSSelectorList* sel = mSelector; sel; sel = sel->mNext) {
    ++count;
  }
  return count;
}

nsresult
StyleRule::GetSelectorText(uint32_t aSelectorIndex, nsAString& aText)
{
  ErrorResult rv;
  nsCSSSelectorList* sel = GetSelectorAtIndex(aSelectorIndex, rv);
  if (rv.Failed()) {
    return rv.StealNSResult();
  }

  sel->mSelectors->ToString(aText, GetStyleSheet(), false);

  return NS_OK;
}

nsresult
StyleRule::GetSpecificity(uint32_t aSelectorIndex, uint64_t* aSpecificity)
{
  ErrorResult rv;
  nsCSSSelectorList* sel = GetSelectorAtIndex(aSelectorIndex, rv);
  if (rv.Failed()) {
    return rv.StealNSResult();
  }

  *aSpecificity = sel->mWeight;
  return NS_OK;
}

nsresult
StyleRule::SelectorMatchesElement(Element* aElement,
                                  uint32_t aSelectorIndex,
                                  const nsAString& aPseudo,
                                  bool* aMatches)
{
  ErrorResult rv;
  nsCSSSelectorList* tail = GetSelectorAtIndex(aSelectorIndex, rv);
  if (rv.Failed()) {
    return rv.StealNSResult();
  }

  // We want just the one list item, not the whole list tail
  nsAutoPtr<nsCSSSelectorList> sel(tail->Clone(false));

  // Do not attempt to match if a pseudo element is requested and this is not
  // a pseudo element selector, or vice versa.
  if (aPseudo.IsEmpty() == sel->mSelectors->IsPseudoElement()) {
    *aMatches = false;
    return NS_OK;
  }

  if (!aPseudo.IsEmpty()) {
    // We need to make sure that the requested pseudo element type
    // matches the selector pseudo element type before proceeding.
    RefPtr<nsAtom> pseudoElt = NS_Atomize(aPseudo);
    if (sel->mSelectors->PseudoType() != nsCSSPseudoElements::
          GetPseudoType(pseudoElt, CSSEnabledState::eIgnoreEnabledState)) {
      *aMatches = false;
      return NS_OK;
    }

    // We have a matching pseudo element, now remove it so we can compare
    // directly against |element| when proceeding into SelectorListMatches.
    // It's OK to do this - we just cloned sel and nothing else is using it.
    sel->RemoveRightmostSelector();
  }

  // XXXbz what exactly should we do with visited state here?  If we ever start
  // caring about it, remember to do FlushPendingLinkUpdates().
  TreeMatchContext matchingContext(false,
                                   nsRuleWalker::eRelevantLinkUnvisited,
                                   aElement->OwnerDoc(),
                                   TreeMatchContext::eNeverMatchVisited);
  *aMatches = nsCSSRuleProcessor::SelectorListMatches(aElement, matchingContext,
                                                      sel);
  return NS_OK;
}

NotNull<DeclarationBlock*>
StyleRule::GetDeclarationBlock() const
{
  return WrapNotNull(mDeclaration);
}

} // namespace css
} // namespace mozilla
