layout/style/GeckoBindings.cpp
author J.C. Jones <jc@mozilla.com>
Mon, 15 Jul 2019 21:40:37 +0000
changeset 482810 4d719512b650570bcb67c44ee6cdbdd17d1ad12a
parent 481544 8a393a44cfd0d84c1dacc7da190b8138d5b031e4
permissions -rw-r--r--
Bug 1564499 - land NSS 8c6fad5544a6 UPGRADE_NSS_RELEASE, r=me

/* -*- 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/. */

/* FFI functions for Servo to call into Gecko */

#include "mozilla/GeckoBindings.h"

#include "ChildIterator.h"
#include "ErrorReporter.h"
#include "GeckoProfiler.h"
#include "gfxFontFamilyList.h"
#include "gfxFontFeatures.h"
#include "nsAnimationManager.h"
#include "nsAttrValueInlines.h"
#include "nsCSSFrameConstructor.h"
#include "nsCSSProps.h"
#include "nsCSSPseudoElements.h"
#include "nsContentUtils.h"
#include "nsDOMTokenList.h"
#include "nsDeviceContext.h"
#include "nsIContentInlines.h"
#include "nsICrashReporter.h"
#include "mozilla/dom/DocumentInlines.h"
#include "nsILoadContext.h"
#include "nsIFrame.h"
#include "nsIMemoryReporter.h"
#include "nsIMozBrowserFrame.h"
#include "nsINode.h"
#include "nsIPrincipal.h"
#include "nsIURI.h"
#include "nsFontMetrics.h"
#include "nsHTMLStyleSheet.h"
#include "nsMappedAttributes.h"
#include "nsMediaFeatures.h"
#include "nsNameSpaceManager.h"
#include "nsNetUtil.h"
#include "nsProxyRelease.h"
#include "nsString.h"
#include "nsStyleStruct.h"
#include "nsStyleUtil.h"
#include "nsTArray.h"
#include "nsTransitionManager.h"
#include "nsWindowSizes.h"

#include "mozilla/css/ImageLoader.h"
#include "mozilla/DeclarationBlock.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/EventStates.h"
#include "mozilla/FontPropertyTypes.h"
#include "mozilla/Keyframe.h"
#include "mozilla/Mutex.h"
#include "mozilla/Preferences.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/StaticPrefs.h"
#include "mozilla/RestyleManager.h"
#include "mozilla/SizeOfState.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/SystemGroup.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoTraversalStatistics.h"
#include "mozilla/Telemetry.h"
#include "mozilla/RWLock.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/HTMLTableCellElement.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/dom/MediaList.h"
#include "mozilla/dom/SVGElement.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/URLExtraData.h"
#include "mozilla/dom/CSSMozDocumentRule.h"

#if defined(MOZ_MEMORY)
#  include "mozmemory.h"
#endif

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

// Definitions of the global traversal stats.
bool ServoTraversalStatistics::sActive = false;
ServoTraversalStatistics ServoTraversalStatistics::sSingleton;

static RWLock* sServoFFILock = nullptr;

static const nsFont* ThreadSafeGetDefaultFontHelper(
    const Document& aDocument, nsAtom* aLanguage,
    StyleGenericFontFamily aGenericId) {
  bool needsCache = false;
  const nsFont* retval;

  auto GetDefaultFont = [&](bool* aNeedsToCache) {
    auto* prefs = aDocument.GetFontPrefsForLang(aLanguage, aNeedsToCache);
    return prefs ? prefs->GetDefaultFont(aGenericId) : nullptr;
  };

  {
    AutoReadLock guard(*sServoFFILock);
    retval = GetDefaultFont(&needsCache);
  }
  if (!needsCache) {
    return retval;
  }
  {
    AutoWriteLock guard(*sServoFFILock);
    retval = GetDefaultFont(nullptr);
  }
  return retval;
}

/*
 * Does this child count as significant for selector matching?
 *
 * See nsStyleUtil::IsSignificantChild for details.
 */
bool Gecko_IsSignificantChild(const nsINode* aNode,
                              bool aWhitespaceIsSignificant) {
  return nsStyleUtil::ThreadSafeIsSignificantChild(aNode->AsContent(),
                                                   aWhitespaceIsSignificant);
}

const nsINode* Gecko_GetLastChild(const nsINode* aNode) {
  return aNode->GetLastChild();
}

const nsINode* Gecko_GetPreviousSibling(const nsINode* aNode) {
  return aNode->GetPreviousSibling();
}

const nsINode* Gecko_GetFlattenedTreeParentNode(const nsINode* aNode) {
  return aNode->GetFlattenedTreeParentNodeForStyle();
}

const Element* Gecko_GetBeforeOrAfterPseudo(const Element* aElement,
                                            bool aIsBefore) {
  MOZ_ASSERT(aElement);
  MOZ_ASSERT(aElement->HasProperties());

  return aIsBefore ? nsLayoutUtils::GetBeforePseudo(aElement)
                   : nsLayoutUtils::GetAfterPseudo(aElement);
}

const Element* Gecko_GetMarkerPseudo(const Element* aElement) {
  MOZ_ASSERT(aElement);
  MOZ_ASSERT(aElement->HasProperties());

  return nsLayoutUtils::GetMarkerPseudo(aElement);
}

nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(
    const Element* aElement) {
  nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame());
  if (!ac) {
    return nullptr;
  }

  auto* array = new nsTArray<nsIContent*>();
  nsContentUtils::AppendNativeAnonymousChildren(aElement, *array, 0);
  return array;
}

void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* aAnonContent) {
  MOZ_ASSERT(aAnonContent);
  delete aAnonContent;
}

const nsTArray<RefPtr<nsINode>>* Gecko_GetAssignedNodes(
    const Element* aElement) {
  MOZ_ASSERT(HTMLSlotElement::FromNode(aElement));
  return &static_cast<const HTMLSlotElement*>(aElement)->AssignedNodes();
}

void Gecko_ComputedStyle_Init(ComputedStyle* aStyle,
                              const ServoComputedData* aValues,
                              PseudoStyleType aPseudoType) {
  new (KnownNotNull, aStyle)
      ComputedStyle(aPseudoType, ServoComputedDataForgotten(aValues));
}

ServoComputedData::ServoComputedData(const ServoComputedDataForgotten aValue) {
  PodAssign(this, aValue.mPtr);
}

MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleStructsMallocEnclosingSizeOf)

void ServoComputedData::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const {
  // Note: GetStyleFoo() returns a pointer to an nsStyleFoo that sits within a
  // servo_arc::Arc, i.e. it is preceded by a word-sized refcount. So we need
  // to measure it with a function that can handle an interior pointer. We use
  // ServoStyleStructsEnclosingMallocSizeOf to clearly identify in DMD's
  // output the memory measured here.
#define STYLE_STRUCT(name_)                                       \
  static_assert(alignof(nsStyle##name_) <= sizeof(size_t),        \
                "alignment will break AddSizeOfExcludingThis()"); \
  const void* p##name_ = GetStyle##name_();                       \
  if (!aSizes.mState.HaveSeenPtr(p##name_)) {                     \
    aSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_) +=             \
        ServoStyleStructsMallocEnclosingSizeOf(p##name_);         \
  }
#include "nsStyleStructList.h"
#undef STYLE_STRUCT

  if (visited_style.mPtr && !aSizes.mState.HaveSeenPtr(visited_style.mPtr)) {
    visited_style.mPtr->AddSizeOfIncludingThis(
        aSizes, &aSizes.mLayoutComputedValuesVisited);
  }

  // Measurement of the following members may be added later if DMD finds it is
  // worthwhile:
  // - custom_properties
  // - writing_mode
  // - rules
  // - font_computation_data
}

void Gecko_ComputedStyle_Destroy(ComputedStyle* aStyle) {
  aStyle->~ComputedStyle();
}

void Gecko_ConstructStyleChildrenIterator(const Element* aElement,
                                          StyleChildrenIterator* aIterator) {
  MOZ_ASSERT(aElement);
  MOZ_ASSERT(aIterator);
  new (aIterator) StyleChildrenIterator(aElement);
}

void Gecko_DestroyStyleChildrenIterator(StyleChildrenIterator* aIterator) {
  MOZ_ASSERT(aIterator);

  aIterator->~StyleChildrenIterator();
}

const nsINode* Gecko_GetNextStyleChild(StyleChildrenIterator* aIterator) {
  MOZ_ASSERT(aIterator);
  return aIterator->GetNextChild();
}

bool Gecko_VisitedStylesEnabled(const Document* aDoc) {
  MOZ_ASSERT(aDoc);
  MOZ_ASSERT(NS_IsMainThread());

  if (!StaticPrefs::layout_css_visited_links_enabled()) {
    return false;
  }

  if (aDoc->IsBeingUsedAsImage()) {
    return false;
  }

  nsILoadContext* loadContext = aDoc->GetLoadContext();
  if (loadContext && loadContext->UsePrivateBrowsing()) {
    return false;
  }

  return true;
}

EventStates::ServoType Gecko_ElementState(const Element* aElement) {
  return aElement->StyleState().ServoValue();
}

bool Gecko_IsRootElement(const Element* aElement) {
  return aElement->OwnerDoc()->GetRootElement() == aElement;
}

// Dirtiness tracking.
void Gecko_SetNodeFlags(const nsINode* aNode, uint32_t aFlags) {
  const_cast<nsINode*>(aNode)->SetFlags(aFlags);
}

void Gecko_UnsetNodeFlags(const nsINode* aNode, uint32_t aFlags) {
  const_cast<nsINode*>(aNode)->UnsetFlags(aFlags);
}

void Gecko_NoteDirtyElement(const Element* aElement) {
  MOZ_ASSERT(NS_IsMainThread());
  const_cast<Element*>(aElement)->NoteDirtyForServo();
}

void Gecko_NoteDirtySubtreeForInvalidation(const Element* aElement) {
  MOZ_ASSERT(NS_IsMainThread());
  const_cast<Element*>(aElement)->NoteDirtySubtreeForServo();
}

void Gecko_NoteAnimationOnlyDirtyElement(const Element* aElement) {
  MOZ_ASSERT(NS_IsMainThread());
  const_cast<Element*>(aElement)->NoteAnimationOnlyDirtyForServo();
}

bool Gecko_AnimationNameMayBeReferencedFromStyle(
    const nsPresContext* aPresContext, nsAtom* aName) {
  MOZ_ASSERT(aPresContext);
  return aPresContext->AnimationManager()->AnimationMayBeReferenced(aName);
}

PseudoStyleType Gecko_GetImplementedPseudo(const Element* aElement) {
  return aElement->GetPseudoElementType();
}

uint32_t Gecko_CalcStyleDifference(const ComputedStyle* aOldStyle,
                                   const ComputedStyle* aNewStyle,
                                   bool* aAnyStyleStructChanged,
                                   bool* aOnlyResetStructsChanged) {
  MOZ_ASSERT(aOldStyle);
  MOZ_ASSERT(aNewStyle);

  uint32_t equalStructs;
  nsChangeHint result =
      aOldStyle->CalcStyleDifference(*aNewStyle, &equalStructs);

  *aAnyStyleStructChanged =
      equalStructs != StyleStructConstants::kAllStructsMask;

  const auto kInheritedStructsMask =
      StyleStructConstants::kInheritedStructsMask;
  *aOnlyResetStructsChanged =
      (equalStructs & kInheritedStructsMask) == kInheritedStructsMask;

  return result;
}

const ServoElementSnapshot* Gecko_GetElementSnapshot(
    const ServoElementSnapshotTable* aTable, const Element* aElement) {
  MOZ_ASSERT(aTable);
  MOZ_ASSERT(aElement);

  return aTable->Get(const_cast<Element*>(aElement));
}

bool Gecko_HaveSeenPtr(SeenPtrs* aTable, const void* aPtr) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aTable);
  // Empty Rust allocations are indicated by small values up to the alignment
  // of the relevant type. We shouldn't see anything like that here.
  MOZ_ASSERT(uintptr_t(aPtr) > 16);

  return aTable->HaveSeenPtr(aPtr);
}

