gecko-dev/layout/style/ServoBindings.cpp
Hiroyuki Ikezoe 0b9b61abdd Bug 1418806 - Try to allocate possible size for AnimationValueMap before composing. r=birtles
The EffectSet count does not exactly represent the count what we really need
for AnimationValueMap, but in most cases it matches.  For example;

1) The element has two different keyframes animations

 @keyframes anim1 {
   to { opacity: 0; }
 }
 @keyframes anim2 {
   to { transform: rotate(360deg); }
 }

 In this case the number matches.

2) The element has two animations but both keyframes have the same CSS property

 @keyframes anim1 {
   to { opacity: 0; }
 }
 @keyframes anim2 {
   to { opacity: 0.1; }
 }

 In this case the number doesn't match, moreover it results more memory than we
 ever needed, but this case is presumably less common.

3) The element has an animation having keyframes for two different CSS
   properties.

 @keyframes anim {
   from { opacity: 0; transform: rotate(360deg); }
 }

 In this kind of cases, the number doesn't match.  But even so, this patch
 reduces the opportunities that the AnimationValueMap tries to allocate a new
 memory (i.e. less opportunities on expanding the map).

Note that when the hash map is expanded, we do allocate a new RawTable with the
new size then replace the old one with the new one [1], so I believe this
change will reduce the crash rate to some extent.

[1] https://hg.mozilla.org/mozilla-central/file/15c95df467be/servo/components/hashglobe/src/hash_map.rs#l734

MozReview-Commit-ID: 6tcF9aqXh7a

--HG--
extra : rebase_source : 366989d3a2756f5a5711503a57f42f3b746d93a5
2018-06-26 11:08:24 +09:00

2933 lines
86 KiB
C++

