/* -*- 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 "gfxFontFeatures.h" #include "gfxMathTable.h" #include "gfxTextRun.h" #include "imgLoader.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 "nsLayoutUtils.h" #include "nsIContentInlines.h" #include "mozilla/dom/DocumentInlines.h" #include "nsILoadContext.h" #include "nsIFrame.h" #include "nsINode.h" #include "nsIURI.h" #include "nsFontMetrics.h" #include "nsNameSpaceManager.h" #include "nsNetUtil.h" #include "nsProxyRelease.h" #include "nsString.h" #include "nsStyleStruct.h" #include "nsStyleUtil.h" #include "nsTArray.h" #include "nsTransitionManager.h" #include "nsWindowSizes.h" #include "mozilla/css/ImageLoader.h" #include "mozilla/DeclarationBlock.h" #include "mozilla/AttributeStyles.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/EffectCompositor.h" #include "mozilla/EffectSet.h" #include "mozilla/FontPropertyTypes.h" #include "mozilla/Hal.h" #include "mozilla/Keyframe.h" #include "mozilla/Mutex.h" #include "mozilla/Preferences.h" #include "mozilla/ServoElementSnapshot.h" #include "mozilla/ShadowParts.h" #include "mozilla/StaticPresData.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/StaticPtr.h" #include "mozilla/RestyleManager.h" #include "mozilla/SizeOfState.h" #include "mozilla/StyleAnimationValue.h" #include "mozilla/ServoBindings.h" #include "mozilla/ServoTraversalStatistics.h" #include "mozilla/Telemetry.h" #include "mozilla/TimelineManager.h" #include "mozilla/RWLock.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ElementInlines.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLTableCellElement.h" #include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/dom/HTMLSelectElement.h" #include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/MediaList.h" #include "mozilla/dom/ReferrerInfo.h" #include "mozilla/dom/SVGElement.h" #include "mozilla/dom/WorkerCommon.h" #include "mozilla/LookAndFeel.h" #include "mozilla/URLExtraData.h" #include "mozilla/dom/CSSMozDocumentRule.h" #if defined(MOZ_MEMORY) # include "mozmemory.h" #endif using namespace mozilla; using namespace mozilla::css; using namespace mozilla::dom; // Definitions of the global traversal stats. bool ServoTraversalStatistics::sActive = false; ServoTraversalStatistics ServoTraversalStatistics::sSingleton; static StaticAutoPtr sServoFFILock; static const LangGroupFontPrefs* ThreadSafeGetLangGroupFontPrefs( const Document& aDocument, nsAtom* aLanguage) { bool needsCache = false; { AutoReadLock guard(*sServoFFILock); if (auto* prefs = aDocument.GetFontPrefsForLang(aLanguage, &needsCache)) { return prefs; } } MOZ_ASSERT(needsCache); AutoWriteLock guard(*sServoFFILock); return aDocument.GetFontPrefsForLang(aLanguage); } static const nsFont& ThreadSafeGetDefaultVariableFont(const Document& aDocument, nsAtom* aLanguage) { return ThreadSafeGetLangGroupFontPrefs(aDocument, aLanguage) ->mDefaultVariableFont; } /* * Does this child count as significant for selector matching? * * See nsStyleUtil::IsSignificantChild for details. */ bool Gecko_IsSignificantChild(const nsINode* aNode, bool aWhitespaceIsSignificant) { return nsStyleUtil::ThreadSafeIsSignificantChild(aNode->AsContent(), aWhitespaceIsSignificant); } const nsINode* Gecko_GetLastChild(const nsINode* aNode) { return aNode->GetLastChild(); } const nsINode* Gecko_GetFlattenedTreeParentNode(const nsINode* aNode) { return aNode->GetFlattenedTreeParentNodeForStyle(); } const Element* Gecko_GetBeforeOrAfterPseudo(const Element* aElement, bool aIsBefore) { MOZ_ASSERT(aElement); MOZ_ASSERT(aElement->HasProperties()); return aIsBefore ? nsLayoutUtils::GetBeforePseudo(aElement) : nsLayoutUtils::GetAfterPseudo(aElement); } const Element* Gecko_GetMarkerPseudo(const Element* aElement) { MOZ_ASSERT(aElement); MOZ_ASSERT(aElement->HasProperties()); return nsLayoutUtils::GetMarkerPseudo(aElement); } nsTArray* Gecko_GetAnonymousContentForElement( const Element* 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( const Element* aElement) { MOZ_ASSERT(HTMLSlotElement::FromNode(aElement)); return &static_cast(aElement)->AssignedNodes(); } void Gecko_GetQueryContainerSize(const Element* aElement, nscoord* aOutWidth, nscoord* aOutHeight) { MOZ_ASSERT(aElement); const nsIFrame* frame = aElement->GetPrimaryFrame(); if (!frame) { return; } const auto containAxes = frame->GetContainSizeAxes(); if (!containAxes.IsAny()) { return; } nsSize size = frame->GetContentRectRelativeToSelf().Size(); bool isVertical = frame->GetWritingMode().IsVertical(); if (isVertical ? containAxes.mBContained : containAxes.mIContained) { *aOutWidth = size.width; } if (isVertical ? containAxes.mIContained : containAxes.mBContained) { *aOutHeight = size.height; } } void Gecko_ComputedStyle_Init(ComputedStyle* aStyle, const ServoComputedData* aValues, PseudoStyleType aPseudoType) { new (KnownNotNull, aStyle) ComputedStyle(aPseudoType, ServoComputedDataForgotten(aValues)); } ServoComputedData::ServoComputedData(const ServoComputedDataForgotten aValue) { PodAssign(this, aValue.mPtr); } MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleStructsMallocEnclosingSizeOf) void ServoComputedData::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { // Note: GetStyleFoo() returns a pointer to an nsStyleFoo that sits within a // servo_arc::Arc, i.e. it is preceded by a word-sized refcount. So we need // to measure it with a function that can handle an interior pointer. We use // ServoStyleStructsEnclosingMallocSizeOf to clearly identify in DMD's // output the memory measured here. #define STYLE_STRUCT(name_) \ static_assert(alignof(nsStyle##name_) <= sizeof(size_t), \ "alignment will break AddSizeOfExcludingThis()"); \ const void* p##name_ = Style##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 && !aSizes.mState.HaveSeenPtr(visited_style)) { visited_style->AddSizeOfIncludingThis(aSizes, &aSizes.mLayoutComputedValuesVisited); } // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - custom_properties // - writing_mode // - rules // - font_computation_data } void Gecko_ComputedStyle_Destroy(ComputedStyle* aStyle) { aStyle->~ComputedStyle(); } void Gecko_ConstructStyleChildrenIterator(const Element* aElement, StyleChildrenIterator* aIterator) { MOZ_ASSERT(aElement); MOZ_ASSERT(aIterator); new (aIterator) StyleChildrenIterator(aElement); } void Gecko_DestroyStyleChildrenIterator(StyleChildrenIterator* aIterator) { MOZ_ASSERT(aIterator); aIterator->~StyleChildrenIterator(); } const nsINode* Gecko_GetNextStyleChild(StyleChildrenIterator* aIterator) { MOZ_ASSERT(aIterator); return aIterator->GetNextChild(); } bool Gecko_VisitedStylesEnabled(const Document* aDoc) { MOZ_ASSERT(aDoc); MOZ_ASSERT(NS_IsMainThread()); if (!StaticPrefs::layout_css_visited_links_enabled()) { return false; } if (aDoc->IsBeingUsedAsImage()) { return false; } nsILoadContext* loadContext = aDoc->GetLoadContext(); if (loadContext && loadContext->UsePrivateBrowsing()) { return false; } return true; } ElementState::InternalType Gecko_ElementState(const Element* aElement) { return aElement->StyleState().GetInternalValue(); } bool Gecko_IsRootElement(const Element* aElement) { return aElement->OwnerDoc()->GetRootElement() == aElement; } void Gecko_NoteDirtyElement(const Element* aElement) { MOZ_ASSERT(NS_IsMainThread()); const_cast(aElement)->NoteDirtyForServo(); } void Gecko_NoteDirtySubtreeForInvalidation(const Element* aElement) { MOZ_ASSERT(NS_IsMainThread()); const_cast(aElement)->NoteDirtySubtreeForServo(); } void Gecko_NoteAnimationOnlyDirtyElement(const Element* aElement) { MOZ_ASSERT(NS_IsMainThread()); const_cast(aElement)->NoteAnimationOnlyDirtyForServo(); } bool Gecko_AnimationNameMayBeReferencedFromStyle( const nsPresContext* aPresContext, nsAtom* aName) { MOZ_ASSERT(aPresContext); return aPresContext->AnimationManager()->AnimationMayBeReferenced(aName); } float Gecko_GetScrollbarInlineSize(const nsPresContext* aPc) { MOZ_ASSERT(aPc); AutoWriteLock guard(*sServoFFILock); // We read some look&feel values. auto overlay = aPc->UseOverlayScrollbars() ? nsITheme::Overlay::Yes : nsITheme::Overlay::No; LayoutDeviceIntCoord size = aPc->Theme()->GetScrollbarSize(aPc, StyleScrollbarWidth::Auto, overlay); return aPc->DevPixelsToFloatCSSPixels(size); } PseudoStyleType Gecko_GetImplementedPseudo(const Element* aElement) { return aElement->GetPseudoElementType(); } uint32_t Gecko_CalcStyleDifference(const ComputedStyle* aOldStyle, const ComputedStyle* aNewStyle, bool* aAnyStyleStructChanged, bool* aOnlyResetStructsChanged) { MOZ_ASSERT(aOldStyle); MOZ_ASSERT(aNewStyle); uint32_t equalStructs; nsChangeHint result = aOldStyle->CalcStyleDifference(*aNewStyle, &equalStructs); *aAnyStyleStructChanged = equalStructs != StyleStructConstants::kAllStructsMask; const auto kInheritedStructsMask = StyleStructConstants::kInheritedStructsMask; *aOnlyResetStructsChanged = (equalStructs & kInheritedStructsMask) == kInheritedStructsMask; return result; } nscoord Gecko_CalcLineHeight(const StyleLineHeight* aLh, const nsPresContext* aPc, bool aVertical, const nsStyleFont* aAgainstFont, const mozilla::dom::Element* aElement) { // Normal line-height depends on font metrics. AutoWriteLock guard(*sServoFFILock); return ReflowInput::CalcLineHeight(*aLh, *aAgainstFont, const_cast(aPc), aVertical, aElement, NS_UNCONSTRAINEDSIZE, 1.0f); } 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); } const StyleLockedDeclarationBlock* Gecko_GetStyleAttrDeclarationBlock( const Element* aElement) { DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); if (!decl) { return nullptr; } return decl->Raw(); } void Gecko_UnsetDirtyStyleAttr(const Element* aElement) { DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); if (!decl) { return; } decl->UnsetDirty(); } const StyleLockedDeclarationBlock* Gecko_GetHTMLPresentationAttrDeclarationBlock(const Element* aElement) { return aElement->GetMappedAttributeStyle(); } const StyleLockedDeclarationBlock* Gecko_GetExtraContentStyleDeclarations( const Element* aElement) { if (const auto* cell = HTMLTableCellElement::FromNode(aElement)) { return cell->GetMappedAttributesInheritedFromTable(); } if (const auto* img = HTMLImageElement::FromNode(aElement)) { return img->GetMappedAttributesFromSource(); } return nullptr; } const StyleLockedDeclarationBlock* Gecko_GetUnvisitedLinkAttrDeclarationBlock( const Element* aElement) { AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); if (!attrStyles) { return nullptr; } return attrStyles->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); // 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(); } const StyleLockedDeclarationBlock* Gecko_GetVisitedLinkAttrDeclarationBlock( const Element* aElement) { AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); if (!attrStyles) { return nullptr; } return attrStyles->GetServoVisitedLinkDecl(); } const StyleLockedDeclarationBlock* Gecko_GetActiveLinkAttrDeclarationBlock( const Element* aElement) { AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); if (!attrStyles) { return nullptr; } return attrStyles->GetServoActiveLinkDecl(); } bool Gecko_GetAnimationRule(const Element* aElement, EffectCompositor::CascadeLevel aCascadeLevel, StyleAnimationValueMap* aAnimationValues) { MOZ_ASSERT(aElement); Document* doc = aElement->GetComposedDoc(); if (!doc) { return false; } nsPresContext* presContext = doc->GetPresContext(); if (!presContext) { return false; } const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElement); return presContext->EffectCompositor()->GetServoAnimationRule( element, pseudoType, aCascadeLevel, aAnimationValues); } bool Gecko_StyleAnimationsEquals(const nsStyleAutoArray* aA, const nsStyleAutoArray* aB) { return *aA == *aB; } bool Gecko_StyleScrollTimelinesEquals( const nsStyleAutoArray* aA, const nsStyleAutoArray* aB) { return *aA == *aB; } bool Gecko_StyleViewTimelinesEquals( const nsStyleAutoArray* aA, const nsStyleAutoArray* aB) { return *aA == *aB; } void Gecko_UpdateAnimations(const Element* aElement, const ComputedStyle* aOldComputedData, const ComputedStyle* aComputedData, UpdateAnimationsTasks aTasks) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aElement); if (!aElement->IsInComposedDoc()) { return; } nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement); if (!presContext || !presContext->IsDynamic()) { return; } nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElement); // Handle scroll/view timelines first because CSS animations may refer to the // timeline defined by itself. if (aTasks & UpdateAnimationsTasks::ScrollTimelines) { presContext->TimelineManager()->UpdateTimelines( const_cast(element), pseudoType, aComputedData, TimelineManager::ProgressTimelineType::Scroll); } if (aTasks & UpdateAnimationsTasks::ViewTimelines) { presContext->TimelineManager()->UpdateTimelines( const_cast(element), pseudoType, aComputedData, TimelineManager::ProgressTimelineType::View); } if (aTasks & UpdateAnimationsTasks::CSSAnimations) { presContext->AnimationManager()->UpdateAnimations( const_cast(element), pseudoType, aComputedData); } // aComputedData might be nullptr if the target element is now in a // display:none subtree. We still call Gecko_UpdateAnimations in this case // because we need to stop CSS animations in the display:none subtree. // However, we don't need to update transitions since they are stopped by // RestyleManager::AnimationsWithDestroyedFrame so we just return early // here. if (!aComputedData) { return; } if (aTasks & UpdateAnimationsTasks::CSSTransitions) { MOZ_ASSERT(aOldComputedData); presContext->TransitionManager()->UpdateTransitions( const_cast(element), pseudoType, *aOldComputedData, *aComputedData); } if (aTasks & UpdateAnimationsTasks::EffectProperties) { presContext->EffectCompositor()->UpdateEffectProperties( aComputedData, const_cast(element), pseudoType); } if (aTasks & UpdateAnimationsTasks::CascadeResults) { EffectSet* effectSet = EffectSet::Get(element, 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), pseudoType); } } if (aTasks & UpdateAnimationsTasks::DisplayChangedFromNone) { presContext->EffectCompositor()->RequestRestyle( const_cast(element), pseudoType, EffectCompositor::RestyleType::Standard, EffectCompositor::CascadeLevel::Animations); } } size_t Gecko_GetAnimationEffectCount(const Element* aElementOrPseudo) { const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElementOrPseudo); EffectSet* effectSet = EffectSet::Get(element, pseudoType); return effectSet ? effectSet->Count() : 0; } bool Gecko_ElementHasAnimations(const Element* aElement) { const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElement); return !!EffectSet::Get(element, pseudoType); } bool Gecko_ElementHasCSSAnimations(const Element* aElement) { const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElement); auto* collection = nsAnimationManager::CSSAnimationCollection::Get(element, pseudoType); return collection && !collection->mAnimations.IsEmpty(); } bool Gecko_ElementHasCSSTransitions(const Element* aElement) { const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElement); auto* collection = nsTransitionManager::CSSTransitionCollection::Get(element, pseudoType); return collection && !collection->mAnimations.IsEmpty(); } size_t Gecko_ElementTransitions_Length(const Element* aElement) { const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElement); auto* collection = nsTransitionManager::CSSTransitionCollection::Get(element, pseudoType); return collection ? collection->mAnimations.Length() : 0; } static CSSTransition* GetCurrentTransitionAt(const Element* aElement, size_t aIndex) { const auto [element, pseudoType] = AnimationUtils::GetElementPseudoPair(aElement); auto* collection = nsTransitionManager::CSSTransitionCollection ::Get(element, pseudoType); if (!collection) { return nullptr; } return collection->mAnimations.SafeElementAt(aIndex); } nsCSSPropertyID Gecko_ElementTransitions_PropertyAt(const Element* aElement, size_t aIndex) { CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); return transition ? transition->TransitionProperty().mID : nsCSSPropertyID::eCSSProperty_UNKNOWN; } const StyleAnimationValue* Gecko_ElementTransitions_EndValueAt( const Element* aElement, size_t aIndex) { CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); return transition ? transition->ToValue().mServo.get() : nullptr; } double Gecko_GetProgressFromComputedTiming(const ComputedTiming* aTiming) { return aTiming->mProgress.Value(); } double Gecko_GetPositionInSegment(const AnimationPropertySegment* aSegment, double aProgress, bool 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 StyleComputedTimingFunction::GetPortion( aSegment->mTimingFunction, positionInSegment, aBeforeFlag); } const StyleAnimationValue* Gecko_AnimationGetBaseStyle( const RawServoAnimationValueTable* aBaseStyles, const mozilla::AnimatedPropertyID* aProperty) { const auto* base = reinterpret_cast, StyleAnimationValue>*>(aBaseStyles); return base->GetWeak(*aProperty); } void Gecko_FillAllImageLayers(nsStyleImageLayers* aLayers, uint32_t aMaxLen) { aLayers->FillAllLayers(aMaxLen); } bool Gecko_IsDocumentBody(const Element* aElement) { Document* doc = aElement->GetUncomposedDoc(); return doc && doc->GetBodyElement() == aElement; } bool Gecko_IsDarkColorScheme(const Document* aDoc, const StyleColorScheme* aStyle) { return LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits) == ColorScheme::Dark; } nscolor Gecko_ComputeSystemColor(StyleSystemColor aColor, const Document* aDoc, const StyleColorScheme* aStyle) { auto colorScheme = LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits); const auto& prefs = PreferenceSheet::PrefsFor(*aDoc); if (prefs.mMustUseLightSystemColors) { colorScheme = ColorScheme::Light; } const auto& colors = prefs.ColorsFor(colorScheme); switch (aColor) { case StyleSystemColor::Canvastext: return colors.mDefault; case StyleSystemColor::Canvas: return colors.mDefaultBackground; case StyleSystemColor::Linktext: return colors.mLink; case StyleSystemColor::Activetext: return colors.mActiveLink; case StyleSystemColor::Visitedtext: return colors.mVisitedLink; default: break; } auto useStandins = LookAndFeel::ShouldUseStandins(*aDoc, aColor); AutoWriteLock guard(*sServoFFILock); return LookAndFeel::Color(aColor, colorScheme, useStandins); } int32_t Gecko_GetLookAndFeelInt(int32_t aId) { auto intId = static_cast(aId); AutoWriteLock guard(*sServoFFILock); return LookAndFeel::GetInt(intId); } float Gecko_GetLookAndFeelFloat(int32_t aId) { auto id = static_cast(aId); AutoWriteLock guard(*sServoFFILock); return LookAndFeel::GetFloat(id); } bool Gecko_MatchLang(const Element* aElement, nsAtom* aOverrideLang, bool aHasOverrideLang, const char16_t* aValue) { MOZ_ASSERT(!(aOverrideLang && !aHasOverrideLang), "aHasOverrideLang should only be set when aOverrideLang is null"); MOZ_ASSERT(aValue, "null lang parameter"); if (!aValue || !*aValue) { return false; } // We have to determine the language of the current element. Since // this is currently no property and since the language is inherited // from the parent we have to be prepared to look at all parent // nodes. The language itself is encoded in the LANG attribute. if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) { return nsStyleUtil::LangTagCompare(nsAtomCString(language), NS_ConvertUTF16toUTF8(aValue)); } // 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. // FIXME: We're not really consistent in our treatment of comma-separated // content-language values. if (nsAtom* language = aElement->OwnerDoc()->GetContentLanguage()) { const NS_ConvertUTF16toUTF8 langString(aValue); nsAtomCString docLang(language); docLang.StripWhitespace(); for (auto const& lang : docLang.Split(',')) { if (nsStyleUtil::LangTagCompare(lang, langString)) { return true; } } } return false; } nsAtom* Gecko_GetXMLLangValue(const Element* aElement) { const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML); if (!attr) { return nullptr; } MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom); RefPtr atom = attr->GetAtomValue(); return atom.forget().take(); } const PreferenceSheet::Prefs* Gecko_GetPrefSheetPrefs(const Document* aDoc) { return &PreferenceSheet::PrefsFor(*aDoc); } bool Gecko_IsTableBorderNonzero(const Element* aElement) { if (!aElement->IsHTMLElement(nsGkAtoms::table)) { return false; } const nsAttrValue* val = aElement->GetParsedAttr(nsGkAtoms::border); return val && (val->Type() != nsAttrValue::eInteger || val->GetIntegerValue() != 0); } bool Gecko_IsSelectListBox(const Element* aElement) { const auto* select = HTMLSelectElement::FromNode(aElement); return select && !select->IsCombobox(); } 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(); } bool Gecko_AttrEquals(const nsAttrValue* aValue, const nsAtom* aStr, bool aIgnoreCase) { return aValue->Equals(aStr, aIgnoreCase ? eIgnoreCase : eCaseMatters); } #define WITH_COMPARATOR(ignore_case_, c_, expr_) \ auto c_ = (ignore_case_) ? nsASCIICaseInsensitiveStringComparator \ : nsTDefaultStringComparator; \ return expr_; bool Gecko_AttrDashEquals(const nsAttrValue* aValue, const nsAtom* aStr, bool aIgnoreCase) { nsAutoString str; aValue->ToString(str); WITH_COMPARATOR( aIgnoreCase, c, nsStyleUtil::DashMatchCompare(str, nsDependentAtomString(aStr), c)) } bool Gecko_AttrIncludes(const nsAttrValue* aValue, const nsAtom* aStr, bool aIgnoreCase) { if (aStr == nsGkAtoms::_empty) { return false; } nsAutoString str; aValue->ToString(str); WITH_COMPARATOR( aIgnoreCase, c, nsStyleUtil::ValueIncludes(str, nsDependentAtomString(aStr), c)) } bool Gecko_AttrHasSubstring(const nsAttrValue* aValue, const nsAtom* aStr, bool aIgnoreCase) { return aStr != nsGkAtoms::_empty && aValue->HasSubstring(nsDependentAtomString(aStr), aIgnoreCase ? eIgnoreCase : eCaseMatters); } bool Gecko_AttrHasPrefix(const nsAttrValue* aValue, const nsAtom* aStr, bool aIgnoreCase) { return aStr != nsGkAtoms::_empty && aValue->HasPrefix(nsDependentAtomString(aStr), aIgnoreCase ? eIgnoreCase : eCaseMatters); } bool Gecko_AttrHasSuffix(const nsAttrValue* aValue, const nsAtom* aStr, bool aIgnoreCase) { return aStr != nsGkAtoms::_empty && aValue->HasSuffix(nsDependentAtomString(aStr), aIgnoreCase ? eIgnoreCase : eCaseMatters); } #define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \ nsAtom* prefix_##LangValue(implementor_ aElement) { \ return LangValue(aElement); \ } SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, const Element*) SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, const ServoElementSnapshot*) #undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS nsAtom* Gecko_Atomize(const char* aString, uint32_t aLength) { return NS_Atomize(nsDependentCSubstring(aString, aLength)).take(); } nsAtom* Gecko_Atomize16(const nsAString* aString) { return NS_Atomize(*aString).take(); } void Gecko_AddRefAtom(nsAtom* aAtom) { NS_ADDREF(aAtom); } void Gecko_ReleaseAtom(nsAtom* aAtom) { NS_RELEASE(aAtom); } void Gecko_nsFont_InitSystem(nsFont* aDest, StyleSystemFont aFontId, const nsStyleFont* aFont, const Document* aDocument) { const nsFont& defaultVariableFont = ThreadSafeGetDefaultVariableFont(*aDocument, aFont->mLanguage); // We have passed uninitialized memory to this function, // initialize it. We can't simply return an nsFont because then // we need to know its size beforehand. Servo cannot initialize nsFont // itself, so this will do. new (aDest) nsFont(defaultVariableFont); AutoWriteLock guard(*sServoFFILock); nsLayoutUtils::ComputeSystemFont(aDest, aFontId, defaultVariableFont, aDocument); } void Gecko_nsFont_Destroy(nsFont* aDest) { aDest->~nsFont(); } StyleGenericFontFamily Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage( const Document* aDoc, nsAtom* aLanguage) { return ThreadSafeGetLangGroupFontPrefs(*aDoc, aLanguage)->GetDefaultGeneric(); } Length Gecko_GetBaseSize(const Document* aDoc, nsAtom* aLang, StyleGenericFontFamily aGeneric) { return ThreadSafeGetLangGroupFontPrefs(*aDoc, aLang) ->GetDefaultFont(aGeneric) ->size; } gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet() { return new gfxFontFeatureValueSet(); } nsTArray* Gecko_AppendFeatureValueHashEntry( gfxFontFeatureValueSet* aFontFeatureValues, nsAtom* aFamily, uint32_t aAlternate, nsAtom* aName) { MOZ_ASSERT(NS_IsMainThread()); return aFontFeatureValues->AppendFeatureValueHashEntry(nsAtomCString(aFamily), aName, aAlternate); } gfx::FontPaletteValueSet* Gecko_ConstructFontPaletteValueSet() { return new gfx::FontPaletteValueSet(); } gfx::FontPaletteValueSet::PaletteValues* Gecko_AppendPaletteValueHashEntry( gfx::FontPaletteValueSet* aPaletteValueSet, nsAtom* aFamily, nsAtom* aName) { MOZ_ASSERT(NS_IsMainThread()); return aPaletteValueSet->Insert(aName, nsAtomCString(aFamily)); } void Gecko_SetFontPaletteBase(gfx::FontPaletteValueSet::PaletteValues* aValues, int32_t aBasePaletteIndex) { aValues->mBasePalette = aBasePaletteIndex; } void Gecko_SetFontPaletteOverride( gfx::FontPaletteValueSet::PaletteValues* aValues, int32_t aIndex, StyleAbsoluteColor* aColor) { if (aIndex < 0) { return; } aValues->mOverrides.AppendElement(gfx::FontPaletteValueSet::OverrideColor{ uint32_t(aIndex), gfx::sRGBColor::FromABGR(aColor->ToColor())}); } void Gecko_CounterStyle_ToPtr(const StyleCounterStyle* aStyle, CounterStylePtr* aPtr) { *aPtr = CounterStylePtr::FromStyle(*aStyle); } void Gecko_SetCounterStyleToNone(CounterStylePtr* aPtr) { *aPtr = nsGkAtoms::none; } 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_EnsureTArrayCapacity(void* aArray, size_t aCapacity, size_t aElemSize) { auto base = reinterpret_cast*>(aArray); base->EnsureCapacity(aCapacity, aElemSize); } void Gecko_ClearPODTArray(void* aArray, size_t aElementSize, size_t aElementAlign) { auto base = reinterpret_cast*>(aArray); base->template ShiftData( 0, base->Length(), 0, aElementSize, aElementAlign); } void Gecko_ResizeTArrayForStrings(nsTArray* aArray, uint32_t aLength) { aArray->SetLength(aLength); } void Gecko_ResizeAtomArray(nsTArray>* aArray, uint32_t aLength) { aArray->SetLength(aLength); } 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) { aArray->EnsureLengthAtLeast(aLen); } 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_EnsureStyleScrollTimelineArrayLength(void* aArray, size_t aLen) { auto* base = static_cast*>(aArray); EnsureStyleAutoArrayLength(base, aLen); } void Gecko_EnsureStyleViewTimelineArrayLength(void* aArray, size_t aLen) { auto* base = static_cast*>(aArray); EnsureStyleAutoArrayLength(base, aLen); } enum class KeyframeSearchDirection { Forwards, Backwards, }; enum class KeyframeInsertPosition { Prepend, LastForOffset, }; static Keyframe* GetOrCreateKeyframe( nsTArray* aKeyframes, float aOffset, const StyleComputedTimingFunction* aTimingFunction, const CompositeOperationOrAuto aComposition, 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, aComposition, keyframeIndex)) { return &(*aKeyframes)[keyframeIndex]; } break; case KeyframeSearchDirection::Backwards: if (nsAnimationManager::FindMatchingKeyframe( Reversed(*aKeyframes), aOffset, *aTimingFunction, aComposition, 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->IsLinearKeyword()) { keyframe->mTimingFunction.emplace(*aTimingFunction); } keyframe->mComposite = aComposition; return keyframe; } Keyframe* Gecko_GetOrCreateKeyframeAtStart( nsTArray* aKeyframes, float aOffset, const StyleComputedTimingFunction* aTimingFunction, const CompositeOperationOrAuto aComposition) { 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, aComposition, KeyframeSearchDirection::Forwards, KeyframeInsertPosition::Prepend); } Keyframe* Gecko_GetOrCreateInitialKeyframe( nsTArray* aKeyframes, const StyleComputedTimingFunction* aTimingFunction, const CompositeOperationOrAuto aComposition) { return GetOrCreateKeyframe(aKeyframes, 0., aTimingFunction, aComposition, KeyframeSearchDirection::Forwards, KeyframeInsertPosition::LastForOffset); } Keyframe* Gecko_GetOrCreateFinalKeyframe( nsTArray* aKeyframes, const StyleComputedTimingFunction* aTimingFunction, const CompositeOperationOrAuto aComposition) { return GetOrCreateKeyframe(aKeyframes, 1., aTimingFunction, aComposition, KeyframeSearchDirection::Backwards, KeyframeInsertPosition::LastForOffset); } PropertyValuePair* Gecko_AppendPropertyValuePair( nsTArray* aProperties, const mozilla::AnimatedPropertyID* aProperty) { MOZ_ASSERT(aProperties); MOZ_ASSERT( aProperty->IsCustom() || !nsCSSProps::PropHasFlags(aProperty->mID, CSSPropFlags::IsLogical)); return aProperties->AppendElement(PropertyValuePair{*aProperty}); } void Gecko_GetComputedURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) { MOZ_ASSERT(aURL); MOZ_ASSERT(aOut); if (aURL->IsLocalRef()) { aOut->Assign(aURL->SpecifiedSerialization()); return; } Gecko_GetComputedImageURLSpec(aURL, aOut); } void Gecko_GetComputedImageURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) { if (aURL->IsLocalRef() && StaticPrefs::layout_css_computed_style_dont_resolve_image_local_refs()) { aOut->Assign(aURL->SpecifiedSerialization()); return; } if (nsIURI* uri = aURL->GetURI()) { nsresult rv = uri->GetSpec(*aOut); if (NS_SUCCEEDED(rv)) { return; } } // Empty URL computes to empty, per spec: if (aURL->SpecifiedSerialization().IsEmpty()) { aOut->Truncate(); } else { aOut->AssignLiteral("about:invalid"); } } bool Gecko_IsSupportedImageMimeType(const uint8_t* aMimeType, const uint32_t aLen) { nsDependentCSubstring mime(reinterpret_cast(aMimeType), aLen); return imgLoader::SupportImageWithMimeType( mime, AcceptedMimeTypes::IMAGES_AND_DOCUMENTS); } void Gecko_nsIURI_Debug(nsIURI* aURI, nsCString* aOut) { // TODO(emilio): Do we have more useful stuff to put here, maybe? if (aURI) { *aOut = aURI->GetSpecOrDefault(); } } // XXX Implemented by hand because even though it's thread-safe, only the // subclasses have the HasThreadSafeRefCnt bits. void Gecko_AddRefnsIURIArbitraryThread(nsIURI* aPtr) { NS_ADDREF(aPtr); } void Gecko_ReleasensIURIArbitraryThread(nsIURI* aPtr) { NS_RELEASE(aPtr); } void Gecko_nsIReferrerInfo_Debug(nsIReferrerInfo* aReferrerInfo, nsCString* aOut) { if (aReferrerInfo) { if (nsCOMPtr referrer = aReferrerInfo->GetComputedReferrer()) { *aOut = referrer->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(const Element* aElement, nsCString* aOut) { DebugListAttributes(*aElement, *aOut); } void Gecko_Snapshot_DebugListAttributes(const ServoElementSnapshot* aSnapshot, nsCString* aOut) { DebugListAttributes(*aSnapshot, *aOut); } NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData); void Gecko_nsStyleFont_SetLang(nsStyleFont* aFont, nsAtom* aAtom) { aFont->mLanguage = dont_AddRef(aAtom); aFont->mExplicitLanguage = true; } void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, const nsStyleFont* aSource) { aFont->mLanguage = aSource->mLanguage; } Length Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont* aFont, const Document* aDocument) { // Don't change font-size:0, since that would un-hide hidden text. if (aFont->mSize.IsZero()) { return {0}; } // Don't change it for docs where we don't enable the min-font-size. if (!aFont->MinFontSizeEnabled()) { return {0}; } Length minFontSize; bool needsCache = false; auto MinFontSize = [&](bool* aNeedsToCache) { const auto* prefs = aDocument->GetFontPrefsForLang(aFont->mLanguage, aNeedsToCache); return prefs ? prefs->mMinimumFontSize : Length{0}; }; { AutoReadLock guard(*sServoFFILock); minFontSize = MinFontSize(&needsCache); } if (needsCache) { AutoWriteLock guard(*sServoFFILock); minFontSize = MinFontSize(nullptr); } if (minFontSize.ToCSSPixels() <= 0.0f) { return {0}; } minFontSize.ScaleBy(aFont->mMinFontSizeRatio); minFontSize.ScaleBy(1.0f / 100.0f); return minFontSize; } static StaticRefPtr gUACacheReporter; namespace mozilla { void InitializeServo() { URLExtraData::Init(); Servo_Initialize(URLExtraData::Dummy(), URLExtraData::DummyChrome()); gUACacheReporter = new UACacheReporter(); RegisterWeakMemoryReporter(gUACacheReporter); sServoFFILock = new RWLock("Servo::FFILock"); } void ShutdownServo() { MOZ_ASSERT(sServoFFILock); UnregisterWeakMemoryReporter(gUACacheReporter); gUACacheReporter = nullptr; sServoFFILock = nullptr; Servo_Shutdown(); URLExtraData::Shutdown(); } void AssertIsMainThreadOrServoFontMetricsLocked() { if (!NS_IsMainThread()) { MOZ_ASSERT(sServoFFILock && sServoFFILock->LockedForWritingByCurrentThread()); } } } // namespace mozilla GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext, bool aIsVertical, const nsStyleFont* aFont, Length aFontSize, bool aUseUserFontSet, bool aRetrieveMathScales) { AutoWriteLock guard(*sServoFFILock); // 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); RefPtr fm = nsLayoutUtils::GetMetricsFor( presContext, aIsVertical, aFont, aFontSize, aUseUserFontSet); auto* fontGroup = fm->GetThebesFontGroup(); auto metrics = fontGroup->GetMetricsForCSSUnits(fm->Orientation()); float scriptPercentScaleDown = 0; float scriptScriptPercentScaleDown = 0; if (aRetrieveMathScales) { RefPtr font = fontGroup->GetFirstValidFont(); if (font->TryGetMathTable()) { scriptPercentScaleDown = static_cast( font->MathTable()->Constant(gfxMathTable::ScriptPercentScaleDown)); scriptScriptPercentScaleDown = static_cast(font->MathTable()->Constant( gfxMathTable::ScriptScriptPercentScaleDown)); } } int32_t d2a = aPresContext->AppUnitsPerDevPixel(); auto ToLength = [](nscoord aLen) { return Length::FromPixels(CSSPixel::FromAppUnits(aLen)); }; return {ToLength(NS_round(metrics.xHeight * d2a)), ToLength(NS_round(metrics.zeroWidth * d2a)), ToLength(NS_round(metrics.capHeight * d2a)), ToLength(NS_round(metrics.ideographicWidth * d2a)), ToLength(NS_round(metrics.maxAscent * d2a)), ToLength(NS_round(fontGroup->GetStyle()->size * d2a)), scriptPercentScaleDown, scriptScriptPercentScaleDown}; } NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder); void Gecko_StyleSheet_FinishAsyncParse( SheetLoadDataHolder* aData, StyleStrong aSheetContents, StyleUseCounters* aUseCounters) { UniquePtr useCounters(aUseCounters); RefPtr loadData = aData; RefPtr sheetContents = aSheetContents.Consume(); NS_DispatchToMainThreadQueue( NS_NewRunnableFunction( __func__, [d = std::move(loadData), contents = std::move(sheetContents), counters = std::move(useCounters)]() mutable { MOZ_ASSERT(NS_IsMainThread()); SheetLoadData* data = d->get(); data->mSheet->FinishAsyncParse(contents.forget(), std::move(counters)); }), EventQueuePriority::RenderBlocking); } static already_AddRefed LoadImportSheet( Loader* aLoader, StyleSheet* aParent, SheetLoadData* aParentLoadData, LoaderReusableStyleSheets* aReusableSheets, const StyleCssUrl& 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!"); RefPtr media = new MediaList(std::move(aMediaList)); nsCOMPtr uri = aURL.GetURI(); nsresult rv = uri ? NS_OK : NS_ERROR_FAILURE; size_t previousSheetCount = aParent->ChildSheets().Length(); if (NS_SUCCEEDED(rv)) { // TODO(emilio): We should probably make LoadChildSheet return the // stylesheet rather than the return code. rv = aLoader->LoadChildSheet(*aParent, aParentLoadData, uri, media, aReusableSheets); } if (NS_FAILED(rv) || previousSheetCount == aParent->ChildSheets().Length()) { // 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), "about:invalid"_ns); } emptySheet->SetURIs(uri, uri, uri); emptySheet->SetPrincipal(aURL.ExtraData().Principal()); nsCOMPtr referrerInfo = ReferrerInfo::CreateForExternalCSSResources(emptySheet); emptySheet->SetReferrerInfo(referrerInfo); emptySheet->SetComplete(); aParent->AppendStyleSheet(*emptySheet); return emptySheet.forget(); } RefPtr sheet = aParent->ChildSheets().LastElement(); return sheet.forget(); } StyleSheet* Gecko_LoadStyleSheet(Loader* aLoader, StyleSheet* aParent, SheetLoadData* aParentLoadData, LoaderReusableStyleSheets* aReusableSheets, const StyleCssUrl* aUrl, StyleStrong aMediaList) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aUrl); return LoadImportSheet(aLoader, aParent, aParentLoadData, aReusableSheets, *aUrl, aMediaList.Consume()) .take(); } void Gecko_LoadStyleSheetAsync(SheetLoadDataHolder* aParentData, const StyleCssUrl* aUrl, StyleStrong aMediaList, StyleStrong aImportRule) { MOZ_ASSERT(aUrl); RefPtr loadData = aParentData; RefPtr mediaList = aMediaList.Consume(); RefPtr importRule = aImportRule.Consume(); NS_DispatchToMainThreadQueue( NS_NewRunnableFunction( __func__, [data = std::move(loadData), url = StyleCssUrl(*aUrl), media = std::move(mediaList), import = std::move(importRule)]() mutable { MOZ_ASSERT(NS_IsMainThread()); SheetLoadData* d = data->get(); RefPtr sheet = LoadImportSheet( d->mLoader, d->mSheet, d, nullptr, url, media.forget()); Servo_ImportRule_SetSheet(import, sheet); }), EventQueuePriority::RenderBlocking); } void Gecko_AddPropertyToSet(nsCSSPropertyIDSet* aPropertySet, nsCSSPropertyID aProperty) { aPropertySet->AddProperty(aProperty); } bool Gecko_DocumentRule_UseForPresentation( const Document* aDocument, const nsACString* aPattern, DocumentMatchingFunction aMatchingFunction) { MOZ_ASSERT(NS_IsMainThread()); nsIURI* docURI = aDocument->GetDocumentURI(); nsAutoCString docURISpec; if (docURI) { // If GetSpec fails (due to OOM) just skip these URI-specific CSS rules. nsresult rv = docURI->GetSpec(docURISpec); NS_ENSURE_SUCCESS(rv, false); } return CSSMozDocumentRule::Match(aDocument, docURI, docURISpec, *aPattern, aMatchingFunction); } void Gecko_SetJemallocThreadLocalArena(bool enabled) { #if defined(MOZ_MEMORY) jemalloc_thread_local_arena(enabled); #endif } template void Construct(T* aPtr, const Document* aDoc) { if constexpr (std::is_constructible_v) { MOZ_ASSERT(aDoc); new (KnownNotNull, aPtr) T(*aDoc); } else { MOZ_ASSERT(!aDoc); new (KnownNotNull, aPtr) T(); // These instance are intentionally global, and we don't want leakcheckers // to report them. aPtr->MarkLeaked(); } } #define STYLE_STRUCT(name) \ void Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr, \ const Document* doc) { \ Construct(ptr, doc); \ } \ void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \ const nsStyle##name* other) { \ new (ptr) nsStyle##name(*other); \ } \ void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr) { \ ptr->~nsStyle##name(); \ } #include "nsStyleStructList.h" #undef STYLE_STRUCT bool Gecko_ErrorReportingEnabled(const StyleSheet* aSheet, const Loader* aLoader, uint64_t* aOutWindowId) { if (!ErrorReporter::ShouldReportErrors(aSheet, aLoader)) { return false; } *aOutWindowId = ErrorReporter::FindInnerWindowId(aSheet, aLoader); return true; } void Gecko_ReportUnexpectedCSSError( const uint64_t aWindowId, nsIURI* aURI, const char* message, const char* param, uint32_t paramLen, const char* prefix, const char* prefixParam, uint32_t prefixParamLen, const char* suffix, const char* source, uint32_t sourceLen, const char* selectors, uint32_t selectorsLen, uint32_t lineNumber, uint32_t colNumber) { MOZ_RELEASE_ASSERT(NS_IsMainThread()); ErrorReporter reporter(aWindowId); if (prefix) { if (prefixParam) { nsDependentCSubstring paramValue(prefixParam, prefixParamLen); AutoTArray wideParam; CopyUTF8toUTF16(paramValue, *wideParam.AppendElement()); reporter.ReportUnexpectedUnescaped(prefix, wideParam); } else { reporter.ReportUnexpected(prefix); } } if (param) { nsDependentCSubstring paramValue(param, paramLen); AutoTArray wideParam; CopyUTF8toUTF16(paramValue, *wideParam.AppendElement()); reporter.ReportUnexpectedUnescaped(message, wideParam); } else { reporter.ReportUnexpected(message); } if (suffix) { reporter.ReportUnexpected(suffix); } nsDependentCSubstring sourceValue(source, sourceLen); nsDependentCSubstring selectorsValue(selectors, selectorsLen); reporter.OutputError(sourceValue, selectorsValue, lineNumber + 1, colNumber, aURI); } 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(aId); } const nsTArray* Gecko_ShadowRoot_GetElementsWithId( const ShadowRoot* aShadowRoot, nsAtom* aId) { MOZ_ASSERT(aShadowRoot); MOZ_ASSERT(aId); return aShadowRoot->GetAllElementsForId(aId); } bool Gecko_ComputeBoolPrefMediaQuery(nsAtom* aPref) { MOZ_ASSERT(NS_IsMainThread()); // This map leaks until shutdown, but that's fine, all the values are // controlled by us so it's not expected to be big. static StaticAutoPtr, bool>> sRegisteredPrefs; if (!sRegisteredPrefs) { if (PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) { // Styling doesn't really matter much at this point, don't bother. return false; } sRegisteredPrefs = new nsTHashMap, bool>(); ClearOnShutdown(&sRegisteredPrefs); } return sRegisteredPrefs->LookupOrInsertWith(aPref, [&] { nsAutoAtomCString prefName(aPref); Preferences::RegisterCallback( [](const char* aPrefName, void*) { if (sRegisteredPrefs) { RefPtr name = NS_Atomize(nsDependentCString(aPrefName)); sRegisteredPrefs->InsertOrUpdate(name, Preferences::GetBool(aPrefName)); } LookAndFeel::NotifyChangedAllWindows( widget::ThemeChangeKind::MediaQueriesOnly); }, prefName); return Preferences::GetBool(prefName.get()); }); } bool Gecko_IsFontFormatSupported(StyleFontFaceSourceFormatKeyword aFormat) { return gfxPlatform::GetPlatform()->IsFontFormatSupported( aFormat, StyleFontFaceSourceTechFlags::Empty()); } bool Gecko_IsFontTechSupported(StyleFontFaceSourceTechFlags aFlag) { return gfxPlatform::GetPlatform()->IsFontFormatSupported( StyleFontFaceSourceFormatKeyword::None, aFlag); } bool Gecko_IsKnownIconFontFamily(const nsAtom* aFamilyName) { return gfxPlatform::GetPlatform()->IsKnownIconFontFamily(aFamilyName); } bool Gecko_IsInServoTraversal() { return ServoStyleSet::IsInServoTraversal(); } bool Gecko_IsMainThread() { return NS_IsMainThread(); } bool Gecko_IsDOMWorkerThread() { return !!GetCurrentThreadWorkerPrivate(); } int32_t Gecko_GetNumStyleThreads() { if (const auto& cpuInfo = hal::GetHeterogeneousCpuInfo()) { size_t numBigCpus = cpuInfo->mBigCpus.Count(); // If CPUs are homogeneous we do not need to override stylo's // default number of threads. if (numBigCpus != cpuInfo->mTotalNumCpus) { // From testing on a variety of devices it appears using only // the number of big cores gives best performance when there are // 2 or more big cores. If there are fewer than 2 big cores then // additionally using the medium cores performs better. if (numBigCpus >= 2) { return static_cast(numBigCpus); } return static_cast(numBigCpus + cpuInfo->mMediumCpus.Count()); } } return -1; } const nsAttrValue* Gecko_GetSVGAnimatedClass(const Element* 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; } void Gecko_GetSafeAreaInsets(const nsPresContext* aPresContext, float* aTop, float* aRight, float* aBottom, float* aLeft) { MOZ_ASSERT(aPresContext); ScreenIntMargin safeAreaInsets = aPresContext->GetSafeAreaInsets(); *aTop = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.top); *aRight = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.right); *aBottom = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.bottom); *aLeft = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.left); } void Gecko_PrintfStderr(const nsCString* aStr) { printf_stderr("%s", aStr->get()); } nsAtom* Gecko_Element_ImportedPart(const nsAttrValue* aValue, nsAtom* aPartName) { if (aValue->Type() != nsAttrValue::eShadowParts) { return nullptr; } return aValue->GetShadowPartsValue().GetReverse(aPartName); } nsAtom** Gecko_Element_ExportedParts(const nsAttrValue* aValue, nsAtom* aPartName, size_t* aOutLength) { if (aValue->Type() != nsAttrValue::eShadowParts) { return nullptr; } auto* parts = aValue->GetShadowPartsValue().Get(aPartName); if (!parts) { return nullptr; } *aOutLength = parts->Length(); static_assert(sizeof(RefPtr) == sizeof(nsAtom*)); static_assert(alignof(RefPtr) == alignof(nsAtom*)); return reinterpret_cast(parts->Elements()); } bool StyleSingleFontFamily::IsNamedFamily(const nsAString& aFamilyName) const { if (!IsFamilyName()) { return false; } nsDependentAtomString name(AsFamilyName().name.AsAtom()); return name.Equals(aFamilyName, nsCaseInsensitiveStringComparator); } StyleSingleFontFamily StyleSingleFontFamily::Parse( const nsACString& aFamilyOrGenericName) { // should only be passed a single font - not entirely correct, a family // *could* have a comma in it but in practice never does so // for debug purposes this is fine NS_ASSERTION(aFamilyOrGenericName.FindChar(',') == -1, "Convert method should only be passed a single family name"); auto genericType = Servo_GenericFontFamily_Parse(&aFamilyOrGenericName); if (genericType != StyleGenericFontFamily::None) { return Generic(genericType); } return FamilyName({StyleAtom(NS_Atomize(aFamilyOrGenericName)), StyleFontFamilyNameSyntax::Identifiers}); } void StyleSingleFontFamily::AppendToString(nsACString& aName, bool aQuote) const { if (IsFamilyName()) { const auto& name = AsFamilyName(); if (!aQuote) { aName.Append(nsAutoAtomCString(name.name.AsAtom())); return; } Servo_FamilyName_Serialize(&name, &aName); return; } switch (AsGeneric()) { case StyleGenericFontFamily::None: case StyleGenericFontFamily::MozEmoji: MOZ_FALLTHROUGH_ASSERT("Should never appear in a font-family name!"); case StyleGenericFontFamily::Serif: return aName.AppendLiteral("serif"); case StyleGenericFontFamily::SansSerif: return aName.AppendLiteral("sans-serif"); case StyleGenericFontFamily::Monospace: return aName.AppendLiteral("monospace"); case StyleGenericFontFamily::Cursive: return aName.AppendLiteral("cursive"); case StyleGenericFontFamily::Fantasy: return aName.AppendLiteral("fantasy"); case StyleGenericFontFamily::SystemUi: return aName.AppendLiteral("system-ui"); } MOZ_ASSERT_UNREACHABLE("Unknown generic font-family!"); return aName.AppendLiteral("serif"); } StyleFontFamilyList StyleFontFamilyList::WithNames( nsTArray&& aNames) { StyleFontFamilyList list; Servo_FontFamilyList_WithNames(&aNames, &list); return list; } StyleFontFamilyList StyleFontFamilyList::WithOneUnquotedFamily( const nsACString& aName) { AutoTArray names; names.AppendElement(StyleSingleFontFamily::FamilyName( {StyleAtom(NS_Atomize(aName)), StyleFontFamilyNameSyntax::Identifiers})); return WithNames(std::move(names)); }