const StyleStrong<RawServoDeclarationBlock>* Gecko_GetStyleAttrDeclarationBlock(
    const Element* aElement) {
  DeclarationBlock* decl = aElement->GetInlineStyleDeclaration();
  if (!decl) {
    return nullptr;
  }
  return decl->RefRawStrong();
}

void Gecko_UnsetDirtyStyleAttr(const Element* aElement) {
  DeclarationBlock* decl = aElement->GetInlineStyleDeclaration();
  if (!decl) {
    return;
  }
  decl->UnsetDirty();
}

static const StyleStrong<RawServoDeclarationBlock>* AsRefRawStrong(
    const RefPtr<RawServoDeclarationBlock>& aDecl) {
  static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
                    sizeof(StyleStrong<RawServoDeclarationBlock>),
                "RefPtr should just be a pointer");
  return reinterpret_cast<const StyleStrong<RawServoDeclarationBlock>*>(&aDecl);
}

const StyleStrong<RawServoDeclarationBlock>*
Gecko_GetHTMLPresentationAttrDeclarationBlock(const Element* aElement) {
  const nsMappedAttributes* attrs = aElement->GetMappedAttributes();
  if (!attrs) {
    auto* svg = SVGElement::FromNodeOrNull(aElement);
    if (svg) {
      if (auto decl = svg->GetContentDeclarationBlock()) {
        return decl->RefRawStrong();
      }
    }
    return nullptr;
  }

  return AsRefRawStrong(attrs->GetServoStyle());
}

const StyleStrong<RawServoDeclarationBlock>*
Gecko_GetExtraContentStyleDeclarations(const Element* aElement) {
  if (!aElement->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) {
    return nullptr;
  }
  const HTMLTableCellElement* cell =
      static_cast<const HTMLTableCellElement*>(aElement);
  if (nsMappedAttributes* attrs =
          cell->GetMappedAttributesInheritedFromTable()) {
    return AsRefRawStrong(attrs->GetServoStyle());
  }
  return nullptr;
}

const StyleStrong<RawServoDeclarationBlock>*
Gecko_GetUnvisitedLinkAttrDeclarationBlock(const Element* aElement) {
  nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet();
  if (!sheet) {
    return nullptr;
  }

  return AsRefRawStrong(sheet->GetServoUnvisitedLinkDecl());
}

StyleSheet* Gecko_StyleSheet_Clone(const StyleSheet* aSheet,
                                   const StyleSheet* aNewParentSheet) {
  MOZ_ASSERT(aSheet);
  MOZ_ASSERT(aSheet->GetParentSheet(), "Should only be used for @import");
  MOZ_ASSERT(aNewParentSheet, "Wat");

  RefPtr<StyleSheet> newSheet =
      aSheet->Clone(nullptr, nullptr, nullptr, nullptr);

  // NOTE(emilio): This code runs in the StylesheetInner constructor, which
  // means that the inner pointer of `aNewParentSheet` still points to the old
  // one.
  //
  // So we _don't_ update neither the parent pointer of the stylesheet, nor the
  // child list (yet). This is fixed up in that same constructor.
  return static_cast<StyleSheet*>(newSheet.forget().take());
}

void Gecko_StyleSheet_AddRef(const StyleSheet* aSheet) {
  MOZ_ASSERT(NS_IsMainThread());
  const_cast<StyleSheet*>(aSheet)->AddRef();
}

void Gecko_StyleSheet_Release(const StyleSheet* aSheet) {
  MOZ_ASSERT(NS_IsMainThread());
  const_cast<StyleSheet*>(aSheet)->Release();
}

const StyleStrong<RawServoDeclarationBlock>*
Gecko_GetVisitedLinkAttrDeclarationBlock(const Element* aElement) {
  nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet();
  if (!sheet) {
    return nullptr;
  }

  return AsRefRawStrong(sheet->GetServoVisitedLinkDecl());
}

const StyleStrong<RawServoDeclarationBlock>*
Gecko_GetActiveLinkAttrDeclarationBlock(const Element* aElement) {
  nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet();
  if (!sheet) {
    return nullptr;
  }

  return AsRefRawStrong(sheet->GetServoActiveLinkDecl());
}

static PseudoStyleType GetPseudoTypeFromElementForAnimation(
    const Element*& aElementOrPseudo) {
  if (aElementOrPseudo->IsGeneratedContentContainerForBefore()) {
    aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement();
    return PseudoStyleType::before;
  }

  if (aElementOrPseudo->IsGeneratedContentContainerForAfter()) {
    aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement();
    return PseudoStyleType::after;
  }

  if (aElementOrPseudo->IsGeneratedContentContainerForMarker()) {
    aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement();
    return PseudoStyleType::marker;
  }

  return PseudoStyleType::NotPseudo;
}

bool Gecko_GetAnimationRule(const Element* aElement,
                            EffectCompositor::CascadeLevel aCascadeLevel,
                            RawServoAnimationValueMap* aAnimationValues) {
  MOZ_ASSERT(aElement);

  Document* doc = aElement->GetComposedDoc();
  if (!doc) {
    return false;
  }
  nsPresContext* presContext = doc->GetPresContext();
  if (!presContext || !presContext->IsDynamic()) {
    // For print or print preview, ignore animations.
    return false;
  }

  PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement);

  return presContext->EffectCompositor()->GetServoAnimationRule(
      aElement, pseudoType, aCascadeLevel, aAnimationValues);
}

bool Gecko_StyleAnimationsEquals(const nsStyleAutoArray<StyleAnimation>* aA,
                                 const nsStyleAutoArray<StyleAnimation>* aB) {
  return *aA == *aB;
}

void Gecko_CopyAnimationNames(nsStyleAutoArray<StyleAnimation>* aDest,
                              const nsStyleAutoArray<StyleAnimation>* aSrc) {
  size_t srcLength = aSrc->Length();
  aDest->EnsureLengthAtLeast(srcLength);

  for (size_t index = 0; index < srcLength; index++) {
    (*aDest)[index].SetName((*aSrc)[index].GetName());
  }
}

void Gecko_SetAnimationName(StyleAnimation* aStyleAnimation, nsAtom* aAtom) {
  MOZ_ASSERT(aStyleAnimation);
  aStyleAnimation->SetName(already_AddRefed<nsAtom>(aAtom));
}

