Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/servo/ports/geckolib/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 311 kB image not shown  

Quellcode-Bibliothek glue.rs   Sprache: unbekannt

 
Columbo aufrufen.rs Download desUnknown {[0] [0] [0]}Datei anzeigen

/* 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 https://mozilla.org/MPL/2.0/. */

use super::error_reporter::ErrorReporter;
use super::stylesheet_loader::{AsyncStylesheetParser, StylesheetLoader};
use cssparser::ToCss as ParserToCss;
use cssparser::{
    BasicParseError, ParseError as CssParseError, Parser, ParserInput, ParserState, SourceLocation,
    Token, UnicodeRange,
};
use dom::{DocumentState, ElementState};
use malloc_size_of::MallocSizeOfOps;
use nsstring::{nsCString, nsString};
use selectors::matching::{ElementSelectorFlags, MatchingForInvalidation, SelectorCaches};
use selectors::{Element, OpaqueElement};
use servo_arc::{Arc, ArcBorrow};
use smallvec::SmallVec;
use style::values::generics::length::AnchorResolutionResult;
use std::collections::BTreeSet;
use std::fmt::Write;
use std::iter;
use std::os::raw::c_void;
use std::ptr;
use style::color::mix::ColorInterpolationMethod;
use style::color::{AbsoluteColor, ColorSpace};
use style::computed_value_flags::ComputedValueFlags;
use style::context::ThreadLocalStyleContext;
use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
use style::counter_style;
use style::custom_properties::DeferFontRelativeCustomPropertyResolution;
use style::data::{self, ElementStyles};
use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
use style::driver;
use style::error_reporting::{ParseErrorReporter, SelectorWarningKind};
use style::font_face::{self, FontFaceSourceFormat, FontFaceSourceListComponent, Source};
use style::gecko::arc_types::{
    LockedCounterStyleRule, LockedCssRules, LockedDeclarationBlock, LockedFontFaceRule,
    LockedImportRule, LockedKeyframe, LockedKeyframesRule, LockedMediaList, LockedPageRule,
    LockedPositionTryRule, LockedNestedDeclarationsRule, LockedStyleRule,
};
use style::gecko::data::{
    AuthorStyles, GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl,
};
use style::gecko::restyle_damage::GeckoRestyleDamage;
use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
use style::gecko::snapshot_helpers::classes_changed;
use style::gecko::traversal::RecalcStyleOnly;
use style::gecko::url;
use style::gecko::wrapper::{
    slow_selector_flags_from_node_selector_flags, GeckoElement, GeckoNode,
};
use style::gecko_bindings::bindings;
use style::gecko_bindings::bindings::nsACString;
use style::gecko_bindings::bindings::nsAString;
use style::gecko_bindings::bindings::Gecko_AddPropertyToSet;
use style::gecko_bindings::bindings::Gecko_ConstructFontFeatureValueSet;
use style::gecko_bindings::bindings::Gecko_ConstructFontPaletteValueSet;
use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
use style::gecko_bindings::structs;
use style::gecko_bindings::structs::gfx::FontPaletteValueSet;
use style::gecko_bindings::structs::gfxFontFeatureValueSet;
use style::gecko_bindings::structs::nsAtom;
use style::gecko_bindings::structs::nsCSSCounterDesc;
use style::gecko_bindings::structs::nsCSSFontDesc;
use style::gecko_bindings::structs::nsCSSPropertyID;
use style::gecko_bindings::structs::nsChangeHint;
use style::gecko_bindings::structs::nsCompatibility;
use style::gecko_bindings::structs::nsresult;
use style::gecko_bindings::structs::CallerType;
use style::gecko_bindings::structs::CompositeOperation;
use style::gecko_bindings::structs::DeclarationBlockMutationClosure;
use style::gecko_bindings::structs::GeckoFontMetrics;
use style::gecko_bindings::structs::IterationCompositeOperation;
use style::gecko_bindings::structs::Loader;
use style::gecko_bindings::structs::LoaderReusableStyleSheets;
use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
use style::gecko_bindings::structs::OriginFlags;
use style::gecko_bindings::structs::PropertyValuePair;
use style::gecko_bindings::structs::PseudoStyleType;
use style::gecko_bindings::structs::SeenPtrs;
use style::gecko_bindings::structs::ServoElementSnapshotTable;
use style::gecko_bindings::structs::ServoStyleSetSizes;
use style::gecko_bindings::structs::ServoTraversalFlags;
use style::gecko_bindings::structs::SheetLoadData;
use style::gecko_bindings::structs::SheetLoadDataHolder;
use style::gecko_bindings::structs::SheetParsingMode;
use style::gecko_bindings::structs::StyleRuleInclusion;
use style::gecko_bindings::structs::StyleSheet as DomStyleSheet;
use style::gecko_bindings::structs::URLExtraData;
use style::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};
use style::gecko_bindings::sugar::ownership::Strong;
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::global_style_data::{
    GlobalStyleData, PlatformThreadHandle, StyleThreadPool, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL,
};
use style::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
use style::invalidation::element::invalidation_map::{
    RelativeSelectorInvalidationMap, TSStateForInvalidation,
};
use style::invalidation::element::invalidator::{InvalidationResult, SiblingTraversalMap};
use style::invalidation::element::relative_selector::{
    DomMutationOperation, RelativeSelectorDependencyCollector, RelativeSelectorInvalidator,
};
use style::invalidation::element::restyle_hints::RestyleHint;
use style::invalidation::stylesheets::RuleChangeKind;
use style::logical_geometry::PhysicalSide;
use style::media_queries::MediaList;
use style::parser::{Parse, ParserContext};
#[cfg(feature = "gecko_debug")]
use style::properties::LonghandIdSet;
use style::properties::{
    animated_properties::{AnimationValue, AnimationValueMap},
    parse_one_declaration_into, parse_style_attribute, ComputedValues, CountedUnknownProperty,
    Importance, LonghandId, NonCustomPropertyId, OwnedPropertyDeclarationId,
    PropertyDeclarationBlock, PropertyDeclarationId, PropertyDeclarationIdSet, PropertyId,
    ShorthandId, SourcePropertyDeclaration, StyleBuilder,
};
use style::properties_and_values::registry::{PropertyRegistration, PropertyRegistrationData};
use style::properties_and_values::rule::Inherits as PropertyInherits;
use style::rule_cache::RuleCacheConditions;
use style::rule_tree::StrongRuleNode;
use style::selector_parser::PseudoElementCascadeType;
use style::shared_lock::{
    Locked, SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard,
};
use style::string_cache::{Atom, WeakAtom};
use style::style_adjuster::StyleAdjuster;
use style::stylesheets::container_rule::ContainerSizeQuery;
use style::stylesheets::import_rule::{ImportLayer, ImportSheet};
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
use style::stylesheets::supports_rule::parse_condition_or_declaration;
use style::stylesheets::{
    AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleType, CssRuleTypes,
    CssRules, CssRulesHelpers, DocumentRule, FontFaceRule, FontFeatureValuesRule,
    FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
    MarginRule, MediaRule, NamespaceRule, NestedDeclarationsRule, Origin, OriginSet,
    PagePseudoClassFlags, PageRule, PositionTryRule, PropertyRule, SanitizationData,
    SanitizationKind, ScopeRule, StartingStyleRule, StyleRule, StylesheetContents,
    StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData,
};
use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
use style::thread_state;
use style::traversal::resolve_style;
use style::traversal::DomTraversal;
use style::traversal_flags::{self, TraversalFlags};
use style::use_counters::UseCounters;
use style::values::animated::{Animate, Procedure, ToAnimatedZero};
use style::values::computed::easing::ComputedTimingFunction;
use style::values::computed::effects::Filter;
use style::values::computed::font::{
    FamilyName, FontFamily, FontFamilyList, FontStretch, FontStyle, FontWeight, GenericFontFamily,
};
use style::values::computed::length::AnchorSizeFunction;
use style::values::computed::position::AnchorFunction;
use style::values::computed::{self, Context, PositionProperty, ToComputedValue};
use style::values::distance::ComputeSquaredDistance;
use style::values::generics::color::ColorMixFlags;
use style::values::generics::easing::BeforeFlag;
use style::values::resolved;
use style::values::specified::gecko::IntersectionObserverRootMargin;
use style::values::specified::source_size_list::SourceSizeList;
use style::values::specified::{AbsoluteLength, NoCalcLength};
use style::values::{specified, AtomIdent, CustomIdent, KeyframesName};
use style::values::specified::svg_path::PathCommand;
use style_traits::{CssWriter, ParseError, ParsingMode, ToCss};
use thin_vec::ThinVec as nsTArray;
use to_shmem::SharedMemoryBuilder;