/* -*- 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/. */
#include "mozilla/ServoBindings.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 "nsIDocumentInlines.h"
#include "nsILoadContext.h"
#include "nsIFrame.h"
#include "nsIMemoryReporter.h"
#include "nsIMozBrowserFrame.h"
#include "nsINode.h"
#include "nsIPresShell.h"
#include "nsIPresShellInlines.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 "nsSVGElement.h"
#include "nsTArray.h"
#include "nsTransitionManager.h"
#include "nsWindowSizes.h"
#include "mozilla/CORSMode.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/RestyleManager.h"
#include "mozilla/SizeOfState.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/SystemGroup.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/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;
#define SERVO_ARC_TYPE(name_, type_) \
already_AddRefed<type_> \
type_##Strong::Consume() { \
RefPtr<type_> result; \
result.swap(mPtr); \
return result.forget(); \
}
#include "mozilla/ServoArcTypeList.h"
SERVO_ARC_TYPE(ComputedStyle, ComputedStyle)
#undef SERVO_ARC_TYPE
// Definitions of the global traversal stats.
bool ServoTraversalStatistics::sActive = false;
ServoTraversalStatistics ServoTraversalStatistics::sSingleton;
static RWLock* sServoFFILock = nullptr;
static
const nsFont*
ThreadSafeGetDefaultFontHelper(const nsPresContext* aPresContext,
nsAtom* aLanguage, uint8_t aGenericId)
{
bool needsCache = false;
const nsFont* retval;
{
AutoReadLock guard(*sServoFFILock);
retval = aPresContext->GetDefaultFont(aGenericId, aLanguage, &needsCache);
}
if (!needsCache) {
return retval;
}
{
AutoWriteLock guard(*sServoFFILock);
retval = aPresContext->GetDefaultFont(aGenericId, aLanguage, nullptr);
}
return retval;
}
void
AssertIsMainThreadOrServoLangFontPrefsCacheLocked()
{
MOZ_ASSERT(NS_IsMainThread() || sServoFFILock->LockedForWritingByCurrentThread());
}
/*
* Does this child count as significant for selector matching?
*
* See nsStyleUtil::IsSignificantChild for details.
*/
bool
Gecko_IsSignificantChild(RawGeckoNodeBorrowed aNode,
bool aWhitespaceIsSignificant)
{
return nsStyleUtil::ThreadSafeIsSignificantChild(aNode->AsContent(),
aWhitespaceIsSignificant);
}
RawGeckoNodeBorrowedOrNull
Gecko_GetLastChild(RawGeckoNodeBorrowed aNode)
{
return aNode->GetLastChild();
}
RawGeckoNodeBorrowedOrNull
Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed aNode)
{
return aNode->GetFlattenedTreeParentNodeForStyle();
}
RawGeckoElementBorrowedOrNull
Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed aElement, bool aIsBefore)
{
MOZ_ASSERT(aElement);
MOZ_ASSERT(aElement->HasProperties());
return aIsBefore
? nsLayoutUtils::GetBeforePseudo(aElement)
: nsLayoutUtils::GetAfterPseudo(aElement);
}
nsTArray<nsIContent*>*
Gecko_GetAnonymousContentForElement(RawGeckoElementBorrowed 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(RawGeckoElementBorrowed aElement)
{
MOZ_ASSERT(HTMLSlotElement::FromNode(aElement));
return &static_cast<const HTMLSlotElement*>(aElement)->AssignedNodes();
}
void
Gecko_ComputedStyle_Init(
mozilla::ComputedStyle* aStyle,
const mozilla::ComputedStyle* aParentContext,
RawGeckoPresContextBorrowed aPresContext,
const ServoComputedData* aValues,
mozilla::CSSPseudoElementType aPseudoType,
nsAtom* aPseudoTag)
{
auto* presContext = const_cast<nsPresContext*>(aPresContext);
new (KnownNotNull, aStyle) mozilla::ComputedStyle(
presContext, aPseudoTag, 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(mozilla::ComputedStyle* aStyle)
{
aStyle->~ComputedStyle();
}
void
Gecko_ConstructStyleChildrenIterator(
RawGeckoElementBorrowed aElement,
RawGeckoStyleChildrenIteratorBorrowedMut aIterator)
{
MOZ_ASSERT(aElement);
MOZ_ASSERT(aIterator);
new (aIterator) StyleChildrenIterator(aElement);
}
void
Gecko_DestroyStyleChildrenIterator(
RawGeckoStyleChildrenIteratorBorrowedMut aIterator)
{
MOZ_ASSERT(aIterator);
aIterator->~StyleChildrenIterator();
}
RawGeckoNodeBorrowed
Gecko_GetNextStyleChild(RawGeckoStyleChildrenIteratorBorrowedMut aIterator)
{
MOZ_ASSERT(aIterator);
return aIterator->GetNextChild();
}
bool
Gecko_IsPrivateBrowsingEnabled(const nsIDocument* aDoc)
{
MOZ_ASSERT(aDoc);
MOZ_ASSERT(NS_IsMainThread());
nsILoadContext* loadContext = aDoc->GetLoadContext();
return loadContext && loadContext->UsePrivateBrowsing();
}
EventStates::ServoType
Gecko_ElementState(RawGeckoElementBorrowed aElement)
{
return aElement->StyleState().ServoValue();
}
bool
Gecko_IsRootElement(RawGeckoElementBorrowed aElement)
{
return aElement->OwnerDoc()->GetRootElement() == aElement;
}
// Dirtiness tracking.
void
Gecko_SetNodeFlags(RawGeckoNodeBorrowed aNode, uint32_t aFlags)
{
const_cast<nsINode*>(aNode)->SetFlags(aFlags);
}
void
Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed aNode, uint32_t aFlags)
{
const_cast<nsINode*>(aNode)->UnsetFlags(aFlags);
}
void
Gecko_NoteDirtyElement(RawGeckoElementBorrowed aElement)
{
MOZ_ASSERT(NS_IsMainThread());
const_cast<Element*>(aElement)->NoteDirtyForServo();
}
void
Gecko_NoteDirtySubtreeForInvalidation(RawGeckoElementBorrowed aElement)
{
MOZ_ASSERT(NS_IsMainThread());
const_cast<Element*>(aElement)->NoteDirtySubtreeForServo();
}
void
Gecko_NoteAnimationOnlyDirtyElement(RawGeckoElementBorrowed aElement)
{
MOZ_ASSERT(NS_IsMainThread());
const_cast<Element*>(aElement)->NoteAnimationOnlyDirtyForServo();
}
bool Gecko_AnimationNameMayBeReferencedFromStyle(
RawGeckoPresContextBorrowed aPresContext,
nsAtom* aName)
{
MOZ_ASSERT(aPresContext);
return aPresContext->AnimationManager()->AnimationMayBeReferenced(aName);
}
CSSPseudoElementType
Gecko_GetImplementedPseudo(RawGeckoElementBorrowed aElement)
{
return aElement->GetPseudoElementType();
}
uint32_t
Gecko_CalcStyleDifference(ComputedStyleBorrowed aOldStyle,
ComputedStyleBorrowed aNewStyle,
bool* aAnyStyleStructChanged,
bool* aOnlyResetStructsChanged)
{
MOZ_ASSERT(aOldStyle);
MOZ_ASSERT(aNewStyle);
uint32_t equalStructs;
nsChangeHint result = const_cast<ComputedStyle*>(aOldStyle)->
CalcStyleDifference(const_cast<ComputedStyle*>(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);
}
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetStyleAttrDeclarationBlock(RawGeckoElementBorrowed aElement)
{
DeclarationBlock* decl = aElement->GetInlineStyleDeclaration();
if (!decl) {
return nullptr;
}
return decl->RefRawStrong();
}
void
Gecko_UnsetDirtyStyleAttr(RawGeckoElementBorrowed aElement)
{
DeclarationBlock* decl = aElement->GetInlineStyleDeclaration();
if (!decl) {
return;
}
decl->UnsetDirty();
}
static const RawServoDeclarationBlockStrong*
AsRefRawStrong(const RefPtr<RawServoDeclarationBlock>& aDecl)
{
static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
sizeof(RawServoDeclarationBlockStrong),
"RefPtr should just be a pointer");
return reinterpret_cast<const RawServoDeclarationBlockStrong*>(&aDecl);
}
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetHTMLPresentationAttrDeclarationBlock(RawGeckoElementBorrowed aElement)
{
const nsMappedAttributes* attrs = aElement->GetMappedAttributes();
if (!attrs) {
auto* svg = nsSVGElement::FromNodeOrNull(aElement);
if (svg) {
if (auto decl = svg->GetContentDeclarationBlock()) {
return decl->RefRawStrong();
}
}
return nullptr;
}
return AsRefRawStrong(attrs->GetServoStyle());
}
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetExtraContentStyleDeclarations(RawGeckoElementBorrowed 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;
}
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetUnvisitedLinkAttrDeclarationBlock(RawGeckoElementBorrowed 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();
}
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetVisitedLinkAttrDeclarationBlock(RawGeckoElementBorrowed aElement)
{
nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet();
if (!sheet) {
return nullptr;
}
return AsRefRawStrong(sheet->GetServoVisitedLinkDecl());
}
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetActiveLinkAttrDeclarationBlock(RawGeckoElementBorrowed aElement)
{
nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet();
if (!sheet) {
return nullptr;
}
return AsRefRawStrong(sheet->GetServoActiveLinkDecl());
}
static CSSPseudoElementType
GetPseudoTypeFromElementForAnimation(const Element*& aElementOrPseudo) {
if (aElementOrPseudo->IsGeneratedContentContainerForBefore()) {
aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement();
return CSSPseudoElementType::before;
}
if (aElementOrPseudo->IsGeneratedContentContainerForAfter()) {
aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement();
return CSSPseudoElementType::after;
}
return CSSPseudoElementType::NotPseudo;
}
bool
Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
EffectCompositor::CascadeLevel aCascadeLevel,
RawServoAnimationValueMapBorrowedMut aAnimationValues)
{
MOZ_ASSERT(aElement);
nsIDocument* doc = aElement->GetComposedDoc();
if (!doc) {
return false;
}
nsPresContext* presContext = doc->GetPresContext();
if (!presContext || !presContext->IsDynamic()) {
// For print or print preview, ignore animations.
return false;
}
CSSPseudoElementType pseudoType =
GetPseudoTypeFromElementForAnimation(aElement);
return presContext->EffectCompositor()
->GetServoAnimationRule(aElement,
pseudoType,
aCascadeLevel,
aAnimationValues);
}
bool
Gecko_StyleAnimationsEquals(RawGeckoStyleAnimationListBorrowed aA,
RawGeckoStyleAnimationListBorrowed aB)
{
return *aA == *aB;
}
void
Gecko_CopyAnimationNames(RawGeckoStyleAnimationListBorrowedMut aDest,
RawGeckoStyleAnimationListBorrowed 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(RawGeckoElementBorrowed aElement,
ComputedStyleBorrowedOrNull aOldComputedData,
ComputedStyleBorrowedOrNull 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());
CSSPseudoElementType pseudoType =
GetPseudoTypeFromElementForAnimation(aElement);
if (aTasks & UpdateAnimationsTasks::CSSAnimations) {
presContext->AnimationManager()->
UpdateAnimations(const_cast<dom::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<dom::Element*>(aElement), pseudoType,
*aOldComputedData,
*aComputedData);
}
if (aTasks & UpdateAnimationsTasks::EffectProperties) {
presContext->EffectCompositor()->UpdateEffectProperties(
aComputedData, const_cast<dom::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(RawGeckoElementBorrowed aElementOrPseudo)
{
CSSPseudoElementType pseudoType =
GetPseudoTypeFromElementForAnimation(aElementOrPseudo);
EffectSet* effectSet = EffectSet::GetEffectSet(aElementOrPseudo, pseudoType);
return effectSet ? effectSet->Count() : 0;
}
bool
Gecko_ElementHasAnimations(RawGeckoElementBorrowed aElement)
{
CSSPseudoElementType pseudoType =
GetPseudoTypeFromElementForAnimation(aElement);
return !!EffectSet::GetEffectSet(aElement, pseudoType);
}
bool
Gecko_ElementHasCSSAnimations(RawGeckoElementBorrowed aElement)
{
CSSPseudoElementType pseudoType =
GetPseudoTypeFromElementForAnimation(aElement);
nsAnimationManager::CSSAnimationCollection* collection =
nsAnimationManager::CSSAnimationCollection
::GetAnimationCollection(aElement, pseudoType);
return collection && !collection->mAnimations.IsEmpty();
}
bool
Gecko_ElementHasCSSTransitions(RawGeckoElementBorrowed aElement)
{
CSSPseudoElementType pseudoType =
GetPseudoTypeFromElementForAnimation(aElement);
nsTransitionManager::CSSTransitionCollection* collection =
nsTransitionManager::CSSTransitionCollection
::GetAnimationCollection(aElement, pseudoType);
return collection && !collection->mAnimations.IsEmpty();
}
size_t
Gecko_ElementTransitions_Length(RawGeckoElementBorrowed aElement)
{
CSSPseudoElementType pseudoType =
GetPseudoTypeFromElementForAnimation(aElement);
nsTransitionManager::CSSTransitionCollection* collection =
nsTransitionManager::CSSTransitionCollection
::GetAnimationCollection(aElement, pseudoType);
return collection ? collection->mAnimations.Length() : 0;
}
static CSSTransition*
GetCurrentTransitionAt(RawGeckoElementBorrowed aElement, size_t aIndex)
{
CSSPseudoElementType 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(RawGeckoElementBorrowed aElement,
size_t aIndex)
{
CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex);
return transition ? transition->TransitionProperty()
: nsCSSPropertyID::eCSSProperty_UNKNOWN;
}
RawServoAnimationValueBorrowedOrNull
Gecko_ElementTransitions_EndValueAt(RawGeckoElementBorrowed aElement,
size_t aIndex)
{
CSSTransition* transition = GetCurrentTransitionAt(aElement,
aIndex);
return transition ? transition->ToValue().mServo.get() : nullptr;
}
double
Gecko_GetProgressFromComputedTiming(RawGeckoComputedTimingBorrowed aComputedTiming)
{
return aComputedTiming->mProgress.Value();
}
double
Gecko_GetPositionInSegment(RawGeckoAnimationPropertySegmentBorrowed 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);
}
RawServoAnimationValueBorrowedOrNull
Gecko_AnimationGetBaseStyle(void* aBaseStyles, nsCSSPropertyID aProperty)
{
auto base =
static_cast<nsRefPtrHashtable<nsUint32HashKey, RawServoAnimationValue>*>
(aBaseStyles);
return base->GetWeak(aProperty);
}
void
Gecko_FillAllImageLayers(nsStyleImageLayers* aLayers, uint32_t aMaxLen)
{
aLayers->FillAllLayers(aMaxLen);
}
bool
Gecko_IsDocumentBody(RawGeckoElementBorrowed aElement)
{
nsIDocument* doc = aElement->GetUncomposedDoc();
return doc && doc->GetBodyElement() == aElement;
}
nscolor
Gecko_GetLookAndFeelSystemColor(int32_t aId,
RawGeckoPresContextBorrowed aPresContext)
{
bool useStandinsForNativeColors = aPresContext && !aPresContext->IsChrome();
nscolor result;
LookAndFeel::ColorID colorId = static_cast<LookAndFeel::ColorID>(aId);
AutoWriteLock guard(*sServoFFILock);
LookAndFeel::GetColor(colorId, useStandinsForNativeColors, &result);
return result;
}
bool
Gecko_MatchLang(RawGeckoElementBorrowed 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(RawGeckoElementBorrowed 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();
}
nsIDocument::DocumentTheme
Gecko_GetDocumentLWTheme(const nsIDocument* aDocument)
{
return aDocument->ThreadSafeGetDocumentLWTheme();
}
bool
Gecko_IsTableBorderNonzero(RawGeckoElementBorrowed 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(RawGeckoElementBorrowed aElement)
{
nsIMozBrowserFrame* browserFrame =
const_cast<Element*>(aElement)->GetAsMozBrowserFrame();
return browserFrame && browserFrame->GetReallyIsBrowser();
}
template <typename Implementor>
static nsAtom*
AtomAttrValue(Implementor* aElement, nsAtom* aName)
{
const nsAttrValue* attr = aElement->GetParsedAttr(aName);
return attr ? attr->GetAtomValue() : nullptr;
}
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_ \
= nsCaseInsensitiveStringComparator(); \
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);
}
/**
* Returns whether an element contains a class in its class list or not.
*/
template <typename Implementor>
static bool
HasClass(Implementor* aElement, nsAtom* aClass, bool aIgnoreCase)
{
const nsAttrValue* attr = aElement->DoGetClasses();
if (!attr) {
return false;
}
return attr->Contains(aClass, aIgnoreCase ? eIgnoreCase : eCaseMatters);
}
/**
* Gets the class or class list (if any) of the implementor. The calling
* convention here is rather hairy, and is optimized for getting Servo the
* information it needs for hot calls.
*
* The return value indicates the number of classes. If zero, neither outparam
* is valid. If one, the class_ outparam is filled with the atom of the class.
* If two or more, the classList outparam is set to point to an array of atoms
* representing the class list.
*
* The array is borrowed and the atoms are not addrefed. These values can be
* invalidated by any DOM mutation. Use them in a tight scope.
*/
template <typename Implementor>
static uint32_t
ClassOrClassList(Implementor* aElement, nsAtom** aClass, nsAtom*** aClassList)
{
const nsAttrValue* attr = aElement->DoGetClasses();
if (!attr) {
return 0;
}
// For class values with only whitespace, Gecko just stores a string. For the
// purposes of the style system, there is no class in this case.
nsAttrValue::ValueType type = attr->Type();
if (MOZ_UNLIKELY(type == nsAttrValue::eString)) {
MOZ_ASSERT(nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
attr->GetStringValue()).IsEmpty());
return 0;
}
// Single tokens are generally stored as an atom. Check that case.
if (type == nsAttrValue::eAtom) {
*aClass = attr->GetAtomValue();
return 1;
}
// At this point we should have an atom array. It is likely, but not
// guaranteed, that we have two or more elements in the array.
MOZ_ASSERT(type == nsAttrValue::eAtomArray);
nsTArray<RefPtr<nsAtom>>* atomArray = attr->GetAtomArrayValue();
uint32_t length = atomArray->Length();
// Special case: zero elements.
if (length == 0) {
return 0;
}
// Special case: one element.
if (length == 1) {
*aClass = atomArray->ElementAt(0);
return 1;
}
// General case: Two or more elements.
//
// Note: We could also expose this array as an array of nsCOMPtrs, since
// bindgen knows what those look like, and eliminate the reinterpret_cast.
// But it's not obvious that that would be preferable.
static_assert(sizeof(RefPtr<nsAtom>) == sizeof(nsAtom*), "Bad simplification");
static_assert(alignof(RefPtr<nsAtom>) == alignof(nsAtom*), "Bad simplification");
RefPtr<nsAtom>* elements = atomArray->Elements();
nsAtom** rawElements = reinterpret_cast<nsAtom**>(elements);
*aClassList = rawElements;
return atomArray->Length();
}
#define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \
nsAtom* prefix_##AtomAttrValue(implementor_ aElement, nsAtom* aName) \
{ \
return AtomAttrValue(aElement, aName); \
} \
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); \
} \
uint32_t prefix_##ClassOrClassList(implementor_ aElement, nsAtom** aClass, \
nsAtom*** aClassList) \
{ \
return ClassOrClassList(aElement, aClass, aClassList); \
} \
bool prefix_##HasClass(implementor_ aElement, nsAtom* aClass, bool aIgnoreCase)\
{ \
return HasClass(aElement, aClass, aIgnoreCase); \
} \
SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
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,
bool aQuoted)
{
FontFamilyName family;
aName->ToString(family.mName);
if (aQuoted) {
family.mType = eFamily_named_quoted;
}
aNames->AppendElement(family);
}
void
Gecko_nsTArray_FontFamilyName_AppendGeneric(nsTArray<FontFamilyName>* aNames,
FontFamilyType 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(mozilla::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, RawGeckoPresContextBorrowed aPresContext)
{
const nsFont* defaultVariableFont = ThreadSafeGetDefaultFontHelper(aPresContext, aFont->mLanguage,
kPresContext_DefaultVariableFont_ID);
// 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.
nsFont* system = new (aDest) nsFont(*defaultVariableFont);
MOZ_RELEASE_ASSERT(system);
*aDest = *defaultVariableFont;
LookAndFeel::FontID fontID = static_cast<LookAndFeel::FontID>(aFontId);
AutoWriteLock guard(*sServoFFILock);
nsLayoutUtils::ComputeSystemFont(aDest, fontID, aPresContext,
defaultVariableFont);
}
void
Gecko_nsFont_Destroy(nsFont* aDest)
{
aDest->~nsFont();
}
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(
nsDependentAtomString(aFamily),
nsDependentAtomString(aName),
aAlternate
);
}
void
Gecko_nsFont_SetFontFeatureValuesLookup(nsFont* aFont,
const RawGeckoPresContext* aPresContext)
{
aFont->featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
}
float
Gecko_FontStretch_ToFloat(mozilla::FontStretch aStretch)
{
// Servo represents percentages with 1. being 100%.
return aStretch.Percentage() / 100.0f;
}
void
Gecko_FontStretch_SetFloat(mozilla::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(mozilla::FontSlantStyle* aStyle)
{
*aStyle = mozilla::FontSlantStyle::Normal();
}
void
Gecko_FontSlantStyle_SetItalic(mozilla::FontSlantStyle* aStyle)
{
*aStyle = mozilla::FontSlantStyle::Italic();
}
void
Gecko_FontSlantStyle_SetOblique(mozilla::FontSlantStyle* aStyle,
float aAngleInDegrees)
{
*aStyle = mozilla::FontSlantStyle::Oblique(aAngleInDegrees);
}
void
Gecko_FontSlantStyle_Get(mozilla::FontSlantStyle aStyle,
bool* aNormal,
bool* aItalic,
float* aObliqueAngle)
{
*aNormal = aStyle.IsNormal();
*aItalic = aStyle.IsItalic();
if (aStyle.IsOblique()) {
*aObliqueAngle = aStyle.ObliqueAngle();
}
}
float
Gecko_FontWeight_ToFloat(mozilla::FontWeight aWeight)
{
return aWeight.ToFloat();
}
void
Gecko_FontWeight_SetFloat(mozilla::FontWeight* aWeight,
float aFloat)
{
*aWeight = mozilla::FontWeight(aFloat);
}
void
Gecko_nsFont_ResetFontFeatureValuesLookup(nsFont* aFont)
{
aFont->featureValueLookup = nullptr;
}
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);
aDest->featureValueLookup = aSrc->featureValueLookup;
}
void
Gecko_SetImageOrientation(nsStyleVisibility* aVisibility,
uint8_t aOrientation, bool aFlip)
{
aVisibility->mImageOrientation =
nsStyleImageOrientation::CreateAsOrientationAndFlip(aOrientation, aFlip);
}
void
Gecko_SetImageOrientationAsFromImage(nsStyleVisibility* aVisibility)
{
aVisibility->mImageOrientation = nsStyleImageOrientation::CreateAsFromImage();
}
void
Gecko_CopyImageOrientationFrom(nsStyleVisibility* aDst,
const nsStyleVisibility* aSrc)
{
aDst->mImageOrientation = aSrc->mImageOrientation;
}
void
Gecko_SetCounterStyleToName(CounterStylePtr* aPtr, nsAtom* aName,
RawGeckoPresContextBorrowed aPresContext)
{
// Try resolving the counter style if possible, and keep it unresolved
// otherwise.
CounterStyleManager* manager = aPresContext->CounterStyleManager();
RefPtr<nsAtom> name = already_AddRefed<nsAtom>(aName);
if (CounterStyle* style = manager->GetCounterStyle(name)) {
*aPtr = style;
} else {
*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)
{
if (!aPtr->IsResolved()) {
return aPtr->AsAtom();
}
return (*aPtr)->GetStyleName();
}
const AnonymousCounterStyle*
Gecko_CounterStyle_GetAnonymous(const CounterStylePtr* aPtr)
{
return aPtr->AsAnonymous();
}
already_AddRefed<css::URLValue>
ServoBundledURI::IntoCssUrl()
{
MOZ_ASSERT(mExtraData->GetReferrer());
MOZ_ASSERT(mExtraData->GetPrincipal());
RefPtr<css::URLValue> urlValue =
new css::URLValue(mURLString, do_AddRef(mExtraData));
return urlValue.forget();
}
already_AddRefed<css::ImageValue>
ServoBundledURI::IntoCssImage(mozilla::CORSMode aCorsMode)
{
RefPtr<css::ImageValue> urlValue =
new css::ImageValue(mURLString, do_AddRef(mExtraData), aCorsMode);
return urlValue.forget();
}
void
Gecko_SetNullImageValue(nsStyleImage* aImage)
{
MOZ_ASSERT(aImage);
aImage->SetNull();
}
void
Gecko_SetGradientImageValue(nsStyleImage* aImage, nsStyleGradient* aGradient)
{
MOZ_ASSERT(aImage);
aImage->SetGradientData(aGradient);
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::ImageValue, ImageValue);
static already_AddRefed<nsStyleImageRequest>
CreateStyleImageRequest(nsStyleImageRequest::Mode aModeFlags,
mozilla::css::ImageValue* aImageValue)
{
RefPtr<nsStyleImageRequest> req =
new nsStyleImageRequest(aModeFlags, aImageValue);
return req.forget();
}
mozilla::css::ImageValue*
Gecko_ImageValue_Create(ServoBundledURI aURI)
{
// Bug 1434963: Change this to accept a CORS mode from the caller.
return aURI.IntoCssImage(mozilla::CORSMode::CORS_NONE).take();
}
MOZ_DEFINE_MALLOC_SIZE_OF(GeckoImageValueMallocSizeOf)
size_t
Gecko_ImageValue_SizeOfIncludingThis(mozilla::css::ImageValue* aImageValue)
{
MOZ_ASSERT(NS_IsMainThread());
return aImageValue->SizeOfIncludingThis(GeckoImageValueMallocSizeOf);
}
void
Gecko_SetLayerImageImageValue(nsStyleImage* aImage,
mozilla::css::ImageValue* aImageValue)
{
MOZ_ASSERT(aImage && aImageValue);
RefPtr<nsStyleImageRequest> req =
CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aImageValue);
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<nsStyleSides>());
}
void
Gecko_SetCursorArrayLength(nsStyleUserInterface* aStyleUI, size_t aLen)
{
aStyleUI->mCursorImages.Clear();
aStyleUI->mCursorImages.SetLength(aLen);
}
void
Gecko_SetCursorImageValue(nsCursorImage* aCursor,
mozilla::css::ImageValue* aImageValue)
{
MOZ_ASSERT(aCursor && aImageValue);
aCursor->mImage =
CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard, aImageValue);
}
void
Gecko_CopyCursorArrayFrom(nsStyleUserInterface* aDest,
const nsStyleUserInterface* aSrc)
{
aDest->mCursorImages = aSrc->mCursorImages;
}
void
Gecko_SetContentDataImageValue(nsStyleContentData* aContent,
mozilla::css::ImageValue* aImageValue)
{
MOZ_ASSERT(aContent && aImageValue);
RefPtr<nsStyleImageRequest> req =
CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aImageValue);
aContent->SetImageRequest(req.forget());
}
nsStyleContentData::CounterFunction*
Gecko_SetCounterFunction(nsStyleContentData* aContent, nsStyleContentType aType)
{
RefPtr<nsStyleContentData::CounterFunction>
counterFunc = new nsStyleContentData::CounterFunction();
nsStyleContentData::CounterFunction* ptr = counterFunc;
aContent->SetCounters(aType, counterFunc.forget());
return ptr;
}
nsStyleGradient*
Gecko_CreateGradient(uint8_t aShape,
uint8_t aSize,
bool aRepeating,
bool aLegacySyntax,
bool aMozLegacySyntax,
uint32_t aStopCount)
{
nsStyleGradient* result = new nsStyleGradient();
result->mShape = aShape;
result->mSize = aSize;
result->mRepeating = aRepeating;
result->mLegacySyntax = aLegacySyntax;
result->mMozLegacySyntax = aMozLegacySyntax;
result->mAngle.SetNoneValue();
result->mBgPosX.SetNoneValue();
result->mBgPosY.SetNoneValue();
result->mRadiusX.SetNoneValue();
result->mRadiusY.SetNoneValue();
nsStyleGradientStop dummyStop = {
nsStyleCoord(eStyleUnit_None),
StyleComplexColor::FromColor(NS_RGB(0, 0, 0)),
0
};
for (uint32_t i = 0; i < aStopCount; i++) {
result->mStops.AppendElement(dummyStop);
}
return result;
}
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());
}
const nsStyleGradient*
Gecko_GetGradientImageValue(const nsStyleImage* aImage)
{
MOZ_ASSERT(aImage && aImage->GetType() == eStyleImageType_Gradient);
return aImage->GetGradientData();
}
void
Gecko_SetListStyleImageNone(nsStyleList* aList)
{
aList->mListStyleImage = nullptr;
}
void
Gecko_SetListStyleImageImageValue(nsStyleList* aList,
mozilla::css::ImageValue* aImageValue)
{
MOZ_ASSERT(aList && aImageValue);
aList->mListStyleImage =
CreateStyleImageRequest(nsStyleImageRequest::Mode(0), aImageValue);
}
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_SetStyleGridTemplate(UniquePtr<nsStyleGridTemplate>* aGridTemplate,
nsStyleGridTemplate* aValue)
{
aGridTemplate->reset(aValue);
}
nsStyleGridTemplate*
Gecko_CreateStyleGridTemplate(uint32_t aTrackSizes, uint32_t aNameSize)
{
nsStyleGridTemplate* result = new nsStyleGridTemplate;
result->mMinTrackSizingFunctions.SetLength(aTrackSizes);
result->mMaxTrackSizingFunctions.SetLength(aTrackSizes);
result->mLineNameLists.SetLength(aNameSize);
return result;
}
void
Gecko_CopyStyleGridTemplateValues(UniquePtr<nsStyleGridTemplate>* aGridTemplate,
const nsStyleGridTemplate* aOther)
{
if (aOther) {
*aGridTemplate = MakeUnique<nsStyleGridTemplate>(*aOther);
} else {
*aGridTemplate = nullptr;
}
}
mozilla::css::GridTemplateAreasValue*
Gecko_NewGridTemplateAreasValue(uint32_t aAreas, uint32_t aTemplates, uint32_t aColumns)
{
RefPtr<mozilla::css::GridTemplateAreasValue> value = new mozilla::css::GridTemplateAreasValue;
value->mNamedAreas.SetLength(aAreas);
value->mTemplates.SetLength(aTemplates);
value->mNColumns = aColumns;
return value.forget().take();
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::GridTemplateAreasValue, GridTemplateAreasValue);
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_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);
}
void
Gecko_ClearWillChange(nsStyleDisplay* aDisplay, size_t aLength)
{
aDisplay->mWillChange.Clear();
aDisplay->mWillChange.SetCapacity(aLength);
}
void
Gecko_AppendWillChange(nsStyleDisplay* aDisplay, nsAtom* aAtom)
{
aDisplay->mWillChange.AppendElement(aAtom);
}
void
Gecko_CopyWillChangeFrom(nsStyleDisplay* aDest, nsStyleDisplay* aSrc)
{
aDest->mWillChange.Clear();
aDest->mWillChange.AppendElements(aSrc->mWillChange);
}
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->mType != nsTimingFunction::Type::Linear) {
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);
return aProperties->AppendElement(PropertyValuePair {aProperty});
}
void
Gecko_ResetStyleCoord(nsStyleUnit* aUnit, nsStyleUnion* aValue)
{
nsStyleCoord::Reset(*aUnit, *aValue);
}
void
Gecko_SetStyleCoordCalcValue(nsStyleUnit* aUnit, nsStyleUnion* aValue, nsStyleCoord::CalcValue aCalc)
{
// Calc units should be cleaned up first
MOZ_ASSERT(*aUnit != nsStyleUnit::eStyleUnit_Calc);
nsStyleCoord::Calc* calcRef = new nsStyleCoord::Calc();
calcRef->mLength = aCalc.mLength;
calcRef->mPercent = aCalc.mPercent;
calcRef->mHasPercent = aCalc.mHasPercent;
*aUnit = nsStyleUnit::eStyleUnit_Calc;
aValue->mPointer = calcRef;
calcRef->AddRef();
}
void
Gecko_CopyShapeSourceFrom(mozilla::StyleShapeSource* aDst, const mozilla::StyleShapeSource* aSrc)
{
MOZ_ASSERT(aDst);
MOZ_ASSERT(aSrc);
*aDst = *aSrc;
}
void
Gecko_DestroyShapeSource(mozilla::StyleShapeSource* aShape)
{
aShape->~StyleShapeSource();
}
void
Gecko_StyleShapeSource_SetURLValue(StyleShapeSource* aShape, URLValue* aURL)
{
aShape->SetURL(aURL);
}
void
Gecko_NewBasicShape(mozilla::StyleShapeSource* aShape,
mozilla::StyleBasicShapeType aType)
{
aShape->SetBasicShape(MakeUnique<mozilla::StyleBasicShape>(aType),
StyleGeometryBox::NoBox);
}
void
Gecko_NewShapeImage(mozilla::StyleShapeSource* aShape)
{
aShape->SetShapeImage(MakeUnique<nsStyleImage>());
}
void
Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len)
{
effects->mFilters.Clear();
effects->mFilters.SetLength(new_len);
}
void
Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest)
{
aDest->mFilters = aSrc->mFilters;
}
void
Gecko_nsStyleFilter_SetURLValue(nsStyleFilter* aEffects, URLValue* aURL)
{
aEffects->SetURL(aURL);
}
void
Gecko_nsStyleSVGPaint_CopyFrom(nsStyleSVGPaint* aDest, const nsStyleSVGPaint* aSrc)
{
*aDest = *aSrc;
}
void
Gecko_nsStyleSVGPaint_SetURLValue(nsStyleSVGPaint* aPaint, URLValue* aURL)
{
aPaint->SetPaintServer(aURL);
}
void Gecko_nsStyleSVGPaint_Reset(nsStyleSVGPaint* aPaint)
{
aPaint->SetNone();
}
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_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* aSvg, uint32_t aLen)
{
aSvg->mContextProps.Clear();
aSvg->mContextProps.SetLength(aLen);
}
void
Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* aDst, const nsStyleSVG* aSrc)
{
aDst->mContextProps = aSrc->mContextProps;
aDst->mContextPropsBits = aSrc->mContextPropsBits;
}
css::URLValue*
Gecko_NewURLValue(ServoBundledURI aURI)
{
RefPtr<css::URLValue> url = aURI.IntoCssUrl();
return url.forget().take();
}
MOZ_DEFINE_MALLOC_SIZE_OF(GeckoURLValueMallocSizeOf)
size_t
Gecko_URLValue_SizeOfIncludingThis(URLValue* aURL)
{
MOZ_ASSERT(NS_IsMainThread());
return aURL->SizeOfIncludingThis(GeckoURLValueMallocSizeOf);
}
void
Gecko_GetComputedURLSpec(const URLValueData* aURL, nsCString* aOut)
{
MOZ_ASSERT(aURL);
MOZ_ASSERT(aOut);
if (aURL->IsLocalRef()) {
aOut->Assign(aURL->GetString());
return;
}
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();
}
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(css::URLValue, CSSURLValue);
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData);
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
nsCSSShadowArray*
Gecko_NewCSSShadowArray(uint32_t aLen)
{
RefPtr<nsCSSShadowArray> arr = new(aLen) nsCSSShadowArray(aLen);
return arr.forget().take();
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
nsStyleQuoteValues*
Gecko_NewStyleQuoteValues(uint32_t aLen)
{
RefPtr<nsStyleQuoteValues> values = new nsStyleQuoteValues;
values->mQuotePairs.SetLength(aLen);
return values.forget().take();
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsStyleQuoteValues, QuoteValues);
nsCSSValueSharedList*
Gecko_NewCSSValueSharedList(uint32_t aLen)
{
RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList;
if (aLen == 0) {
return list.forget().take();
}
list->mHead = new nsCSSValueList;
nsCSSValueList* cur = list->mHead;
for (uint32_t i = 0; i < aLen - 1; i++) {
cur->mNext = new nsCSSValueList;
cur = cur->mNext;
}
return list.forget().take();
}
nsCSSValueSharedList*
Gecko_NewNoneTransform()
{
RefPtr<nsCSSValueSharedList> list = new nsCSSValueSharedList;
list->mHead = new nsCSSValueList;
list->mHead->mValue.SetNoneValue();
return list.forget().take();
}
void
Gecko_CSSValue_SetNumber(nsCSSValueBorrowedMut aCSSValue, float aNumber)
{
aCSSValue->SetFloatValue(aNumber, eCSSUnit_Number);
}
float
Gecko_CSSValue_GetNumber(nsCSSValueBorrowed aCSSValue)
{
return aCSSValue->GetFloatValue();
}
void
Gecko_CSSValue_SetKeyword(nsCSSValueBorrowedMut aCSSValue, nsCSSKeyword aKeyword)
{
aCSSValue->SetEnumValue(aKeyword);
}
nsCSSKeyword
Gecko_CSSValue_GetKeyword(nsCSSValueBorrowed aCSSValue)
{
return aCSSValue->GetKeywordValue();
}
void
Gecko_CSSValue_SetPercentage(nsCSSValueBorrowedMut aCSSValue, float aPercent)
{
aCSSValue->SetPercentValue(aPercent);
}
float
Gecko_CSSValue_GetPercentage(nsCSSValueBorrowed aCSSValue)
{
return aCSSValue->GetPercentValue();
}
void
Gecko_CSSValue_SetPixelLength(nsCSSValueBorrowedMut aCSSValue, float aLen)
{
MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null ||
aCSSValue->GetUnit() == eCSSUnit_Pixel);
aCSSValue->SetFloatValue(aLen, eCSSUnit_Pixel);
}
void
Gecko_CSSValue_SetCalc(nsCSSValueBorrowedMut aCSSValue, nsStyleCoord::CalcValue aCalc)
{
aCSSValue->SetCalcValue(&aCalc);
}
nsStyleCoord::CalcValue
Gecko_CSSValue_GetCalc(nsCSSValueBorrowed aCSSValue)
{
return aCSSValue->GetCalcValue();
}
void
Gecko_CSSValue_SetFunction(nsCSSValueBorrowedMut aCSSValue, int32_t aLen)
{
nsCSSValue::Array* arr = nsCSSValue::Array::Create(aLen);
aCSSValue->SetArrayValue(arr, eCSSUnit_Function);
}
void
Gecko_CSSValue_SetString(nsCSSValueBorrowedMut aCSSValue,
const uint8_t* aString, uint32_t aLength,
nsCSSUnit aUnit)
{
MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null);
nsString string;
nsDependentCSubstring slice(reinterpret_cast<const char*>(aString),
aLength);
AppendUTF8toUTF16(slice, string);
aCSSValue->SetStringValue(string, aUnit);
}
void
Gecko_CSSValue_SetStringFromAtom(nsCSSValueBorrowedMut aCSSValue,
nsAtom* aAtom, nsCSSUnit aUnit)
{
aCSSValue->SetStringValue(nsDependentAtomString(aAtom), aUnit);
}
void
Gecko_CSSValue_SetAtomIdent(nsCSSValueBorrowedMut aCSSValue, nsAtom* aAtom)
{
aCSSValue->SetAtomIdentValue(already_AddRefed<nsAtom>(aAtom));
}
void
Gecko_CSSValue_SetArray(nsCSSValueBorrowedMut aCSSValue, int32_t aLength)
{
MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null);
RefPtr<nsCSSValue::Array> array
= nsCSSValue::Array::Create(aLength);
aCSSValue->SetArrayValue(array, eCSSUnit_Array);
}
void
Gecko_CSSValue_SetURL(nsCSSValueBorrowedMut aCSSValue, URLValue* aURL)
{
MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null);
aCSSValue->SetURLValue(aURL);
}
void
Gecko_CSSValue_SetInt(nsCSSValueBorrowedMut aCSSValue,
int32_t aInteger, nsCSSUnit aUnit)
{
aCSSValue->SetIntValue(aInteger, aUnit);
}
void
Gecko_CSSValue_SetFloat(nsCSSValueBorrowedMut aCSSValue,
float aValue, nsCSSUnit aUnit)
{
aCSSValue->SetFloatValue(aValue, aUnit);
}
nsCSSValueBorrowedMut
Gecko_CSSValue_GetArrayItem(nsCSSValueBorrowedMut aCSSValue, int32_t aIndex)
{
return &aCSSValue->GetArrayValue()->Item(aIndex);
}
nsCSSValueBorrowed
Gecko_CSSValue_GetArrayItemConst(nsCSSValueBorrowed aCSSValue, int32_t aIndex)
{
return &aCSSValue->GetArrayValue()->Item(aIndex);
}
void
Gecko_CSSValue_SetPair(nsCSSValueBorrowedMut aCSSValue,
nsCSSValueBorrowed aXValue, nsCSSValueBorrowed aYValue)
{
MOZ_ASSERT(NS_IsMainThread());
aCSSValue->SetPairValue(*aXValue, *aYValue);
}
void
Gecko_CSSValue_SetList(nsCSSValueBorrowedMut aCSSValue, uint32_t aLen)
{
MOZ_ASSERT(NS_IsMainThread());
nsCSSValueList* item = aCSSValue->SetListValue();
for (uint32_t i = 1; i < aLen; ++i) {
item->mNext = new nsCSSValueList;
item = item->mNext;
}
}
void
Gecko_CSSValue_SetPairList(nsCSSValueBorrowedMut aCSSValue, uint32_t aLen)
{
MOZ_ASSERT(NS_IsMainThread());
nsCSSValuePairList* item = aCSSValue->SetPairListValue();
for (uint32_t i = 1; i < aLen; ++i) {
item->mNext = new nsCSSValuePairList;
item = item->mNext;
}
}
void
Gecko_CSSValue_InitSharedList(nsCSSValueBorrowedMut aCSSValue,
uint32_t aLen)
{
MOZ_ASSERT(aLen > 0, "Must create at least one nsCSSValueList (mHead)");
nsCSSValueSharedList* list = new nsCSSValueSharedList;
aCSSValue->SetSharedListValue(list);
list->mHead = new nsCSSValueList;
nsCSSValueList* cur = list->mHead;
for (uint32_t i = 1; i < aLen; ++i) {
cur->mNext = new nsCSSValueList;
cur = cur->mNext;
}
}
void
Gecko_CSSValue_Drop(nsCSSValueBorrowedMut aCSSValue)
{
aCSSValue->~nsCSSValue();
}
void
Gecko_CSSValue_SetFontStretch(nsCSSValueBorrowedMut aCSSValue,
float stretch)
{
aCSSValue->SetFontStretch(
FontStretch(std::min(stretch * 100.0f, float(FontStretch::kMax))));
}
// FIXME(emilio): This function should probably have `Oblique` in its name.
void
Gecko_CSSValue_SetFontSlantStyle(nsCSSValueBorrowedMut aCSSValue,
float aAngle)
{
aCSSValue->SetFontSlantStyle(mozilla::FontSlantStyle::Oblique(aAngle));
}
void
Gecko_CSSValue_SetFontWeight(nsCSSValueBorrowedMut aCSSValue,
float weight)
{
aCSSValue->SetFontWeight(mozilla::FontWeight(weight));
}
void
Gecko_nsStyleFont_SetLang(nsStyleFont* aFont, nsAtom* aAtom)
{
already_AddRefed<nsAtom> atom = already_AddRefed<nsAtom>(aAtom);
aFont->mLanguage = atom;
aFont->mExplicitLanguage = true;
}
void
Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, const nsStyleFont* aSource)
{
aFont->mLanguage = aSource->mLanguage;
}
void
Gecko_nsStyleFont_FixupNoneGeneric(nsStyleFont* aFont,
RawGeckoPresContextBorrowed aPresContext)
{
const nsFont* defaultVariableFont =
ThreadSafeGetDefaultFontHelper(aPresContext, aFont->mLanguage,
kPresContext_DefaultVariableFont_ID);
nsLayoutUtils::FixupNoneGeneric(&aFont->mFont, aPresContext,
aFont->mGenericID, defaultVariableFont);
}
void
Gecko_nsStyleFont_PrefillDefaultForGeneric(nsStyleFont* aFont,
RawGeckoPresContextBorrowed aPresContext,
uint8_t aGenericId)
{
const nsFont* defaultFont = ThreadSafeGetDefaultFontHelper(aPresContext, aFont->mLanguage,
aGenericId);
// In case of just the language changing, the parent could have had no generic,
// which Gecko just does regular cascading with. Do the same.
// This can only happen in the case where the language changed but the family did not
if (aGenericId != kGenericFont_NONE) {
aFont->mFont.fontlist = defaultFont->fontlist;
} else {
aFont->mFont.fontlist.SetDefaultFontType(defaultFont->fontlist.GetDefaultFontType());
}
}
void
Gecko_nsStyleFont_FixupMinFontSize(nsStyleFont* aFont,
RawGeckoPresContextBorrowed aPresContext)
{
nscoord minFontSize;
bool needsCache = false;
{
AutoReadLock guard(*sServoFFILock);
minFontSize = aPresContext->MinFontSize(aFont->mLanguage, &needsCache);
}
if (needsCache) {
AutoWriteLock guard(*sServoFFILock);
minFontSize = aPresContext->MinFontSize(aFont->mLanguage, nullptr);
}
nsLayoutUtils::ApplyMinFontSize(aFont, aPresContext, minFontSize);
}
void
FontSizePrefs::CopyFrom(const LangGroupFontPrefs& prefs)
{
mDefaultVariableSize = prefs.mDefaultVariableFont.size;
mDefaultFixedSize = prefs.mDefaultFixedFont.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;
RefPtr<nsAtom> langGroupAtom = StaticPresData::Get()->GetUncachedLangGroup(aLanguage);
prefs.Initialize(langGroupAtom);
FontSizePrefs sizes;
sizes.CopyFrom(prefs);
return sizes;
}
RawGeckoElementBorrowedOrNull
Gecko_GetBindingParent(RawGeckoElementBorrowed aElement)
{
nsIContent* parent = aElement->GetBindingParent();
return parent ? parent->AsElement() : nullptr;
}
RawServoAuthorStylesBorrowedOrNull
Gecko_XBLBinding_GetRawServoStyles(RawGeckoXBLBindingBorrowed aXBLBinding)
{
return aXBLBinding->GetServoStyles();
}
bool
Gecko_XBLBinding_InheritsStyle(RawGeckoXBLBindingBorrowed aXBLBinding)
{
return aXBLBinding->InheritsStyle();
}
static StaticRefPtr<UACacheReporter> gUACacheReporter;
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();
}
namespace mozilla {
void
AssertIsMainThreadOrServoFontMetricsLocked()
{
if (!NS_IsMainThread()) {
MOZ_ASSERT(sServoFFILock &&
sServoFFILock->LockedForWritingByCurrentThread());
}
}
} // namespace mozilla
GeckoFontMetrics
Gecko_GetFontMetrics(RawGeckoPresContextBorrowed 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,
nsLayoutUtils::FlushUserFontSet::No);
ret.mXSize = fm->XHeight();
gfxFloat zeroWidth = fm->GetThebesFontGroup()->GetFirstValidFont()->
GetMetrics(fm->Orientation()).zeroOrAveCharWidth;
ret.mChSize = ceil(aPresContext->AppUnitsPerDevPixel() * zeroWidth);
return ret;
}
int32_t
Gecko_GetAppUnitsPerPhysicalInch(RawGeckoPresContextBorrowed aPresContext)
{
nsPresContext* presContext = const_cast<nsPresContext*>(aPresContext);
return presContext->DeviceContext()->AppUnitsPerPhysicalInch();
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder);
void
Gecko_StyleSheet_FinishAsyncParse(SheetLoadDataHolder* aData,
RawServoStyleSheetContentsStrong aSheetContents)
{
RefPtr<SheetLoadDataHolder> loadData = aData;
RefPtr<RawServoStyleSheetContents> sheetContents = aSheetContents.Consume();
NS_DispatchToMainThread(NS_NewRunnableFunction(__func__,
[d = std::move(loadData),
s = std::move(sheetContents)]() mutable {
MOZ_ASSERT(NS_IsMainThread());
d->get()->mSheet->FinishAsyncParse(s.forget());
}));
}
static already_AddRefed<StyleSheet>
LoadImportSheet(css::Loader* aLoader,
StyleSheet* aParent,
SheetLoadData* aParentLoadData,
css::LoaderReusableStyleSheets* aReusableSheets,
css::URLValue* 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!");
MOZ_ASSERT(aURL, "Invalid URLs shouldn't be loaded!");
RefPtr<dom::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->mExtraData->GetPrincipal());
emptySheet->SetComplete();
aParent->PrependStyleSheet(emptySheet);
return emptySheet.forget();
}
RefPtr<StyleSheet> sheet =
static_cast<StyleSheet*>(aParent->GetFirstChild());
return sheet.forget();
}
StyleSheet*
Gecko_LoadStyleSheet(css::Loader* aLoader,
StyleSheet* aParent,
SheetLoadData* aParentLoadData,
css::LoaderReusableStyleSheets* aReusableSheets,
ServoBundledURI aServoURL,
RawServoMediaListStrong aMediaList)
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<css::URLValue> url = aServoURL.IntoCssUrl();
return LoadImportSheet(aLoader, aParent, aParentLoadData, aReusableSheets,
url, aMediaList.Consume()).take();
}
void
Gecko_LoadStyleSheetAsync(css::SheetLoadDataHolder* aParentData,
ServoBundledURI aServoURL,
RawServoMediaListStrong aMediaList,
RawServoImportRuleStrong aImportRule)
{
RefPtr<SheetLoadDataHolder> loadData = aParentData;
RefPtr<css::URLValue> urlVal = aServoURL.IntoCssUrl();
RefPtr<RawServoMediaList> mediaList = aMediaList.Consume();
RefPtr<RawServoImportRule> importRule = aImportRule.Consume();
NS_DispatchToMainThread(NS_NewRunnableFunction(__func__,
[data = std::move(loadData),
url = std::move(urlVal),
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(nsCSSPropertyIDSetBorrowedMut aPropertySet,
nsCSSPropertyID aProperty)
{
aPropertySet->AddProperty(aProperty);
}
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
#define STYLE_STRUCT(name) \
\
void \
Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr, \
const nsPresContext* pres_context) \
{ \
new (ptr) nsStyle##name(pres_context); \
} \
\
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();
}
bool
Gecko_DocumentRule_UseForPresentation(RawGeckoPresContextBorrowed aPresContext,
const nsACString* aPattern,
css::URLMatchingFunction aURLMatchingFunction)
{
MOZ_ASSERT(NS_IsMainThread());
nsIDocument *doc = aPresContext->Document();
nsIURI *docURI = doc->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(doc, docURI, docURISpec, *aPattern,
aURLMatchingFunction);
}
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,
uint32_t lineNumber,
uint32_t colNumber)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
ErrorReporter reporter(aSheet, aLoader, aURI);
if (prefix) {
if (prefixParam) {
nsDependentCSubstring paramValue(prefixParam, prefixParamLen);
nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
reporter.ReportUnexpectedUnescaped(prefix, wideParam);
} else {
reporter.ReportUnexpected(prefix);
}
}
if (param) {
nsDependentCSubstring paramValue(param, paramLen);
nsAutoString wideParam = NS_ConvertUTF8toUTF16(paramValue);
reporter.ReportUnexpectedUnescaped(message, wideParam);
} else {
reporter.ReportUnexpected(message);
}
if (suffix) {
reporter.ReportUnexpected(suffix);
}
nsDependentCSubstring sourceValue(source, sourceLen);
reporter.OutputError(lineNumber, colNumber, sourceValue);
}
void
Gecko_AddBufferToCrashReport(const void* addr, size_t len)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsICrashReporter> cr = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
NS_ENSURE_TRUE_VOID(cr);
cr->RegisterAppMemory((uint64_t) addr, len);
}
void
Gecko_AnnotateCrashReport(const char* key_str, const char* value_str)
{
MOZ_ASSERT(NS_IsMainThread());
nsDependentCString key(key_str);
nsDependentCString value(value_str);
nsCOMPtr<nsICrashReporter> cr = do_GetService("@mozilla.org/toolkit/crash-reporter;1");
NS_ENSURE_TRUE_VOID(cr);
cr->AnnotateCrashReport(key, value);
}
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 nsIDocument* 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();
}