void Gecko_UpdateAnimations(const Element* aElement,
                            const ComputedStyle* aOldComputedData,
                            const ComputedStyle* aComputedData,
                            UpdateAnimationsTasks aTasks) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aElement);

  if (!aElement->IsInComposedDoc()) {
    return;
  }

  nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement);
  if (!presContext || !presContext->IsDynamic()) {
    return;
  }

  nsAutoAnimationMutationBatch mb(aElement->OwnerDoc());

  PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement);

  if (aTasks & UpdateAnimationsTasks::CSSAnimations) {
    presContext->AnimationManager()->UpdateAnimations(
        const_cast<Element*>(aElement), pseudoType, aComputedData);
  }

  // aComputedData might be nullptr if the target element is now in a
  // display:none subtree. We still call Gecko_UpdateAnimations in this case
  // because we need to stop CSS animations in the display:none subtree.
  // However, we don't need to update transitions since they are stopped by
  // RestyleManager::AnimationsWithDestroyedFrame so we just return early
  // here.
  if (!aComputedData) {
    return;
  }

  if (aTasks & UpdateAnimationsTasks::CSSTransitions) {
    MOZ_ASSERT(aOldComputedData);
    presContext->TransitionManager()->UpdateTransitions(
        const_cast<Element*>(aElement), pseudoType, *aOldComputedData,
        *aComputedData);
  }

  if (aTasks & UpdateAnimationsTasks::EffectProperties) {
    presContext->EffectCompositor()->UpdateEffectProperties(
        aComputedData, const_cast<Element*>(aElement), pseudoType);
  }

  if (aTasks & UpdateAnimationsTasks::CascadeResults) {
    EffectSet* effectSet = EffectSet::GetEffectSet(aElement, pseudoType);
    // CSS animations/transitions might have been destroyed as part of the above
    // steps so before updating cascade results, we check if there are still any
    // animations to update.
    if (effectSet) {
      // We call UpdateCascadeResults directly (intead of
      // MaybeUpdateCascadeResults) since we know for sure that the cascade has
      // changed, but we were unable to call MarkCascadeUpdated when we noticed
      // it since we avoid mutating state as part of the Servo parallel
      // traversal.
      presContext->EffectCompositor()->UpdateCascadeResults(
          *effectSet, const_cast<Element*>(aElement), pseudoType);
    }
  }

  if (aTasks & UpdateAnimationsTasks::DisplayChangedFromNone) {
    presContext->EffectCompositor()->RequestRestyle(
        const_cast<Element*>(aElement), pseudoType,
        EffectCompositor::RestyleType::Standard,
        EffectCompositor::CascadeLevel::Animations);
  }
}

size_t Gecko_GetAnimationEffectCount(const Element* aElementOrPseudo) {
  PseudoStyleType pseudoType =
      GetPseudoTypeFromElementForAnimation(aElementOrPseudo);

  EffectSet* effectSet = EffectSet::GetEffectSet(aElementOrPseudo, pseudoType);
  return effectSet ? effectSet->Count() : 0;
}

bool Gecko_ElementHasAnimations(const Element* aElement) {
  PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement);

  return !!EffectSet::GetEffectSet(aElement, pseudoType);
}

bool Gecko_ElementHasCSSAnimations(const Element* aElement) {
  PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement);
  nsAnimationManager::CSSAnimationCollection* collection =
      nsAnimationManager::CSSAnimationCollection ::GetAnimationCollection(
          aElement, pseudoType);

  return collection && !collection->mAnimations.IsEmpty();
}

bool Gecko_ElementHasCSSTransitions(const Element* aElement) {
  PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement);
  nsTransitionManager::CSSTransitionCollection* collection =
      nsTransitionManager::CSSTransitionCollection ::GetAnimationCollection(
          aElement, pseudoType);

  return collection && !collection->mAnimations.IsEmpty();
}

size_t Gecko_ElementTransitions_Length(const Element* aElement) {
  PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement);
  nsTransitionManager::CSSTransitionCollection* collection =
      nsTransitionManager::CSSTransitionCollection ::GetAnimationCollection(
          aElement, pseudoType);

  return collection ? collection->mAnimations.Length() : 0;
}

static CSSTransition* GetCurrentTransitionAt(const Element* aElement,
                                             size_t aIndex) {
  PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement);
  nsTransitionManager::CSSTransitionCollection* collection =
      nsTransitionManager::CSSTransitionCollection ::GetAnimationCollection(
          aElement, pseudoType);
  if (!collection) {
    return nullptr;
  }
  nsTArray<RefPtr<CSSTransition>>& transitions = collection->mAnimations;
  return aIndex < transitions.Length() ? transitions[aIndex].get() : nullptr;
}

nsCSSPropertyID Gecko_ElementTransitions_PropertyAt(const Element* aElement,
                                                    size_t aIndex) {
  CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex);
  return transition ? transition->TransitionProperty()
                    : nsCSSPropertyID::eCSSProperty_UNKNOWN;
}

const RawServoAnimationValue* Gecko_ElementTransitions_EndValueAt(
    const Element* aElement, size_t aIndex) {
  CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex);
  return transition ? transition->ToValue().mServo.get() : nullptr;
}

double Gecko_GetProgressFromComputedTiming(const ComputedTiming* aTiming) {
  return aTiming->mProgress.Value();
}

double Gecko_GetPositionInSegment(
    const AnimationPropertySegment* aSegment, double aProgress,
    ComputedTimingFunction::BeforeFlag aBeforeFlag) {
  MOZ_ASSERT(aSegment->mFromKey < aSegment->mToKey,
             "The segment from key should be less than to key");

  double positionInSegment = (aProgress - aSegment->mFromKey) /
                             // To avoid floating precision inaccuracies, make
                             // sure we calculate both the numerator and
                             // denominator using double precision.
                             (double(aSegment->mToKey) - aSegment->mFromKey);

  return ComputedTimingFunction::GetPortion(aSegment->mTimingFunction,
                                            positionInSegment, aBeforeFlag);
}

const RawServoAnimationValue* Gecko_AnimationGetBaseStyle(
    const RawServoAnimationValueTable* aBaseStyles, nsCSSPropertyID aProperty) {
  auto base = reinterpret_cast<
      const nsRefPtrHashtable<nsUint32HashKey, RawServoAnimationValue>*>(
      aBaseStyles);
  return base->GetWeak(aProperty);
}

void Gecko_FillAllImageLayers(nsStyleImageLayers* aLayers, uint32_t aMaxLen) {
  aLayers->FillAllLayers(aMaxLen);
}

bool Gecko_IsDocumentBody(const Element* aElement) {
  Document* doc = aElement->GetUncomposedDoc();
  return doc && doc->GetBodyElement() == aElement;
}

nscolor Gecko_GetLookAndFeelSystemColor(int32_t aId, const Document* aDoc) {
  bool useStandinsForNativeColors = !nsContentUtils::IsChromeDoc(aDoc);
  nscolor result;
  LookAndFeel::ColorID colorId = static_cast<LookAndFeel::ColorID>(aId);
  AutoWriteLock guard(*sServoFFILock);
  LookAndFeel::GetColor(colorId, useStandinsForNativeColors, &result);
  return result;
}

bool Gecko_MatchLang(const Element* aElement, nsAtom* aOverrideLang,
                     bool aHasOverrideLang, const char16_t* aValue) {
  MOZ_ASSERT(!(aOverrideLang && !aHasOverrideLang),
             "aHasOverrideLang should only be set when aOverrideLang is null");
  MOZ_ASSERT(aValue, "null lang parameter");
  if (!aValue || !*aValue) {
    return false;
  }

  // We have to determine the language of the current element.  Since
  // this is currently no property and since the language is inherited
  // from the parent we have to be prepared to look at all parent
  // nodes.  The language itself is encoded in the LANG attribute.
  if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) {
    return nsStyleUtil::DashMatchCompare(
        nsDependentAtomString(language), nsDependentString(aValue),
        nsASCIICaseInsensitiveStringComparator());
  }

  // Try to get the language from the HTTP header or if this
  // is missing as well from the preferences.
  // The content language can be a comma-separated list of
  // language codes.
  nsAutoString language;
  aElement->OwnerDoc()->GetContentLanguage(language);

  nsDependentString langString(aValue);
  language.StripWhitespace();
  for (auto const& lang : language.Split(char16_t(','))) {
    if (nsStyleUtil::DashMatchCompare(
            lang, langString, nsASCIICaseInsensitiveStringComparator())) {
      return true;
    }
  }
  return false;
}

nsAtom* Gecko_GetXMLLangValue(const Element* aElement) {
  const nsAttrValue* attr =
      aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);

  if (!attr) {
    return nullptr;
  }

  MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);

  RefPtr<nsAtom> atom = attr->GetAtomValue();
  return atom.forget().take();
}

Document::DocumentTheme Gecko_GetDocumentLWTheme(const Document* aDocument) {
  return aDocument->ThreadSafeGetDocumentLWTheme();
}

const PreferenceSheet::Prefs* Gecko_GetPrefSheetPrefs(const Document* aDoc) {
  return &PreferenceSheet::PrefsFor(*aDoc);
}

bool Gecko_IsTableBorderNonzero(const Element* aElement) {
  if (!aElement->IsHTMLElement(nsGkAtoms::table)) {
    return false;
  }
  const nsAttrValue* val = aElement->GetParsedAttr(nsGkAtoms::border);
  return val &&
         (val->Type() != nsAttrValue::eInteger || val->GetIntegerValue() != 0);
}

bool Gecko_IsBrowserFrame(const Element* aElement) {
  nsIMozBrowserFrame* browserFrame =
      const_cast<Element*>(aElement)->GetAsMozBrowserFrame();
  return browserFrame && browserFrame->GetReallyIsBrowser();
}

template <typename Implementor>
static nsAtom* LangValue(Implementor* aElement) {
  // TODO(emilio): Deduplicate a bit with nsIContent::GetLang().
  const nsAttrValue* attr =
      aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
  if (!attr && aElement->SupportsLangAttr()) {
    attr = aElement->GetParsedAttr(nsGkAtoms::lang);
  }

  if (!attr) {
    return nullptr;
  }

  MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
  RefPtr<nsAtom> atom = attr->GetAtomValue();
  return atom.forget().take();
}