trait ClosureHelper {
    fn invoke(&self, property_id: Option<NonCustomPropertyId>);
}

impl ClosureHelper for DeclarationBlockMutationClosure {
    #[inline]
    fn invoke(&self, property_id: Option<NonCustomPropertyId>) {
        if let Some(function) = self.function.as_ref() {
            let gecko_prop_id = match property_id {
                Some(p) => p.to_nscsspropertyid(),
                None => nsCSSPropertyID::eCSSPropertyExtra_variable,
            };
            unsafe { function(self.data, gecko_prop_id) }
        }
    }
}

/*
 * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
 * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
 * those signatures as well, giving us a second declaration of all the Servo_* functions in this
 * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
 * depend on but good enough for our purposes.
 */

// A dummy url data for where we don't pass url data in.
static mut DUMMY_URL_DATA: *mut URLExtraData = 0 as *mut _;
static mut DUMMY_CHROME_URL_DATA: *mut URLExtraData = 0 as *mut _;

#[no_mangle]
pub unsafe extern "C" fn Servo_Initialize(
    dummy_url_data: *mut URLExtraData,
    dummy_chrome_url_data: *mut URLExtraData,
) {
    use style::gecko_bindings::sugar::origin_flags;

    // Pretend that we're a Servo Layout thread, to make some assertions happy.
    thread_state::initialize(thread_state::ThreadState::LAYOUT);

    debug_assert!(is_main_thread());
    lazy_static::initialize(&STYLE_THREAD_POOL);

    // Perform some debug-only runtime assertions.
    origin_flags::assert_flags_match();
    traversal_flags::assert_traversal_flags_match();

    DUMMY_URL_DATA = dummy_url_data;
    DUMMY_CHROME_URL_DATA = dummy_chrome_url_data;
}

#[no_mangle]
pub unsafe extern "C" fn Servo_Shutdown() {
    DUMMY_URL_DATA = ptr::null_mut();
    DUMMY_CHROME_URL_DATA = ptr::null_mut();
    Stylist::shutdown();
    url::shutdown();
}

#[inline(always)]
unsafe fn dummy_url_data() -> &'static UrlExtraData {
    UrlExtraData::from_ptr_ref(std::ptr::addr_of!(DUMMY_URL_DATA).as_ref().unwrap())
}

#[inline(always)]
unsafe fn dummy_chrome_url_data() -> &'static UrlExtraData {
    UrlExtraData::from_ptr_ref(std::ptr::addr_of!(DUMMY_CHROME_URL_DATA).as_ref().unwrap())
}

#[allow(dead_code)]
fn is_main_thread() -> bool {
    unsafe { bindings::Gecko_IsMainThread() }
}

#[allow(dead_code)]
fn is_dom_worker_thread() -> bool {
    unsafe { bindings::Gecko_IsDOMWorkerThread() }
}

thread_local! {
    /// Thread-local style data for DOM workers
    static DOM_WORKER_RWLOCK: SharedRwLock = SharedRwLock::new();
}

#[allow(dead_code)]
fn is_in_servo_traversal() -> bool {
    unsafe { bindings::Gecko_IsInServoTraversal() }
}

fn create_shared_context<'a>(
    global_style_data: &GlobalStyleData,
    guard: &'a SharedRwLockReadGuard,
    stylist: &'a Stylist,
    traversal_flags: TraversalFlags,
    snapshot_map: &'a ServoElementSnapshotTable,
) -> SharedStyleContext<'a> {
    SharedStyleContext {
        stylist: &stylist,
        visited_styles_enabled: stylist.device().visited_styles_enabled(),
        options: global_style_data.options.clone(),
        guards: StylesheetGuards::same(guard),
        current_time_for_animations: 0.0, // Unused for Gecko, at least for now.
        traversal_flags,
        snapshot_map,
    }
}

fn traverse_subtree(
    element: GeckoElement,
    global_style_data: &GlobalStyleData,
    per_doc_data: &PerDocumentStyleDataImpl,
    guard: &SharedRwLockReadGuard,
    traversal_flags: TraversalFlags,
    snapshots: &ServoElementSnapshotTable,
) {
    let shared_style_context = create_shared_context(
        &global_style_data,
        &guard,
        &per_doc_data.stylist,
        traversal_flags,
        snapshots,
    );

    let token = RecalcStyleOnly::pre_traverse(element, &shared_style_context);

    if !token.should_traverse() {
        return;
    }

    debug!("Traversing subtree from {:?}", element);

    let thread_pool_holder = &*STYLE_THREAD_POOL;
    let pool;
    let thread_pool = if traversal_flags.contains(TraversalFlags::ParallelTraversal) {
        pool = thread_pool_holder.pool();
        pool.as_ref()
    } else {
        None
    };

    let traversal = RecalcStyleOnly::new(shared_style_context);
    driver::traverse_dom(&traversal, token, thread_pool);
}

/// Traverses the subtree rooted at `root` for restyling.
///
/// Returns whether the root was restyled. Whether anything else was restyled or
/// not can be inferred from the dirty bits in the rest of the tree.
#[no_mangle]
pub extern "C" fn Servo_TraverseSubtree(
    root: &RawGeckoElement,
    raw_data: &PerDocumentStyleData,
    snapshots: *const ServoElementSnapshotTable,
    raw_flags: ServoTraversalFlags,
) -> bool {
    let traversal_flags = TraversalFlags::from_bits_retain(raw_flags);
    debug_assert!(!snapshots.is_null());

    let element = GeckoElement(root);

    debug!("Servo_TraverseSubtree (flags={:?})", traversal_flags);
    debug!("{:?}", ShowSubtreeData(element.as_node()));

    if cfg!(debug_assertions) {
        if let Some(parent) = element.traversal_parent() {
            let data = parent
                .borrow_data()
                .expect("Styling element with unstyled parent");
            assert!(
                !data.styles.is_display_none(),
                "Styling element with display: none parent"
            );
        }
    }

    let needs_animation_only_restyle =
        element.has_animation_only_dirty_descendants() || element.has_animation_restyle_hints();

    let per_doc_data = raw_data.borrow();
    debug_assert!(!per_doc_data.stylist.stylesheets_have_changed());

    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();

    let was_initial_style = !element.has_data();

    if needs_animation_only_restyle {
        debug!(
            "Servo_TraverseSubtree doing animation-only restyle (aodd={})",
            element.has_animation_only_dirty_descendants()
        );
        traverse_subtree(
            element,
            &global_style_data,
            &per_doc_data,
            &guard,
            traversal_flags | TraversalFlags::AnimationOnly,
            unsafe { &*snapshots },
        );
    }

    traverse_subtree(
        element,
        &global_style_data,
        &per_doc_data,
        &guard,
        traversal_flags,
        unsafe { &*snapshots },
    );

    debug!(
        "Servo_TraverseSubtree complete (dd={}, aodd={}, lfcd={}, lfc={}, data={:?})",
        element.has_dirty_descendants(),
        element.has_animation_only_dirty_descendants(),
        element.descendants_need_frames(),
        element.needs_frame(),
        element.borrow_data().unwrap()
    );

    if was_initial_style {
        debug_assert!(!element.borrow_data().unwrap().contains_restyle_data());
        false
    } else {
        let element_was_restyled = element.borrow_data().unwrap().contains_restyle_data();
        element_was_restyled
    }
}

