/* -*- 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 "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 "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/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; #define SERVO_ARC_TYPE(name_, type_) \ already_AddRefed type_##Strong::Consume() { \ RefPtr 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; auto GetDefaultFont = [&](bool* aNeedsToCache) { auto* prefs = aPresContext->Document()->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(RawGeckoNodeBorrowed aNode, bool aWhitespaceIsSignificant) { return nsStyleUtil::ThreadSafeIsSignificantChild(aNode->AsContent(), aWhitespaceIsSignificant); } RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed aNode) { return aNode->GetLastChild(); } RawGeckoNodeBorrowedOrNull Gecko_GetPreviousSibling( RawGeckoNodeBorrowed aNode) { return aNode->GetPreviousSibling(); } 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* Gecko_GetAnonymousContentForElement( RawGeckoElementBorrowed aElement) { nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame()); if (!ac) { return nullptr; } auto* array = new nsTArray(); nsContentUtils::AppendNativeAnonymousChildren(aElement, *array, 0); return array; } void Gecko_DestroyAnonymousContentList(nsTArray* aAnonContent) { MOZ_ASSERT(aAnonContent); delete aAnonContent; } const nsTArray>* Gecko_GetAssignedNodes( RawGeckoElementBorrowed aElement) { MOZ_ASSERT(HTMLSlotElement::FromNode(aElement)); return &static_cast(aElement)->AssignedNodes(); } void Gecko_ComputedStyle_Init(ComputedStyle* aStyle, const ServoComputedData* aValues, CSSPseudoElementType aPseudoType, nsAtom* aPseudoTag) { new (KnownNotNull, aStyle) ComputedStyle(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(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_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(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(aNode)->SetFlags(aFlags); } void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed aNode, uint32_t aFlags) { const_cast(aNode)->UnsetFlags(aFlags); } void Gecko_NoteDirtyElement(RawGeckoElementBorrowed aElement) { MOZ_ASSERT(NS_IsMainThread()); const_cast(aElement)->NoteDirtyForServo(); } void Gecko_NoteDirtySubtreeForInvalidation(RawGeckoElementBorrowed aElement) { MOZ_ASSERT(NS_IsMainThread()); const_cast(aElement)->NoteDirtySubtreeForServo(); } void Gecko_NoteAnimationOnlyDirtyElement(RawGeckoElementBorrowed aElement) { MOZ_ASSERT(NS_IsMainThread()); const_cast(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 = 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(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& aDecl) { static_assert(sizeof(RefPtr) == sizeof(RawServoDeclarationBlockStrong), "RefPtr should just be a pointer"); return reinterpret_cast(&aDecl); } RawServoDeclarationBlockStrongBorrowedOrNull Gecko_GetHTMLPresentationAttrDeclarationBlock( RawGeckoElementBorrowed 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()); } RawServoDeclarationBlockStrongBorrowedOrNull Gecko_GetExtraContentStyleDeclarations(RawGeckoElementBorrowed aElement) { if (!aElement->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th)) { return nullptr; } const HTMLTableCellElement* cell = static_cast(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 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(newSheet.forget().take()); } void Gecko_StyleSheet_AddRef(const StyleSheet* aSheet) { MOZ_ASSERT(NS_IsMainThread()); const_cast(aSheet)->AddRef(); } void Gecko_StyleSheet_Release(const StyleSheet* aSheet) { MOZ_ASSERT(NS_IsMainThread()); const_cast(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); 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; } 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(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(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(aElement), pseudoType, *aOldComputedData, *aComputedData); } if (aTasks & UpdateAnimationsTasks::EffectProperties) { presContext->EffectCompositor()->UpdateEffectProperties( aComputedData, const_cast(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(aElement), pseudoType); } } if (aTasks & UpdateAnimationsTasks::DisplayChangedFromNone) { presContext->EffectCompositor()->RequestRestyle( const_cast(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>& 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*>( aBaseStyles); return base->GetWeak(aProperty); } void Gecko_FillAllImageLayers(nsStyleImageLayers* aLayers, uint32_t aMaxLen) { aLayers->FillAllLayers(aMaxLen); } bool Gecko_IsDocumentBody(RawGeckoElementBorrowed aElement) { Document* 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(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 atom = attr->GetAtomValue(); return atom.forget().take(); } Document::DocumentTheme Gecko_GetDocumentLWTheme(const Document* 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(aElement)->GetAsMozBrowserFrame(); return browserFrame && browserFrame->GetReallyIsBrowser(); } template 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 atom = attr->GetAtomValue(); return atom.forget().take(); } template 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 static bool HasAttr(Implementor* aElement, nsAtom* aNS, nsAtom* aName) { auto match = [](const nsAttrValue* aValue) { return true; }; return DoMatch(aElement, aNS, aName, match); } template 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 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 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 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 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 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_, 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* aNames, nsAtom* aName, bool aQuoted) { aNames->AppendElement( FontFamilyName(aName, aQuoted ? eQuotedName : eUnquotedName)); } void Gecko_nsTArray_FontFamilyName_AppendGeneric( nsTArray* aNames, FontFamilyType aType) { aNames->AppendElement(FontFamilyName(aType)); } SharedFontList* Gecko_SharedFontList_Create() { RefPtr 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(aFontId); AutoWriteLock guard(*sServoFFILock); nsLayoutUtils::ComputeSystemFont(aDest, fontID, defaultVariableFont); } void Gecko_nsFont_Destroy(nsFont* aDest) { aDest->~nsFont(); } gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet() { return new gfxFontFeatureValueSet(); } nsTArray* 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); } 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_SetCounterStyleToName(CounterStylePtr* aPtr, nsAtom* aName, RawGeckoPresContextBorrowed aPresContext) { RefPtr name = already_AddRefed(aName); *aPtr = name.forget(); } void Gecko_SetCounterStyleToSymbols(CounterStylePtr* aPtr, uint8_t aSymbolsType, nsACString const* const* aSymbols, uint32_t aSymbolsCount) { nsTArray 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, nsStyleGradient* aGradient) { MOZ_ASSERT(aImage); aImage->SetGradientData(aGradient); } static already_AddRefed CreateStyleImageRequest( nsStyleImageRequest::Mode aModeFlags, URLValue* aImageValue) { RefPtr req = new nsStyleImageRequest(aModeFlags, aImageValue); return req.forget(); } void Gecko_SetLayerImageImageValue(nsStyleImage* aImage, URLValue* aImageValue) { MOZ_ASSERT(aImage && aImageValue); RefPtr 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()); } void Gecko_SetCursorArrayLength(nsStyleUI* aStyleUI, size_t aLen) { aStyleUI->mCursorImages.Clear(); aStyleUI->mCursorImages.SetLength(aLen); } void Gecko_SetCursorImageValue(nsCursorImage* aCursor, URLValue* aImageValue) { MOZ_ASSERT(aCursor && aImageValue); aCursor->mImage = CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard, aImageValue); } void Gecko_CopyCursorArrayFrom(nsStyleUI* aDest, const nsStyleUI* aSrc) { aDest->mCursorImages = aSrc->mCursorImages; } void Gecko_SetContentDataImageValue(nsStyleContentData* aContent, URLValue* aImageValue) { MOZ_ASSERT(aContent && aImageValue); RefPtr req = CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aImageValue); aContent->SetImageRequest(req.forget()); } nsStyleContentData::CounterFunction* Gecko_SetCounterFunction( nsStyleContentData* aContent, StyleContentType aType) { auto counterFunc = MakeRefPtr(); auto* ptr = counterFunc.get(); 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::Black(), 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(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, URLValue* 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*>( aArray); base->EnsureCapacity(aCapacity, aElemSize); } void Gecko_ClearPODTArray(void* aArray, size_t aElementSize, size_t aElementAlign) { auto base = reinterpret_cast< nsTArray_base*>( aArray); base->template ShiftData( 0, base->Length(), 0, aElementSize, aElementAlign); } void Gecko_ResizeTArrayForStrings(nsTArray* aArray, uint32_t aLength) { aArray->SetLength(aLength); } void Gecko_SetStyleGridTemplate(UniquePtr* 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* aGridTemplate, const nsStyleGridTemplate* aOther) { if (aOther) { *aGridTemplate = MakeUnique(*aOther); } else { *aGridTemplate = nullptr; } } GridTemplateAreasValue* Gecko_NewGridTemplateAreasValue(uint32_t aAreas, uint32_t aTemplates, uint32_t aColumns) { RefPtr value = new GridTemplateAreasValue; value->mNamedAreas.SetLength(aAreas); value->mTemplates.SetLength(aTemplates); value->mNColumns = aColumns; return value.forget().take(); } NS_IMPL_THREADSAFE_FFI_REFCOUNTING(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 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*>(aArray); EnsureStyleAutoArrayLength(base, aLen); } void Gecko_EnsureStyleTransitionArrayLength(void* aArray, size_t aLen) { auto base = reinterpret_cast*>(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* 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* 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* aKeyframes, const nsTimingFunction* aTimingFunction) { return GetOrCreateKeyframe(aKeyframes, 0., aTimingFunction, KeyframeSearchDirection::Forwards, KeyframeInsertPosition::LastForOffset); } Keyframe* Gecko_GetOrCreateFinalKeyframe( nsTArray* aKeyframes, const nsTimingFunction* aTimingFunction) { return GetOrCreateKeyframe(aKeyframes, 1., aTimingFunction, KeyframeSearchDirection::Backwards, KeyframeInsertPosition::LastForOffset); } PropertyValuePair* Gecko_AppendPropertyValuePair( nsTArray* aProperties, nsCSSPropertyID aProperty) { MOZ_ASSERT(aProperties); MOZ_ASSERT(aProperty == eCSSPropertyExtra_variable || !nsCSSProps::PropHasFlags(aProperty, CSSPropFlags::IsLogical)); 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(aType), StyleGeometryBox::NoBox); } void Gecko_NewShapeImage(mozilla::StyleShapeSource* aShape) { aShape->SetShapeImage(MakeUnique()); } void Gecko_NewStyleSVGPath(mozilla::StyleShapeSource* aShape) { MOZ_ASSERT(aShape); aShape->SetPath(MakeUnique()); } void Gecko_SetStyleMotion(UniquePtr* aMotion, mozilla::StyleMotion* aValue) { MOZ_ASSERT(aMotion); aMotion->reset(aValue); } mozilla::StyleMotion* Gecko_NewStyleMotion() { return new StyleMotion(); } void Gecko_CopyStyleMotions(mozilla::UniquePtr* aMotion, const mozilla::StyleMotion* aOther) { MOZ_ASSERT(aMotion); *aMotion = aOther ? MakeUnique(*aOther) : nullptr; } 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_URLValue_Create(RawServoCssUrlDataStrong aCssUrl, CORSMode aCORSMode) { RefPtr url = new css::URLValue(aCssUrl.Consume(), aCORSMode); 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 URLValue* aURL, nsCString* aOut) { MOZ_ASSERT(aURL); MOZ_ASSERT(aOut); if (aURL->IsLocalRef()) { aOut->Assign(aURL->GetString()); return; } Gecko_GetComputedImageURLSpec(aURL, aOut); } void Gecko_GetComputedImageURLSpec(const URLValue* 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(); } } template 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(RawGeckoElementBorrowed aElement, nsCString* aOut) { DebugListAttributes(*aElement, *aOut); } void Gecko_Snapshot_DebugListAttributes(const ServoElementSnapshot* aSnapshot, nsCString* aOut) { DebugListAttributes(*aSnapshot, *aOut); } 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 arr = new (aLen) nsCSSShadowArray(aLen); return arr.forget().take(); } NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray); nsCSSValueSharedList* Gecko_NewCSSValueSharedList(uint32_t aLen) { RefPtr 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 list = new nsCSSValueSharedList; list->mHead = new nsCSSValueList; list->mHead->mValue.SetNoneValue(); return list.forget().take(); } void Gecko_StyleDisplay_GenerateCombinedTransform(nsStyleDisplay* aDisplay) { aDisplay->GenerateCombinedIndividualTransform(); } 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(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(aAtom)); } void Gecko_CSSValue_SetArray(nsCSSValueBorrowedMut aCSSValue, int32_t aLength) { MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null); RefPtr array = nsCSSValue::Array::Create(aLength); aCSSValue->SetArrayValue(array, eCSSUnit_Array); } 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_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_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; auto MinFontSize = [&](bool* aNeedsToCache) { auto* prefs = aPresContext->Document()->GetFontPrefsForLang( aFont->mLanguage, aNeedsToCache); return prefs ? prefs->mMinimumFontSize : 0; }; { AutoReadLock guard(*sServoFFILock); minFontSize = MinFontSize(&needsCache); } if (needsCache) { AutoWriteLock guard(*sServoFFILock); minFontSize = MinFontSize(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 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 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(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(aPresContext); presContext->SetUsesExChUnits(true); RefPtr fm = nsLayoutUtils::GetMetricsFor( presContext, aIsVertical, aFont, aFontSize, aUseUserFontSet); ret.mXSize = fm->XHeight(); gfxFloat zeroWidth = fm->GetThebesFontGroup() ->GetFirstValidFont() ->GetMetrics(fm->Orientation()) .zeroOrAveCharWidth; ret.mChSize = NS_round(aPresContext->AppUnitsPerDevPixel() * zeroWidth); return ret; } int32_t Gecko_GetAppUnitsPerPhysicalInch( RawGeckoPresContextBorrowed aPresContext) { nsPresContext* presContext = const_cast(aPresContext); return presContext->DeviceContext()->AppUnitsPerPhysicalInch(); } NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder); void Gecko_StyleSheet_FinishAsyncParse( SheetLoadDataHolder* aData, RawServoStyleSheetContentsStrong aSheetContents, StyleUseCountersOwned aUseCounters) { UniquePtr useCounters(aUseCounters); RefPtr loadData = aData; RefPtr 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 LoadImportSheet( css::Loader* aLoader, StyleSheet* aParent, SheetLoadData* aParentLoadData, css::LoaderReusableStyleSheets* aReusableSheets, css::URLValue* aURL, already_AddRefed 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 media = new MediaList(std::move(aMediaList)); nsCOMPtr 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 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 sheet = static_cast(aParent->GetFirstChild()); return sheet.forget(); } StyleSheet* Gecko_LoadStyleSheet( css::Loader* aLoader, StyleSheet* aParent, SheetLoadData* aParentLoadData, css::LoaderReusableStyleSheets* aReusableSheets, RawServoCssUrlDataStrong aCssUrl, RawServoMediaListStrong aMediaList) { MOZ_ASSERT(NS_IsMainThread()); // The CORS mode in the URLValue is irrelevant here. // (CORS_NONE is used for all imported sheets in Load::LoadChildSheet.) RefPtr url = new css::URLValue(aCssUrl.Consume(), CORS_NONE); return LoadImportSheet(aLoader, aParent, aParentLoadData, aReusableSheets, url, aMediaList.Consume()) .take(); } void Gecko_LoadStyleSheetAsync(css::SheetLoadDataHolder* aParentData, RawServoCssUrlDataStrong aCssUrl, RawServoMediaListStrong aMediaList, RawServoImportRuleStrong aImportRule) { RefPtr loadData = aParentData; // The CORS mode in the URLValue is irrelevant here. // (CORS_NONE is used for all imported sheets in Load::LoadChildSheet.) RefPtr urlVal = new css::URLValue(aCssUrl.Consume(), CORS_NONE); RefPtr mediaList = aMediaList.Consume(); RefPtr 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 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(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->Document()); \ } \ \ 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::DocumentMatchingFunction aMatchingFunction) { MOZ_ASSERT(NS_IsMainThread()); Document* 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, 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, 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 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 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(aElements[i])); } } const nsTArray* Gecko_Document_GetElementsWithId(const Document* aDoc, nsAtom* aId) { MOZ_ASSERT(aDoc); MOZ_ASSERT(aId); return aDoc->GetAllElementsForId(nsDependentAtomString(aId)); } const nsTArray* 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(RawGeckoElementBorrowed aElement) { MOZ_ASSERT(aElement->IsSVGElement()); return static_cast(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( aValue->GetStringValue()) .IsEmpty()); return true; }