template <typename Implementor, typename MatchFn>
static bool DoMatch(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
                    MatchFn aMatch) {
  if (MOZ_LIKELY(aNS)) {
    int32_t ns = aNS == nsGkAtoms::_empty
                     ? kNameSpaceID_None
                     : nsContentUtils::NameSpaceManager()->GetNameSpaceID(
                           aNS, aElement->IsInChromeDocument());

    MOZ_ASSERT(ns == nsContentUtils::NameSpaceManager()->GetNameSpaceID(
                         aNS, aElement->IsInChromeDocument()));
    NS_ENSURE_TRUE(ns != kNameSpaceID_Unknown, false);
    const nsAttrValue* value = aElement->GetParsedAttr(aName, ns);
    return value && aMatch(value);
  }

  // No namespace means any namespace - we have to check them all. :-(
  BorrowedAttrInfo attrInfo;
  for (uint32_t i = 0; (attrInfo = aElement->GetAttrInfoAt(i)); ++i) {
    if (attrInfo.mName->LocalName() != aName) {
      continue;
    }
    if (aMatch(attrInfo.mValue)) {
      return true;
    }
  }
  return false;
}

template <typename Implementor>
static bool HasAttr(Implementor* aElement, nsAtom* aNS, nsAtom* aName) {
  auto match = [](const nsAttrValue* aValue) { return true; };
  return DoMatch(aElement, aNS, aName, match);
}

template <typename Implementor>
static bool AttrEquals(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
                       nsAtom* aStr, bool aIgnoreCase) {
  auto match = [aStr, aIgnoreCase](const nsAttrValue* aValue) {
    return aValue->Equals(aStr, aIgnoreCase ? eIgnoreCase : eCaseMatters);
  };
  return DoMatch(aElement, aNS, aName, match);
}

#define WITH_COMPARATOR(ignore_case_, c_, expr_) \
  if (ignore_case_) {                            \
    const nsCaseInsensitiveStringComparator c_;  \
    return expr_;                                \
  } else {                                       \
    const nsDefaultStringComparator c_;          \
    return expr_;                                \
  }

template <typename Implementor>
static bool AttrDashEquals(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
                           nsAtom* aStr, bool aIgnoreCase) {
  auto match = [aStr, aIgnoreCase](const nsAttrValue* aValue) {
    nsAutoString str;
    aValue->ToString(str);
    WITH_COMPARATOR(
        aIgnoreCase, c,
        nsStyleUtil::DashMatchCompare(str, nsDependentAtomString(aStr), c))
  };
  return DoMatch(aElement, aNS, aName, match);
}

template <typename Implementor>
static bool AttrIncludes(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
                         nsAtom* aStr, bool aIgnoreCase) {
  auto match = [aStr, aIgnoreCase](const nsAttrValue* aValue) {
    nsAutoString str;
    aValue->ToString(str);
    WITH_COMPARATOR(
        aIgnoreCase, c,
        nsStyleUtil::ValueIncludes(str, nsDependentAtomString(aStr), c))
  };
  return DoMatch(aElement, aNS, aName, match);
}

template <typename Implementor>
static bool AttrHasSubstring(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
                             nsAtom* aStr, bool aIgnoreCase) {
  auto match = [aStr, aIgnoreCase](const nsAttrValue* aValue) {
    nsAutoString str;
    aValue->ToString(str);
    WITH_COMPARATOR(aIgnoreCase, c,
                    FindInReadable(nsDependentAtomString(aStr), str, c))
  };
  return DoMatch(aElement, aNS, aName, match);
}

template <typename Implementor>
static bool AttrHasPrefix(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
                          nsAtom* aStr, bool aIgnoreCase) {
  auto match = [aStr, aIgnoreCase](const nsAttrValue* aValue) {
    nsAutoString str;
    aValue->ToString(str);
    WITH_COMPARATOR(aIgnoreCase, c,
                    StringBeginsWith(str, nsDependentAtomString(aStr), c))
  };
  return DoMatch(aElement, aNS, aName, match);
}

template <typename Implementor>
static bool AttrHasSuffix(Implementor* aElement, nsAtom* aNS, nsAtom* aName,
                          nsAtom* aStr, bool aIgnoreCase) {
  auto match = [aStr, aIgnoreCase](const nsAttrValue* aValue) {
    nsAutoString str;
    aValue->ToString(str);
    WITH_COMPARATOR(aIgnoreCase, c,
                    StringEndsWith(str, nsDependentAtomString(aStr), c))
  };
  return DoMatch(aElement, aNS, aName, match);
}

#define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_)      \
  nsAtom* prefix_##LangValue(implementor_ aElement) {                          \
    return LangValue(aElement);                                                \
  }                                                                            \
  bool prefix_##HasAttr(implementor_ aElement, nsAtom* aNS, nsAtom* aName) {   \
    return HasAttr(aElement, aNS, aName);                                      \
  }                                                                            \
  bool prefix_##AttrEquals(implementor_ aElement, nsAtom* aNS, nsAtom* aName,  \
                           nsAtom* aStr, bool aIgnoreCase) {                   \
    return AttrEquals(aElement, aNS, aName, aStr, aIgnoreCase);                \
  }                                                                            \
  bool prefix_##AttrDashEquals(implementor_ aElement, nsAtom* aNS,             \
                               nsAtom* aName, nsAtom* aStr,                    \
                               bool aIgnoreCase) {                             \
    return AttrDashEquals(aElement, aNS, aName, aStr, aIgnoreCase);            \
  }                                                                            \
  bool prefix_##AttrIncludes(implementor_ aElement, nsAtom* aNS,               \
                             nsAtom* aName, nsAtom* aStr, bool aIgnoreCase) {  \
    return AttrIncludes(aElement, aNS, aName, aStr, aIgnoreCase);              \
  }                                                                            \
  bool prefix_##AttrHasSubstring(implementor_ aElement, nsAtom* aNS,           \
                                 nsAtom* aName, nsAtom* aStr,                  \
                                 bool aIgnoreCase) {                           \
    return AttrHasSubstring(aElement, aNS, aName, aStr, aIgnoreCase);          \
  }                                                                            \
  bool prefix_##AttrHasPrefix(implementor_ aElement, nsAtom* aNS,              \
                              nsAtom* aName, nsAtom* aStr, bool aIgnoreCase) { \
    return AttrHasPrefix(aElement, aNS, aName, aStr, aIgnoreCase);             \
  }                                                                            \
  bool prefix_##AttrHasSuffix(implementor_ aElement, nsAtom* aNS,              \
                              nsAtom* aName, nsAtom* aStr, bool aIgnoreCase) { \
    return AttrHasSuffix(aElement, aNS, aName, aStr, aIgnoreCase);             \
  }

SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, const Element*)
SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot,
                                           const ServoElementSnapshot*)

#undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS

nsAtom* Gecko_Atomize(const char* aString, uint32_t aLength) {
  return NS_Atomize(nsDependentCSubstring(aString, aLength)).take();
}

nsAtom* Gecko_Atomize16(const nsAString* aString) {
  return NS_Atomize(*aString).take();
}

void Gecko_AddRefAtom(nsAtom* aAtom) { NS_ADDREF(aAtom); }

void Gecko_ReleaseAtom(nsAtom* aAtom) { NS_RELEASE(aAtom); }

void Gecko_nsTArray_FontFamilyName_AppendNamed(
    nsTArray<FontFamilyName>* aNames, nsAtom* aName,
    StyleFontFamilyNameSyntax aSyntax) {
  aNames->AppendElement(FontFamilyName(aName, aSyntax));
}

void Gecko_nsTArray_FontFamilyName_AppendGeneric(
    nsTArray<FontFamilyName>* aNames, StyleGenericFontFamily aType) {
  aNames->AppendElement(FontFamilyName(aType));
}

SharedFontList* Gecko_SharedFontList_Create() {
  RefPtr<SharedFontList> fontlist = new SharedFontList();
  return fontlist.forget().take();
}

MOZ_DEFINE_MALLOC_SIZE_OF(GeckoSharedFontListMallocSizeOf)

size_t Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(
    SharedFontList* aFontlist) {
  MOZ_ASSERT(NS_IsMainThread());
  return aFontlist->SizeOfIncludingThisIfUnshared(
      GeckoSharedFontListMallocSizeOf);
}

size_t Gecko_SharedFontList_SizeOfIncludingThis(SharedFontList* aFontlist) {
  MOZ_ASSERT(NS_IsMainThread());
  return aFontlist->SizeOfIncludingThis(GeckoSharedFontListMallocSizeOf);
}

NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SharedFontList, SharedFontList);

void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src) {
  dst->fontlist = src->fontlist;
}

void Gecko_nsFont_InitSystem(nsFont* aDest, int32_t aFontId,
                             const nsStyleFont* aFont,
                             const Document* aDocument) {
  const nsFont* defaultVariableFont = ThreadSafeGetDefaultFontHelper(
      *aDocument, aFont->mLanguage, StyleGenericFontFamily::None);

  // We have passed uninitialized memory to this function,
  // initialize it. We can't simply return an nsFont because then
  // we need to know its size beforehand. Servo cannot initialize nsFont
  // itself, so this will do.
  new (aDest) nsFont(*defaultVariableFont);

  LookAndFeel::FontID fontID = static_cast<LookAndFeel::FontID>(aFontId);

  AutoWriteLock guard(*sServoFFILock);
  nsLayoutUtils::ComputeSystemFont(aDest, fontID, defaultVariableFont);
}

void Gecko_nsFont_Destroy(nsFont* aDest) { aDest->~nsFont(); }

StyleGenericFontFamily Gecko_nsStyleFont_ComputeDefaultFontType(
    const Document* aDoc, StyleGenericFontFamily aGenericId,
    nsAtom* aLanguage) {
  const nsFont* defaultFont =
      ThreadSafeGetDefaultFontHelper(*aDoc, aLanguage, aGenericId);
  return defaultFont->fontlist.GetDefaultFontType();
}

gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet() {
  return new gfxFontFeatureValueSet();
}

