/* -*- 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 "gfxTextRun.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 "mozilla/dom/DocumentInlines.h" #include "nsILoadContext.h" #include "nsIFrame.h" #include "nsIMozBrowserFrame.h" #include "nsINode.h" #include "nsIURI.h" #include "nsFontMetrics.h" #include "nsHTMLStyleSheet.h" #include "nsMappedAttributes.h" #include "nsMediaFeatures.h" #include "nsNameSpaceManager.h" #include "nsNetUtil.h" #include "nsProxyRelease.h" #include "nsString.h" #include "nsStyleStruct.h" #include "nsStyleUtil.h" #include "nsTArray.h" #include "nsTransitionManager.h" #include "nsWindowSizes.h" #include "mozilla/css/ImageLoader.h" #include "mozilla/DeclarationBlock.h" #include "mozilla/EffectCompositor.h" #include "mozilla/EffectSet.h" #include "mozilla/EventStates.h" #include "mozilla/FontPropertyTypes.h" #include "mozilla/Keyframe.h" #include "mozilla/Mutex.h" #include "mozilla/Preferences.h" #include "mozilla/ServoElementSnapshot.h" #include "mozilla/ShadowParts.h" #include "mozilla/StaticPresData.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_layout.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/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/HTMLSelectElement.h" #include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/MediaList.h" #include "mozilla/dom/SVGElement.h" #include "mozilla/LookAndFeel.h" #include "mozilla/URLExtraData.h" #include "mozilla/dom/CSSMozDocumentRule.h" #if defined(MOZ_MEMORY) # include "mozmemory.h" #endif using namespace mozilla; using namespace mozilla::css; using namespace mozilla::dom; // Definitions of the global traversal stats. bool ServoTraversalStatistics::sActive = false; ServoTraversalStatistics ServoTraversalStatistics::sSingleton; static RWLock* sServoFFILock = nullptr; static const nsFont* ThreadSafeGetDefaultFontHelper( const Document& aDocument, nsAtom* aLanguage, StyleGenericFontFamily aGenericId) { bool needsCache = false; const nsFont* retval; auto GetDefaultFont = [&](bool* aNeedsToCache) { auto* prefs = aDocument.GetFontPrefsForLang(aLanguage, aNeedsToCache); return prefs ? prefs->GetDefaultFont(aGenericId) : nullptr; }; { AutoReadLock guard(*sServoFFILock); retval = GetDefaultFont(&needsCache); } if (!needsCache) { return retval; } { AutoWriteLock guard(*sServoFFILock); retval = GetDefaultFont(nullptr); } return retval; } /* * Does this child count as significant for selector matching? * * See nsStyleUtil::IsSignificantChild for details. */ bool Gecko_IsSignificantChild(const nsINode* aNode, bool aWhitespaceIsSignificant) { return nsStyleUtil::ThreadSafeIsSignificantChild(aNode->AsContent(), aWhitespaceIsSignificant); } const nsINode* Gecko_GetLastChild(const nsINode* aNode) { return aNode->GetLastChild(); } const nsINode* Gecko_GetPreviousSibling(const nsINode* aNode) { return aNode->GetPreviousSibling(); } const nsINode* Gecko_GetFlattenedTreeParentNode(const nsINode* aNode) { return aNode->GetFlattenedTreeParentNodeForStyle(); } const Element* Gecko_GetBeforeOrAfterPseudo(const Element* aElement, bool aIsBefore) { MOZ_ASSERT(aElement); MOZ_ASSERT(aElement->HasProperties()); return aIsBefore ? nsLayoutUtils::GetBeforePseudo(aElement) : nsLayoutUtils::GetAfterPseudo(aElement); } const Element* Gecko_GetMarkerPseudo(const Element* aElement) { MOZ_ASSERT(aElement); MOZ_ASSERT(aElement->HasProperties()); return nsLayoutUtils::GetMarkerPseudo(aElement); } nsTArray* 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_ComputedStyle_Init(ComputedStyle* aStyle, const ServoComputedData* aValues, PseudoStyleType aPseudoType) { new (KnownNotNull, aStyle) ComputedStyle(aPseudoType, ServoComputedDataForgotten(aValues)); } ServoComputedData::ServoComputedData(const ServoComputedDataForgotten aValue) { PodAssign(this, aValue.mPtr); } MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleStructsMallocEnclosingSizeOf) void ServoComputedData::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { // Note: GetStyleFoo() returns a pointer to an nsStyleFoo that sits within a // servo_arc::Arc, i.e. it is preceded by a word-sized refcount. So we need // to measure it with a function that can handle an interior pointer. We use // ServoStyleStructsEnclosingMallocSizeOf to clearly identify in DMD's // output the memory measured here. #define STYLE_STRUCT(name_) \ static_assert(alignof(nsStyle##name_) <= sizeof(size_t), \ "alignment will break AddSizeOfExcludingThis()"); \ const void* p##name_ = GetStyle##name_(); \ if (!aSizes.mState.HaveSeenPtr(p##name_)) { \ aSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \ ServoStyleStructsMallocEnclosingSizeOf(p##name_); \ } #include "nsStyleStructList.h" #undef STYLE_STRUCT if (visited_style.mPtr && !aSizes.mState.HaveSeenPtr(visited_style.mPtr)) { visited_style.mPtr->AddSizeOfIncludingThis( aSizes, &aSizes.mLayoutComputedValuesVisited); } // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - custom_properties // - writing_mode // - rules // - font_computation_data } void Gecko_ComputedStyle_Destroy(ComputedStyle* aStyle) { aStyle->~ComputedStyle(); } void Gecko_ConstructStyleChildrenIterator(const Element* aElement, StyleChildrenIterator* aIterator) { MOZ_ASSERT(aElement); MOZ_ASSERT(aIterator); new (aIterator) StyleChildrenIterator(aElement); } void Gecko_DestroyStyleChildrenIterator(StyleChildrenIterator* aIterator) { MOZ_ASSERT(aIterator); aIterator->~StyleChildrenIterator(); } const nsINode* Gecko_GetNextStyleChild(StyleChildrenIterator* aIterator) { MOZ_ASSERT(aIterator); return aIterator->GetNextChild(); } bool Gecko_VisitedStylesEnabled(const Document* aDoc) { MOZ_ASSERT(aDoc); MOZ_ASSERT(NS_IsMainThread()); if (!StaticPrefs::layout_css_visited_links_enabled()) { return false; } if (aDoc->IsBeingUsedAsImage()) { return false; } nsILoadContext* loadContext = aDoc->GetLoadContext(); if (loadContext && loadContext->UsePrivateBrowsing()) { return false; } return true; } EventStates::ServoType Gecko_ElementState(const Element* aElement) { return aElement->StyleState().ServoValue(); } bool Gecko_IsRootElement(const Element* aElement) { return aElement->OwnerDoc()->GetRootElement() == aElement; } // Dirtiness tracking. void Gecko_SetNodeFlags(const nsINode* aNode, uint32_t aFlags) { const_cast(aNode)->SetFlags(aFlags); } void Gecko_UnsetNodeFlags(const nsINode* aNode, uint32_t aFlags) { const_cast(aNode)->UnsetFlags(aFlags); } 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); } PseudoStyleType Gecko_GetImplementedPseudo(const Element* aElement) { return aElement->GetPseudoElementType(); } uint32_t Gecko_CalcStyleDifference(const ComputedStyle* aOldStyle, const ComputedStyle* aNewStyle, bool* aAnyStyleStructChanged, bool* aOnlyResetStructsChanged) { MOZ_ASSERT(aOldStyle); MOZ_ASSERT(aNewStyle); uint32_t equalStructs; nsChangeHint result = aOldStyle->CalcStyleDifference(*aNewStyle, &equalStructs); *aAnyStyleStructChanged = equalStructs != StyleStructConstants::kAllStructsMask; const auto kInheritedStructsMask = StyleStructConstants::kInheritedStructsMask; *aOnlyResetStructsChanged = (equalStructs & kInheritedStructsMask) == kInheritedStructsMask; return result; } const ServoElementSnapshot* Gecko_GetElementSnapshot( const ServoElementSnapshotTable* aTable, const Element* aElement) { MOZ_ASSERT(aTable); MOZ_ASSERT(aElement); return aTable->Get(const_cast(aElement)); } bool Gecko_HaveSeenPtr(SeenPtrs* aTable, const void* aPtr) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aTable); // Empty Rust allocations are indicated by small values up to the alignment // of the relevant type. We shouldn't see anything like that here. MOZ_ASSERT(uintptr_t(aPtr) > 16); return aTable->HaveSeenPtr(aPtr); } const StyleStrong* Gecko_GetStyleAttrDeclarationBlock( const Element* aElement) { DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); if (!decl) { return nullptr; } return decl->RefRawStrong(); } void Gecko_UnsetDirtyStyleAttr(const Element* aElement) { DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); if (!decl) { return; } decl->UnsetDirty(); } static const StyleStrong* AsRefRawStrong( const RefPtr& aDecl) { static_assert(sizeof(RefPtr) == sizeof(StyleStrong), "RefPtr should just be a pointer"); return reinterpret_cast*>(&aDecl); } const StyleStrong* Gecko_GetHTMLPresentationAttrDeclarationBlock(const Element* aElement) { const nsMappedAttributes* attrs = aElement->GetMappedAttributes(); if (!attrs) { auto* svg = SVGElement::FromNodeOrNull(aElement); if (svg) { if (auto decl = svg->GetContentDeclarationBlock()) { return decl->RefRawStrong(); } } return nullptr; } return AsRefRawStrong(attrs->GetServoStyle()); } const StyleStrong* Gecko_GetExtraContentStyleDeclarations(const Element* 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; } const StyleStrong* Gecko_GetUnvisitedLinkAttrDeclarationBlock(const Element* aElement) { nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet(); if (!sheet) { return nullptr; } return AsRefRawStrong(sheet->GetServoUnvisitedLinkDecl()); } StyleSheet* Gecko_StyleSheet_Clone(const StyleSheet* aSheet, const StyleSheet* aNewParentSheet) { MOZ_ASSERT(aSheet); MOZ_ASSERT(aSheet->GetParentSheet(), "Should only be used for @import"); MOZ_ASSERT(aNewParentSheet, "Wat"); RefPtr 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(); } const StyleStrong* Gecko_GetVisitedLinkAttrDeclarationBlock(const Element* aElement) { nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet(); if (!sheet) { return nullptr; } return AsRefRawStrong(sheet->GetServoVisitedLinkDecl()); } const StyleStrong* Gecko_GetActiveLinkAttrDeclarationBlock(const Element* aElement) { nsHTMLStyleSheet* sheet = aElement->OwnerDoc()->GetAttributeStyleSheet(); if (!sheet) { return nullptr; } return AsRefRawStrong(sheet->GetServoActiveLinkDecl()); } static PseudoStyleType GetPseudoTypeFromElementForAnimation( const Element*& aElementOrPseudo) { if (aElementOrPseudo->IsGeneratedContentContainerForBefore()) { aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement(); return PseudoStyleType::before; } if (aElementOrPseudo->IsGeneratedContentContainerForAfter()) { aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement(); return PseudoStyleType::after; } if (aElementOrPseudo->IsGeneratedContentContainerForMarker()) { aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement(); return PseudoStyleType::marker; } return PseudoStyleType::NotPseudo; } bool Gecko_GetAnimationRule(const Element* aElement, EffectCompositor::CascadeLevel aCascadeLevel, RawServoAnimationValueMap* aAnimationValues) { MOZ_ASSERT(aElement); Document* doc = aElement->GetComposedDoc(); if (!doc) { return false; } nsPresContext* presContext = doc->GetPresContext(); if (!presContext) { return false; } PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement); return presContext->EffectCompositor()->GetServoAnimationRule( aElement, pseudoType, aCascadeLevel, aAnimationValues); } bool Gecko_StyleAnimationsEquals(const nsStyleAutoArray* aA, const nsStyleAutoArray* aB) { return *aA == *aB; } void Gecko_CopyAnimationNames(nsStyleAutoArray* aDest, const nsStyleAutoArray* 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(const Element* aElement, const ComputedStyle* aOldComputedData, const ComputedStyle* aComputedData, UpdateAnimationsTasks aTasks) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aElement); if (!aElement->IsInComposedDoc()) { return; } nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement); if (!presContext || !presContext->IsDynamic()) { return; } nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement); if (aTasks & UpdateAnimationsTasks::CSSAnimations) { presContext->AnimationManager()->UpdateAnimations( const_cast(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(const Element* aElementOrPseudo) { PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElementOrPseudo); EffectSet* effectSet = EffectSet::GetEffectSet(aElementOrPseudo, pseudoType); return effectSet ? effectSet->Count() : 0; } bool Gecko_ElementHasAnimations(const Element* aElement) { PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement); return !!EffectSet::GetEffectSet(aElement, pseudoType); } bool Gecko_ElementHasCSSAnimations(const Element* aElement) { PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement); nsAnimationManager::CSSAnimationCollection* collection = nsAnimationManager::CSSAnimationCollection ::GetAnimationCollection( aElement, pseudoType); return collection && !collection->mAnimations.IsEmpty(); } bool Gecko_ElementHasCSSTransitions(const Element* aElement) { PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement); nsTransitionManager::CSSTransitionCollection* collection = nsTransitionManager::CSSTransitionCollection ::GetAnimationCollection( aElement, pseudoType); return collection && !collection->mAnimations.IsEmpty(); } size_t Gecko_ElementTransitions_Length(const Element* aElement) { PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement); nsTransitionManager::CSSTransitionCollection* collection = nsTransitionManager::CSSTransitionCollection ::GetAnimationCollection( aElement, pseudoType); return collection ? collection->mAnimations.Length() : 0; } static CSSTransition* GetCurrentTransitionAt(const Element* aElement, size_t aIndex) { PseudoStyleType pseudoType = GetPseudoTypeFromElementForAnimation(aElement); nsTransitionManager::CSSTransitionCollection* collection = nsTransitionManager::CSSTransitionCollection ::GetAnimationCollection( aElement, pseudoType); if (!collection) { return nullptr; } nsTArray>& transitions = collection->mAnimations; return aIndex < transitions.Length() ? transitions[aIndex].get() : nullptr; } nsCSSPropertyID Gecko_ElementTransitions_PropertyAt(const Element* aElement, size_t aIndex) { CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); return transition ? transition->TransitionProperty() : nsCSSPropertyID::eCSSProperty_UNKNOWN; } const RawServoAnimationValue* Gecko_ElementTransitions_EndValueAt( const Element* aElement, size_t aIndex) { CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); return transition ? transition->ToValue().mServo.get() : nullptr; } double Gecko_GetProgressFromComputedTiming(const ComputedTiming* aTiming) { return aTiming->mProgress.Value(); } double Gecko_GetPositionInSegment( const AnimationPropertySegment* aSegment, double aProgress, ComputedTimingFunction::BeforeFlag aBeforeFlag) { MOZ_ASSERT(aSegment->mFromKey < aSegment->mToKey, "The segment from key should be less than to key"); double positionInSegment = (aProgress - aSegment->mFromKey) / // To avoid floating precision inaccuracies, make // sure we calculate both the numerator and // denominator using double precision. (double(aSegment->mToKey) - aSegment->mFromKey); return ComputedTimingFunction::GetPortion(aSegment->mTimingFunction, positionInSegment, aBeforeFlag); } const RawServoAnimationValue* Gecko_AnimationGetBaseStyle( const RawServoAnimationValueTable* aBaseStyles, nsCSSPropertyID aProperty) { auto base = reinterpret_cast< const nsRefPtrHashtable*>( aBaseStyles); return base->GetWeak(aProperty); } void Gecko_FillAllImageLayers(nsStyleImageLayers* aLayers, uint32_t aMaxLen) { aLayers->FillAllLayers(aMaxLen); } bool Gecko_IsDocumentBody(const Element* aElement) { Document* doc = aElement->GetUncomposedDoc(); return doc && doc->GetBodyElement() == aElement; } nscolor Gecko_GetLookAndFeelSystemColor(int32_t aId, const Document* aDoc) { bool useStandinsForNativeColors = !nsContentUtils::IsChromeDoc(aDoc); nscolor result; LookAndFeel::ColorID colorId = static_cast(aId); AutoWriteLock guard(*sServoFFILock); LookAndFeel::GetColor(colorId, useStandinsForNativeColors, &result); return result; } bool Gecko_MatchLang(const Element* aElement, nsAtom* aOverrideLang, bool aHasOverrideLang, const char16_t* aValue) { MOZ_ASSERT(!(aOverrideLang && !aHasOverrideLang), "aHasOverrideLang should only be set when aOverrideLang is null"); MOZ_ASSERT(aValue, "null lang parameter"); if (!aValue || !*aValue) { return false; } // We have to determine the language of the current element. Since // this is currently no property and since the language is inherited // from the parent we have to be prepared to look at all parent // nodes. The language itself is encoded in the LANG attribute. if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) { return nsStyleUtil::DashMatchCompare( nsDependentAtomString(language), nsDependentString(aValue), nsASCIICaseInsensitiveStringComparator); } // Try to get the language from the HTTP header or if this // is missing as well from the preferences. // The content language can be a comma-separated list of // language codes. nsAutoString language; aElement->OwnerDoc()->GetContentLanguage(language); nsDependentString langString(aValue); language.StripWhitespace(); for (auto const& lang : language.Split(char16_t(','))) { if (nsStyleUtil::DashMatchCompare(lang, langString, nsASCIICaseInsensitiveStringComparator)) { return true; } } return false; } nsAtom* Gecko_GetXMLLangValue(const Element* aElement) { const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML); if (!attr) { return nullptr; } MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom); RefPtr atom = attr->GetAtomValue(); return atom.forget().take(); } Document::DocumentTheme Gecko_GetDocumentLWTheme(const Document* aDocument) { return aDocument->ThreadSafeGetDocumentLWTheme(); } const PreferenceSheet::Prefs* Gecko_GetPrefSheetPrefs(const Document* aDoc) { return &PreferenceSheet::PrefsFor(*aDoc); } bool Gecko_IsTableBorderNonzero(const Element* aElement) { if (!aElement->IsHTMLElement(nsGkAtoms::table)) { return false; } const nsAttrValue* val = aElement->GetParsedAttr(nsGkAtoms::border); return val && (val->Type() != nsAttrValue::eInteger || val->GetIntegerValue() != 0); } bool Gecko_IsBrowserFrame(const Element* aElement) { nsIMozBrowserFrame* browserFrame = const_cast(aElement)->GetAsMozBrowserFrame(); return browserFrame && browserFrame->GetReallyIsBrowser(); } 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(); } 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_) \ auto c_ = ignore_case_ ? nsASCIICaseInsensitiveStringComparator \ : nsTDefaultStringComparator; \ 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) { return aValue->HasSubstring(nsDependentAtomString(aStr), aIgnoreCase ? eIgnoreCase : eCaseMatters); }; 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) { return aValue->HasPrefix(nsDependentAtomString(aStr), aIgnoreCase ? eIgnoreCase : eCaseMatters); }; 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) { return aValue->HasSuffix(nsDependentAtomString(aStr), aIgnoreCase ? eIgnoreCase : eCaseMatters); }; return DoMatch(aElement, aNS, aName, match); } #define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \ nsAtom* prefix_##LangValue(implementor_ aElement) { \ return LangValue(aElement); \ } \ bool prefix_##HasAttr(implementor_ aElement, nsAtom* aNS, nsAtom* aName) { \ return HasAttr(aElement, aNS, aName); \ } \ bool prefix_##AttrEquals(implementor_ aElement, nsAtom* aNS, nsAtom* aName, \ nsAtom* aStr, bool aIgnoreCase) { \ return AttrEquals(aElement, aNS, aName, aStr, aIgnoreCase); \ } \ bool prefix_##AttrDashEquals(implementor_ aElement, nsAtom* aNS, \ nsAtom* aName, nsAtom* aStr, \ bool aIgnoreCase) { \ return AttrDashEquals(aElement, aNS, aName, aStr, aIgnoreCase); \ } \ bool prefix_##AttrIncludes(implementor_ aElement, nsAtom* aNS, \ nsAtom* aName, nsAtom* aStr, bool aIgnoreCase) { \ return AttrIncludes(aElement, aNS, aName, aStr, aIgnoreCase); \ } \ bool prefix_##AttrHasSubstring(implementor_ aElement, nsAtom* aNS, \ nsAtom* aName, nsAtom* aStr, \ bool aIgnoreCase) { \ return AttrHasSubstring(aElement, aNS, aName, aStr, aIgnoreCase); \ } \ bool prefix_##AttrHasPrefix(implementor_ aElement, nsAtom* aNS, \ nsAtom* aName, nsAtom* aStr, bool aIgnoreCase) { \ return AttrHasPrefix(aElement, aNS, aName, aStr, aIgnoreCase); \ } \ bool prefix_##AttrHasSuffix(implementor_ aElement, nsAtom* aNS, \ nsAtom* aName, nsAtom* aStr, bool aIgnoreCase) { \ return AttrHasSuffix(aElement, aNS, aName, aStr, aIgnoreCase); \ } SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, const Element*) SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, const ServoElementSnapshot*) #undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS nsAtom* Gecko_Atomize(const char* aString, uint32_t aLength) { return NS_Atomize(nsDependentCSubstring(aString, aLength)).take(); } nsAtom* Gecko_Atomize16(const nsAString* aString) { return NS_Atomize(*aString).take(); } void Gecko_AddRefAtom(nsAtom* aAtom) { NS_ADDREF(aAtom); } void Gecko_ReleaseAtom(nsAtom* aAtom) { NS_RELEASE(aAtom); } void Gecko_nsTArray_FontFamilyName_AppendNamed( nsTArray* aNames, nsAtom* aName, StyleFontFamilyNameSyntax aSyntax) { aNames->AppendElement(FontFamilyName(aName, aSyntax)); } void Gecko_nsTArray_FontFamilyName_AppendGeneric( nsTArray* aNames, StyleGenericFontFamily 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(SharedFontList, SharedFontList); void Gecko_CopyFontFamilyFrom(nsFont* dst, const nsFont* src) { dst->fontlist = src->fontlist; } void Gecko_nsFont_InitSystem(nsFont* aDest, int32_t aFontId, const nsStyleFont* aFont, const Document* aDocument) { const nsFont* defaultVariableFont = ThreadSafeGetDefaultFontHelper( *aDocument, aFont->mLanguage, StyleGenericFontFamily::None); // We have passed uninitialized memory to this function, // initialize it. We can't simply return an nsFont because then // we need to know its size beforehand. Servo cannot initialize nsFont // itself, so this will do. new (aDest) nsFont(*defaultVariableFont); LookAndFeel::FontID fontID = static_cast(aFontId); AutoWriteLock guard(*sServoFFILock); nsLayoutUtils::ComputeSystemFont(aDest, fontID, defaultVariableFont, aDocument); } void Gecko_nsFont_Destroy(nsFont* aDest) { aDest->~nsFont(); } StyleGenericFontFamily Gecko_nsStyleFont_ComputeDefaultFontType( const Document* aDoc, StyleGenericFontFamily aGenericId, nsAtom* aLanguage) { const nsFont* defaultFont = ThreadSafeGetDefaultFontHelper(*aDoc, aLanguage, aGenericId); return defaultFont->fontlist.GetDefaultFontType(); } gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet() { return new gfxFontFeatureValueSet(); } nsTArray* Gecko_AppendFeatureValueHashEntry( gfxFontFeatureValueSet* aFontFeatureValues, nsAtom* aFamily, uint32_t aAlternate, nsAtom* aName) { MOZ_ASSERT(NS_IsMainThread()); return aFontFeatureValues->AppendFeatureValueHashEntry(nsAtomCString(aFamily), aName, aAlternate); } float Gecko_FontStretch_ToFloat(FontStretch aStretch) { // Servo represents percentages with 1. being 100%. return aStretch.Percentage() / 100.0f; } void Gecko_FontStretch_SetFloat(FontStretch* aStretch, float aFloat) { // Servo represents percentages with 1. being 100%. // // Also, the font code assumes a given maximum that style doesn't really need // to know about. So clamp here at the boundary. *aStretch = FontStretch(std::min(aFloat * 100.0f, float(FontStretch::kMax))); } void Gecko_FontSlantStyle_SetNormal(FontSlantStyle* aStyle) { *aStyle = FontSlantStyle::Normal(); } void Gecko_FontSlantStyle_SetItalic(FontSlantStyle* aStyle) { *aStyle = FontSlantStyle::Italic(); } void Gecko_FontSlantStyle_SetOblique(FontSlantStyle* aStyle, float aAngleInDegrees) { *aStyle = FontSlantStyle::Oblique(aAngleInDegrees); } void Gecko_FontSlantStyle_Get(FontSlantStyle aStyle, bool* aNormal, bool* aItalic, float* aObliqueAngle) { *aNormal = aStyle.IsNormal(); *aItalic = aStyle.IsItalic(); if (aStyle.IsOblique()) { *aObliqueAngle = aStyle.ObliqueAngle(); } } float Gecko_FontWeight_ToFloat(FontWeight aWeight) { return aWeight.ToFloat(); } void Gecko_FontWeight_SetFloat(FontWeight* aWeight, float aFloat) { *aWeight = FontWeight(aFloat); } void Gecko_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) { 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); } 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_GetComputedURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) { MOZ_ASSERT(aURL); MOZ_ASSERT(aOut); if (aURL->IsLocalRef()) { aOut->Assign(aURL->SpecifiedSerialization()); return; } Gecko_GetComputedImageURLSpec(aURL, aOut); } void Gecko_GetComputedImageURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) { // Image URIs don't serialize local refs as local. if (nsIURI* uri = aURL->GetURI()) { nsresult rv = uri->GetSpec(*aOut); if (NS_SUCCEEDED(rv)) { return; } } aOut->AssignLiteral("about:invalid"); } void Gecko_nsIURI_Debug(nsIURI* aURI, nsCString* aOut) { // TODO(emilio): Do we have more useful stuff to put here, maybe? if (aURI) { *aOut = aURI->GetSpecOrDefault(); } } // XXX Implemented by hand because even though it's thread-safe, only the // subclasses have the HasThreadSafeRefCnt bits. void Gecko_AddRefnsIURIArbitraryThread(nsIURI* aPtr) { NS_ADDREF(aPtr); } void Gecko_ReleasensIURIArbitraryThread(nsIURI* aPtr) { NS_RELEASE(aPtr); } 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; } void Gecko_nsStyleFont_PrioritizeUserFonts( nsStyleFont* aFont, StyleGenericFontFamily aDefaultGeneric) { MOZ_ASSERT(!StaticPrefs::browser_display_use_document_fonts()); MOZ_ASSERT(aDefaultGeneric != StyleGenericFontFamily::None); if (!aFont->mFont.fontlist.PrioritizeFirstGeneric()) { aFont->mFont.fontlist.PrependGeneric(aDefaultGeneric); } } Length Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont* aFont, const Document* aDocument) { // Don't change font-size:0, since that would un-hide hidden text, // or SVG text, or chrome docs, we assume those know what they do. if (aFont->mSize.IsZero() || !aFont->mAllowZoomAndMinSize || nsContentUtils::IsChromeDoc(aDocument)) { return {0}; } Length minFontSize; bool needsCache = false; auto MinFontSize = [&](bool* aNeedsToCache) { 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; } StyleDefaultFontSizes Gecko_GetBaseSize(nsAtom* aLanguage) { LangGroupFontPrefs prefs; nsStaticAtom* langGroupAtom = StaticPresData::Get()->GetUncachedLangGroup(aLanguage); prefs.Initialize(langGroupAtom); return {prefs.mDefaultVariableFont.size, prefs.mDefaultSerifFont.size, prefs.mDefaultSansSerifFont.size, prefs.mDefaultMonospaceFont.size, prefs.mDefaultCursiveFont.size, prefs.mDefaultFantasyFont.size}; } 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; delete sServoFFILock; 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) { 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); presContext->SetUsesExChUnits(true); RefPtr fm = nsLayoutUtils::GetMetricsFor( presContext, aIsVertical, aFont, aFontSize, aUseUserFontSet); const auto& metrics = fm->GetThebesFontGroup()->GetFirstValidFont()->GetMetrics( fm->Orientation()); 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))}; } NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder); void Gecko_StyleSheet_FinishAsyncParse( SheetLoadDataHolder* aData, StyleStrong aSheetContents, StyleOwnedOrNull aUseCounters) { UniquePtr useCounters = aUseCounters.Consume(); 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(); data->mUseCounters = std::move(counters); data->mSheet->FinishAsyncParse(contents.forget()); })); } 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_DispatchToMainThread(NS_NewRunnableFunction( __func__, [data = std::move(loadData), url = StyleCssUrl(*aUrl), media = std::move(mediaList), import = std::move(importRule)]() mutable { MOZ_ASSERT(NS_IsMainThread()); SheetLoadData* d = data->get(); RefPtr sheet = LoadImportSheet( d->mLoader, d->mSheet, d, nullptr, url, media.forget()); Servo_ImportRule_SetSheet(import, sheet); })); } void Gecko_AddPropertyToSet(nsCSSPropertyIDSet* aPropertySet, nsCSSPropertyID aProperty) { aPropertySet->AddProperty(aProperty); } #define STYLE_STRUCT(name) \ \ void Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr, \ const Document* doc) { \ new (ptr) nsStyle##name(*doc); \ } \ \ void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \ const nsStyle##name* other) { \ new (ptr) nsStyle##name(*other); \ } \ \ void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr) { \ ptr->~nsStyle##name(); \ } void Gecko_RegisterProfilerThread(const char* name) { PROFILER_REGISTER_THREAD(name); } void Gecko_UnregisterProfilerThread() { PROFILER_UNREGISTER_THREAD(); } #ifdef MOZ_GECKO_PROFILER void Gecko_Construct_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel, JS::ProfilingCategoryPair aCatPair) { new (aAutoLabel) AutoProfilerLabel( "", nullptr, aCatPair, uint32_t( js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR)); } void Gecko_Destroy_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel) { aAutoLabel->~AutoProfilerLabel(); } #endif bool Gecko_DocumentRule_UseForPresentation( const Document* aDocument, const nsACString* aPattern, DocumentMatchingFunction aMatchingFunction) { MOZ_ASSERT(NS_IsMainThread()); nsIURI* docURI = aDocument->GetDocumentURI(); nsAutoCString docURISpec; if (docURI) { // If GetSpec fails (due to OOM) just skip these URI-specific CSS rules. nsresult rv = docURI->GetSpec(docURISpec); NS_ENSURE_SUCCESS(rv, false); } return CSSMozDocumentRule::Match(aDocument, docURI, docURISpec, *aPattern, aMatchingFunction); } void Gecko_SetJemallocThreadLocalArena(bool enabled) { #if defined(MOZ_MEMORY) jemalloc_thread_local_arena(enabled); #endif } #include "nsStyleStructList.h" #undef STYLE_STRUCT bool Gecko_ErrorReportingEnabled(const StyleSheet* aSheet, const Loader* aLoader, 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, 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(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(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()); }