/// Checks whether the rule tree has crossed its threshold for unused nodes, and
/// if so, frees them.
#[no_mangle]
pub extern "C" fn Servo_MaybeGCRuleTree(raw_data: &PerDocumentStyleData) {
    let per_doc_data = raw_data.borrow_mut();
    per_doc_data.stylist.rule_tree().maybe_gc();
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValues_Interpolate(
    from: &AnimationValue,
    to: &AnimationValue,
    progress: f64,
) -> Strong<AnimationValue> {
    if let Ok(value) = from.animate(to, Procedure::Interpolate { progress }) {
        Arc::new(value).into()
    } else {
        Strong::null()
    }
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValues_IsInterpolable(
    from: &AnimationValue,
    to: &AnimationValue,
) -> bool {
    from.interpolable_with(to)
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValues_Add(
    a: &AnimationValue,
    b: &AnimationValue,
) -> Strong<AnimationValue> {
    if let Ok(value) = a.animate(b, Procedure::Add) {
        Arc::new(value).into()
    } else {
        Strong::null()
    }
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValues_Accumulate(
    a: &AnimationValue,
    b: &AnimationValue,
    count: u64,
) -> Strong<AnimationValue> {
    if let Ok(value) = a.animate(b, Procedure::Accumulate { count }) {
        Arc::new(value).into()
    } else {
        Strong::null()
    }
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValues_GetZeroValue(
    value_to_match: &AnimationValue,
) -> Strong<AnimationValue> {
    if let Ok(zero_value) = value_to_match.to_animated_zero() {
        Arc::new(zero_value).into()
    } else {
        Strong::null()
    }
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValues_ComputeDistance(
    from: &AnimationValue,
    to: &AnimationValue,
) -> f64 {
    // If compute_squared_distance() failed, this function will return negative value
    // in order to check whether we support the specified paced animation values.
    from.compute_squared_distance(to).map_or(-1.0, |d| d.sqrt())
}

/// Compute one of the endpoints for the interpolation interval, compositing it with the
/// underlying value if needed.
/// An None returned value means, "Just use endpoint_value as-is."
/// It is the responsibility of the caller to ensure that |underlying_value| is provided
/// when it will be used.
fn composite_endpoint(
    endpoint_value: Option<&AnimationValue>,
    composite: CompositeOperation,
    underlying_value: Option<&AnimationValue>,
) -> Option<AnimationValue> {
    match endpoint_value {
        Some(endpoint_value) => match composite {
            CompositeOperation::Add => underlying_value
                .expect("We should have an underlying_value")
                .animate(endpoint_value, Procedure::Add)
                .ok(),
            CompositeOperation::Accumulate => underlying_value
                .expect("We should have an underlying value")
                .animate(endpoint_value, Procedure::Accumulate { count: 1 })
                .ok(),
            _ => None,
        },
        None => underlying_value.map(|v| v.clone()),
    }
}

/// Accumulate one of the endpoints of the animation interval.
/// A returned value of None means, "Just use endpoint_value as-is."
fn accumulate_endpoint(
    endpoint_value: Option<&AnimationValue>,
    composited_value: Option<AnimationValue>,
    last_value: &AnimationValue,
    current_iteration: u64,
) -> Option<AnimationValue> {
    debug_assert!(
        endpoint_value.is_some() || composited_value.is_some(),
        "Should have a suitable value to use"
    );

    let count = current_iteration;
    match composited_value {
        Some(endpoint) => last_value
            .animate(&endpoint, Procedure::Accumulate { count })
            .ok()
            .or(Some(endpoint)),
        None => last_value
            .animate(endpoint_value.unwrap(), Procedure::Accumulate { count })
            .ok(),
    }
}

/// Compose the animation segment. We composite it with the underlying_value and last_value if
/// needed.
/// The caller is responsible for providing an underlying value and last value
/// in all situations where there are needed.
fn compose_animation_segment(
    segment: &structs::AnimationPropertySegment,
    underlying_value: Option<&AnimationValue>,
    last_value: Option<&AnimationValue>,
    iteration_composite: IterationCompositeOperation,
    current_iteration: u64,
    total_progress: f64,
    segment_progress: f64,
) -> AnimationValue {
    // Extract keyframe values.
    let keyframe_from_value = unsafe { segment.mFromValue.mServo.mRawPtr.as_ref() };
    let keyframe_to_value = unsafe { segment.mToValue.mServo.mRawPtr.as_ref() };
    let mut composited_from_value = composite_endpoint(
        keyframe_from_value,
        segment.mFromComposite,
        underlying_value,
    );
    let mut composited_to_value =
        composite_endpoint(keyframe_to_value, segment.mToComposite, underlying_value);

    debug_assert!(
        keyframe_from_value.is_some() || composited_from_value.is_some(),
        "Should have a suitable from value to use"
    );
    debug_assert!(
        keyframe_to_value.is_some() || composited_to_value.is_some(),
        "Should have a suitable to value to use"
    );

    // Apply iteration composite behavior.
    if iteration_composite == IterationCompositeOperation::Accumulate && current_iteration > 0 {
        let last_value = last_value
            .unwrap_or_else(|| underlying_value.expect("Should have a valid underlying value"));

        composited_from_value = accumulate_endpoint(
            keyframe_from_value,
            composited_from_value,
            last_value,
            current_iteration,
        );
        composited_to_value = accumulate_endpoint(
            keyframe_to_value,
            composited_to_value,
            last_value,
            current_iteration,
        );
    }

    // Use the composited value if there is one, otherwise, use the original keyframe value.
    let from = composited_from_value
        .as_ref()
        .unwrap_or_else(|| keyframe_from_value.unwrap());
    let to = composited_to_value
        .as_ref()
        .unwrap_or_else(|| keyframe_to_value.unwrap());

    if segment.mToKey == segment.mFromKey {
        return if total_progress < 0. {
            from.clone()
        } else {
            to.clone()
        };
    }

    match from.animate(
        to,
        Procedure::Interpolate {
            progress: segment_progress,
        },
    ) {
        Ok(value) => value,
        _ => {
            if segment_progress < 0.5 {
                from.clone()
            } else {
                to.clone()
            }
        },
    }
}

#[no_mangle]
pub extern "C" fn Servo_ComposeAnimationSegment(
    segment: &structs::AnimationPropertySegment,
    underlying_value: Option<&AnimationValue>,
    last_value: Option<&AnimationValue>,
    iteration_composite: IterationCompositeOperation,
    progress: f64,
    current_iteration: u64,
) -> Strong<AnimationValue> {
    let result = compose_animation_segment(
        segment,
        underlying_value,
        last_value,
        iteration_composite,
        current_iteration,
        progress,
        progress,
    );
    Arc::new(result).into()
}

#[no_mangle]
pub extern "C" fn Servo_AnimationCompose(
    value_map: &mut AnimationValueMap,
    base_values: &structs::RawServoAnimationValueTable,
    css_property: &structs::AnimatedPropertyID,
    segment: &structs::AnimationPropertySegment,
    last_segment: &structs::AnimationPropertySegment,
    computed_timing: &structs::ComputedTiming,
    iteration_composite: IterationCompositeOperation,
) {
    use style::gecko_bindings::bindings::Gecko_AnimationGetBaseStyle;
    use style::gecko_bindings::bindings::Gecko_GetPositionInSegment;
    use style::gecko_bindings::bindings::Gecko_GetProgressFromComputedTiming;

    let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(css_property) {
        Some(property) if property.as_borrowed().is_animatable() => property,
        _ => return,
    };

    // We will need an underlying value if either of the endpoints is null...
    let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
                                segment.mToValue.mServo.mRawPtr.is_null() ||
                                // ... or if they have a non-replace composite mode ...
                                segment.mFromComposite != CompositeOperation::Replace ||
                                segment.mToComposite != CompositeOperation::Replace ||
                                // ... or if we accumulate onto the last value and it is null.
                                (iteration_composite == IterationCompositeOperation::Accumulate &&
                                 computed_timing.mCurrentIteration > 0 &&
                                 last_segment.mToValue.mServo.mRawPtr.is_null());

    // If either of the segment endpoints are null, get the underlying value to
    // use from the current value in the values map (set by a lower-priority
    // effect), or, if there is no current value, look up the cached base value
    // for this property.
    let underlying_value = if need_underlying_value {
        let previous_composed_value = value_map.get(&property).map(|v| &*v);
        previous_composed_value
            .or_else(|| unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property).as_ref() })
    } else {
        None
    };

    if need_underlying_value && underlying_value.is_none() {
        warn!("Underlying value should be valid when we expect to use it");
        return;
    }

    let last_value = unsafe { last_segment.mToValue.mServo.mRawPtr.as_ref() };
    let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
    let position = if segment.mToKey == segment.mFromKey {
        // Note: compose_animation_segment doesn't use this value
        // if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine.
        progress
    } else {
        unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }
    };

    let result = compose_animation_segment(
        segment,
        underlying_value,
        last_value,
        iteration_composite,
        computed_timing.mCurrentIteration,
        progress,
        position,
    );
    value_map.insert(property, result);
}

macro_rules! get_property_id_from_nscsspropertyid {
    ($property_id: ident, $ret: expr) => {{
        match PropertyId::from_nscsspropertyid($property_id) {
            Some(property_id) => property_id,
            None => {
                return $ret;
            },
        }
    }};
}

macro_rules! get_property_id_from_animatedpropertyid {
    ($property_id: ident, $ret: expr) => {{
        match PropertyId::from_gecko_animated_property_id($property_id) {
            Some(property_id) => property_id,
            None => {
                return $ret;
            },
        }
    }};
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Serialize(
    value: &AnimationValue,
    property: &structs::AnimatedPropertyID,
    raw_data: &PerDocumentStyleData,
    buffer: &mut nsACString,
) {
    let uncomputed_value = value.uncompute();
    let data = raw_data.borrow();
    let rv = PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal)
        .single_value_to_css(
            &get_property_id_from_animatedpropertyid!(property, ()),
            buffer,
            None,
            &data.stylist,
        );
    debug_assert!(rv.is_ok());
}

/// Debug: MOZ_DBG for AnimationValue.
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Dump(value: &AnimationValue, result: &mut nsACString) {
    write!(result, "{:?}", value).unwrap();
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_GetColor(
    value: &AnimationValue,
    foreground_color: structs::nscolor,
) -> structs::nscolor {
    use style::gecko::values::{
        convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color,
    };
    use style::values::computed::color::Color as ComputedColor;
    match *value {
        AnimationValue::BackgroundColor(ref color) => {
            let computed: ComputedColor = color.clone();
            let foreground_color = convert_nscolor_to_absolute_color(foreground_color);
            convert_absolute_color_to_nscolor(&computed.resolve_to_absolute(&foreground_color))
        },
        _ => panic!("Other color properties are not supported yet"),
    }
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_IsCurrentColor(value: &AnimationValue) -> bool {
    match *value {
        AnimationValue::BackgroundColor(ref color) => color.is_currentcolor(),
        _ => {
            debug_assert!(false, "Other color properties are not supported yet");
            false
        },
    }
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_GetOpacity(value: &AnimationValue) -> f32 {
    if let AnimationValue::Opacity(opacity) = *value {
        opacity
    } else {
        panic!("The AnimationValue should be Opacity");
    }
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Opacity(opacity: f32) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::Opacity(opacity)).into()
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Color(
    color_property: nsCSSPropertyID,
    color: structs::nscolor,
) -> Strong<AnimationValue> {
    use style::gecko::values::convert_nscolor_to_absolute_color;
    use style::values::animated::color::Color;

    let property = LonghandId::from_nscsspropertyid(color_property)
        .expect("We don't have shorthand property animation value");

    let animated = convert_nscolor_to_absolute_color(color);

    match property {
        LonghandId::BackgroundColor => {
            Arc::new(AnimationValue::BackgroundColor(Color::Absolute(animated))).into()
        },
        _ => panic!("Should be background-color property"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetScale(
    value: &AnimationValue,
) -> *const computed::Scale {
    match *value {
        AnimationValue::Scale(ref value) => value,
        _ => unreachable!("Expected scale"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetTranslate(
    value: &AnimationValue,
) -> *const computed::Translate {
    match *value {
        AnimationValue::Translate(ref value) => value,
        _ => unreachable!("Expected translate"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetRotate(
    value: &AnimationValue,
) -> *const computed::Rotate {
    match *value {
        AnimationValue::Rotate(ref value) => value,
        _ => unreachable!("Expected rotate"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
    value: &AnimationValue,
) -> *const computed::Transform {
    match *value {
        AnimationValue::Transform(ref value) => value,
        _ => unreachable!("Unsupported transform animation value"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPath(
    value: &AnimationValue,
    output: &mut computed::motion::OffsetPath,
) {
    use style::values::animated::ToAnimatedValue;
    match *value {
        AnimationValue::OffsetPath(ref value) => {
            *output = ToAnimatedValue::from_animated_value(value.clone())
        },
        _ => unreachable!("Expected offset-path"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetDistance(
    value: &AnimationValue,
) -> *const computed::LengthPercentage {
    match *value {
        AnimationValue::OffsetDistance(ref value) => value,
        _ => unreachable!("Expected offset-distance"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetRotate(
    value: &AnimationValue,
) -> *const computed::motion::OffsetRotate {
    match *value {
        AnimationValue::OffsetRotate(ref value) => value,
        _ => unreachable!("Expected offset-rotate"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetAnchor(
    value: &AnimationValue,
) -> *const computed::position::PositionOrAuto {
    match *value {
        AnimationValue::OffsetAnchor(ref value) => value,
        _ => unreachable!("Expected offset-anchor"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPosition(
    value: &AnimationValue,
) -> *const computed::motion::OffsetPosition {
    match *value {
        AnimationValue::OffsetPosition(ref value) => value,
        _ => unreachable!("Expected offset-position"),
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_IsOffsetPathUrl(value: &AnimationValue) -> bool {
    use style::values::generics::motion::{GenericOffsetPath, GenericOffsetPathFunction};
    if let AnimationValue::OffsetPath(ref op) = value {
        if let GenericOffsetPath::OffsetPath { path, coord_box: _ } = op {
            return matches!(**path, GenericOffsetPathFunction::Url(_));
        }
    }
    false
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Rotate(
    r: &computed::Rotate,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::Rotate(r.clone())).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Translate(
    t: &computed::Translate,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::Translate(t.clone())).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Scale(s: &computed::Scale) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::Scale(s.clone())).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Transform(
    transform: &computed::Transform,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::Transform(transform.clone())).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetPath(
    p: &computed::OffsetPath,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::OffsetPath(std::mem::transmute(p.clone()))).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetDistance(
    d: &computed::LengthPercentage,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::OffsetDistance(d.clone())).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetRotate(
    r: &computed::motion::OffsetRotate,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::OffsetRotate(*r)).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetAnchor(
    p: &computed::position::PositionOrAuto,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::OffsetAnchor(p.clone())).into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetPosition(
    p: &computed::motion::OffsetPosition,
) -> Strong<AnimationValue> {
    Arc::new(AnimationValue::OffsetPosition(p.clone())).into()
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_DeepEqual(
    this: &AnimationValue,
    other: &AnimationValue,
) -> bool {
    this == other
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Uncompute(
    value: &AnimationValue,
) -> Strong<LockedDeclarationBlock> {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    Arc::new(
        global_style_data
            .shared_lock
            .wrap(PropertyDeclarationBlock::with_one(
                value.uncompute(),
                Importance::Normal,
            )),
    )
    .into()
}

ipdl_utils::define_ffi_serializer!(
    computed::LengthPercentage,
    Servo_LengthPercentage_Serialize,
    Servo_LengthPercentage_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::transform::Rotate,
    Servo_StyleRotate_Serialize,
    Servo_StyleRotate_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::transform::Scale,
    Servo_StyleScale_Serialize,
    Servo_StyleScale_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::transform::Translate,
    Servo_StyleTranslate_Serialize,
    Servo_StyleTranslate_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::transform::Transform,
    Servo_StyleTransform_Serialize,
    Servo_StyleTransform_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::motion::OffsetPath,
    Servo_StyleOffsetPath_Serialize,
    Servo_StyleOffsetPath_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::motion::OffsetRotate,
    Servo_StyleOffsetRotate_Serialize,
    Servo_StyleOffsetRotate_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::position::PositionOrAuto,
    Servo_StylePositionOrAuto_Serialize,
    Servo_StylePositionOrAuto_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    computed::motion::OffsetPosition,
    Servo_StyleOffsetPosition_Serialize,
    Servo_StyleOffsetPosition_Deserialize
);

ipdl_utils::define_ffi_serializer!(
    ComputedTimingFunction,
    Servo_StyleComputedTimingFunction_Serialize,
    Servo_StyleComputedTimingFunction_Deserialize
);

// Return the ComputedValues by a base ComputedValues and the rules.
fn resolve_rules_for_element_with_context<'a>(
    element: GeckoElement<'a>,
    mut context: StyleContext<GeckoElement<'a>>,
    rules: StrongRuleNode,
    original_computed_values: &ComputedValues,
) -> Arc<ComputedValues> {
    use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};

    // This currently ignores visited styles, which seems acceptable, as
    // existing browsers don't appear to animate visited styles.
    let inputs = CascadeInputs {
        rules: Some(rules),
        visited_rules: None,
        flags: original_computed_values.flags.for_cascade_inputs(),
    };

    // Actually `PseudoElementResolution` doesn't matter.
    let mut resolver = StyleResolverForElement::new(
        element,
        &mut context,
        RuleInclusion::All,
        PseudoElementResolution::IfApplicable,
    );
    resolver
        .cascade_style_and_visited_with_default_parents(inputs)
        .0
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValueMap_Create() -> *mut AnimationValueMap {
    Box::into_raw(Box::default())
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValueMap_Drop(value_map: *mut AnimationValueMap) {
    let _ = Box::from_raw(value_map);
}

#[no_mangle]
pub extern "C" fn Servo_AnimationValueMap_GetValue(
    value_map: &AnimationValueMap,
    property_id: &structs::AnimatedPropertyID,
) -> Strong<AnimationValue> {
    let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
        Some(property) => property,
        None => return Strong::null(),
    };
    value_map
        .get(&property)
        .map_or(Strong::null(), |value| Arc::new(value.clone()).into())
}

#[no_mangle]
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
    raw_style_set: &PerDocumentStyleData,
    element: &RawGeckoElement,
    computed_values: &ComputedValues,
    snapshots: *const ServoElementSnapshotTable,
) -> Strong<ComputedValues> {
    debug_assert!(!snapshots.is_null());
    let computed_values = unsafe { ArcBorrow::from_ref(computed_values) };

    let rules = match computed_values.rules {
        None => return computed_values.clone_arc().into(),
        Some(ref rules) => rules,
    };

    let doc_data = raw_style_set.borrow();
    let without_animations_rules = doc_data.stylist.rule_tree().remove_animation_rules(rules);
    if without_animations_rules == *rules {
        return computed_values.clone_arc().into();
    }

    let element = GeckoElement(element);

    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();
    let shared = create_shared_context(
        &global_style_data,
        &guard,
        &doc_data.stylist,
        TraversalFlags::empty(),
        unsafe { &*snapshots },
    );
    let mut tlc = ThreadLocalStyleContext::new();
    let context = StyleContext {
        shared: &shared,
        thread_local: &mut tlc,
    };

    resolve_rules_for_element_with_context(
        element,
        context,
        without_animations_rules,
        &computed_values,
    )
    .into()
}

#[repr(C)]
#[derive(Default)]
pub struct ShouldTransitionResult {
    should_animate: bool,
    old_transition_value_matches: bool,
}

#[inline]
fn is_transitionable(prop: PropertyDeclarationId, behavior: computed::TransitionBehavior) -> bool {
    if !prop.is_animatable() {
        return false;
    }
    // TODO(bug 1885995): Return `false` in is_discrete_animatable for interpolatable custom
    // property types.
    if matches!(prop, PropertyDeclarationId::Custom(..)) {
        return true;
    }

    match behavior {
        computed::TransitionBehavior::Normal => !prop.is_discrete_animatable(),
        // If transition-behavior is allow-discrete, transitionable is the same as animatable.
        computed::TransitionBehavior::AllowDiscrete => true,
    }
}

// Note: |new| is the after-change style; however, |old| is the computed values as of the previous
// style change event, and it includes the running transitions and animations.
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_ShouldTransition(
    old: &ComputedValues,
    new: &ComputedValues,
    prop: &structs::AnimatedPropertyID,
    behavior: computed::TransitionBehavior,
    old_transition_end_value: Option<&AnimationValue>,
    current_start_value: Option<&AnimationValue>,
    current_end_value: Option<&AnimationValue>,
    progress: Option<&f64>,
    start: &mut structs::RefPtr<AnimationValue>,
    end: &mut structs::RefPtr<AnimationValue>,
) -> ShouldTransitionResult {
    let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
        return Default::default();
    };
    let prop = prop.as_borrowed();
    if !is_transitionable(prop, behavior) {
        return Default::default();
    }

    let Some(new_value) = AnimationValue::from_computed_values(prop, new) else {
        return Default::default();
    };

    // If the element has a running transition for the property, there is a matching
    // transition-property value, and the end value of the running transition is not equal to the
    // value of the property in the after-change style.
    if let Some(old_transition_end_value) = old_transition_end_value {
        if *old_transition_end_value == new_value {
            return ShouldTransitionResult {
                should_animate: false,
                old_transition_value_matches: true,
            };
        }
    }

    let Some(old_value) = AnimationValue::from_computed_values(prop, old) else {
        return Default::default();
    };

    // For main thread animations, it's fine to use |old_value| because it represents the value in
    // before-change style [1] if not transitions and animations, or the current value [2] if it
    // has a running transition.
    //
    // If this property is replacing a running or pending transition, we might want to compute a
    // more accurate current value, to make sure the check of `current_or_old_value == new_value`
    // below makes sense. |old_value| might be stale, even being the initial value of the
    // transition (if we've throttled the animation on the main thread, due to it being off-screen,
    // or a compositor animation). This prevents us from creating a reversing transition
    // incorrectly.
    //
    // [1] https://drafts.csswg.org/css-transitions-1/#before-change-style
    // [2] https://drafts.csswg.org/css-transitions-1/#current-value
    let current_value = match (current_start_value, current_end_value, progress) {
        (Some(from), Some(to), Some(p)) => {
            // Compute the current value for the compositor animations.
            from.animate(to, Procedure::Interpolate { progress: *p }).ok()
        },
        _ => None,
    };

    // Per spec (https://drafts.csswg.org/css-transitions-1/#starting):
    // 1. If the element does not have a running transition for the property, we have to check if
    //    the before-change style is different from the after-change style for that property, and
    //    if the values for the property are transitionable.
    // ...
    // 4. If the element has a running transition for the property, there is a matching
    //    transition-property value, and the end value is not equal to the value of the property
    //    in the after-change style. Also, if the **current value** of the property in the
    //    running transition is equal to the value of the property in the after-change style, or
    //    if these two values are not transitionable. In this case, we don't create new transition
    //    and we will cancel the running transition.
    let current_or_old_value = current_value.unwrap_or(old_value);
    if current_or_old_value == new_value ||
        matches!(behavior, computed::TransitionBehavior::Normal if !current_or_old_value.interpolable_with(&new_value))
    {
        return Default::default();
    }

    start.set_arc(Arc::new(current_or_old_value));
    end.set_arc(Arc::new(new_value));

    ShouldTransitionResult {
        should_animate: true,
        old_transition_value_matches: false,
    }
}

#[no_mangle]
pub extern "C" fn Servo_ComputedValues_TransitionValueMatches(
    style: &ComputedValues,
    prop: &structs::AnimatedPropertyID,
    transition_value: &AnimationValue,
) -> bool {
    let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
        return false;
    };
    // Note: the running transitions should be transitionable, so it is always allow-discrete.
    let prop = prop.as_borrowed();
    if !is_transitionable(prop, computed::TransitionBehavior::AllowDiscrete) {
        return false;
    }
    let Some(value) = AnimationValue::from_computed_values(prop, style) else {
        return false;
    };
    value == *transition_value
}

#[no_mangle]
pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(
    computed_values: &ComputedValues,
    property_id: &structs::AnimatedPropertyID,
) -> Strong<AnimationValue> {
    let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
        Some(property) => property,
        None => return Strong::null(),
    };
    match AnimationValue::from_computed_values(property.as_borrowed(), &computed_values) {
        Some(v) => Arc::new(v).into(),
        None => Strong::null(),
    }
}

#[no_mangle]
pub extern "C" fn Servo_ResolveLogicalProperty(
    property_id: nsCSSPropertyID,
    style: &ComputedValues,
) -> nsCSSPropertyID {
    let longhand = LonghandId::from_nscsspropertyid(property_id)
        .expect("We shouldn't need to care about shorthands");

    longhand
        .to_physical(style.writing_mode)
        .to_nscsspropertyid()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_Property_LookupEnabledForAllContent(
    prop: &nsACString,
) -> nsCSSPropertyID {
    match PropertyId::parse_enabled_for_all_content(prop.as_str_unchecked()) {
        Ok(p) => p.to_nscsspropertyid_resolving_aliases(),
        Err(..) => nsCSSPropertyID::eCSSProperty_UNKNOWN,
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_Property_GetName(
    prop: nsCSSPropertyID,
    out_length: *mut u32,
) -> *const u8 {
    let (ptr, len) = match NonCustomPropertyId::from_nscsspropertyid(prop) {
        Some(p) => {
            let name = p.name();
            (name.as_bytes().as_ptr(), name.len())
        },
        None => (ptr::null(), 0),
    };

    *out_length = len as u32;
    ptr
}

macro_rules! parse_enabled_property_name {
    ($prop_name:ident, $found:ident, $default:expr) => {{
        let prop_name = $prop_name.as_str_unchecked();
        match PropertyId::parse_enabled_for_all_content(prop_name) {
            Ok(p) => {
                *$found = true;
                p
            },
            Err(..) => {
                *$found = false;
                return $default;
            },
        }
    }};
}

#[no_mangle]
pub unsafe extern "C" fn Servo_Property_IsShorthand(
    prop_name: &nsACString,
    found: *mut bool,
) -> bool {
    let prop_id = parse_enabled_property_name!(prop_name, found, false);
    prop_id.is_shorthand()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_Property_IsInherited(
    per_doc_data: &PerDocumentStyleData,
    prop_name: &nsACString,
) -> bool {
    let prop_name = prop_name.as_str_unchecked();
    let prop_id = match PropertyId::parse_enabled_for_all_content(prop_name) {
        Ok(id) => id,
        Err(_) => return false,
    };
    let longhand_id = match prop_id {
        PropertyId::Custom(property_name) => {
            let stylist = &per_doc_data.borrow().stylist;
            return stylist
                .get_custom_property_registration(&property_name)
                .inherits();
        },
        PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
            Ok(lh) => lh,
            Err(sh) => sh.longhands().next().unwrap(),
        },
    };
    longhand_id.inherited()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_Property_SupportsType(
    prop_name: &nsACString,
    ty: u8,
    found: *mut bool,
) -> bool {
    let prop_id = parse_enabled_property_name!(prop_name, found, false);
    prop_id.supports_type(ty)
}

#[no_mangle]
pub unsafe extern "C" fn Servo_Property_GetCSSValuesForProperty(
    prop_name: &nsACString,
    found: *mut bool,
    result: &mut nsTArray<nsString>,
) {
    let prop_id = parse_enabled_property_name!(prop_name, found, ());
    // Use B-tree set for unique and sorted result.
    let mut values = BTreeSet::<&'static str>::new();
    prop_id.collect_property_completion_keywords(&mut |list| values.extend(list.iter()));

    if values.contains("transparent") {
        // This is a special value devtools use to avoid inserting the
        // long list of color keywords. We need to prepend it to values.
        result.push("COLOR".into());
    }

    for value in values {
        result.push(value.into());
    }
}

#[no_mangle]
pub extern "C" fn Servo_Property_IsAnimatable(prop: &structs::AnimatedPropertyID) -> bool {
    PropertyId::from_gecko_animated_property_id(prop).map_or(false, |p| p.is_animatable())
}

#[no_mangle]
pub extern "C" fn Servo_Property_IsDiscreteAnimatable(property: nsCSSPropertyID) -> bool {
    match LonghandId::from_nscsspropertyid(property) {
        Some(longhand) => longhand.is_discrete_animatable(),
        None => return false,
    }
}

#[no_mangle]
pub extern "C" fn Servo_Element_ClearData(element: &RawGeckoElement) {
    unsafe { GeckoElement(element).clear_data() };
}

#[no_mangle]
pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(
    malloc_size_of: GeckoMallocSizeOf,
    malloc_enclosing_size_of: GeckoMallocSizeOf,
    seen_ptrs: *mut SeenPtrs,
    element: &RawGeckoElement,
) -> usize {
    let element = GeckoElement(element);
    let borrow = element.borrow_data();
    if let Some(data) = borrow {
        let have_seen_ptr = move |ptr| unsafe { Gecko_HaveSeenPtr(seen_ptrs, ptr) };
        let mut ops = MallocSizeOfOps::new(
            malloc_size_of.unwrap(),
            Some(malloc_enclosing_size_of.unwrap()),
            Some(Box::new(have_seen_ptr)),
        );
        (*data).size_of_excluding_cvs(&mut ops)
    } else {
        0
    }
}

#[no_mangle]
pub extern "C" fn Servo_Element_GetMaybeOutOfDateStyle(
    element: &RawGeckoElement,
) -> *const ComputedValues {
    let element = GeckoElement(element);
    let data = match element.borrow_data() {
        Some(d) => d,
        None => return ptr::null(),
    };
    &**data.styles.primary() as *const _
}

#[no_mangle]
pub extern "C" fn Servo_Element_GetMaybeOutOfDatePseudoStyle(
    element: &RawGeckoElement,
    index: usize,
) -> *const ComputedValues {
    let element = GeckoElement(element);
    let data = match element.borrow_data() {
        Some(d) => d,
        None => return ptr::null(),
    };
    match data.styles.pseudos.as_array()[index].as_ref() {
        Some(style) => &**style as *const _,
        None => ptr::null(),
    }
}

// Some functions are so hot and main-thread-only that we have to bypass the AtomicRefCell.
//
// It would be nice to also assert that we're not in the servo traversal, but this function is
// called at various intermediate checkpoints when managing the traversal on the Gecko side.
#[cfg(debug_assertions)]
unsafe fn borrow_assert_main_thread<T>(cell: &atomic_refcell::AtomicRefCell<T>) -> atomic_refcell::AtomicRef<T> {
    debug_assert!(is_main_thread());
    cell.borrow()
}

#[cfg(not(debug_assertions))]
unsafe fn borrow_assert_main_thread<T>(cell: &atomic_refcell::AtomicRefCell<T>) -> &T {
    unsafe { &*cell.as_ptr() }
}

#[no_mangle]
pub extern "C" fn Servo_Element_IsDisplayNone(element: &RawGeckoElement) -> bool {
    let element = GeckoElement(element);
    let data = element
        .get_data()
        .expect("Invoking Servo_Element_IsDisplayNone on unstyled element");

    let is_display_none = unsafe { borrow_assert_main_thread(data) }
        .styles
        .is_display_none();
    is_display_none
}

#[no_mangle]
pub extern "C" fn Servo_Element_IsDisplayContents(element: &RawGeckoElement) -> bool {
    let element = GeckoElement(element);
    let data = element
        .get_data()
        .expect("Invoking Servo_Element_IsDisplayContents on unstyled element");

    let is_display_contents = unsafe { borrow_assert_main_thread(data) }
        .styles
        .primary()
        .get_box()
        .clone_display()
        .is_contents();
    is_display_contents
}

#[no_mangle]
pub extern "C" fn Servo_Element_IsPrimaryStyleReusedViaRuleNode(element: &RawGeckoElement) -> bool {
    let element = GeckoElement(element);
    let data = element
        .borrow_data()
        .expect("Invoking Servo_Element_IsPrimaryStyleReusedViaRuleNode on unstyled element");
    data.flags
        .contains(data::ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE)
}

#[no_mangle]
pub extern "C" fn Servo_Element_MayHaveStartingStyle(element: &RawGeckoElement) -> bool {
    let element = GeckoElement(element);
    let data = match element.borrow_data() {
        Some(d) => d,
        None => return false,
    };
    data.flags
        .contains(data::ElementDataFlags::MAY_HAVE_STARTING_STYLE)
}

fn mode_to_origin(mode: SheetParsingMode) -> Origin {
    match mode {
        SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
        SheetParsingMode::eUserSheetFeatures => Origin::User,
        SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
    }
}

#[no_mangle]
pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> Strong<StylesheetContents> {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    let origin = mode_to_origin(mode);
    let shared_lock = &global_style_data.shared_lock;
    StylesheetContents::from_str(
        "",
        unsafe { dummy_url_data() }.clone(),
        origin,
        shared_lock,
        /* loader = */ None,
        None,
        QuirksMode::NoQuirks,
        /* use_counters = */ None,
        AllowImportRules::Yes,
        /* sanitization_data = */ None,
    )
    .into()
}

/// Note: The load_data corresponds to this sheet, and is passed as the parent
/// load data for child sheet loads. It may be null for certain cases where we
/// know we won't have child loads.
#[no_mangle]
pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8Bytes(
    loader: *mut Loader,
    stylesheet: *mut DomStyleSheet,
    load_data: *mut SheetLoadData,
    bytes: &nsACString,
    mode: SheetParsingMode,
    extra_data: *mut URLExtraData,
    quirks_mode: nsCompatibility,
    reusable_sheets: *mut LoaderReusableStyleSheets,
    use_counters: Option<&UseCounters>,
    allow_import_rules: AllowImportRules,
    sanitization_kind: SanitizationKind,
    sanitized_output: Option<&mut nsAString>,
) -> Strong<StylesheetContents> {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    let input = bytes.as_str_unchecked();

    let reporter = ErrorReporter::new(stylesheet, loader, extra_data);
    let url_data = UrlExtraData::from_ptr_ref(&extra_data);
    let loader = if loader.is_null() {
        None
    } else {
        debug_assert!(
            sanitized_output.is_none(),
            "Shouldn't trigger @import loads for sanitization",
        );
        Some(StylesheetLoader::new(
            loader,
            stylesheet,
            load_data,
            reusable_sheets,
        ))
    };

    // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
    let loader: Option<&dyn StyleStylesheetLoader> = match loader {
        None => None,
        Some(ref s) => Some(s),
    };

    let mut sanitization_data = SanitizationData::new(sanitization_kind);

    let contents = StylesheetContents::from_str(
        input,
        url_data.clone(),
        mode_to_origin(mode),
        &global_style_data.shared_lock,
        loader,
        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
        quirks_mode.into(),
        use_counters,
        allow_import_rules,
        sanitization_data.as_mut(),
    );

    if let Some(data) = sanitization_data {
        sanitized_output
            .unwrap()
            .assign_utf8(data.take().as_bytes());
    }

    contents.into()
}

#[no_mangle]
pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
    load_data: *mut SheetLoadDataHolder,
    extra_data: *mut URLExtraData,
    bytes: &nsACString,
    mode: SheetParsingMode,
    quirks_mode: nsCompatibility,
    should_record_use_counters: bool,
    allow_import_rules: AllowImportRules,
) {
    let load_data = RefPtr::new(load_data);
    let extra_data = UrlExtraData::new(extra_data);

    let mut sheet_bytes = nsCString::new();
    sheet_bytes.assign(bytes);

    let async_parser = AsyncStylesheetParser::new(
        load_data,
        extra_data,
        sheet_bytes,
        mode_to_origin(mode),
        quirks_mode.into(),
        should_record_use_counters,
        allow_import_rules,
    );

    if let Some(thread_pool) = STYLE_THREAD_POOL.pool().as_ref() {
        thread_pool.spawn(|| {
            gecko_profiler_label!(Layout, CSSParsing);
            async_parser.parse();
        });
    } else {
        async_parser.parse();
    }
}

#[no_mangle]
pub unsafe extern "C" fn Servo_ShutdownThreadPool() {
    debug_assert!(is_main_thread() && !is_in_servo_traversal());
    StyleThreadPool::shutdown();
}

#[no_mangle]
pub unsafe extern "C" fn Servo_ThreadPool_GetThreadHandles(
    handles: &mut nsTArray<PlatformThreadHandle>,
) {
    StyleThreadPool::get_thread_handles(handles);
}

#[no_mangle]
pub unsafe extern "C" fn Servo_StyleSheet_FromSharedData(
    extra_data: *mut URLExtraData,
    shared_rules: &LockedCssRules,
) -> Strong<StylesheetContents> {
    StylesheetContents::from_shared_data(
        Arc::from_raw_addrefed(shared_rules),
        Origin::UserAgent,
        UrlExtraData::new(extra_data),
        QuirksMode::NoQuirks,
    )
    .into()
}

#[no_mangle]
pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
    raw_data: &PerDocumentStyleData,
    sheet: *const DomStyleSheet,
) {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    let mut data = raw_data.borrow_mut();
    let data = &mut *data;
    let guard = global_style_data.shared_lock.read();
    let sheet = unsafe { GeckoStyleSheet::new(sheet) };
    data.stylist.append_stylesheet(sheet, &guard);
}

#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_Create() -> *mut AuthorStyles {
    Box::into_raw(Box::new(AuthorStyles::new()))
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_Drop(styles: *mut AuthorStyles) {
    let _ = Box::from_raw(styles);
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet(
    styles: &mut AuthorStyles,
    sheet: *const DomStyleSheet,
) {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();
    let sheet = GeckoStyleSheet::new(sheet);
    styles.stylesheets.append_stylesheet(None, sheet, &guard);
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_InsertStyleSheetBefore(
    styles: &mut AuthorStyles,
    sheet: *const DomStyleSheet,
    before_sheet: *const DomStyleSheet,
) {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();
    styles.stylesheets.insert_stylesheet_before(
        None,
        GeckoStyleSheet::new(sheet),
        GeckoStyleSheet::new(before_sheet),
        &guard,
    );
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_RemoveStyleSheet(
    styles: &mut AuthorStyles,
    sheet: *const DomStyleSheet,
) {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();
    styles
        .stylesheets
        .remove_stylesheet(None, GeckoStyleSheet::new(sheet), &guard);
}

#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_ForceDirty(styles: &mut AuthorStyles) {
    styles.stylesheets.force_dirty();
}

#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_IsDirty(styles: &AuthorStyles) -> bool {
    styles.stylesheets.dirty()
}

#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_Flush(
    styles: &mut AuthorStyles,
    document_set: &PerDocumentStyleData,
) {
    // Try to avoid the atomic borrow below if possible.
    if !styles.stylesheets.dirty() {
        return;
    }

    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();

    let mut document_data = document_set.borrow_mut();

    // TODO(emilio): This is going to need an element or something to do proper
    // invalidation in Shadow roots.
    styles.flush::<GeckoElement>(&mut document_data.stylist, &guard);
}

#[no_mangle]
pub extern "C" fn Servo_StyleSet_RemoveUniqueEntriesFromAuthorStylesCache(
    document_set: &PerDocumentStyleData,
) {
    let mut document_data = document_set.borrow_mut();
    document_data
        .stylist
        .remove_unique_author_data_cache_entries();
}

#[no_mangle]
pub unsafe extern "C" fn Servo_DeclarationBlock_SizeOfIncludingThis(
    malloc_size_of: GeckoMallocSizeOf,
    malloc_enclosing_size_of: GeckoMallocSizeOf,
    declarations: &LockedDeclarationBlock,
) -> usize {
    use malloc_size_of::MallocSizeOf;
    use malloc_size_of::MallocUnconditionalShallowSizeOf;

    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();

    let mut ops = MallocSizeOfOps::new(
        malloc_size_of.unwrap(),
        Some(malloc_enclosing_size_of.unwrap()),
        None,
    );

    ArcBorrow::from_ref(declarations).with_arc(|declarations| {
        let mut n = 0;
        n += declarations.unconditional_shallow_size_of(&mut ops);
        n += declarations.read_with(&guard).size_of(&mut ops);
        n
    })
}

#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_SizeOfIncludingThis(
    malloc_size_of: GeckoMallocSizeOf,
    malloc_enclosing_size_of: GeckoMallocSizeOf,
    styles: &AuthorStyles,
) -> usize {
    // We cannot `use` MallocSizeOf at the top level, otherwise the compiler
    // would complain in `Servo_StyleSheet_SizeOfIncludingThis` for `size_of`
    // there.
    use malloc_size_of::MallocSizeOf;
    let malloc_size_of = malloc_size_of.unwrap();
    let malloc_size_of_this = malloc_size_of(styles as *const AuthorStyles as *const c_void);

    let mut ops = MallocSizeOfOps::new(
        malloc_size_of,
        Some(malloc_enclosing_size_of.unwrap()),
        None,
    );
    malloc_size_of_this + styles.size_of(&mut ops)
}

#[no_mangle]
pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
    document_set: &PerDocumentStyleData,
    non_document_styles: &mut nsTArray<&mut AuthorStyles>,
    may_affect_default_style: bool,
) -> structs::MediumFeaturesChangedResult {
    let global_style_data = &*GLOBAL_STYLE_DATA;
    let guard = global_style_data.shared_lock.read();

    // NOTE(emilio): We don't actually need to flush the stylist here and ensure
    // it's up to date.
    //
    // In case it isn't we would trigger a rebuild + restyle as needed too.
    //
    // We need to ensure the default computed values are up to date though,
    // because those can influence the result of media query evaluation.
    let mut document_data = document_set.borrow_mut();

    if may_affect_default_style {
        document_data.stylist.device_mut().reset_computed_values();
    }
    let guards = StylesheetGuards::same(&guard);

    let origins_in_which_rules_changed = document_data
        .stylist
        .media_features_change_changed_style(&guards, document_data.stylist.device());

    let affects_document_rules = !origins_in_which_rules_changed.is_empty();
    if affects_document_rules {
        document_data
            .stylist
            .force_stylesheet_origins_dirty(origins_in_which_rules_changed);
    }

    let mut affects_non_document_rules = false;
    for author_styles in &mut **non_document_styles {
        let affected_style = author_styles.stylesheets.iter().any(|sheet| {
            !author_styles.data.media_feature_affected_matches(
                sheet,
                &guards.author,
                document_data.stylist.device(),
                document_data.stylist.quirks_mode(),
            )
        });
        if affected_style {
            affects_non_document_rules = true;
            author_styles.stylesheets.force_dirty();
        }
    }

    structs::MediumFeaturesChangedResult {
        mAffectsDocumentRules: affects_document_rules,
        mAffectsNonDocumentRules: affects_non_document_rules,
    }
}

#[no_mangle]
pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
    raw_data: &PerDocumentStyleData,
    sheet: *const DomStyleSheet,
--> --------------------

--> maximum size reached

--> --------------------

[ 0.84Quellennavigators  ]