nsTArray<unsigned int>* Gecko_AppendFeatureValueHashEntry(
    gfxFontFeatureValueSet* aFontFeatureValues, nsAtom* aFamily,
    uint32_t aAlternate, nsAtom* aName) {
  MOZ_ASSERT(NS_IsMainThread());
  static_assert(sizeof(unsigned int) == sizeof(uint32_t),
                "sizeof unsigned int and uint32_t must be the same");
  return aFontFeatureValues->AppendFeatureValueHashEntry(
      nsAtomCString(aFamily), nsDependentAtomString(aName), aAlternate);
}

float Gecko_FontStretch_ToFloat(FontStretch aStretch) {
  // Servo represents percentages with 1. being 100%.
  return aStretch.Percentage() / 100.0f;
}

void Gecko_FontStretch_SetFloat(FontStretch* aStretch, float aFloat) {
  // Servo represents percentages with 1. being 100%.
  //
  // Also, the font code assumes a given maximum that style doesn't really need
  // to know about. So clamp here at the boundary.
  *aStretch = FontStretch(std::min(aFloat * 100.0f, float(FontStretch::kMax)));
}

void Gecko_FontSlantStyle_SetNormal(FontSlantStyle* aStyle) {
  *aStyle = FontSlantStyle::Normal();
}

void Gecko_FontSlantStyle_SetItalic(FontSlantStyle* aStyle) {
  *aStyle = FontSlantStyle::Italic();
}

void Gecko_FontSlantStyle_SetOblique(FontSlantStyle* aStyle,
                                     float aAngleInDegrees) {
  *aStyle = FontSlantStyle::Oblique(aAngleInDegrees);
}

void Gecko_FontSlantStyle_Get(FontSlantStyle aStyle, bool* aNormal,
                              bool* aItalic, float* aObliqueAngle) {
  *aNormal = aStyle.IsNormal();
  *aItalic = aStyle.IsItalic();
  if (aStyle.IsOblique()) {
    *aObliqueAngle = aStyle.ObliqueAngle();
  }
}

float Gecko_FontWeight_ToFloat(FontWeight aWeight) { return aWeight.ToFloat(); }

void Gecko_FontWeight_SetFloat(FontWeight* aWeight, float aFloat) {
  *aWeight = FontWeight(aFloat);
}

void Gecko_ClearAlternateValues(nsFont* aFont, size_t aLength) {
  aFont->alternateValues.Clear();
  aFont->alternateValues.SetCapacity(aLength);
}

void Gecko_AppendAlternateValues(nsFont* aFont, uint32_t aAlternateName,
                                 nsAtom* aAtom) {
  aFont->alternateValues.AppendElement(
      gfxAlternateValue{aAlternateName, nsDependentAtomString(aAtom)});
}

void Gecko_CopyAlternateValuesFrom(nsFont* aDest, const nsFont* aSrc) {
  aDest->alternateValues.Clear();
  aDest->alternateValues.AppendElements(aSrc->alternateValues);
}

void Gecko_SetCounterStyleToName(CounterStylePtr* aPtr, nsAtom* aName) {
  RefPtr<nsAtom> name = already_AddRefed<nsAtom>(aName);
  *aPtr = name.forget();
}

void Gecko_SetCounterStyleToSymbols(CounterStylePtr* aPtr, uint8_t aSymbolsType,
                                    nsACString const* const* aSymbols,
                                    uint32_t aSymbolsCount) {
  nsTArray<nsString> symbols(aSymbolsCount);
  for (uint32_t i = 0; i < aSymbolsCount; i++) {
    symbols.AppendElement(NS_ConvertUTF8toUTF16(*aSymbols[i]));
  }
  *aPtr = new AnonymousCounterStyle(aSymbolsType, std::move(symbols));
}

void Gecko_SetCounterStyleToString(CounterStylePtr* aPtr,
                                   const nsACString* aSymbol) {
  *aPtr = new AnonymousCounterStyle(NS_ConvertUTF8toUTF16(*aSymbol));
}

void Gecko_CopyCounterStyle(CounterStylePtr* aDst,
                            const CounterStylePtr* aSrc) {
  *aDst = *aSrc;
}

nsAtom* Gecko_CounterStyle_GetName(const CounterStylePtr* aPtr) {
  return aPtr->IsAtom() ? aPtr->AsAtom() : nullptr;
}

const AnonymousCounterStyle* Gecko_CounterStyle_GetAnonymous(
    const CounterStylePtr* aPtr) {
  return aPtr->AsAnonymous();
}

void Gecko_SetNullImageValue(nsStyleImage* aImage) {
  MOZ_ASSERT(aImage);
  aImage->SetNull();
}

void Gecko_SetGradientImageValue(nsStyleImage* aImage,
                                 StyleGradient* aGradient) {
  MOZ_ASSERT(aImage);
  aImage->SetGradientData(UniquePtr<StyleGradient>(aGradient));
}

static already_AddRefed<nsStyleImageRequest> CreateStyleImageRequest(
    nsStyleImageRequest::Mode aModeFlags, const StyleComputedImageUrl* aUrl) {
  RefPtr<nsStyleImageRequest> req = new nsStyleImageRequest(aModeFlags, *aUrl);
  return req.forget();
}

void Gecko_SetLayerImageImageValue(nsStyleImage* aImage,
                                   const StyleComputedImageUrl* aUrl) {
  MOZ_ASSERT(aImage && aUrl);

  RefPtr<nsStyleImageRequest> req =
      CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aUrl);
  aImage->SetImageRequest(req.forget());
}

void Gecko_SetImageElement(nsStyleImage* aImage, nsAtom* aAtom) {
  MOZ_ASSERT(aImage);
  aImage->SetElementId(do_AddRef(aAtom));
}

void Gecko_CopyImageValueFrom(nsStyleImage* aImage,
                              const nsStyleImage* aOther) {
  MOZ_ASSERT(aImage);
  MOZ_ASSERT(aOther);

  *aImage = *aOther;
}

void Gecko_InitializeImageCropRect(nsStyleImage* aImage) {
  MOZ_ASSERT(aImage);
  aImage->SetCropRect(MakeUnique<nsStyleImage::CropRect>());
}

void Gecko_SetCursorArrayLength(nsStyleUI* aStyleUI, size_t aLen) {
  aStyleUI->mCursorImages.Clear();
  aStyleUI->mCursorImages.SetLength(aLen);
}

void Gecko_SetCursorImageValue(nsCursorImage* aCursor,
                               const StyleComputedImageUrl* aUrl) {
  MOZ_ASSERT(aCursor && aUrl);

  aCursor->mImage =
      CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard, aUrl);
}

void Gecko_CopyCursorArrayFrom(nsStyleUI* aDest, const nsStyleUI* aSrc) {
  aDest->mCursorImages = aSrc->mCursorImages;
}

void Gecko_SetContentDataImageValue(nsStyleContentData* aContent,
                                    const StyleComputedImageUrl* aUrl) {
  MOZ_ASSERT(aContent && aUrl);

  RefPtr<nsStyleImageRequest> req =
      CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aUrl);
  aContent->SetImageRequest(req.forget());
}

nsStyleContentData::CounterFunction* Gecko_SetCounterFunction(
    nsStyleContentData* aContent, StyleContentType aType) {
  auto counterFunc = MakeRefPtr<nsStyleContentData::CounterFunction>();
  auto* ptr = counterFunc.get();
  aContent->SetCounters(aType, counterFunc.forget());
  return ptr;
}

const nsStyleImageRequest* Gecko_GetImageRequest(const nsStyleImage* aImage) {
  MOZ_ASSERT(aImage);
  return aImage->ImageRequest();
}

nsAtom* Gecko_GetImageElement(const nsStyleImage* aImage) {
  MOZ_ASSERT(aImage && aImage->GetType() == eStyleImageType_Element);
  return const_cast<nsAtom*>(aImage->GetElementId());
}

void Gecko_SetListStyleImageNone(nsStyleList* aList) {
  aList->mListStyleImage = nullptr;
}

void Gecko_SetListStyleImageImageValue(nsStyleList* aList,
                                       const StyleComputedImageUrl* aUrl) {
  MOZ_ASSERT(aList && aUrl);

  aList->mListStyleImage =
      CreateStyleImageRequest(nsStyleImageRequest::Mode(0), aUrl);
}

void Gecko_CopyListStyleImageFrom(nsStyleList* aList,
                                  const nsStyleList* aSource) {
  aList->mListStyleImage = aSource->mListStyleImage;
}

void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity,
                                size_t aElemSize) {
  auto base = reinterpret_cast<
      nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithMemutils>*>(
      aArray);

  base->EnsureCapacity<nsTArrayInfallibleAllocator>(aCapacity, aElemSize);
}

void Gecko_ClearPODTArray(void* aArray, size_t aElementSize,
                          size_t aElementAlign) {
  auto base = reinterpret_cast<
      nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithMemutils>*>(
      aArray);

  base->template ShiftData<nsTArrayInfallibleAllocator>(
      0, base->Length(), 0, aElementSize, aElementAlign);
}

void Gecko_ResizeTArrayForStrings(nsTArray<nsString>* aArray,
                                  uint32_t aLength) {
  aArray->SetLength(aLength);
}

void Gecko_ResizeAtomArray(nsTArray<RefPtr<nsAtom>>* aArray, uint32_t aLength) {
  aArray->SetLength(aLength);
}

void Gecko_SetStyleGridTemplate(UniquePtr<nsStyleGridTemplate>* aGridTemplate,
                                nsStyleGridTemplate* aValue) {
  aGridTemplate->reset(aValue);
}

