GeckoBindings.cpp (75266B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* FFI functions for Servo to call into Gecko */ 8 9 #include "mozilla/GeckoBindings.h" 10 11 #include "AnchorPositioningUtils.h" 12 #include "ChildIterator.h" 13 #include "ErrorReporter.h" 14 #include "gfxFontFeatures.h" 15 #include "gfxMathTable.h" 16 #include "gfxTextRun.h" 17 #include "imgLoader.h" 18 #include "mozilla/AttributeStyles.h" 19 #include "mozilla/ClearOnShutdown.h" 20 #include "mozilla/DeclarationBlock.h" 21 #include "mozilla/EffectCompositor.h" 22 #include "mozilla/EffectSet.h" 23 #include "mozilla/FontPropertyTypes.h" 24 #include "mozilla/Hal.h" 25 #include "mozilla/Keyframe.h" 26 #include "mozilla/LookAndFeel.h" 27 #include "mozilla/Mutex.h" 28 #include "mozilla/Preferences.h" 29 #include "mozilla/RWLock.h" 30 #include "mozilla/RestyleManager.h" 31 #include "mozilla/ServoBindings.h" 32 #include "mozilla/ServoElementSnapshot.h" 33 #include "mozilla/ServoTraversalStatistics.h" 34 #include "mozilla/ShadowParts.h" 35 #include "mozilla/SizeOfState.h" 36 #include "mozilla/StaticPrefs_browser.h" 37 #include "mozilla/StaticPrefs_layout.h" 38 #include "mozilla/StaticPresData.h" 39 #include "mozilla/StaticPtr.h" 40 #include "mozilla/StyleAnimationValue.h" 41 #include "mozilla/TimelineManager.h" 42 #include "mozilla/URLExtraData.h" 43 #include "mozilla/css/ImageLoader.h" 44 #include "mozilla/dom/CSSMozDocumentRule.h" 45 #include "mozilla/dom/DocumentInlines.h" 46 #include "mozilla/dom/Element.h" 47 #include "mozilla/dom/ElementInlines.h" 48 #include "mozilla/dom/HTMLBodyElement.h" 49 #include "mozilla/dom/HTMLImageElement.h" 50 #include "mozilla/dom/HTMLSelectElement.h" 51 #include "mozilla/dom/HTMLSlotElement.h" 52 #include "mozilla/dom/HTMLTableCellElement.h" 53 #include "mozilla/dom/MediaList.h" 54 #include "mozilla/dom/ReferrerInfo.h" 55 #include "mozilla/dom/SVGElement.h" 56 #include "mozilla/dom/ViewTransition.h" 57 #include "mozilla/dom/WorkerCommon.h" 58 #include "nsAnimationManager.h" 59 #include "nsAttrValueInlines.h" 60 #include "nsCSSFrameConstructor.h" 61 #include "nsCSSProps.h" 62 #include "nsCSSPseudoElements.h" 63 #include "nsContentUtils.h" 64 #include "nsDOMTokenList.h" 65 #include "nsDeviceContext.h" 66 #include "nsFontMetrics.h" 67 #include "nsIContentInlines.h" 68 #include "nsIFrame.h" 69 #include "nsIFrameInlines.h" 70 #include "nsILoadContext.h" 71 #include "nsINode.h" 72 #include "nsIURI.h" 73 #include "nsLayoutUtils.h" 74 #include "nsNameSpaceManager.h" 75 #include "nsNetUtil.h" 76 #include "nsProxyRelease.h" 77 #include "nsString.h" 78 #include "nsStyleStruct.h" 79 #include "nsStyleUtil.h" 80 #include "nsTArray.h" 81 #include "nsTransitionManager.h" 82 #include "nsWindowSizes.h" 83 84 #if defined(MOZ_MEMORY) 85 # include "mozmemory.h" 86 #endif 87 88 using namespace mozilla; 89 using namespace mozilla::css; 90 using namespace mozilla::dom; 91 92 // Definitions of the global traversal stats. 93 bool ServoTraversalStatistics::sActive = false; 94 ServoTraversalStatistics ServoTraversalStatistics::sSingleton; 95 96 static StaticAutoPtr<RWLock> sServoFFILock; 97 98 static const LangGroupFontPrefs* ThreadSafeGetLangGroupFontPrefs( 99 const Document& aDocument, nsAtom* aLanguage) { 100 bool needsCache = false; 101 { 102 AutoReadLock guard(*sServoFFILock); 103 if (auto* prefs = aDocument.GetFontPrefsForLang(aLanguage, &needsCache)) { 104 return prefs; 105 } 106 } 107 MOZ_ASSERT(needsCache); 108 AutoWriteLock guard(*sServoFFILock); 109 return aDocument.GetFontPrefsForLang(aLanguage); 110 } 111 112 static const nsFont& ThreadSafeGetDefaultVariableFont(const Document& aDocument, 113 nsAtom* aLanguage) { 114 return ThreadSafeGetLangGroupFontPrefs(aDocument, aLanguage) 115 ->mDefaultVariableFont; 116 } 117 118 /* 119 * Does this child count as significant for selector matching? 120 * 121 * See nsStyleUtil::IsSignificantChild for details. 122 */ 123 bool Gecko_IsSignificantChild(const nsINode* aNode, 124 bool aWhitespaceIsSignificant) { 125 return nsStyleUtil::ThreadSafeIsSignificantChild(aNode->AsContent(), 126 aWhitespaceIsSignificant); 127 } 128 129 const nsINode* Gecko_GetLastChild(const nsINode* aNode) { 130 return aNode->GetLastChild(); 131 } 132 133 const nsINode* Gecko_GetFlattenedTreeParentNode(const nsINode* aNode) { 134 return aNode->GetFlattenedTreeParentNodeForStyle(); 135 } 136 137 void Gecko_GetAnonymousContentForElement(const Element* aElement, 138 nsTArray<nsIContent*>* aArray) { 139 MOZ_ASSERT(aElement->MayHaveAnonymousChildren()); 140 if (aElement->HasProperties()) { 141 if (auto* backdrop = nsLayoutUtils::GetBackdropPseudo(aElement)) { 142 aArray->AppendElement(backdrop); 143 } 144 if (auto* marker = nsLayoutUtils::GetMarkerPseudo(aElement)) { 145 aArray->AppendElement(marker); 146 } 147 if (auto* before = nsLayoutUtils::GetBeforePseudo(aElement)) { 148 aArray->AppendElement(before); 149 } 150 if (auto* after = nsLayoutUtils::GetAfterPseudo(aElement)) { 151 aArray->AppendElement(after); 152 } 153 } 154 nsContentUtils::AppendNativeAnonymousChildren( 155 aElement, *aArray, nsIContent::eSkipDocumentLevelNativeAnonymousContent); 156 } 157 158 void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* aAnonContent) { 159 MOZ_ASSERT(aAnonContent); 160 delete aAnonContent; 161 } 162 163 RustSpan<const nsINode* const> Gecko_GetAssignedNodes(const Element* aElement) { 164 MOZ_ASSERT(HTMLSlotElement::FromNode(aElement)); 165 Span<const RefPtr<nsINode>> span = 166 static_cast<const HTMLSlotElement*>(aElement)->AssignedNodes(); 167 return {reinterpret_cast<const nsINode* const*>(span.Elements()), 168 span.Length()}; 169 } 170 171 void Gecko_GetQueryContainerSize(const Element* aElement, nscoord* aOutWidth, 172 nscoord* aOutHeight) { 173 MOZ_ASSERT(aElement); 174 const nsIFrame* frame = aElement->GetPrimaryFrame(); 175 if (!frame) { 176 return; 177 } 178 const auto containAxes = frame->GetContainSizeAxes(); 179 if (!containAxes.IsAny()) { 180 return; 181 } 182 nsSize size = frame->GetContentRectRelativeToSelf().Size(); 183 bool isVertical = frame->GetWritingMode().IsVertical(); 184 if (isVertical ? containAxes.mBContained : containAxes.mIContained) { 185 *aOutWidth = size.width; 186 } 187 if (isVertical ? containAxes.mIContained : containAxes.mBContained) { 188 *aOutHeight = size.height; 189 } 190 } 191 192 void Gecko_ComputedStyle_Init(ComputedStyle* aStyle, 193 const ServoComputedData* aValues, 194 PseudoStyleType aPseudoType) { 195 new (KnownNotNull, aStyle) 196 ComputedStyle(aPseudoType, ServoComputedDataForgotten(aValues)); 197 } 198 199 ServoComputedData::ServoComputedData(const ServoComputedDataForgotten aValue) { 200 memcpy((void*)this, aValue.mPtr, sizeof(*this)); 201 } 202 203 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleStructsMallocEnclosingSizeOf) 204 205 void ServoComputedData::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { 206 // Note: GetStyleFoo() returns a pointer to an nsStyleFoo that sits within a 207 // servo_arc::Arc, i.e. it is preceded by a word-sized refcount. So we need 208 // to measure it with a function that can handle an interior pointer. We use 209 // ServoStyleStructsEnclosingMallocSizeOf to clearly identify in DMD's 210 // output the memory measured here. 211 #define MEASURE_STRUCT(name_) \ 212 static_assert(alignof(nsStyle##name_) <= sizeof(size_t), \ 213 "alignment will break AddSizeOfExcludingThis()"); \ 214 const void* p##name_ = Style##name_(); \ 215 if (!aSizes.mState.HaveSeenPtr(p##name_)) { \ 216 aSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \ 217 ServoStyleStructsMallocEnclosingSizeOf(p##name_); \ 218 } 219 FOR_EACH_STYLE_STRUCT(MEASURE_STRUCT, MEASURE_STRUCT) 220 #undef MEASURE_STRUCT 221 222 if (visited_style && !aSizes.mState.HaveSeenPtr(visited_style)) { 223 visited_style->AddSizeOfIncludingThis(aSizes, 224 &aSizes.mLayoutComputedValuesVisited); 225 } 226 227 // Measurement of the following members may be added later if DMD finds it is 228 // worthwhile: 229 // - custom_properties 230 // - writing_mode 231 // - rules 232 // - font_computation_data 233 } 234 235 void Gecko_ComputedStyle_Destroy(ComputedStyle* aStyle) { 236 aStyle->~ComputedStyle(); 237 } 238 239 void Gecko_ConstructStyleChildrenIterator(const Element* aElement, 240 StyleChildrenIterator* aIterator) { 241 MOZ_ASSERT(aElement); 242 MOZ_ASSERT(aIterator); 243 new (aIterator) StyleChildrenIterator(aElement); 244 } 245 246 void Gecko_DestroyStyleChildrenIterator(StyleChildrenIterator* aIterator) { 247 MOZ_ASSERT(aIterator); 248 249 aIterator->~StyleChildrenIterator(); 250 } 251 252 const nsINode* Gecko_GetNextStyleChild(StyleChildrenIterator* aIterator) { 253 MOZ_ASSERT(aIterator); 254 return aIterator->GetNextChild(); 255 } 256 257 bool Gecko_VisitedStylesEnabled(const Document* aDoc) { 258 MOZ_ASSERT(aDoc); 259 MOZ_ASSERT(NS_IsMainThread()); 260 261 if (!StaticPrefs::layout_css_visited_links_enabled()) { 262 return false; 263 } 264 265 if (aDoc->IsBeingUsedAsImage()) { 266 return false; 267 } 268 269 nsILoadContext* loadContext = aDoc->GetLoadContext(); 270 if (loadContext && loadContext->UsePrivateBrowsing()) { 271 return false; 272 } 273 274 return true; 275 } 276 277 ElementState::InternalType Gecko_ElementState(const Element* aElement) { 278 return aElement->StyleState().GetInternalValue(); 279 } 280 281 bool Gecko_IsRootElement(const Element* aElement) { 282 return aElement->OwnerDoc()->GetRootElement() == aElement; 283 } 284 285 void Gecko_NoteDirtyElement(const Element* aElement) { 286 MOZ_ASSERT(NS_IsMainThread()); 287 const_cast<Element*>(aElement)->NoteDirtyForServo(); 288 } 289 290 void Gecko_NoteDirtySubtreeForInvalidation(const Element* aElement) { 291 MOZ_ASSERT(NS_IsMainThread()); 292 const_cast<Element*>(aElement)->NoteDirtySubtreeForServo(); 293 } 294 295 void Gecko_NoteAnimationOnlyDirtyElement(const Element* aElement) { 296 MOZ_ASSERT(NS_IsMainThread()); 297 const_cast<Element*>(aElement)->NoteAnimationOnlyDirtyForServo(); 298 } 299 300 bool Gecko_AnimationNameMayBeReferencedFromStyle( 301 const nsPresContext* aPresContext, nsAtom* aName) { 302 MOZ_ASSERT(aPresContext); 303 return aPresContext->AnimationManager()->AnimationMayBeReferenced(aName); 304 } 305 306 void Gecko_InvalidatePositionTry(const Element* aElement) { 307 auto* f = aElement->GetPrimaryFrame(); 308 if (!f || !f->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 309 return; 310 } 311 f->RemoveProperty(nsIFrame::LastSuccessfulPositionFallback()); 312 f->PresShell()->MarkPositionedFrameForReflow(f); 313 } 314 315 float Gecko_GetScrollbarInlineSize(const nsPresContext* aPc) { 316 MOZ_ASSERT(aPc); 317 auto overlay = aPc->UseOverlayScrollbars() ? nsITheme::Overlay::Yes 318 : nsITheme::Overlay::No; 319 LayoutDeviceIntCoord size = 320 aPc->Theme()->GetScrollbarSize(aPc, StyleScrollbarWidth::Auto, overlay); 321 return aPc->DevPixelsToFloatCSSPixels(size); 322 } 323 324 PseudoStyleType Gecko_GetImplementedPseudoType(const Element* aElement) { 325 return aElement->GetPseudoElementType(); 326 } 327 328 nsAtom* Gecko_GetImplementedPseudoIdentifier(const Element* aElement) { 329 if (!PseudoStyle::IsNamedViewTransitionPseudoElement( 330 aElement->GetPseudoElementType())) { 331 return nullptr; 332 } 333 334 if (!aElement->HasName()) { 335 return nullptr; 336 } 337 338 return aElement->GetParsedAttr(nsGkAtoms::name)->GetAtomValue(); 339 } 340 341 uint32_t Gecko_CalcStyleDifference(const ComputedStyle* aOldStyle, 342 const ComputedStyle* aNewStyle, 343 bool* aAnyStyleStructChanged, 344 bool* aOnlyResetStructsChanged) { 345 MOZ_ASSERT(aOldStyle); 346 MOZ_ASSERT(aNewStyle); 347 348 uint32_t equalStructs; 349 nsChangeHint result = 350 aOldStyle->CalcStyleDifference(*aNewStyle, &equalStructs); 351 352 *aAnyStyleStructChanged = 353 equalStructs != StyleStructConstants::kAllStructsMask; 354 355 const auto kInheritedStructsMask = 356 StyleStructConstants::kInheritedStructsMask; 357 *aOnlyResetStructsChanged = 358 (equalStructs & kInheritedStructsMask) == kInheritedStructsMask; 359 360 return result; 361 } 362 363 nscoord Gecko_CalcLineHeight(const StyleLineHeight* aLh, 364 const nsPresContext* aPc, bool aVertical, 365 const nsStyleFont* aAgainstFont, 366 const mozilla::dom::Element* aElement) { 367 // Normal line-height depends on font metrics. 368 AutoWriteLock guard(*sServoFFILock); 369 return ReflowInput::CalcLineHeight(*aLh, *aAgainstFont, 370 const_cast<nsPresContext*>(aPc), aVertical, 371 aElement, NS_UNCONSTRAINEDSIZE, 1.0f); 372 } 373 374 const ServoElementSnapshot* Gecko_GetElementSnapshot( 375 const ServoElementSnapshotTable* aTable, const Element* aElement) { 376 MOZ_ASSERT(aTable); 377 MOZ_ASSERT(aElement); 378 379 return aTable->Get(const_cast<Element*>(aElement)); 380 } 381 382 bool Gecko_HaveSeenPtr(SeenPtrs* aTable, const void* aPtr) { 383 MOZ_ASSERT(NS_IsMainThread()); 384 MOZ_ASSERT(aTable); 385 // Empty Rust allocations are indicated by small values up to the alignment 386 // of the relevant type. We shouldn't see anything like that here. 387 MOZ_ASSERT(uintptr_t(aPtr) > 16); 388 389 return aTable->HaveSeenPtr(aPtr); 390 } 391 392 const StyleLockedDeclarationBlock* Gecko_GetStyleAttrDeclarationBlock( 393 const Element* aElement) { 394 DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); 395 if (!decl) { 396 return nullptr; 397 } 398 return decl->Raw(); 399 } 400 401 void Gecko_UnsetDirtyStyleAttr(const Element* aElement) { 402 DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); 403 if (!decl) { 404 return; 405 } 406 decl->UnsetDirty(); 407 } 408 409 const StyleLockedDeclarationBlock* 410 Gecko_GetHTMLPresentationAttrDeclarationBlock(const Element* aElement) { 411 return aElement->GetMappedAttributeStyle(); 412 } 413 414 const StyleLockedDeclarationBlock* Gecko_GetViewTransitionDynamicRule( 415 const Element* aElement) { 416 const auto* vt = aElement->OwnerDoc()->GetActiveViewTransition(); 417 if (!vt) { 418 return nullptr; 419 } 420 return vt->GetDynamicRuleFor(*aElement); 421 } 422 423 const StyleLockedDeclarationBlock* Gecko_GetExtraContentStyleDeclarations( 424 const Element* aElement) { 425 if (const auto* cell = HTMLTableCellElement::FromNode(aElement)) { 426 return cell->GetMappedAttributesInheritedFromTable(); 427 } 428 if (const auto* img = HTMLImageElement::FromNode(aElement)) { 429 return img->GetMappedAttributesFromSource(); 430 } 431 return nullptr; 432 } 433 434 const StyleLockedDeclarationBlock* Gecko_GetUnvisitedLinkAttrDeclarationBlock( 435 const Element* aElement) { 436 AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); 437 if (!attrStyles) { 438 return nullptr; 439 } 440 441 return attrStyles->GetServoUnvisitedLinkDecl(); 442 } 443 444 StyleSheet* Gecko_StyleSheet_Clone(const StyleSheet* aSheet) { 445 MOZ_ASSERT(aSheet); 446 MOZ_ASSERT(aSheet->GetParentSheet(), "Should only be used for @import"); 447 // NOTE(emilio): We don't pass either the parent pointer of the stylesheet, 448 // nor fix up the child list (yet). This is fixed up in the StylesheetInner 449 // constructor. 450 RefPtr<StyleSheet> newSheet = aSheet->Clone(nullptr, nullptr); 451 return static_cast<StyleSheet*>(newSheet.forget().take()); 452 } 453 454 void Gecko_StyleSheet_AddRef(const StyleSheet* aSheet) { 455 MOZ_ASSERT(NS_IsMainThread()); 456 const_cast<StyleSheet*>(aSheet)->AddRef(); 457 } 458 459 void Gecko_StyleSheet_Release(const StyleSheet* aSheet) { 460 MOZ_ASSERT(NS_IsMainThread()); 461 const_cast<StyleSheet*>(aSheet)->Release(); 462 } 463 464 GeckoImplicitScopeRoot Gecko_StyleSheet_ImplicitScopeRoot( 465 const mozilla::StyleSheet* aSheet) { 466 if (aSheet->IsConstructed()) { 467 return GeckoImplicitScopeRoot{ 468 .mHost = nullptr, .mRoot = nullptr, .mConstructed = true}; 469 } 470 // https://drafts.csswg.org/css-cascade-6/#scope-limits 471 // "If no <scope-start> is specified, the scoping root is the parent element 472 // of the owner node of the stylesheet where the @scope rule is defined." 473 const auto* node = aSheet->GetOwnerNodeOfOutermostSheet(); 474 if (!node) { 475 return GeckoImplicitScopeRoot{ 476 .mHost = nullptr, .mRoot = nullptr, .mConstructed = false}; 477 } 478 const auto* host = node->GetContainingShadowHost(); 479 480 if (auto* aElement = node->GetParentElement()) { 481 return GeckoImplicitScopeRoot{ 482 .mHost = host, .mRoot = aElement, .mConstructed = false}; 483 } 484 // "[...] If no such element exists, then the scoping root is the root of the 485 // containing node tree." This really should only happen for stylesheets 486 // defined at the edge of the shadow root. 487 return GeckoImplicitScopeRoot{ 488 .mHost = host, .mRoot = host, .mConstructed = false}; 489 } 490 491 const StyleLockedDeclarationBlock* Gecko_GetVisitedLinkAttrDeclarationBlock( 492 const Element* aElement) { 493 AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); 494 if (!attrStyles) { 495 return nullptr; 496 } 497 return attrStyles->GetServoVisitedLinkDecl(); 498 } 499 500 const StyleLockedDeclarationBlock* Gecko_GetActiveLinkAttrDeclarationBlock( 501 const Element* aElement) { 502 AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); 503 if (!attrStyles) { 504 return nullptr; 505 } 506 return attrStyles->GetServoActiveLinkDecl(); 507 } 508 509 bool Gecko_GetAnimationRule(const Element* aElement, 510 EffectCompositor::CascadeLevel aCascadeLevel, 511 StyleAnimationValueMap* aAnimationValues) { 512 MOZ_ASSERT(aElement); 513 514 Document* doc = aElement->GetComposedDoc(); 515 if (!doc) { 516 return false; 517 } 518 nsPresContext* presContext = doc->GetPresContext(); 519 if (!presContext) { 520 return false; 521 } 522 523 const auto [element, pseudoRequest] = 524 AnimationUtils::GetElementPseudoPair(aElement); 525 return presContext->EffectCompositor()->GetServoAnimationRule( 526 element, pseudoRequest, aCascadeLevel, aAnimationValues); 527 } 528 529 bool Gecko_StyleAnimationsEquals(const nsStyleAutoArray<StyleAnimation>* aA, 530 const nsStyleAutoArray<StyleAnimation>* aB) { 531 return *aA == *aB; 532 } 533 534 bool Gecko_StyleScrollTimelinesEquals( 535 const nsStyleAutoArray<StyleScrollTimeline>* aA, 536 const nsStyleAutoArray<StyleScrollTimeline>* aB) { 537 return *aA == *aB; 538 } 539 540 bool Gecko_StyleViewTimelinesEquals( 541 const nsStyleAutoArray<StyleViewTimeline>* aA, 542 const nsStyleAutoArray<StyleViewTimeline>* aB) { 543 return *aA == *aB; 544 } 545 546 void Gecko_UpdateAnimations(const Element* aElement, 547 const ComputedStyle* aOldComputedData, 548 const ComputedStyle* aComputedData, 549 UpdateAnimationsTasks aTasks) { 550 MOZ_ASSERT(NS_IsMainThread()); 551 MOZ_ASSERT(aElement); 552 553 if (!aElement->IsInComposedDoc()) { 554 return; 555 } 556 557 nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement); 558 if (!presContext || !presContext->IsDynamic()) { 559 return; 560 } 561 562 nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); 563 564 const auto [element, pseudoRequest] = 565 AnimationUtils::GetElementPseudoPair(aElement); 566 567 // Handle scroll/view timelines first because CSS animations may refer to the 568 // timeline defined by itself. 569 if (aTasks & UpdateAnimationsTasks::ScrollTimelines) { 570 presContext->TimelineManager()->UpdateTimelines( 571 const_cast<Element*>(element), pseudoRequest, aComputedData, 572 TimelineManager::ProgressTimelineType::Scroll); 573 } 574 575 if (aTasks & UpdateAnimationsTasks::ViewTimelines) { 576 presContext->TimelineManager()->UpdateTimelines( 577 const_cast<Element*>(element), pseudoRequest, aComputedData, 578 TimelineManager::ProgressTimelineType::View); 579 } 580 581 if (aTasks & UpdateAnimationsTasks::CSSAnimations) { 582 presContext->AnimationManager()->UpdateAnimations( 583 const_cast<Element*>(element), pseudoRequest, aComputedData); 584 } 585 586 // aComputedData might be nullptr if the target element is now in a 587 // display:none subtree. We still call Gecko_UpdateAnimations in this case 588 // because we need to stop CSS animations in the display:none subtree. 589 // However, we don't need to update transitions since they are stopped by 590 // RestyleManager::AnimationsWithDestroyedFrame so we just return early 591 // here. 592 if (!aComputedData) { 593 return; 594 } 595 596 if (aTasks & UpdateAnimationsTasks::CSSTransitions) { 597 MOZ_ASSERT(aOldComputedData); 598 presContext->TransitionManager()->UpdateTransitions( 599 const_cast<Element*>(element), pseudoRequest, *aOldComputedData, 600 *aComputedData); 601 } 602 603 if (aTasks & UpdateAnimationsTasks::EffectProperties) { 604 presContext->EffectCompositor()->UpdateEffectProperties( 605 aComputedData, const_cast<Element*>(element), pseudoRequest); 606 } 607 608 if (aTasks & UpdateAnimationsTasks::CascadeResults) { 609 EffectSet* effectSet = EffectSet::Get(element, pseudoRequest); 610 // CSS animations/transitions might have been destroyed as part of the above 611 // steps so before updating cascade results, we check if there are still any 612 // animations to update. 613 if (effectSet) { 614 // We call UpdateCascadeResults directly (intead of 615 // MaybeUpdateCascadeResults) since we know for sure that the cascade has 616 // changed, but we were unable to call MarkCascadeUpdated when we noticed 617 // it since we avoid mutating state as part of the Servo parallel 618 // traversal. 619 presContext->EffectCompositor()->UpdateCascadeResults( 620 *effectSet, const_cast<Element*>(element), pseudoRequest); 621 } 622 } 623 624 if (aTasks & UpdateAnimationsTasks::DisplayChangedFromNone) { 625 presContext->EffectCompositor()->RequestRestyle( 626 const_cast<Element*>(element), pseudoRequest, 627 EffectCompositor::RestyleType::Standard, 628 EffectCompositor::CascadeLevel::Animations); 629 } 630 } 631 632 size_t Gecko_GetAnimationEffectCount(const Element* aElementOrPseudo) { 633 const auto [element, pseudo] = 634 AnimationUtils::GetElementPseudoPair(aElementOrPseudo); 635 636 EffectSet* effectSet = EffectSet::Get(element, pseudo); 637 return effectSet ? effectSet->Count() : 0; 638 } 639 640 bool Gecko_ElementHasAnimations(const Element* aElement) { 641 const auto [element, pseudo] = AnimationUtils::GetElementPseudoPair(aElement); 642 return !!EffectSet::Get(element, pseudo); 643 } 644 645 bool Gecko_ElementHasCSSAnimations(const Element* aElement) { 646 const auto [element, pseudo] = AnimationUtils::GetElementPseudoPair(aElement); 647 auto* collection = 648 nsAnimationManager::CSSAnimationCollection::Get(element, pseudo); 649 return collection && !collection->mAnimations.IsEmpty(); 650 } 651 652 bool Gecko_ElementHasCSSTransitions(const Element* aElement) { 653 const auto [element, pseudo] = AnimationUtils::GetElementPseudoPair(aElement); 654 auto* collection = 655 nsTransitionManager::CSSTransitionCollection::Get(element, pseudo); 656 return collection && !collection->mAnimations.IsEmpty(); 657 } 658 659 size_t Gecko_ElementTransitions_Length(const Element* aElement) { 660 const auto [element, pseudo] = AnimationUtils::GetElementPseudoPair(aElement); 661 auto* collection = 662 nsTransitionManager::CSSTransitionCollection::Get(element, pseudo); 663 return collection ? collection->mAnimations.Length() : 0; 664 } 665 666 static CSSTransition* GetCurrentTransitionAt(const Element* aElement, 667 size_t aIndex) { 668 const auto [element, pseudo] = AnimationUtils::GetElementPseudoPair(aElement); 669 auto* collection = 670 nsTransitionManager::CSSTransitionCollection ::Get(element, pseudo); 671 if (!collection) { 672 return nullptr; 673 } 674 return collection->mAnimations.SafeElementAt(aIndex); 675 } 676 677 NonCustomCSSPropertyId Gecko_ElementTransitions_PropertyAt( 678 const Element* aElement, size_t aIndex) { 679 CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); 680 return transition ? transition->TransitionProperty().mId 681 : NonCustomCSSPropertyId::eCSSProperty_UNKNOWN; 682 } 683 684 const StyleAnimationValue* Gecko_ElementTransitions_EndValueAt( 685 const Element* aElement, size_t aIndex) { 686 CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); 687 return transition ? transition->ToValue().mServo.get() : nullptr; 688 } 689 690 double Gecko_GetProgressFromComputedTiming(const ComputedTiming* aTiming) { 691 return aTiming->mProgress.Value(); 692 } 693 694 double Gecko_GetPositionInSegment(const AnimationPropertySegment* aSegment, 695 double aProgress, bool aBeforeFlag) { 696 MOZ_ASSERT(aSegment->mFromKey < aSegment->mToKey, 697 "The segment from key should be less than to key"); 698 699 double positionInSegment = (aProgress - aSegment->mFromKey) / 700 // To avoid floating precision inaccuracies, make 701 // sure we calculate both the numerator and 702 // denominator using double precision. 703 (double(aSegment->mToKey) - aSegment->mFromKey); 704 705 return StyleComputedTimingFunction::GetPortion( 706 aSegment->mTimingFunction, positionInSegment, aBeforeFlag); 707 } 708 709 const StyleAnimationValue* Gecko_AnimationGetBaseStyle( 710 const RawServoAnimationValueTable* aBaseStyles, 711 const mozilla::CSSPropertyId* aProperty) { 712 const auto* base = reinterpret_cast<const nsRefPtrHashtable< 713 nsGenericHashKey<CSSPropertyId>, StyleAnimationValue>*>(aBaseStyles); 714 return base->GetWeak(*aProperty); 715 } 716 717 void Gecko_FillAllImageLayers(nsStyleImageLayers* aLayers, uint32_t aMaxLen) { 718 aLayers->FillAllLayers(aMaxLen); 719 } 720 721 bool Gecko_IsDocumentBody(const Element* aElement) { 722 Document* doc = aElement->GetUncomposedDoc(); 723 return doc && doc->GetBodyElement() == aElement; 724 } 725 726 bool Gecko_IsDarkColorScheme(const Document* aDoc, 727 const StyleColorSchemeFlags* aStyle) { 728 return LookAndFeel::ColorSchemeForStyle(*aDoc, *aStyle) == ColorScheme::Dark; 729 } 730 731 nscolor Gecko_ComputeSystemColor(StyleSystemColor aColor, const Document* aDoc, 732 const StyleColorSchemeFlags* aStyle) { 733 auto colorScheme = LookAndFeel::ColorSchemeForStyle(*aDoc, *aStyle); 734 const auto& prefs = PreferenceSheet::PrefsFor(*aDoc); 735 if (prefs.mMustUseLightSystemColors) { 736 colorScheme = ColorScheme::Light; 737 } 738 const auto& colors = prefs.ColorsFor(colorScheme); 739 switch (aColor) { 740 case StyleSystemColor::Canvastext: 741 return colors.mDefault; 742 case StyleSystemColor::Canvas: 743 return colors.mDefaultBackground; 744 case StyleSystemColor::Linktext: 745 return colors.mLink; 746 case StyleSystemColor::Activetext: 747 return colors.mActiveLink; 748 case StyleSystemColor::Visitedtext: 749 return colors.mVisitedLink; 750 default: 751 break; 752 } 753 754 auto useStandins = LookAndFeel::ShouldUseStandins(*aDoc, aColor); 755 return LookAndFeel::Color(aColor, colorScheme, useStandins); 756 } 757 758 int32_t Gecko_GetLookAndFeelInt(int32_t aId) { 759 auto intId = static_cast<LookAndFeel::IntID>(aId); 760 return LookAndFeel::GetInt(intId); 761 } 762 763 float Gecko_GetLookAndFeelFloat(int32_t aId) { 764 auto id = static_cast<LookAndFeel::FloatID>(aId); 765 return LookAndFeel::GetFloat(id); 766 } 767 768 bool Gecko_MatchLang(const Element* aElement, nsAtom* aOverrideLang, 769 bool aHasOverrideLang, const char16_t* aValue) { 770 MOZ_ASSERT(!(aOverrideLang && !aHasOverrideLang), 771 "aHasOverrideLang should only be set when aOverrideLang is null"); 772 MOZ_ASSERT(aValue, "null lang parameter"); 773 if (!aValue || !*aValue) { 774 return false; 775 } 776 777 // We have to determine the language of the current element. Since 778 // this is currently no property and since the language is inherited 779 // from the parent we have to be prepared to look at all parent 780 // nodes. The language itself is encoded in the LANG attribute. 781 if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) { 782 return nsStyleUtil::LangTagCompare(nsAtomCString(language), 783 NS_ConvertUTF16toUTF8(aValue)); 784 } 785 786 // Try to get the language from the HTTP header or if this 787 // is missing as well from the preferences. 788 // The content language can be a comma-separated list of 789 // language codes. 790 // FIXME: We're not really consistent in our treatment of comma-separated 791 // content-language values. 792 if (nsAtom* language = aElement->OwnerDoc()->GetContentLanguage()) { 793 const NS_ConvertUTF16toUTF8 langString(aValue); 794 nsAtomCString docLang(language); 795 docLang.StripWhitespace(); 796 for (auto const& lang : docLang.Split(',')) { 797 if (nsStyleUtil::LangTagCompare(lang, langString)) { 798 return true; 799 } 800 } 801 } 802 return false; 803 } 804 805 bool Gecko_MatchViewTransitionClass( 806 const mozilla::dom::Element* aElement, 807 const nsTArray<StyleAtom>* aPtNameAndClassSelector) { 808 MOZ_ASSERT(aElement && aPtNameAndClassSelector); 809 810 const Document* doc = aElement->OwnerDoc(); 811 MOZ_ASSERT(doc); 812 const ViewTransition* vt = doc->GetActiveViewTransition(); 813 MOZ_ASSERT( 814 vt, "We should have an active view transition for this pseudo-element"); 815 816 nsAtom* name = Gecko_GetImplementedPseudoIdentifier(aElement); 817 MOZ_ASSERT(name); 818 return vt->MatchClassList(name, *aPtNameAndClassSelector); 819 } 820 821 static bool IsValidViewTransitionType(nsAtom* aName) { 822 nsDependentAtomString str(aName); 823 return !StringBeginsWith(str, u"-ua-"_ns, 824 nsASCIICaseInsensitiveStringComparator) && 825 !str.LowerCaseEqualsASCII("none"); 826 } 827 828 bool Gecko_HasActiveViewTransitionTypes( 829 const mozilla::dom::Document* aDoc, 830 const nsTArray<StyleCustomIdent>* aNames) { 831 MOZ_ASSERT(aDoc); 832 MOZ_ASSERT(aNames); 833 const ViewTransition* vt = aDoc->GetActiveViewTransition(); 834 if (!vt) { 835 return false; 836 } 837 const auto& typeList = vt->GetTypeList(); 838 if (typeList.IsEmpty()) { 839 return false; 840 } 841 for (const auto& name : *aNames) { 842 if (typeList.Contains(name.AsAtom())) { 843 // NOTE(emilio): This IsValidViewTransitionType() check is not in the spec 844 // and is rather weird, but matches other browsers for now, see: 845 // https://github.com/w3c/csswg-drafts/issues/13141 846 if (IsValidViewTransitionType(name.AsAtom())) { 847 return true; 848 } 849 } 850 } 851 return false; 852 } 853 854 nsAtom* Gecko_GetXMLLangValue(const Element* aElement) { 855 const nsAttrValue* attr = 856 aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML); 857 858 if (!attr) { 859 return nullptr; 860 } 861 862 MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom); 863 864 RefPtr<nsAtom> atom = attr->GetAtomValue(); 865 return atom.forget().take(); 866 } 867 868 const PreferenceSheet::Prefs* Gecko_GetPrefSheetPrefs(const Document* aDoc) { 869 return &PreferenceSheet::PrefsFor(*aDoc); 870 } 871 872 bool Gecko_IsTableBorderNonzero(const Element* aElement) { 873 if (!aElement->IsHTMLElement(nsGkAtoms::table)) { 874 return false; 875 } 876 const nsAttrValue* val = aElement->GetParsedAttr(nsGkAtoms::border); 877 return val && 878 (val->Type() != nsAttrValue::eInteger || val->GetIntegerValue() != 0); 879 } 880 881 bool Gecko_IsSelectListBox(const Element* aElement) { 882 const auto* select = HTMLSelectElement::FromNode(aElement); 883 return select && !select->IsCombobox(); 884 } 885 886 bool Gecko_LookupAttrValue(const Element* aElement, const nsAtom& aName, 887 nsAString& aResult) { 888 return aElement->GetAttr(&aName, aResult); 889 } 890 891 template <typename Implementor> 892 static nsAtom* LangValue(Implementor* aElement) { 893 // TODO(emilio): Deduplicate a bit with nsIContent::GetLang(). 894 const nsAttrValue* attr = 895 aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML); 896 if (!attr && aElement->SupportsLangAttr()) { 897 attr = aElement->GetParsedAttr(nsGkAtoms::lang); 898 } 899 900 if (!attr) { 901 return nullptr; 902 } 903 904 MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom); 905 RefPtr<nsAtom> atom = attr->GetAtomValue(); 906 return atom.forget().take(); 907 } 908 909 bool Gecko_AttrEquals(const nsAttrValue* aValue, const nsAtom* aStr, 910 bool aIgnoreCase) { 911 return aValue->Equals(aStr, aIgnoreCase ? eIgnoreCase : eCaseMatters); 912 } 913 914 #define WITH_COMPARATOR(ignore_case_, c_, expr_) \ 915 auto c_ = (ignore_case_) ? nsASCIICaseInsensitiveStringComparator \ 916 : nsTDefaultStringComparator<char16_t>; \ 917 return expr_; 918 919 bool Gecko_AttrDashEquals(const nsAttrValue* aValue, const nsAtom* aStr, 920 bool aIgnoreCase) { 921 nsAutoString str; 922 aValue->ToString(str); 923 WITH_COMPARATOR( 924 aIgnoreCase, c, 925 nsStyleUtil::DashMatchCompare(str, nsDependentAtomString(aStr), c)) 926 } 927 928 bool Gecko_AttrIncludes(const nsAttrValue* aValue, const nsAtom* aStr, 929 bool aIgnoreCase) { 930 if (aStr == nsGkAtoms::_empty) { 931 return false; 932 } 933 nsAutoString str; 934 aValue->ToString(str); 935 WITH_COMPARATOR( 936 aIgnoreCase, c, 937 nsStyleUtil::ValueIncludes(str, nsDependentAtomString(aStr), c)) 938 } 939 940 bool Gecko_AttrHasSubstring(const nsAttrValue* aValue, const nsAtom* aStr, 941 bool aIgnoreCase) { 942 return aStr != nsGkAtoms::_empty && 943 aValue->HasSubstring(nsDependentAtomString(aStr), 944 aIgnoreCase ? eIgnoreCase : eCaseMatters); 945 } 946 947 bool Gecko_AttrHasPrefix(const nsAttrValue* aValue, const nsAtom* aStr, 948 bool aIgnoreCase) { 949 return aStr != nsGkAtoms::_empty && 950 aValue->HasPrefix(nsDependentAtomString(aStr), 951 aIgnoreCase ? eIgnoreCase : eCaseMatters); 952 } 953 954 bool Gecko_AttrHasSuffix(const nsAttrValue* aValue, const nsAtom* aStr, 955 bool aIgnoreCase) { 956 return aStr != nsGkAtoms::_empty && 957 aValue->HasSuffix(nsDependentAtomString(aStr), 958 aIgnoreCase ? eIgnoreCase : eCaseMatters); 959 } 960 961 #define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \ 962 nsAtom* prefix_##LangValue(implementor_ aElement) { \ 963 return LangValue(aElement); \ 964 } 965 966 SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, const Element*) 967 SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, 968 const ServoElementSnapshot*) 969 970 #undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS 971 972 nsAtom* Gecko_Atomize(const char* aString, uint32_t aLength) { 973 return NS_Atomize(nsDependentCSubstring(aString, aLength)).take(); 974 } 975 976 nsAtom* Gecko_Atomize16(const nsAString* aString) { 977 return NS_Atomize(*aString).take(); 978 } 979 980 void Gecko_AddRefAtom(nsAtom* aAtom) { NS_ADDREF(aAtom); } 981 982 void Gecko_ReleaseAtom(nsAtom* aAtom) { NS_RELEASE(aAtom); } 983 984 void Gecko_nsFont_InitSystem(nsFont* aDest, StyleSystemFont aFontId, 985 const nsStyleFont* aFont, 986 const Document* aDocument) { 987 const nsFont& defaultVariableFont = 988 ThreadSafeGetDefaultVariableFont(*aDocument, aFont->mLanguage); 989 990 // We have passed uninitialized memory to this function, 991 // initialize it. We can't simply return an nsFont because then 992 // we need to know its size beforehand. Servo cannot initialize nsFont 993 // itself, so this will do. 994 new (aDest) nsFont(defaultVariableFont); 995 996 nsLayoutUtils::ComputeSystemFont(aDest, aFontId, defaultVariableFont, 997 aDocument); 998 } 999 1000 void Gecko_nsFont_Destroy(nsFont* aDest) { aDest->~nsFont(); } 1001 1002 StyleGenericFontFamily Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage( 1003 const Document* aDoc, nsAtom* aLanguage) { 1004 return ThreadSafeGetLangGroupFontPrefs(*aDoc, aLanguage)->GetDefaultGeneric(); 1005 } 1006 1007 Length Gecko_GetBaseSize(const Document* aDoc, nsAtom* aLang, 1008 StyleGenericFontFamily aGeneric) { 1009 return ThreadSafeGetLangGroupFontPrefs(*aDoc, aLang) 1010 ->GetDefaultFont(aGeneric) 1011 ->size; 1012 } 1013 1014 gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet() { 1015 return new gfxFontFeatureValueSet(); 1016 } 1017 1018 nsTArray<uint32_t>* Gecko_AppendFeatureValueHashEntry( 1019 gfxFontFeatureValueSet* aFontFeatureValues, nsAtom* aFamily, 1020 uint32_t aAlternate, nsAtom* aName) { 1021 MOZ_ASSERT(NS_IsMainThread()); 1022 return aFontFeatureValues->AppendFeatureValueHashEntry(nsAtomCString(aFamily), 1023 aName, aAlternate); 1024 } 1025 1026 gfx::FontPaletteValueSet* Gecko_ConstructFontPaletteValueSet() { 1027 return new gfx::FontPaletteValueSet(); 1028 } 1029 1030 gfx::FontPaletteValueSet::PaletteValues* Gecko_AppendPaletteValueHashEntry( 1031 gfx::FontPaletteValueSet* aPaletteValueSet, nsAtom* aFamily, 1032 nsAtom* aName) { 1033 MOZ_ASSERT(NS_IsMainThread()); 1034 return aPaletteValueSet->Insert(aName, nsAtomCString(aFamily)); 1035 } 1036 1037 void Gecko_SetFontPaletteBase(gfx::FontPaletteValueSet::PaletteValues* aValues, 1038 int32_t aBasePaletteIndex) { 1039 aValues->mBasePalette = aBasePaletteIndex; 1040 } 1041 1042 void Gecko_SetFontPaletteOverride( 1043 gfx::FontPaletteValueSet::PaletteValues* aValues, int32_t aIndex, 1044 StyleAbsoluteColor* aColor) { 1045 if (aIndex < 0) { 1046 return; 1047 } 1048 aValues->mOverrides.AppendElement(gfx::FontPaletteValueSet::OverrideColor{ 1049 uint32_t(aIndex), gfx::sRGBColor::FromABGR(aColor->ToColor())}); 1050 } 1051 1052 void Gecko_EnsureImageLayersLength(nsStyleImageLayers* aLayers, size_t aLen, 1053 nsStyleImageLayers::LayerType aLayerType) { 1054 size_t oldLength = aLayers->mLayers.Length(); 1055 1056 aLayers->mLayers.EnsureLengthAtLeast(aLen); 1057 1058 for (size_t i = oldLength; i < aLen; ++i) { 1059 aLayers->mLayers[i].Initialize(aLayerType); 1060 } 1061 } 1062 1063 template <typename StyleType> 1064 static void EnsureStyleAutoArrayLength(StyleType* aArray, size_t aLen) { 1065 aArray->EnsureLengthAtLeast(aLen); 1066 } 1067 1068 void Gecko_EnsureStyleAnimationArrayLength(void* aArray, size_t aLen) { 1069 auto* base = static_cast<nsStyleAutoArray<StyleAnimation>*>(aArray); 1070 EnsureStyleAutoArrayLength(base, aLen); 1071 } 1072 1073 void Gecko_EnsureStyleTransitionArrayLength(void* aArray, size_t aLen) { 1074 auto* base = reinterpret_cast<nsStyleAutoArray<StyleTransition>*>(aArray); 1075 EnsureStyleAutoArrayLength(base, aLen); 1076 } 1077 1078 void Gecko_EnsureStyleScrollTimelineArrayLength(void* aArray, size_t aLen) { 1079 auto* base = static_cast<nsStyleAutoArray<StyleScrollTimeline>*>(aArray); 1080 EnsureStyleAutoArrayLength(base, aLen); 1081 } 1082 1083 void Gecko_EnsureStyleViewTimelineArrayLength(void* aArray, size_t aLen) { 1084 auto* base = static_cast<nsStyleAutoArray<StyleViewTimeline>*>(aArray); 1085 EnsureStyleAutoArrayLength(base, aLen); 1086 } 1087 1088 enum class KeyframeSearchDirection { 1089 Forwards, 1090 Backwards, 1091 }; 1092 1093 enum class KeyframeInsertPosition { 1094 Prepend, 1095 LastForOffset, 1096 }; 1097 1098 static Keyframe* GetOrCreateKeyframe( 1099 nsTArray<Keyframe>* aKeyframes, float aOffset, 1100 const StyleComputedTimingFunction* aTimingFunction, 1101 const CompositeOperationOrAuto aComposition, 1102 KeyframeSearchDirection aSearchDirection, 1103 KeyframeInsertPosition aInsertPosition) { 1104 MOZ_ASSERT(aKeyframes, "The keyframe array should be valid"); 1105 MOZ_ASSERT(aTimingFunction, "The timing function should be valid"); 1106 MOZ_ASSERT(aOffset >= 0. && aOffset <= 1., 1107 "The offset should be in the range of [0.0, 1.0]"); 1108 1109 size_t keyframeIndex; 1110 switch (aSearchDirection) { 1111 case KeyframeSearchDirection::Forwards: 1112 if (nsAnimationManager::FindMatchingKeyframe( 1113 *aKeyframes, aOffset, *aTimingFunction, aComposition, 1114 keyframeIndex)) { 1115 return &(*aKeyframes)[keyframeIndex]; 1116 } 1117 break; 1118 case KeyframeSearchDirection::Backwards: 1119 if (nsAnimationManager::FindMatchingKeyframe( 1120 Reversed(*aKeyframes), aOffset, *aTimingFunction, aComposition, 1121 keyframeIndex)) { 1122 return &(*aKeyframes)[aKeyframes->Length() - 1 - keyframeIndex]; 1123 } 1124 keyframeIndex = aKeyframes->Length() - 1; 1125 break; 1126 } 1127 1128 Keyframe* keyframe = aKeyframes->InsertElementAt( 1129 aInsertPosition == KeyframeInsertPosition::Prepend ? 0 : keyframeIndex); 1130 keyframe->mOffset.emplace(aOffset); 1131 if (!aTimingFunction->IsLinearKeyword()) { 1132 keyframe->mTimingFunction.emplace(*aTimingFunction); 1133 } 1134 keyframe->mComposite = aComposition; 1135 1136 return keyframe; 1137 } 1138 1139 Keyframe* Gecko_GetOrCreateKeyframeAtStart( 1140 nsTArray<Keyframe>* aKeyframes, float aOffset, 1141 const StyleComputedTimingFunction* aTimingFunction, 1142 const CompositeOperationOrAuto aComposition) { 1143 MOZ_ASSERT(aKeyframes->IsEmpty() || 1144 aKeyframes->ElementAt(0).mOffset.value() >= aOffset, 1145 "The offset should be less than or equal to the first keyframe's " 1146 "offset if there are exisiting keyframes"); 1147 1148 return GetOrCreateKeyframe(aKeyframes, aOffset, aTimingFunction, aComposition, 1149 KeyframeSearchDirection::Forwards, 1150 KeyframeInsertPosition::Prepend); 1151 } 1152 1153 Keyframe* Gecko_GetOrCreateInitialKeyframe( 1154 nsTArray<Keyframe>* aKeyframes, 1155 const StyleComputedTimingFunction* aTimingFunction, 1156 const CompositeOperationOrAuto aComposition) { 1157 return GetOrCreateKeyframe(aKeyframes, 0., aTimingFunction, aComposition, 1158 KeyframeSearchDirection::Forwards, 1159 KeyframeInsertPosition::LastForOffset); 1160 } 1161 1162 Keyframe* Gecko_GetOrCreateFinalKeyframe( 1163 nsTArray<Keyframe>* aKeyframes, 1164 const StyleComputedTimingFunction* aTimingFunction, 1165 const CompositeOperationOrAuto aComposition) { 1166 return GetOrCreateKeyframe(aKeyframes, 1., aTimingFunction, aComposition, 1167 KeyframeSearchDirection::Backwards, 1168 KeyframeInsertPosition::LastForOffset); 1169 } 1170 1171 void Gecko_GetComputedURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) { 1172 MOZ_ASSERT(aURL); 1173 MOZ_ASSERT(aOut); 1174 if (aURL->IsLocalRef()) { 1175 aOut->Assign(aURL->SpecifiedSerialization()); 1176 return; 1177 } 1178 1179 if (nsIURI* uri = aURL->GetURI()) { 1180 nsresult rv = uri->GetSpec(*aOut); 1181 if (NS_SUCCEEDED(rv)) { 1182 return; 1183 } 1184 } 1185 1186 // Empty URL computes to empty, per spec: 1187 if (aURL->SpecifiedSerialization().IsEmpty()) { 1188 aOut->Truncate(); 1189 } else { 1190 aOut->AssignLiteral("about:invalid"); 1191 } 1192 } 1193 1194 bool Gecko_IsSupportedImageMimeType(const uint8_t* aMimeType, 1195 const uint32_t aLen) { 1196 nsDependentCSubstring mime(reinterpret_cast<const char*>(aMimeType), aLen); 1197 return imgLoader::SupportImageWithMimeType( 1198 mime, AcceptedMimeTypes::IMAGES_AND_DOCUMENTS); 1199 } 1200 1201 void Gecko_nsIURI_Debug(nsIURI* aURI, nsCString* aOut) { 1202 // TODO(emilio): Do we have more useful stuff to put here, maybe? 1203 if (aURI) { 1204 *aOut = aURI->GetSpecOrDefault(); 1205 } 1206 } 1207 1208 // XXX Implemented by hand because even though it's thread-safe, only the 1209 // subclasses have the HasThreadSafeRefCnt bits. 1210 void Gecko_AddRefnsIURIArbitraryThread(nsIURI* aPtr) { NS_ADDREF(aPtr); } 1211 void Gecko_ReleasensIURIArbitraryThread(nsIURI* aPtr) { NS_RELEASE(aPtr); } 1212 1213 void Gecko_nsIReferrerInfo_Debug(nsIReferrerInfo* aReferrerInfo, 1214 nsCString* aOut) { 1215 if (aReferrerInfo) { 1216 if (nsCOMPtr<nsIURI> referrer = aReferrerInfo->GetComputedReferrer()) { 1217 *aOut = referrer->GetSpecOrDefault(); 1218 } 1219 } 1220 } 1221 1222 template <typename ElementLike> 1223 void DebugListAttributes(const ElementLike& aElement, nsCString& aOut) { 1224 const uint32_t kMaxAttributeLength = 40; 1225 1226 uint32_t i = 0; 1227 while (BorrowedAttrInfo info = aElement.GetAttrInfoAt(i++)) { 1228 aOut.AppendLiteral(" "); 1229 if (nsAtom* prefix = info.mName->GetPrefix()) { 1230 aOut.Append(NS_ConvertUTF16toUTF8(nsDependentAtomString(prefix))); 1231 aOut.AppendLiteral(":"); 1232 } 1233 aOut.Append( 1234 NS_ConvertUTF16toUTF8(nsDependentAtomString(info.mName->LocalName()))); 1235 if (!info.mValue) { 1236 continue; 1237 } 1238 aOut.AppendLiteral("=\""); 1239 nsAutoString value; 1240 info.mValue->ToString(value); 1241 if (value.Length() > kMaxAttributeLength) { 1242 value.Truncate(kMaxAttributeLength - 3); 1243 value.AppendLiteral("..."); 1244 } 1245 aOut.Append(NS_ConvertUTF16toUTF8(value)); 1246 aOut.AppendLiteral("\""); 1247 } 1248 } 1249 1250 void Gecko_Element_DebugListAttributes(const Element* aElement, 1251 nsCString* aOut) { 1252 DebugListAttributes(*aElement, *aOut); 1253 } 1254 1255 void Gecko_Snapshot_DebugListAttributes(const ServoElementSnapshot* aSnapshot, 1256 nsCString* aOut) { 1257 DebugListAttributes(*aSnapshot, *aOut); 1258 } 1259 1260 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData); 1261 1262 void Gecko_nsStyleFont_SetLang(nsStyleFont* aFont, nsAtom* aAtom) { 1263 aFont->mLanguage = dont_AddRef(aAtom); 1264 aFont->mExplicitLanguage = true; 1265 } 1266 1267 void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, 1268 const nsStyleFont* aSource) { 1269 aFont->mLanguage = aSource->mLanguage; 1270 } 1271 1272 Length Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont* aFont, 1273 const Document* aDocument) { 1274 // Don't change font-size:0, since that would un-hide hidden text. 1275 if (aFont->mSize.IsZero()) { 1276 return {0}; 1277 } 1278 // Don't change it for docs where we don't enable the min-font-size. 1279 if (!aFont->MinFontSizeEnabled()) { 1280 return {0}; 1281 } 1282 Length minFontSize; 1283 bool needsCache = false; 1284 1285 auto MinFontSize = [&](bool* aNeedsToCache) { 1286 const auto* prefs = 1287 aDocument->GetFontPrefsForLang(aFont->mLanguage, aNeedsToCache); 1288 return prefs ? prefs->mMinimumFontSize : Length{0}; 1289 }; 1290 1291 { 1292 AutoReadLock guard(*sServoFFILock); 1293 minFontSize = MinFontSize(&needsCache); 1294 } 1295 1296 if (needsCache) { 1297 AutoWriteLock guard(*sServoFFILock); 1298 minFontSize = MinFontSize(nullptr); 1299 } 1300 1301 if (minFontSize.ToCSSPixels() <= 0.0f) { 1302 return {0}; 1303 } 1304 1305 minFontSize.ScaleBy(aFont->mMinFontSizeRatio._0); 1306 return minFontSize; 1307 } 1308 1309 static StaticRefPtr<UACacheReporter> gUACacheReporter; 1310 1311 namespace mozilla { 1312 1313 void InitializeServo() { 1314 URLExtraData::Init(); 1315 Servo_Initialize(URLExtraData::Dummy(), URLExtraData::DummyChrome()); 1316 1317 gUACacheReporter = new UACacheReporter(); 1318 RegisterWeakMemoryReporter(gUACacheReporter); 1319 1320 sServoFFILock = new RWLock("Servo::FFILock"); 1321 } 1322 1323 void ShutdownServo() { 1324 MOZ_ASSERT(sServoFFILock); 1325 1326 UnregisterWeakMemoryReporter(gUACacheReporter); 1327 gUACacheReporter = nullptr; 1328 1329 sServoFFILock = nullptr; 1330 Servo_Shutdown(); 1331 1332 URLExtraData::Shutdown(); 1333 } 1334 1335 void AssertIsMainThreadOrServoFontMetricsLocked() { 1336 if (!NS_IsMainThread()) { 1337 MOZ_ASSERT(sServoFFILock && 1338 sServoFFILock->LockedForWritingByCurrentThread()); 1339 } 1340 } 1341 1342 } // namespace mozilla 1343 1344 GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext, 1345 bool aIsVertical, 1346 const nsStyleFont* aFont, 1347 Length aFontSize, 1348 StyleQueryFontMetricsFlags flags) { 1349 AutoWriteLock guard(*sServoFFILock); 1350 1351 // Getting font metrics can require some main thread only work to be 1352 // done, such as work that needs to touch non-threadsafe refcounted 1353 // objects (like the DOM FontFace/FontFaceSet objects), network loads, etc. 1354 // 1355 // To handle this work, font code checks whether we are in a Servo traversal 1356 // and if so, appends PostTraversalTasks to the current ServoStyleSet 1357 // to be performed immediately after the traversal is finished. This 1358 // works well for starting downloadable font loads, since we don't have 1359 // those fonts available to get metrics for anyway. Platform fonts and 1360 // ArrayBuffer-backed FontFace objects are handled synchronously. 1361 1362 nsPresContext* presContext = const_cast<nsPresContext*>(aPresContext); 1363 RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetMetricsFor( 1364 presContext, aIsVertical, aFont, aFontSize, 1365 bool(flags & StyleQueryFontMetricsFlags::USE_USER_FONT_SET)); 1366 auto* fontGroup = fm->GetThebesFontGroup(); 1367 auto metrics = fontGroup->GetMetricsForCSSUnits(fm->Orientation(), flags); 1368 1369 float scriptPercentScaleDown = 0; 1370 float scriptScriptPercentScaleDown = 0; 1371 if (flags & StyleQueryFontMetricsFlags::NEEDS_MATH_SCALES) { 1372 RefPtr<gfxFont> font = fontGroup->GetFirstValidFont(); 1373 if (font->TryGetMathTable()) { 1374 scriptPercentScaleDown = static_cast<float>( 1375 font->MathTable()->Constant(gfxMathTable::ScriptPercentScaleDown)); 1376 scriptScriptPercentScaleDown = 1377 static_cast<float>(font->MathTable()->Constant( 1378 gfxMathTable::ScriptScriptPercentScaleDown)); 1379 } 1380 } 1381 1382 int32_t d2a = aPresContext->AppUnitsPerDevPixel(); 1383 auto ToLength = [](nscoord aLen) { 1384 return Length::FromPixels(CSSPixel::FromAppUnits(aLen)); 1385 }; 1386 return {ToLength(NS_round(metrics.xHeight * d2a)), 1387 ToLength(NS_round(metrics.zeroWidth * d2a)), 1388 ToLength(NS_round(metrics.capHeight * d2a)), 1389 ToLength(NS_round(metrics.ideographicWidth * d2a)), 1390 ToLength(NS_round(metrics.maxAscent * d2a)), 1391 ToLength(NS_round(fontGroup->GetStyle()->size * d2a)), 1392 scriptPercentScaleDown, 1393 scriptScriptPercentScaleDown}; 1394 } 1395 1396 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder); 1397 1398 void Gecko_StyleSheet_FinishAsyncParse( 1399 SheetLoadDataHolder* aData, 1400 StyleStrong<StyleStylesheetContents> aSheetContents) { 1401 RefPtr<SheetLoadDataHolder> loadData = aData; 1402 RefPtr<StyleStylesheetContents> sheetContents = aSheetContents.Consume(); 1403 NS_DispatchToMainThreadQueue( 1404 NS_NewRunnableFunction(__func__, 1405 [d = std::move(loadData), 1406 contents = std::move(sheetContents)]() mutable { 1407 MOZ_ASSERT(NS_IsMainThread()); 1408 SheetLoadData* data = d->get(); 1409 data->mSheet->FinishAsyncParse( 1410 contents.forget()); 1411 }), 1412 EventQueuePriority::RenderBlocking); 1413 } 1414 1415 static already_AddRefed<StyleSheet> LoadImportSheet( 1416 Loader* aLoader, StyleSheet* aParent, SheetLoadData* aParentLoadData, 1417 LoaderReusableStyleSheets* aReusableSheets, const StyleCssUrl& aURL, 1418 already_AddRefed<StyleLockedMediaList> aMediaList) { 1419 MOZ_ASSERT(NS_IsMainThread()); 1420 MOZ_ASSERT(aLoader, "Should've catched this before"); 1421 MOZ_ASSERT(aParent, "Only used for @import, so parent should exist!"); 1422 1423 auto media = MakeRefPtr<MediaList>(std::move(aMediaList)); 1424 nsCOMPtr<nsIURI> uri = aURL.GetURI(); 1425 nsresult rv = uri ? NS_OK : NS_ERROR_FAILURE; 1426 1427 size_t previousSheetCount = aParent->ChildSheets().Length(); 1428 if (NS_SUCCEEDED(rv)) { 1429 // TODO(emilio): We should probably make LoadChildSheet return the 1430 // stylesheet rather than the return code. 1431 rv = aLoader->LoadChildSheet(*aParent, aParentLoadData, uri, media, 1432 aReusableSheets); 1433 } 1434 1435 if (NS_FAILED(rv) || previousSheetCount == aParent->ChildSheets().Length()) { 1436 // Servo and Gecko have different ideas of what a valid URL is, so we might 1437 // get in here with a URL string that NS_NewURI can't handle. We may also 1438 // reach here via an import cycle. For the import cycle case, we need some 1439 // sheet object per spec, even if its empty. DevTools uses the URI to 1440 // realize it has hit an import cycle, so we mark it complete to make the 1441 // sheet readable from JS. 1442 RefPtr<StyleSheet> emptySheet = 1443 aParent->CreateEmptyChildSheet(media.forget()); 1444 // Make a dummy URI if we don't have one because some methods assume 1445 // non-null URIs. 1446 if (!uri) { 1447 NS_NewURI(getter_AddRefs(uri), "about:invalid"_ns); 1448 } 1449 nsCOMPtr<nsIReferrerInfo> referrerInfo = 1450 ReferrerInfo::CreateForExternalCSSResources(emptySheet, uri); 1451 emptySheet->SetURIs(uri, uri, referrerInfo, aURL.ExtraData().Principal()); 1452 emptySheet->SetComplete(); 1453 aParent->AppendStyleSheet(*emptySheet); 1454 return emptySheet.forget(); 1455 } 1456 1457 RefPtr<StyleSheet> sheet = aParent->ChildSheets().LastElement(); 1458 return sheet.forget(); 1459 } 1460 1461 StyleSheet* Gecko_LoadStyleSheet(Loader* aLoader, StyleSheet* aParent, 1462 SheetLoadData* aParentLoadData, 1463 LoaderReusableStyleSheets* aReusableSheets, 1464 const StyleCssUrl* aUrl, 1465 StyleStrong<StyleLockedMediaList> aMediaList) { 1466 MOZ_ASSERT(NS_IsMainThread()); 1467 MOZ_ASSERT(aUrl); 1468 1469 return LoadImportSheet(aLoader, aParent, aParentLoadData, aReusableSheets, 1470 *aUrl, aMediaList.Consume()) 1471 .take(); 1472 } 1473 1474 void Gecko_LoadStyleSheetAsync(SheetLoadDataHolder* aParentData, 1475 const StyleCssUrl* aUrl, 1476 StyleStrong<StyleLockedMediaList> aMediaList, 1477 StyleStrong<StyleLockedImportRule> aImportRule) { 1478 MOZ_ASSERT(aUrl); 1479 RefPtr<SheetLoadDataHolder> loadData = aParentData; 1480 RefPtr<StyleLockedMediaList> mediaList = aMediaList.Consume(); 1481 RefPtr<StyleLockedImportRule> importRule = aImportRule.Consume(); 1482 NS_DispatchToMainThreadQueue( 1483 NS_NewRunnableFunction( 1484 __func__, 1485 [data = std::move(loadData), url = StyleCssUrl(*aUrl), 1486 media = std::move(mediaList), 1487 import = std::move(importRule)]() mutable { 1488 MOZ_ASSERT(NS_IsMainThread()); 1489 SheetLoadData* d = data->get(); 1490 RefPtr<StyleSheet> sheet = LoadImportSheet( 1491 d->mLoader, d->mSheet, d, nullptr, url, media.forget()); 1492 Servo_ImportRule_SetSheet(import, sheet); 1493 }), 1494 EventQueuePriority::RenderBlocking); 1495 } 1496 1497 void Gecko_AddPropertyToSet(nsCSSPropertyIDSet* aPropertySet, 1498 NonCustomCSSPropertyId aProperty) { 1499 aPropertySet->AddProperty(aProperty); 1500 } 1501 1502 bool Gecko_DocumentRule_UseForPresentation( 1503 const Document* aDocument, const nsACString* aPattern, 1504 DocumentMatchingFunction aMatchingFunction) { 1505 MOZ_ASSERT(NS_IsMainThread()); 1506 1507 nsIURI* docURI = aDocument->GetDocumentURI(); 1508 nsAutoCString docURISpec; 1509 if (docURI) { 1510 // If GetSpec fails (due to OOM) just skip these URI-specific CSS rules. 1511 nsresult rv = docURI->GetSpec(docURISpec); 1512 NS_ENSURE_SUCCESS(rv, false); 1513 } 1514 1515 return CSSMozDocumentRule::Match(aDocument, docURI, docURISpec, *aPattern, 1516 aMatchingFunction); 1517 } 1518 1519 void Gecko_SetJemallocThreadLocalArena(bool enabled) { 1520 #if defined(MOZ_MEMORY) 1521 jemalloc_thread_local_arena(enabled); 1522 #endif 1523 } 1524 1525 template <typename T> 1526 void Construct(T* aPtr, const Document* aDoc) { 1527 if constexpr (std::is_constructible_v<T, const Document&>) { 1528 MOZ_ASSERT(aDoc); 1529 new (KnownNotNull, aPtr) T(*aDoc); 1530 } else { 1531 MOZ_ASSERT(!aDoc); 1532 new (KnownNotNull, aPtr) T(); 1533 // These instance are intentionally global, and we don't want leakcheckers 1534 // to report them. 1535 aPtr->MarkLeaked(); 1536 } 1537 } 1538 1539 #define GENERATE_GECKO_FUNCTIONS(name) \ 1540 void Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr, \ 1541 const Document* doc) { \ 1542 Construct(ptr, doc); \ 1543 } \ 1544 void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \ 1545 const nsStyle##name* other) { \ 1546 new (ptr) nsStyle##name(*other); \ 1547 } \ 1548 void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr) { \ 1549 ptr->~nsStyle##name(); \ 1550 } 1551 1552 FOR_EACH_STYLE_STRUCT(GENERATE_GECKO_FUNCTIONS, GENERATE_GECKO_FUNCTIONS) 1553 1554 #undef GENERATE_GECKO_FUNCTIONS 1555 1556 bool Gecko_ErrorReportingEnabled(const StyleSheet* aSheet, 1557 const Loader* aLoader, 1558 uint64_t* aOutWindowId) { 1559 if (!ErrorReporter::ShouldReportErrors(aSheet, aLoader)) { 1560 return false; 1561 } 1562 *aOutWindowId = ErrorReporter::FindInnerWindowId(aSheet, aLoader); 1563 return true; 1564 } 1565 1566 void Gecko_ReportUnexpectedCSSError(const uint64_t aWindowId, nsIURI* aURI, 1567 const char* message, const char* param, 1568 uint32_t paramLen, const char* prefix, 1569 const char* prefixParam, 1570 uint32_t prefixParamLen, const char* suffix, 1571 const char* selectors, 1572 uint32_t selectorsLen, uint32_t lineNumber, 1573 uint32_t colNumber) { 1574 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 1575 1576 ErrorReporter reporter(aWindowId); 1577 1578 if (prefix) { 1579 if (prefixParam) { 1580 nsDependentCSubstring paramValue(prefixParam, prefixParamLen); 1581 AutoTArray<nsString, 1> wideParam; 1582 CopyUTF8toUTF16(paramValue, *wideParam.AppendElement()); 1583 reporter.ReportUnexpectedUnescaped(prefix, wideParam); 1584 } else { 1585 reporter.ReportUnexpected(prefix); 1586 } 1587 } 1588 1589 if (param) { 1590 nsDependentCSubstring paramValue(param, paramLen); 1591 AutoTArray<nsString, 1> wideParam; 1592 CopyUTF8toUTF16(paramValue, *wideParam.AppendElement()); 1593 reporter.ReportUnexpectedUnescaped(message, wideParam); 1594 } else { 1595 reporter.ReportUnexpected(message); 1596 } 1597 1598 if (suffix) { 1599 reporter.ReportUnexpected(suffix); 1600 } 1601 nsDependentCSubstring selectorsValue(selectors, selectorsLen); 1602 reporter.OutputError(selectorsValue, lineNumber + 1, colNumber, aURI); 1603 } 1604 1605 void Gecko_ContentList_AppendAll(nsSimpleContentList* aList, 1606 const Element** aElements, size_t aLength) { 1607 MOZ_ASSERT(NS_IsMainThread()); 1608 MOZ_ASSERT(aElements); 1609 MOZ_ASSERT(aLength); 1610 MOZ_ASSERT(aList); 1611 1612 aList->SetCapacity(aLength); 1613 1614 for (size_t i = 0; i < aLength; ++i) { 1615 aList->AppendElement(const_cast<Element*>(aElements[i])); 1616 } 1617 } 1618 1619 RustSpan<const Element* const> Gecko_Document_GetElementsWithId( 1620 const Document* aDoc, nsAtom* aId) { 1621 MOZ_ASSERT(aDoc); 1622 MOZ_ASSERT(aId); 1623 auto span = aDoc->GetAllElementsForId(aId); 1624 return {span.Elements(), span.Length()}; 1625 } 1626 1627 RustSpan<const Element* const> Gecko_ShadowRoot_GetElementsWithId( 1628 const ShadowRoot* aShadowRoot, nsAtom* aId) { 1629 MOZ_ASSERT(aShadowRoot); 1630 MOZ_ASSERT(aId); 1631 auto span = aShadowRoot->GetAllElementsForId(aId); 1632 return {span.Elements(), span.Length()}; 1633 } 1634 1635 static StyleComputedMozPrefFeatureValue GetPrefValue(const nsCString& aPref) { 1636 using Value = StyleComputedMozPrefFeatureValue; 1637 switch (Preferences::GetType(aPref.get())) { 1638 case nsIPrefBranch::PREF_STRING: { 1639 nsAutoString value; 1640 Preferences::GetString(aPref.get(), value); 1641 return Value::String(StyleAtomString{NS_Atomize(value)}); 1642 } 1643 case nsIPrefBranch::PREF_INT: 1644 return Value::Integer(Preferences::GetInt(aPref.get(), 0)); 1645 case nsIPrefBranch::PREF_BOOL: { 1646 auto value = Preferences::GetBool(aPref.get(), false) 1647 ? StyleBoolValue::True 1648 : StyleBoolValue::False; 1649 return Value::Boolean(value); 1650 } 1651 case nsIPrefBranch::PREF_INVALID: 1652 default: 1653 break; 1654 } 1655 1656 return StyleComputedMozPrefFeatureValue::None(); 1657 } 1658 1659 bool Gecko_EvalMozPrefFeature(nsAtom* aPref, 1660 const StyleComputedMozPrefFeatureValue* aValue) { 1661 MOZ_ASSERT(NS_IsMainThread()); 1662 MOZ_ASSERT(aValue); 1663 using Value = StyleComputedMozPrefFeatureValue; 1664 using PrefMap = nsTHashMap<RefPtr<nsAtom>, Value>; 1665 // This map leaks until shutdown, but that's fine, all the values are 1666 // controlled by us so it's not expected to be big. 1667 static StaticAutoPtr<PrefMap> sRegisteredPrefs; 1668 if (!sRegisteredPrefs) { 1669 if (PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) { 1670 // Styling doesn't really matter much at this point, don't bother. 1671 return false; 1672 } 1673 sRegisteredPrefs = new PrefMap(); 1674 ClearOnShutdown(&sRegisteredPrefs); 1675 } 1676 1677 const auto& value = sRegisteredPrefs->LookupOrInsertWith(aPref, [&] { 1678 nsAutoAtomCString prefName(aPref); 1679 Preferences::RegisterCallback( 1680 [](const char* aPrefName, void*) { 1681 nsDependentCString name(aPrefName); 1682 if (sRegisteredPrefs) { 1683 RefPtr<nsAtom> nameAtom = NS_Atomize(name); 1684 sRegisteredPrefs->InsertOrUpdate(nameAtom, GetPrefValue(name)); 1685 } 1686 LookAndFeel::NotifyChangedAllWindows( 1687 widget::ThemeChangeKind::MediaQueriesOnly); 1688 }, 1689 prefName); 1690 return GetPrefValue(prefName); 1691 }); 1692 if (aValue->IsNone()) { 1693 // For a non-specified query, we return true if the pref is not false, zero, 1694 // empty or invalid 1695 switch (value.tag) { 1696 case Value::Tag::None: 1697 return false; 1698 case Value::Tag::Boolean: 1699 return value.AsBoolean() == StyleBoolValue::True; 1700 case Value::Tag::Integer: 1701 return value.AsInteger() != 0; 1702 case Value::Tag::String: 1703 return !value.AsString().AsAtom()->IsEmpty(); 1704 } 1705 return false; 1706 } 1707 return value == *aValue; 1708 } 1709 1710 bool Gecko_IsFontFormatSupported(StyleFontFaceSourceFormatKeyword aFormat) { 1711 return gfxPlatform::GetPlatform()->IsFontFormatSupported( 1712 aFormat, StyleFontFaceSourceTechFlags::Empty()); 1713 } 1714 1715 bool Gecko_IsFontTechSupported(StyleFontFaceSourceTechFlags aFlag) { 1716 return gfxPlatform::GetPlatform()->IsFontFormatSupported( 1717 StyleFontFaceSourceFormatKeyword::None, aFlag); 1718 } 1719 1720 bool Gecko_IsKnownIconFontFamily(const nsAtom* aFamilyName) { 1721 return gfxPlatform::GetPlatform()->IsKnownIconFontFamily(aFamilyName); 1722 } 1723 1724 bool Gecko_IsInServoTraversal() { return ServoStyleSet::IsInServoTraversal(); } 1725 1726 bool Gecko_IsMainThread() { return NS_IsMainThread(); } 1727 1728 bool Gecko_IsDOMWorkerThread() { return !!GetCurrentThreadWorkerPrivate(); } 1729 1730 int32_t Gecko_GetNumStyleThreads() { 1731 if (const auto& cpuInfo = hal::GetHeterogeneousCpuInfo()) { 1732 size_t numBigCpus = cpuInfo->mBigCpus.Count(); 1733 // If CPUs are homogeneous we do not need to override stylo's 1734 // default number of threads. 1735 if (numBigCpus != cpuInfo->mTotalNumCpus) { 1736 // From testing on a variety of devices it appears using only 1737 // the number of big cores gives best performance when there are 1738 // 2 or more big cores. If there are fewer than 2 big cores then 1739 // additionally using the medium cores performs better. 1740 if (numBigCpus >= 2) { 1741 return static_cast<int32_t>(numBigCpus); 1742 } 1743 return static_cast<int32_t>(numBigCpus + cpuInfo->mMediumCpus.Count()); 1744 } 1745 } 1746 1747 return -1; 1748 } 1749 1750 const nsAttrValue* Gecko_GetSVGAnimatedClass(const Element* aElement) { 1751 MOZ_ASSERT(aElement->IsSVGElement()); 1752 return static_cast<const SVGElement*>(aElement)->GetAnimatedClassName(); 1753 } 1754 1755 bool Gecko_AssertClassAttrValueIsSane(const nsAttrValue* aValue) { 1756 MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom || 1757 aValue->Type() == nsAttrValue::eString || 1758 aValue->Type() == nsAttrValue::eAtomArray); 1759 MOZ_ASSERT_IF( 1760 aValue->Type() == nsAttrValue::eString, 1761 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 1762 aValue->GetStringValue()) 1763 .IsEmpty()); 1764 return true; 1765 } 1766 1767 void Gecko_GetSafeAreaInsets(const nsPresContext* aPresContext, float* aTop, 1768 float* aRight, float* aBottom, float* aLeft) { 1769 MOZ_ASSERT(aPresContext); 1770 const CSSMargin insets = 1771 LayoutDeviceMargin(aPresContext->GetSafeAreaInsets()) / 1772 aPresContext->CSSToDevPixelScale(); 1773 *aTop = insets.top; 1774 *aRight = insets.right; 1775 *aBottom = insets.bottom; 1776 *aLeft = insets.left; 1777 } 1778 1779 void Gecko_PrintfStderr(const nsCString* aStr) { 1780 printf_stderr("%s", aStr->get()); 1781 } 1782 1783 nsAtom* Gecko_Element_ImportedPart(const nsAttrValue* aValue, 1784 nsAtom* aPartName) { 1785 if (aValue->Type() != nsAttrValue::eShadowParts) { 1786 return nullptr; 1787 } 1788 return aValue->GetShadowPartsValue().GetReverse(aPartName); 1789 } 1790 1791 nsAtom** Gecko_Element_ExportedParts(const nsAttrValue* aValue, 1792 nsAtom* aPartName, size_t* aOutLength) { 1793 if (aValue->Type() != nsAttrValue::eShadowParts) { 1794 return nullptr; 1795 } 1796 auto* parts = aValue->GetShadowPartsValue().Get(aPartName); 1797 if (!parts) { 1798 return nullptr; 1799 } 1800 *aOutLength = parts->Length(); 1801 static_assert(sizeof(RefPtr<nsAtom>) == sizeof(nsAtom*)); 1802 static_assert(alignof(RefPtr<nsAtom>) == alignof(nsAtom*)); 1803 return reinterpret_cast<nsAtom**>(parts->Elements()); 1804 } 1805 1806 uint64_t Gecko_Element_GetSubtreeBloomFilter(const Element* aElement) { 1807 return aElement->GetSubtreeBloomFilter(); 1808 } 1809 1810 bool StyleSingleFontFamily::IsNamedFamily(const nsAString& aFamilyName) const { 1811 if (!IsFamilyName()) { 1812 return false; 1813 } 1814 nsDependentAtomString name(AsFamilyName().name.AsAtom()); 1815 return name.Equals(aFamilyName, nsCaseInsensitiveStringComparator); 1816 } 1817 1818 StyleSingleFontFamily StyleSingleFontFamily::Parse( 1819 const nsACString& aFamilyOrGenericName) { 1820 // should only be passed a single font - not entirely correct, a family 1821 // *could* have a comma in it but in practice never does so 1822 // for debug purposes this is fine 1823 NS_ASSERTION(aFamilyOrGenericName.FindChar(',') == -1, 1824 "Convert method should only be passed a single family name"); 1825 1826 auto genericType = Servo_GenericFontFamily_Parse(&aFamilyOrGenericName); 1827 if (genericType != StyleGenericFontFamily::None) { 1828 return Generic(genericType); 1829 } 1830 return FamilyName({StyleAtom(NS_Atomize(aFamilyOrGenericName)), 1831 StyleFontFamilyNameSyntax::Identifiers}); 1832 } 1833 1834 void StyleSingleFontFamily::AppendToString(nsACString& aName, 1835 bool aQuote) const { 1836 if (IsFamilyName()) { 1837 const auto& name = AsFamilyName(); 1838 if (!aQuote) { 1839 aName.Append(nsAutoAtomCString(name.name.AsAtom())); 1840 return; 1841 } 1842 Servo_FamilyName_Serialize(&name, &aName); 1843 return; 1844 } 1845 1846 switch (AsGeneric()) { 1847 case StyleGenericFontFamily::None: 1848 case StyleGenericFontFamily::MozEmoji: 1849 MOZ_FALLTHROUGH_ASSERT("Should never appear in a font-family name!"); 1850 case StyleGenericFontFamily::Serif: 1851 return aName.AppendLiteral("serif"); 1852 case StyleGenericFontFamily::SansSerif: 1853 return aName.AppendLiteral("sans-serif"); 1854 case StyleGenericFontFamily::Monospace: 1855 return aName.AppendLiteral("monospace"); 1856 case StyleGenericFontFamily::Cursive: 1857 return aName.AppendLiteral("cursive"); 1858 case StyleGenericFontFamily::Fantasy: 1859 return aName.AppendLiteral("fantasy"); 1860 case StyleGenericFontFamily::Math: 1861 return aName.AppendLiteral("math"); 1862 case StyleGenericFontFamily::SystemUi: 1863 return aName.AppendLiteral("system-ui"); 1864 } 1865 MOZ_ASSERT_UNREACHABLE("Unknown generic font-family!"); 1866 return aName.AppendLiteral("serif"); 1867 } 1868 1869 StyleFontFamilyList StyleFontFamilyList::WithNames( 1870 nsTArray<StyleSingleFontFamily>&& aNames) { 1871 StyleFontFamilyList list; 1872 Servo_FontFamilyList_WithNames(&aNames, &list); 1873 return list; 1874 } 1875 1876 StyleFontFamilyList StyleFontFamilyList::WithOneUnquotedFamily( 1877 const nsACString& aName) { 1878 AutoTArray<StyleSingleFontFamily, 1> names; 1879 names.AppendElement(StyleSingleFontFamily::FamilyName( 1880 {StyleAtom(NS_Atomize(aName)), StyleFontFamilyNameSyntax::Identifiers})); 1881 return WithNames(std::move(names)); 1882 } 1883 1884 static bool AnchorSideUsesCBWM( 1885 const StyleAnchorSideKeyword& aAnchorSideKeyword) { 1886 switch (aAnchorSideKeyword) { 1887 case StyleAnchorSideKeyword::SelfStart: 1888 case StyleAnchorSideKeyword::SelfEnd: 1889 return false; 1890 case StyleAnchorSideKeyword::Inside: 1891 case StyleAnchorSideKeyword::Outside: 1892 case StyleAnchorSideKeyword::Start: 1893 case StyleAnchorSideKeyword::End: 1894 case StyleAnchorSideKeyword::Center: 1895 return true; 1896 // Return value shouldn't matter for these physical keywords. 1897 case StyleAnchorSideKeyword::Left: 1898 case StyleAnchorSideKeyword::Right: 1899 case StyleAnchorSideKeyword::Top: 1900 case StyleAnchorSideKeyword::Bottom: 1901 return true; 1902 } 1903 return false; 1904 } 1905 1906 bool Gecko_GetAnchorPosOffset(const AnchorPosOffsetResolutionParams* aParams, 1907 const nsAtom* aAnchorName, 1908 StylePhysicalSide aPropSide, 1909 StyleAnchorSideKeyword aAnchorSideKeyword, 1910 float aPercentage, Length* aOut) { 1911 if (!aParams || !aParams->mBaseParams.mFrame) { 1912 return false; 1913 } 1914 const auto* positioned = aParams->mBaseParams.mFrame; 1915 const auto* containingBlock = positioned->GetParent(); 1916 auto* cache = aParams->mBaseParams.mCache; 1917 const auto info = AnchorPositioningUtils::ResolveAnchorPosRect( 1918 positioned, containingBlock, aAnchorName, !aParams->mCBSize, cache); 1919 if (!info) { 1920 return false; 1921 } 1922 if (cache) { 1923 // Cache is set during reflow, which is really the only time we want to 1924 // actively modify scroll compensation state & side. 1925 if (info->mCompensatesForScroll) { 1926 const auto axis = [aPropSide]() { 1927 switch (aPropSide) { 1928 case StylePhysicalSide::Left: 1929 case StylePhysicalSide::Right: 1930 return PhysicalAxis::Horizontal; 1931 case StylePhysicalSide::Top: 1932 case StylePhysicalSide::Bottom: 1933 break; 1934 default: 1935 MOZ_ASSERT_UNREACHABLE("Unhandled side?"); 1936 } 1937 return PhysicalAxis::Vertical; 1938 }(); 1939 cache->mReferenceData->AdjustCompensatingForScroll(axis); 1940 // Non scroll-compensated anchor will not have any impact on the 1941 // containing block due to scrolling. See documentation for 1942 // `mScrollCompensatedSides`. 1943 cache->mReferenceData->mScrollCompensatedSides |= 1944 SideToSideBit(ToSide(aPropSide)); 1945 } 1946 } 1947 // Compute the offset here in C++, where translating between physical/logical 1948 // coordinates is easier. 1949 1950 const auto usesCBWM = AnchorSideUsesCBWM(aAnchorSideKeyword); 1951 const auto cbwm = containingBlock->GetWritingMode(); 1952 const auto wm = 1953 usesCBWM ? aParams->mBaseParams.mFrame->GetWritingMode() : cbwm; 1954 const auto [rect, logicalCBSize] = [&] { 1955 // We need `AnchorPosReferenceData` to compute the anchor offset against 1956 // the adjusted CB, so make the best attempt to retrieve it. 1957 // TODO(dshin, bug 2005207): We really need to unify containing block 1958 // lookups and clean up cache lookups here. 1959 const auto* referenceData = 1960 cache ? cache->mReferenceData 1961 : positioned->GetProperty(nsIFrame::AnchorPosReferences()); 1962 if (!referenceData) { 1963 return std::make_pair( 1964 info->mRect, aParams->mCBSize ? aParams->mCBSize->ConvertTo(wm, cbwm) 1965 : containingBlock->PaddingSize(wm)); 1966 } 1967 // Offset happens from padding rect. 1968 const auto offset = referenceData->mAdjustedContainingBlock.TopLeft() - 1969 referenceData->mOriginalContainingBlockRect.TopLeft(); 1970 return std::make_pair( 1971 info->mRect - offset, 1972 aParams->mCBSize 1973 ? aParams->mCBSize->ConvertTo(wm, cbwm) 1974 : LogicalSize{cbwm, referenceData->mAdjustedContainingBlock.Size()} 1975 .ConvertTo(wm, cbwm)); 1976 }(); 1977 const LogicalRect logicalAnchorRect{wm, rect, 1978 logicalCBSize.GetPhysicalSize(wm)}; 1979 const auto logicalPropSide = wm.LogicalSideForPhysicalSide(ToSide(aPropSide)); 1980 const auto propAxis = GetAxis(logicalPropSide); 1981 const auto propEdge = GetEdge(logicalPropSide); 1982 1983 const auto anchorEdge = [&]() { 1984 switch (aAnchorSideKeyword) { 1985 case StyleAnchorSideKeyword::Left: 1986 return GetEdge(wm.LogicalSideForPhysicalSide(eSideLeft)); 1987 case StyleAnchorSideKeyword::Right: 1988 return GetEdge(wm.LogicalSideForPhysicalSide(eSideRight)); 1989 case StyleAnchorSideKeyword::Top: 1990 return GetEdge(wm.LogicalSideForPhysicalSide(eSideTop)); 1991 case StyleAnchorSideKeyword::Bottom: 1992 return GetEdge(wm.LogicalSideForPhysicalSide(eSideBottom)); 1993 case StyleAnchorSideKeyword::Inside: 1994 return propEdge; 1995 case StyleAnchorSideKeyword::Outside: 1996 return GetOppositeEdge(propEdge); 1997 case StyleAnchorSideKeyword::Start: 1998 case StyleAnchorSideKeyword::SelfStart: 1999 case StyleAnchorSideKeyword::Center: 2000 return LogicalEdge::Start; 2001 case StyleAnchorSideKeyword::End: 2002 case StyleAnchorSideKeyword::SelfEnd: 2003 return LogicalEdge::End; 2004 } 2005 return LogicalEdge::Start; 2006 }(); 2007 2008 nscoord result = [&]() { 2009 // Offset to the desired anchor edge, from the containing block's start 2010 // edge. 2011 const auto anchorOffsetFromStartEdge = 2012 anchorEdge == LogicalEdge::Start ? logicalAnchorRect.Start(propAxis, wm) 2013 : logicalAnchorRect.End(propAxis, wm); 2014 if (propEdge == LogicalEdge::Start) { 2015 return anchorOffsetFromStartEdge; 2016 } 2017 // Need the offset from the end edge of the containing block. 2018 const auto anchorOffsetFromEndEdge = 2019 logicalCBSize.Size(propAxis, wm) - anchorOffsetFromStartEdge; 2020 return anchorOffsetFromEndEdge; 2021 }(); 2022 2023 // Apply the percentage value, with the percentage basis as the anchor 2024 // element's size in the relevant axis. 2025 if (aPercentage != 0.f) { 2026 const nscoord anchorSize = LogicalSize{wm, rect.Size()}.Size(propAxis, wm); 2027 result += (propEdge == LogicalEdge::End ? -1 : 1) * 2028 ((aPercentage != 1.f) 2029 ? NSToCoordRoundWithClamp(aPercentage * 2030 static_cast<float>(anchorSize)) 2031 : anchorSize); 2032 } 2033 *aOut = Length::FromPixels(CSSPixel::FromAppUnits(result)); 2034 return true; 2035 } 2036 2037 bool Gecko_GetAnchorPosSize(const AnchorPosResolutionParams* aParams, 2038 const nsAtom* aAnchorName, 2039 StylePhysicalAxis aPropAxis, 2040 StyleAnchorSizeKeyword aAnchorSizeKeyword, 2041 Length* aOut) { 2042 if (!aParams || !aParams->mFrame) { 2043 return false; 2044 } 2045 const auto* positioned = aParams->mFrame; 2046 const auto size = AnchorPositioningUtils::ResolveAnchorPosSize( 2047 positioned, aAnchorName, aParams->mCache); 2048 if (!size) { 2049 return false; 2050 } 2051 const auto* containingBlock = positioned->GetParent(); 2052 const auto l = [&]() { 2053 switch (aAnchorSizeKeyword) { 2054 case StyleAnchorSizeKeyword::None: 2055 switch (aPropAxis) { 2056 case StylePhysicalAxis::Horizontal: 2057 return size->Width(); 2058 case StylePhysicalAxis::Vertical: 2059 return size->Height(); 2060 } 2061 MOZ_ASSERT_UNREACHABLE("Unexpected physical axis."); 2062 return size->Width(); 2063 case StyleAnchorSizeKeyword::Width: 2064 return size->Width(); 2065 case StyleAnchorSizeKeyword::Height: 2066 return size->Height(); 2067 case StyleAnchorSizeKeyword::Inline: { 2068 const auto wm = containingBlock->GetWritingMode(); 2069 return LogicalSize{wm, *size}.ISize(wm); 2070 } 2071 case StyleAnchorSizeKeyword::Block: { 2072 const auto wm = containingBlock->GetWritingMode(); 2073 return LogicalSize{wm, *size}.BSize(wm); 2074 } 2075 case StyleAnchorSizeKeyword::SelfInline: { 2076 const auto wm = positioned->GetWritingMode(); 2077 return LogicalSize{wm, *size}.ISize(wm); 2078 } 2079 case StyleAnchorSizeKeyword::SelfBlock: { 2080 const auto wm = positioned->GetWritingMode(); 2081 return LogicalSize{wm, *size}.BSize(wm); 2082 } 2083 } 2084 MOZ_ASSERT_UNREACHABLE("Unhandled anchor size keyword."); 2085 return size->Width(); 2086 }(); 2087 *aOut = Length::FromPixels(CSSPixel::FromAppUnits(l)); 2088 return true; 2089 }