nsStyleGridTemplate* Gecko_CreateStyleGridTemplate(uint32_t aTrackSizes,
                                                   uint32_t aNameSize) {
  nsStyleGridTemplate* result = new nsStyleGridTemplate;
  result->mTrackSizingFunctions.SetCapacity(aTrackSizes);
  auto auto_ = StyleTrackSize::Breadth(StyleTrackBreadth::Auto());
  for (auto i : IntegerRange(aTrackSizes)) {
    Unused << i;
    result->mTrackSizingFunctions.AppendElement(auto_);
  }
  result->mLineNameLists.SetLength(aNameSize);
  return result;
}

void Gecko_CopyStyleGridTemplateValues(
    UniquePtr<nsStyleGridTemplate>* aGridTemplate,
    const nsStyleGridTemplate* aOther) {
  if (aOther) {
    *aGridTemplate = MakeUnique<nsStyleGridTemplate>(*aOther);
  } else {
    *aGridTemplate = nullptr;
  }
}

void Gecko_ClearAndResizeStyleContents(nsStyleContent* aContent,
                                       uint32_t aHowMany) {
  aContent->AllocateContents(aHowMany);
}

void Gecko_CopyStyleContentsFrom(nsStyleContent* aContent,
                                 const nsStyleContent* aOther) {
  uint32_t count = aOther->ContentCount();

  aContent->AllocateContents(count);

  for (uint32_t i = 0; i < count; ++i) {
    aContent->ContentAt(i) = aOther->ContentAt(i);
  }
}

void Gecko_ClearAndResizeCounterIncrements(nsStyleContent* aContent,
                                           uint32_t aHowMany) {
  aContent->AllocateCounterIncrements(aHowMany);
}

void Gecko_CopyCounterIncrementsFrom(nsStyleContent* aContent,
                                     const nsStyleContent* aOther) {
  uint32_t count = aOther->CounterIncrementCount();

  aContent->AllocateCounterIncrements(count);

  for (uint32_t i = 0; i < count; ++i) {
    const nsStyleCounterData& data = aOther->CounterIncrementAt(i);
    aContent->SetCounterIncrementAt(i, data.mCounter, data.mValue);
  }
}

void Gecko_ClearAndResizeCounterResets(nsStyleContent* aContent,
                                       uint32_t aHowMany) {
  aContent->AllocateCounterResets(aHowMany);
}

void Gecko_CopyCounterResetsFrom(nsStyleContent* aContent,
                                 const nsStyleContent* aOther) {
  uint32_t count = aOther->CounterResetCount();

  aContent->AllocateCounterResets(count);

  for (uint32_t i = 0; i < count; ++i) {
    const nsStyleCounterData& data = aOther->CounterResetAt(i);
    aContent->SetCounterResetAt(i, data.mCounter, data.mValue);
  }
}

void Gecko_ClearAndResizeCounterSets(nsStyleContent* aContent,
                                     uint32_t aHowMany) {
  aContent->AllocateCounterSets(aHowMany);
}

void Gecko_CopyCounterSetsFrom(nsStyleContent* aContent,
                               const nsStyleContent* aOther) {
  uint32_t count = aOther->CounterSetCount();

  aContent->AllocateCounterSets(count);

  for (uint32_t i = 0; i < count; ++i) {
    const nsStyleCounterData& data = aOther->CounterSetAt(i);
    aContent->SetCounterSetAt(i, data.mCounter, data.mValue);
  }
}

void Gecko_EnsureImageLayersLength(nsStyleImageLayers* aLayers, size_t aLen,
                                   nsStyleImageLayers::LayerType aLayerType) {
  size_t oldLength = aLayers->mLayers.Length();

  aLayers->mLayers.EnsureLengthAtLeast(aLen);

  for (size_t i = oldLength; i < aLen; ++i) {
    aLayers->mLayers[i].Initialize(aLayerType);
  }
}

template <typename StyleType>
static void EnsureStyleAutoArrayLength(StyleType* aArray, size_t aLen) {
  size_t oldLength = aArray->Length();

  aArray->EnsureLengthAtLeast(aLen);

  for (size_t i = oldLength; i < aLen; ++i) {
    (*aArray)[i].SetInitialValues();
  }
}

void Gecko_EnsureStyleAnimationArrayLength(void* aArray, size_t aLen) {
  auto base = static_cast<nsStyleAutoArray<StyleAnimation>*>(aArray);
  EnsureStyleAutoArrayLength(base, aLen);
}

void Gecko_EnsureStyleTransitionArrayLength(void* aArray, size_t aLen) {
  auto base = reinterpret_cast<nsStyleAutoArray<StyleTransition>*>(aArray);
  EnsureStyleAutoArrayLength(base, aLen);
}

enum class KeyframeSearchDirection {
  Forwards,
  Backwards,
};

enum class KeyframeInsertPosition {
  Prepend,
  LastForOffset,
};

static Keyframe* GetOrCreateKeyframe(nsTArray<Keyframe>* aKeyframes,
                                     float aOffset,
                                     const nsTimingFunction* aTimingFunction,
                                     KeyframeSearchDirection aSearchDirection,
                                     KeyframeInsertPosition aInsertPosition) {
  MOZ_ASSERT(aKeyframes, "The keyframe array should be valid");
  MOZ_ASSERT(aTimingFunction, "The timing function should be valid");
  MOZ_ASSERT(aOffset >= 0. && aOffset <= 1.,
             "The offset should be in the range of [0.0, 1.0]");

  size_t keyframeIndex;
  switch (aSearchDirection) {
    case KeyframeSearchDirection::Forwards:
      if (nsAnimationManager::FindMatchingKeyframe(
              *aKeyframes, aOffset, *aTimingFunction, keyframeIndex)) {
        return &(*aKeyframes)[keyframeIndex];
      }
      break;
    case KeyframeSearchDirection::Backwards:
      if (nsAnimationManager::FindMatchingKeyframe(Reversed(*aKeyframes),
                                                   aOffset, *aTimingFunction,
                                                   keyframeIndex)) {
        return &(*aKeyframes)[aKeyframes->Length() - 1 - keyframeIndex];
      }
      keyframeIndex = aKeyframes->Length() - 1;
      break;
  }

  Keyframe* keyframe = aKeyframes->InsertElementAt(
      aInsertPosition == KeyframeInsertPosition::Prepend ? 0 : keyframeIndex);
  keyframe->mOffset.emplace(aOffset);
  if (!aTimingFunction->IsLinear()) {
    keyframe->mTimingFunction.emplace();
    keyframe->mTimingFunction->Init(*aTimingFunction);
  }

  return keyframe;
}

Keyframe* Gecko_GetOrCreateKeyframeAtStart(
    nsTArray<Keyframe>* aKeyframes, float aOffset,
    const nsTimingFunction* aTimingFunction) {
  MOZ_ASSERT(aKeyframes->IsEmpty() ||
                 aKeyframes->ElementAt(0).mOffset.value() >= aOffset,
             "The offset should be less than or equal to the first keyframe's "
             "offset if there are exisiting keyframes");

  return GetOrCreateKeyframe(aKeyframes, aOffset, aTimingFunction,
                             KeyframeSearchDirection::Forwards,
                             KeyframeInsertPosition::Prepend);
}

Keyframe* Gecko_GetOrCreateInitialKeyframe(
    nsTArray<Keyframe>* aKeyframes, const nsTimingFunction* aTimingFunction) {
  return GetOrCreateKeyframe(aKeyframes, 0., aTimingFunction,
                             KeyframeSearchDirection::Forwards,
                             KeyframeInsertPosition::LastForOffset);
}

Keyframe* Gecko_GetOrCreateFinalKeyframe(
    nsTArray<Keyframe>* aKeyframes, const nsTimingFunction* aTimingFunction) {
  return GetOrCreateKeyframe(aKeyframes, 1., aTimingFunction,
                             KeyframeSearchDirection::Backwards,
                             KeyframeInsertPosition::LastForOffset);
}

PropertyValuePair* Gecko_AppendPropertyValuePair(
    nsTArray<PropertyValuePair>* aProperties, nsCSSPropertyID aProperty) {
  MOZ_ASSERT(aProperties);
  MOZ_ASSERT(aProperty == eCSSPropertyExtra_variable ||
             !nsCSSProps::PropHasFlags(aProperty, CSSPropFlags::IsLogical));
  return aProperties->AppendElement(PropertyValuePair{aProperty});
}

void Gecko_CopyShapeSourceFrom(StyleShapeSource* aDst,
                               const StyleShapeSource* aSrc) {
  MOZ_ASSERT(aDst);
  MOZ_ASSERT(aSrc);

  *aDst = *aSrc;
}

void Gecko_DestroyShapeSource(StyleShapeSource* aShape) {
  aShape->~StyleShapeSource();
}

void Gecko_NewShapeImage(StyleShapeSource* aShape) {
  aShape->SetShapeImage(MakeUnique<nsStyleImage>());
}

void Gecko_SetToSVGPath(StyleShapeSource* aShape,
                        StyleForgottenArcSlicePtr<StylePathCommand> aCommands,
                        StyleFillRule aFill) {
  MOZ_ASSERT(aShape);
  aShape->SetPath(MakeUnique<StyleSVGPath>(aCommands, aFill));
}

void Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* aSvg, uint32_t aLen) {
  aSvg->mStrokeDasharray.Clear();
  aSvg->mStrokeDasharray.SetLength(aLen);
}

void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* aDst, const nsStyleSVG* aSrc) {
  aDst->mStrokeDasharray = aSrc->mStrokeDasharray;
}

void Gecko_GetComputedURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) {
  MOZ_ASSERT(aURL);
  MOZ_ASSERT(aOut);
  if (aURL->IsLocalRef()) {
    aOut->Assign(aURL->SpecifiedSerialization());
    return;
  }
  Gecko_GetComputedImageURLSpec(aURL, aOut);
}

void Gecko_GetComputedImageURLSpec(const StyleComputedUrl* aURL,
                                   nsCString* aOut) {
  // Image URIs don't serialize local refs as local.
  if (nsIURI* uri = aURL->GetURI()) {
    nsresult rv = uri->GetSpec(*aOut);
    if (NS_SUCCEEDED(rv)) {
      return;
    }
  }

  aOut->AssignLiteral("about:invalid");
}

void Gecko_nsIURI_Debug(nsIURI* aURI, nsCString* aOut) {
  // TODO(emilio): Do we have more useful stuff to put here, maybe?
  if (aURI) {
    *aOut = aURI->GetSpecOrDefault();
  }
}

// XXX Implemented by hand because even though it's thread-safe, only the
// subclasses have the HasThreadSafeRefCnt bits.
void Gecko_AddRefnsIURIArbitraryThread(nsIURI* aPtr) { NS_ADDREF(aPtr); }
void Gecko_ReleasensIURIArbitraryThread(nsIURI* aPtr) { NS_RELEASE(aPtr); }

template <typename ElementLike>
void DebugListAttributes(const ElementLike& aElement, nsCString& aOut) {
  const uint32_t kMaxAttributeLength = 40;

  uint32_t i = 0;
  while (BorrowedAttrInfo info = aElement.GetAttrInfoAt(i++)) {
    aOut.AppendLiteral(" ");
    if (nsAtom* prefix = info.mName->GetPrefix()) {
      aOut.Append(NS_ConvertUTF16toUTF8(nsDependentAtomString(prefix)));
      aOut.AppendLiteral(":");
    }
    aOut.Append(
        NS_ConvertUTF16toUTF8(nsDependentAtomString(info.mName->LocalName())));
    if (!info.mValue) {
      continue;
    }
    aOut.AppendLiteral("=\"");
    nsAutoString value;
    info.mValue->ToString(value);
    if (value.Length() > kMaxAttributeLength) {
      value.Truncate(kMaxAttributeLength - 3);
      value.AppendLiteral("...");
    }
    aOut.Append(NS_ConvertUTF16toUTF8(value));
    aOut.AppendLiteral("\"");
  }
}

void Gecko_Element_DebugListAttributes(const Element* aElement,
                                       nsCString* aOut) {
  DebugListAttributes(*aElement, *aOut);
}

void Gecko_Snapshot_DebugListAttributes(const ServoElementSnapshot* aSnapshot,
                                        nsCString* aOut) {
  DebugListAttributes(*aSnapshot, *aOut);
}

NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData);

void Gecko_nsStyleFont_SetLang(nsStyleFont* aFont, nsAtom* aAtom) {
  aFont->mLanguage = dont_AddRef(aAtom);
  aFont->mExplicitLanguage = true;
}

void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont,
                                    const nsStyleFont* aSource) {
  aFont->mLanguage = aSource->mLanguage;
}

void Gecko_nsStyleFont_PrioritizeUserFonts(
    nsStyleFont* aFont, StyleGenericFontFamily aDefaultGeneric) {
  MOZ_ASSERT(!StaticPrefs::browser_display_use_document_fonts());
  MOZ_ASSERT(aDefaultGeneric != StyleGenericFontFamily::None);
  if (!aFont->mFont.fontlist.PrioritizeFirstGeneric()) {
    aFont->mFont.fontlist.PrependGeneric(aDefaultGeneric);
  }
}

nscoord Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont* aFont,
                                         const Document* aDocument) {
  // Don't change font-size:0, since that would un-hide hidden text, nor chrome
  // docs, we assume those know what they do.
  if (aFont->mSize == 0 || nsContentUtils::IsChromeDoc(aDocument)) {
    return 0;
  }

  nscoord minFontSize;
  bool needsCache = false;

  auto MinFontSize = [&](bool* aNeedsToCache) {
    auto* prefs =
        aDocument->GetFontPrefsForLang(aFont->mLanguage, aNeedsToCache);
    return prefs ? prefs->mMinimumFontSize : 0;
  };

  {
    AutoReadLock guard(*sServoFFILock);
    minFontSize = MinFontSize(&needsCache);
  }

  if (needsCache) {
    AutoWriteLock guard(*sServoFFILock);
    minFontSize = MinFontSize(nullptr);
  }

  if (minFontSize < 0) {
    return 0;
  }

  return (minFontSize * aFont->mMinFontSizeRatio) / 100;
}

void FontSizePrefs::CopyFrom(const LangGroupFontPrefs& prefs) {
  mDefaultVariableSize = prefs.mDefaultVariableFont.size;
  mDefaultSerifSize = prefs.mDefaultSerifFont.size;
  mDefaultSansSerifSize = prefs.mDefaultSansSerifFont.size;
  mDefaultMonospaceSize = prefs.mDefaultMonospaceFont.size;
  mDefaultCursiveSize = prefs.mDefaultCursiveFont.size;
  mDefaultFantasySize = prefs.mDefaultFantasyFont.size;
}

FontSizePrefs Gecko_GetBaseSize(nsAtom* aLanguage) {
  LangGroupFontPrefs prefs;
  nsStaticAtom* langGroupAtom =
      StaticPresData::Get()->GetUncachedLangGroup(aLanguage);
  prefs.Initialize(langGroupAtom);
  FontSizePrefs sizes;
  sizes.CopyFrom(prefs);
  return sizes;
}

const Element* Gecko_GetBindingParent(const Element* aElement) {
  return aElement->GetBindingParent();
}

static StaticRefPtr<UACacheReporter> gUACacheReporter;

namespace mozilla {

void InitializeServo() {
  URLExtraData::InitDummy();
  Servo_Initialize(URLExtraData::Dummy());

  gUACacheReporter = new UACacheReporter();
  RegisterWeakMemoryReporter(gUACacheReporter);

  sServoFFILock = new RWLock("Servo::FFILock");
}

void ShutdownServo() {
  MOZ_ASSERT(sServoFFILock);

  UnregisterWeakMemoryReporter(gUACacheReporter);
  gUACacheReporter = nullptr;

  delete sServoFFILock;
  Servo_Shutdown();
}

void AssertIsMainThreadOrServoFontMetricsLocked() {
  if (!NS_IsMainThread()) {
    MOZ_ASSERT(sServoFFILock &&
               sServoFFILock->LockedForWritingByCurrentThread());
  }
}

}  // namespace mozilla

GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext,
                                      bool aIsVertical,
                                      const nsStyleFont* aFont,
                                      nscoord aFontSize, bool aUseUserFontSet) {
  AutoWriteLock guard(*sServoFFILock);
  GeckoFontMetrics ret;

  // Getting font metrics can require some main thread only work to be
  // done, such as work that needs to touch non-threadsafe refcounted
  // objects (like the DOM FontFace/FontFaceSet objects), network loads, etc.
  //
  // To handle this work, font code checks whether we are in a Servo traversal
  // and if so, appends PostTraversalTasks to the current ServoStyleSet
  // to be performed immediately after the traversal is finished.  This
  // works well for starting downloadable font loads, since we don't have
  // those fonts available to get metrics for anyway.  Platform fonts and
  // ArrayBuffer-backed FontFace objects are handled synchronously.

  nsPresContext* presContext = const_cast<nsPresContext*>(aPresContext);
  presContext->SetUsesExChUnits(true);
  RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetMetricsFor(
      presContext, aIsVertical, aFont, aFontSize, aUseUserFontSet);

  ret.mXSize = fm->XHeight();
  gfxFloat zeroWidth = fm->GetThebesFontGroup()
                           ->GetFirstValidFont()
                           ->GetMetrics(fm->Orientation())
                           .zeroWidth;
  ret.mChSize = zeroWidth >= 0.0
                    ? NS_round(aPresContext->AppUnitsPerDevPixel() * zeroWidth)
                    : -1.0;
  return ret;
}

NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder);

void Gecko_StyleSheet_FinishAsyncParse(
    SheetLoadDataHolder* aData,
    StyleStrong<RawServoStyleSheetContents> aSheetContents,
    StyleOwnedOrNull<StyleUseCounters> aUseCounters) {
  UniquePtr<StyleUseCounters> useCounters = aUseCounters.Consume();
  RefPtr<SheetLoadDataHolder> loadData = aData;
  RefPtr<RawServoStyleSheetContents> sheetContents = aSheetContents.Consume();
  NS_DispatchToMainThread(NS_NewRunnableFunction(
      __func__, [d = std::move(loadData), contents = std::move(sheetContents),
                 counters = std::move(useCounters)]() mutable {
        MOZ_ASSERT(NS_IsMainThread());
        SheetLoadData* data = d->get();
        if (Document* doc = data->mLoader->GetDocument()) {
          if (const StyleUseCounters* docCounters =
                  doc->GetStyleUseCounters()) {
            Servo_UseCounters_Merge(docCounters, counters.get());
          }
        }
        data->mSheet->FinishAsyncParse(contents.forget());
      }));
}

static already_AddRefed<StyleSheet> LoadImportSheet(
    Loader* aLoader, StyleSheet* aParent, SheetLoadData* aParentLoadData,
    LoaderReusableStyleSheets* aReusableSheets, const StyleCssUrl& aURL,
    already_AddRefed<RawServoMediaList> aMediaList) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aLoader, "Should've catched this before");
  MOZ_ASSERT(aParent, "Only used for @import, so parent should exist!");

  RefPtr<MediaList> media = new MediaList(std::move(aMediaList));
  nsCOMPtr<nsIURI> uri = aURL.GetURI();
  nsresult rv = uri ? NS_OK : NS_ERROR_FAILURE;

  StyleSheet* previousFirstChild = aParent->GetFirstChild();
  if (NS_SUCCEEDED(rv)) {
    rv = aLoader->LoadChildSheet(aParent, aParentLoadData, uri, media,
                                 aReusableSheets);
  }

  if (NS_FAILED(rv) || !aParent->GetFirstChild() ||
      aParent->GetFirstChild() == previousFirstChild) {
    // Servo and Gecko have different ideas of what a valid URL is, so we might
    // get in here with a URL string that NS_NewURI can't handle.  We may also
    // reach here via an import cycle.  For the import cycle case, we need some
    // sheet object per spec, even if its empty.  DevTools uses the URI to
    // realize it has hit an import cycle, so we mark it complete to make the
    // sheet readable from JS.
    RefPtr<StyleSheet> emptySheet =
        aParent->CreateEmptyChildSheet(media.forget());
    // Make a dummy URI if we don't have one because some methods assume
    // non-null URIs.
    if (!uri) {
      NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:invalid"));
    }
    emptySheet->SetURIs(uri, uri, uri);
    emptySheet->SetPrincipal(aURL.ExtraData().Principal());
    emptySheet->SetComplete();
    aParent->PrependStyleSheet(emptySheet);
    return emptySheet.forget();
  }

  RefPtr<StyleSheet> sheet = static_cast<StyleSheet*>(aParent->GetFirstChild());
  return sheet.forget();
}

StyleSheet* Gecko_LoadStyleSheet(Loader* aLoader, StyleSheet* aParent,
                                 SheetLoadData* aParentLoadData,
                                 LoaderReusableStyleSheets* aReusableSheets,
                                 const StyleCssUrl* aUrl,
                                 StyleStrong<RawServoMediaList> aMediaList) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aUrl);

  return LoadImportSheet(aLoader, aParent, aParentLoadData, aReusableSheets,
                         *aUrl, aMediaList.Consume())
      .take();
}

void Gecko_LoadStyleSheetAsync(SheetLoadDataHolder* aParentData,
                               const StyleCssUrl* aUrl,
                               StyleStrong<RawServoMediaList> aMediaList,
                               StyleStrong<RawServoImportRule> aImportRule) {
  MOZ_ASSERT(aUrl);
  RefPtr<SheetLoadDataHolder> loadData = aParentData;
  RefPtr<RawServoMediaList> mediaList = aMediaList.Consume();
  RefPtr<RawServoImportRule> importRule = aImportRule.Consume();
  NS_DispatchToMainThread(NS_NewRunnableFunction(
      __func__,
      [data = std::move(loadData), url = StyleCssUrl(*aUrl),
       media = std::move(mediaList), import = std::move(importRule)]() mutable {
        MOZ_ASSERT(NS_IsMainThread());
        SheetLoadData* d = data->get();
        RefPtr<StyleSheet> sheet = LoadImportSheet(
            d->mLoader, d->mSheet, d, nullptr, url, media.forget());
        Servo_ImportRule_SetSheet(import, sheet);
      }));
}

nsCSSKeyword Gecko_LookupCSSKeyword(const uint8_t* aString, uint32_t aLength) {
  nsDependentCSubstring keyword(reinterpret_cast<const char*>(aString),
                                aLength);
  return nsCSSKeywords::LookupKeyword(keyword);
}

const char* Gecko_CSSKeywordString(nsCSSKeyword aKeyword, uint32_t* aLength) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aLength);
  const nsCString& value = nsCSSKeywords::GetStringValue(aKeyword);
  *aLength = value.Length();
  return value.get();
}

void Gecko_AddPropertyToSet(nsCSSPropertyIDSet* aPropertySet,
                            nsCSSPropertyID aProperty) {
  aPropertySet->AddProperty(aProperty);
}

#define STYLE_STRUCT(name)                                             \
                                                                       \
  void Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr,       \
                                             const Document* doc) {    \
    new (ptr) nsStyle##name(*doc);                                     \
  }                                                                    \
                                                                       \
  void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr,           \
                                         const nsStyle##name* other) { \
    new (ptr) nsStyle##name(*other);                                   \
  }                                                                    \
                                                                       \
  void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr) {               \
    ptr->~nsStyle##name();                                             \
  }

void Gecko_RegisterProfilerThread(const char* name) {
  PROFILER_REGISTER_THREAD(name);
}

void Gecko_UnregisterProfilerThread() { PROFILER_UNREGISTER_THREAD(); }

#ifdef MOZ_GECKO_PROFILER
void Gecko_Construct_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel,
                                       JS::ProfilingCategoryPair aCatPair) {
  new (aAutoLabel) AutoProfilerLabel(
      "", nullptr, aCatPair,
      uint32_t(
          js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR));
}

void Gecko_Destroy_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel) {
  aAutoLabel->~AutoProfilerLabel();
}
#endif

bool Gecko_DocumentRule_UseForPresentation(
    const Document* aDocument, const nsACString* aPattern,
    DocumentMatchingFunction aMatchingFunction) {
  MOZ_ASSERT(NS_IsMainThread());

  nsIURI* docURI = aDocument->GetDocumentURI();
  nsAutoCString docURISpec;
  if (docURI) {
    // If GetSpec fails (due to OOM) just skip these URI-specific CSS rules.
    nsresult rv = docURI->GetSpec(docURISpec);
    NS_ENSURE_SUCCESS(rv, false);
  }

  return CSSMozDocumentRule::Match(aDocument, docURI, docURISpec, *aPattern,
                                   aMatchingFunction);
}

void Gecko_SetJemallocThreadLocalArena(bool enabled) {
#if defined(MOZ_MEMORY)
  jemalloc_thread_local_arena(enabled);
#endif
}

#include "nsStyleStructList.h"

#undef STYLE_STRUCT

bool Gecko_ErrorReportingEnabled(const StyleSheet* aSheet,
                                 const Loader* aLoader) {
  return ErrorReporter::ShouldReportErrors(aSheet, aLoader);
}

void Gecko_ReportUnexpectedCSSError(
    const StyleSheet* aSheet, const Loader* aLoader, nsIURI* aURI,
    const char* message, const char* param, uint32_t paramLen,
    const char* prefix, const char* prefixParam, uint32_t prefixParamLen,
    const char* suffix, const char* source, uint32_t sourceLen,
    const char* selectors, uint32_t selectorsLen, uint32_t lineNumber,
    uint32_t colNumber) {
  MOZ_RELEASE_ASSERT(NS_IsMainThread());

  ErrorReporter reporter(aSheet, aLoader, aURI);

  if (prefix) {
    if (prefixParam) {
      nsDependentCSubstring paramValue(prefixParam, prefixParamLen);
      AutoTArray<nsString, 1> wideParam;
      CopyUTF8toUTF16(paramValue, *wideParam.AppendElement());
      reporter.ReportUnexpectedUnescaped(prefix, wideParam);
    } else {
      reporter.ReportUnexpected(prefix);
    }
  }

  if (param) {
    nsDependentCSubstring paramValue(param, paramLen);
    AutoTArray<nsString, 1> wideParam;
    CopyUTF8toUTF16(paramValue, *wideParam.AppendElement());
    reporter.ReportUnexpectedUnescaped(message, wideParam);
  } else {
    reporter.ReportUnexpected(message);
  }

  if (suffix) {
    reporter.ReportUnexpected(suffix);
  }
  nsDependentCSubstring sourceValue(source, sourceLen);
  nsDependentCSubstring selectorsValue(selectors, selectorsLen);
  reporter.OutputError(lineNumber, colNumber, sourceValue, selectorsValue);
}

void Gecko_ContentList_AppendAll(nsSimpleContentList* aList,
                                 const Element** aElements, size_t aLength) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aElements);
  MOZ_ASSERT(aLength);
  MOZ_ASSERT(aList);

  aList->SetCapacity(aLength);

  for (size_t i = 0; i < aLength; ++i) {
    aList->AppendElement(const_cast<Element*>(aElements[i]));
  }
}

const nsTArray<Element*>* Gecko_Document_GetElementsWithId(const Document* aDoc,
                                                           nsAtom* aId) {
  MOZ_ASSERT(aDoc);
  MOZ_ASSERT(aId);

  return aDoc->GetAllElementsForId(nsDependentAtomString(aId));
}

const nsTArray<Element*>* Gecko_ShadowRoot_GetElementsWithId(
    const ShadowRoot* aShadowRoot, nsAtom* aId) {
  MOZ_ASSERT(aShadowRoot);
  MOZ_ASSERT(aId);

  return aShadowRoot->GetAllElementsForId(nsDependentAtomString(aId));
}

bool Gecko_GetBoolPrefValue(const char* aPrefName) {
  MOZ_ASSERT(NS_IsMainThread());
  return Preferences::GetBool(aPrefName);
}

bool Gecko_IsInServoTraversal() { return ServoStyleSet::IsInServoTraversal(); }

bool Gecko_IsMainThread() { return NS_IsMainThread(); }

const nsAttrValue* Gecko_GetSVGAnimatedClass(const Element* aElement) {
  MOZ_ASSERT(aElement->IsSVGElement());
  return static_cast<const SVGElement*>(aElement)->GetAnimatedClassName();
}

bool Gecko_AssertClassAttrValueIsSane(const nsAttrValue* aValue) {
  MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom ||
             aValue->Type() == nsAttrValue::eString ||
             aValue->Type() == nsAttrValue::eAtomArray);
  MOZ_ASSERT_IF(
      aValue->Type() == nsAttrValue::eString,
      nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
          aValue->GetStringValue())
          .IsEmpty());
  return true;
}

void Gecko_LoadData_DeregisterLoad(const StyleLoadData* aData) {
  MOZ_ASSERT(aData->load_id != 0);
  ImageLoader::DeregisterCSSImageFromAllLoaders(*aData);
}

void Gecko_PrintfStderr(const nsCString* aStr) {
  printf_stderr("%s", aStr->get());
}