tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

glue.rs (357305B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 use super::error_reporter::ErrorReporter;
      6 use super::stylesheet_loader::{AsyncStylesheetParser, StylesheetLoader};
      7 use cssparser::ToCss as ParserToCss;
      8 use cssparser::{
      9    BasicParseError, ParseError as CssParseError, Parser, ParserInput, ParserState, SourceLocation,
     10    Token, UnicodeRange,
     11 };
     12 use dom::{DocumentState, ElementState};
     13 use malloc_size_of::MallocSizeOfOps;
     14 use nsstring::{nsCString, nsString};
     15 use selectors::context::{MatchingContext, MatchingMode, NeedsSelectorFlags};
     16 use selectors::matching::{
     17    matches_selector_list, ElementSelectorFlags, MatchingForInvalidation, SelectorCaches,
     18 };
     19 use selectors::parser::PseudoElement as PseudoElementTrait;
     20 use selectors::{Element, OpaqueElement};
     21 use servo_arc::{Arc, ArcBorrow};
     22 use smallvec::SmallVec;
     23 use std::collections::BTreeSet;
     24 use std::fmt::Write;
     25 use std::iter;
     26 use std::os::raw::c_void;
     27 use std::ptr;
     28 use std::sync::LazyLock;
     29 use style::color::mix::ColorInterpolationMethod;
     30 use style::color::{AbsoluteColor, ColorComponents, ColorSpace};
     31 use style::computed_value_flags::ComputedValueFlags;
     32 use style::context::ThreadLocalStyleContext;
     33 use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
     34 use style::counter_style;
     35 use style::custom_properties::DeferFontRelativeCustomPropertyResolution;
     36 use style::data::{self, ElementStyles};
     37 use style::dom::{ShowSubtreeData, TDocument, TElement, TNode, TShadowRoot};
     38 use style::driver;
     39 use style::error_reporting::{ParseErrorReporter, SelectorWarningKind};
     40 use style::font_face::{self, FontFaceSourceFormat, FontFaceSourceListComponent, Source};
     41 use style::gecko::arc_types::{
     42    LockedCounterStyleRule, LockedCssRules, LockedDeclarationBlock, LockedFontFaceRule,
     43    LockedImportRule, LockedKeyframe, LockedKeyframesRule, LockedMediaList,
     44    LockedNestedDeclarationsRule, LockedPageRule, LockedPositionTryRule, LockedStyleRule,
     45 };
     46 use style::gecko::data::{
     47    AuthorStyles, GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl,
     48 };
     49 use style::gecko::restyle_damage::GeckoRestyleDamage;
     50 use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
     51 use style::gecko::snapshot_helpers::classes_changed;
     52 use style::gecko::traversal::RecalcStyleOnly;
     53 use style::gecko::url;
     54 use style::gecko::wrapper::{
     55    slow_selector_flags_from_node_selector_flags, GeckoElement, GeckoNode,
     56 };
     57 use style::gecko_bindings::bindings;
     58 use style::gecko_bindings::bindings::nsACString;
     59 use style::gecko_bindings::bindings::nsAString;
     60 use style::gecko_bindings::bindings::Gecko_AddPropertyToSet;
     61 use style::gecko_bindings::bindings::Gecko_ConstructFontFeatureValueSet;
     62 use style::gecko_bindings::bindings::Gecko_ConstructFontPaletteValueSet;
     63 use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
     64 use style::gecko_bindings::structs;
     65 use style::gecko_bindings::structs::gfx::FontPaletteValueSet;
     66 use style::gecko_bindings::structs::gfxFontFeatureValueSet;
     67 use style::gecko_bindings::structs::nsAtom;
     68 use style::gecko_bindings::structs::nsCSSCounterDesc;
     69 use style::gecko_bindings::structs::nsCSSFontDesc;
     70 use style::gecko_bindings::structs::nsChangeHint;
     71 use style::gecko_bindings::structs::nsCompatibility;
     72 use style::gecko_bindings::structs::nsresult;
     73 use style::gecko_bindings::structs::CallerType;
     74 use style::gecko_bindings::structs::CompositeOperation;
     75 use style::gecko_bindings::structs::DeclarationBlockMutationClosure;
     76 use style::gecko_bindings::structs::GeckoFontMetrics;
     77 use style::gecko_bindings::structs::IterationCompositeOperation;
     78 use style::gecko_bindings::structs::Loader;
     79 use style::gecko_bindings::structs::LoaderReusableStyleSheets;
     80 use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
     81 use style::gecko_bindings::structs::NonCustomCSSPropertyId;
     82 use style::gecko_bindings::structs::OriginFlags;
     83 use style::gecko_bindings::structs::PropertyValuePair;
     84 use style::gecko_bindings::structs::PseudoStyleType;
     85 use style::gecko_bindings::structs::SeenPtrs;
     86 use style::gecko_bindings::structs::ServoElementSnapshotTable;
     87 use style::gecko_bindings::structs::ServoStyleSetSizes;
     88 use style::gecko_bindings::structs::ServoTraversalFlags;
     89 use style::gecko_bindings::structs::SheetLoadData;
     90 use style::gecko_bindings::structs::SheetLoadDataHolder;
     91 use style::gecko_bindings::structs::SheetParsingMode;
     92 use style::gecko_bindings::structs::StyleRuleInclusion;
     93 use style::gecko_bindings::structs::StyleSheet as DomStyleSheet;
     94 use style::gecko_bindings::structs::URLExtraData;
     95 use style::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};
     96 use style::gecko_bindings::structs::{AnchorPosOffsetResolutionParams, AnchorPosResolutionParams};
     97 use style::gecko_bindings::sugar::ownership::Strong;
     98 use style::gecko_bindings::sugar::refptr::RefPtr;
     99 use style::global_style_data::{
    100    GlobalStyleData, PlatformThreadHandle, StyleThreadPool, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL,
    101 };
    102 use style::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
    103 use style::invalidation::element::invalidation_map::{InvalidationMap, TSStateForInvalidation};
    104 use style::invalidation::element::invalidator::{InvalidationResult, SiblingTraversalMap};
    105 use style::invalidation::element::relative_selector::{
    106    DomMutationOperation, RelativeSelectorDependencyCollector, RelativeSelectorInvalidator,
    107 };
    108 use style::invalidation::element::restyle_hints::RestyleHint;
    109 use style::invalidation::stylesheets::RuleChangeKind;
    110 use style::logical_geometry::{LogicalAxis, PhysicalAxis, PhysicalSide, WritingMode};
    111 use style::media_queries::MediaList;
    112 use style::parser::{Parse, ParserContext};
    113 use style::properties::declaration_block::PropertyTypedValue;
    114 #[cfg(feature = "gecko_debug")]
    115 use style::properties::LonghandIdSet;
    116 use style::properties::{
    117    animated_properties::{AnimationValue, AnimationValueMap},
    118    parse_one_declaration_into, parse_style_attribute, ComputedValues, CountedUnknownProperty,
    119    Importance, LonghandId, NonCustomPropertyId, OwnedPropertyDeclarationId,
    120    PropertyDeclarationBlock, PropertyDeclarationId, PropertyDeclarationIdSet, PropertyId,
    121    ShorthandId, SourcePropertyDeclaration, StyleBuilder,
    122 };
    123 use style::properties_and_values::registry::{PropertyRegistration, PropertyRegistrationData};
    124 use style::properties_and_values::rule::Inherits as PropertyInherits;
    125 use style::rule_cache::RuleCacheConditions;
    126 use style::rule_tree::StrongRuleNode;
    127 use style::selector_parser::PseudoElementCascadeType;
    128 use style::shared_lock::{
    129    Locked, SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard,
    130 };
    131 use style::string_cache::{Atom, WeakAtom};
    132 use style::style_adjuster::StyleAdjuster;
    133 use style::stylesheets::container_rule::ContainerSizeQuery;
    134 use style::stylesheets::import_rule::{ImportLayer, ImportSheet};
    135 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
    136 use style::stylesheets::scope_rule::{ImplicitScopeRoot, ScopeRootCandidate, ScopeSubjectMap};
    137 use style::stylesheets::supports_rule::parse_condition_or_declaration;
    138 use style::stylesheets::{
    139    AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleRef, CssRuleType,
    140    CssRuleTypes, CssRules, CustomMediaCondition, CustomMediaEvaluator, CustomMediaRule,
    141    DocumentRule, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule, ImportRule,
    142    KeyframesRule, LayerBlockRule, LayerStatementRule, MarginRule, MediaRule, NamespaceRule,
    143    NestedDeclarationsRule, Origin, OriginSet, PagePseudoClassFlags, PageRule, PositionTryRule,
    144    PropertyRule, SanitizationData, SanitizationKind, ScopeRule, StartingStyleRule, StyleRule,
    145    StylesheetContents, StylesheetInDocument, StylesheetLoader as StyleStylesheetLoader,
    146    SupportsRule, UrlExtraData,
    147 };
    148 use style::stylist::{
    149    add_size_of_ua_cache, replace_parent_selector_with_implicit_scope, scope_root_candidates,
    150    AuthorStylesEnabled, RuleInclusion, ScopeBoundsWithHashes, ScopeConditionId,
    151    ScopeConditionReference, Stylist,
    152 };
    153 use style::thread_state;
    154 use style::traversal::resolve_style;
    155 use style::traversal::DomTraversal;
    156 use style::traversal_flags::{self, TraversalFlags};
    157 use style::use_counters::{CustomUseCounter, UseCounters};
    158 use style::values::animated::{Animate, Procedure, ToAnimatedZero};
    159 use style::values::computed::easing::ComputedTimingFunction;
    160 use style::values::computed::effects::Filter;
    161 use style::values::computed::font::{
    162    FamilyName, FontFamily, FontFamilyList, FontStretch, FontStyle, FontWeight, GenericFontFamily,
    163 };
    164 use style::values::computed::length_percentage::{
    165    AllowAnchorPosResolutionInCalcPercentage, Unpacked,
    166 };
    167 use style::values::computed::position::{AnchorFunction, PositionArea};
    168 use style::values::computed::{self, ContentVisibility, Context, ToComputedValue};
    169 use style::values::distance::{ComputeSquaredDistance, SquaredDistance};
    170 use style::values::generics::color::ColorMixFlags;
    171 use style::values::generics::easing::BeforeFlag;
    172 use style::values::generics::length::GenericAnchorSizeFunction;
    173 use style::values::resolved;
    174 use style::values::specified::align::AlignFlags;
    175 use style::values::specified::intersection_observer::IntersectionObserverMargin;
    176 use style::values::specified::position::PositionTryFallbacksItem;
    177 use style::values::specified::source_size_list::SourceSizeList;
    178 use style::values::specified::svg_path::PathCommand;
    179 use style::values::specified::{AbsoluteLength, NoCalcLength};
    180 use style::values::{specified, AtomIdent, CustomIdent, KeyframesName};
    181 use style_traits::{CssWriter, ParseError, ParsingMode, ToCss, TypedValue};
    182 use thin_vec::ThinVec as nsTArray;
    183 use to_shmem::SharedMemoryBuilder;
    184 
    185 trait ClosureHelper {
    186    fn invoke(&self, property_id: Option<NonCustomPropertyId>);
    187 }
    188 
    189 const NO_MUTATION_CLOSURE: DeclarationBlockMutationClosure = DeclarationBlockMutationClosure {
    190    data: std::ptr::null_mut(),
    191    function: None,
    192 };
    193 
    194 impl ClosureHelper for DeclarationBlockMutationClosure {
    195    #[inline]
    196    fn invoke(&self, property_id: Option<NonCustomPropertyId>) {
    197        if let Some(function) = self.function.as_ref() {
    198            let gecko_prop_id = match property_id {
    199                Some(p) => p.to_noncustomcsspropertyid(),
    200                None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
    201            };
    202            unsafe { function(self.data, gecko_prop_id) }
    203        }
    204    }
    205 }
    206 
    207 /*
    208 * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
    209 * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
    210 * those signatures as well, giving us a second declaration of all the Servo_* functions in this
    211 * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
    212 * depend on but good enough for our purposes.
    213 */
    214 
    215 // A dummy url data for where we don't pass url data in.
    216 static mut DUMMY_URL_DATA: *mut URLExtraData = 0 as *mut _;
    217 static mut DUMMY_CHROME_URL_DATA: *mut URLExtraData = 0 as *mut _;
    218 
    219 #[no_mangle]
    220 pub unsafe extern "C" fn Servo_Initialize(
    221    dummy_url_data: *mut URLExtraData,
    222    dummy_chrome_url_data: *mut URLExtraData,
    223 ) {
    224    use style::gecko_bindings::sugar::origin_flags;
    225 
    226    // Pretend that we're a Servo Layout thread, to make some assertions happy.
    227    thread_state::initialize(thread_state::ThreadState::LAYOUT);
    228 
    229    debug_assert!(is_main_thread());
    230    LazyLock::force(&STYLE_THREAD_POOL);
    231 
    232    // Perform some debug-only runtime assertions.
    233    origin_flags::assert_flags_match();
    234    traversal_flags::assert_traversal_flags_match();
    235 
    236    DUMMY_URL_DATA = dummy_url_data;
    237    DUMMY_CHROME_URL_DATA = dummy_chrome_url_data;
    238 }
    239 
    240 #[no_mangle]
    241 pub unsafe extern "C" fn Servo_Shutdown() {
    242    DUMMY_URL_DATA = ptr::null_mut();
    243    DUMMY_CHROME_URL_DATA = ptr::null_mut();
    244    Stylist::shutdown();
    245    url::shutdown();
    246 }
    247 
    248 #[inline(always)]
    249 unsafe fn dummy_url_data() -> &'static UrlExtraData {
    250    UrlExtraData::from_ptr_ref(std::ptr::addr_of!(DUMMY_URL_DATA).as_ref().unwrap())
    251 }
    252 
    253 #[inline(always)]
    254 unsafe fn dummy_chrome_url_data() -> &'static UrlExtraData {
    255    UrlExtraData::from_ptr_ref(std::ptr::addr_of!(DUMMY_CHROME_URL_DATA).as_ref().unwrap())
    256 }
    257 
    258 #[allow(dead_code)]
    259 fn is_main_thread() -> bool {
    260    unsafe { bindings::Gecko_IsMainThread() }
    261 }
    262 
    263 #[allow(dead_code)]
    264 fn is_dom_worker_thread() -> bool {
    265    unsafe { bindings::Gecko_IsDOMWorkerThread() }
    266 }
    267 
    268 thread_local! {
    269    /// Thread-local style data for DOM workers
    270    static DOM_WORKER_RWLOCK: SharedRwLock = SharedRwLock::new();
    271 }
    272 
    273 #[allow(dead_code)]
    274 fn is_in_servo_traversal() -> bool {
    275    unsafe { bindings::Gecko_IsInServoTraversal() }
    276 }
    277 
    278 fn create_shared_context<'a>(
    279    global_style_data: &GlobalStyleData,
    280    guard: &'a SharedRwLockReadGuard,
    281    stylist: &'a Stylist,
    282    traversal_flags: TraversalFlags,
    283    snapshot_map: &'a ServoElementSnapshotTable,
    284 ) -> SharedStyleContext<'a> {
    285    SharedStyleContext {
    286        stylist: &stylist,
    287        visited_styles_enabled: stylist.device().visited_styles_enabled(),
    288        options: global_style_data.options.clone(),
    289        guards: StylesheetGuards::same(guard),
    290        current_time_for_animations: 0.0, // Unused for Gecko, at least for now.
    291        traversal_flags,
    292        snapshot_map,
    293    }
    294 }
    295 
    296 fn traverse_subtree(
    297    element: GeckoElement,
    298    global_style_data: &GlobalStyleData,
    299    per_doc_data: &PerDocumentStyleDataImpl,
    300    guard: &SharedRwLockReadGuard,
    301    traversal_flags: TraversalFlags,
    302    snapshots: &ServoElementSnapshotTable,
    303 ) {
    304    let shared_style_context = create_shared_context(
    305        &global_style_data,
    306        &guard,
    307        &per_doc_data.stylist,
    308        traversal_flags,
    309        snapshots,
    310    );
    311 
    312    let token = RecalcStyleOnly::pre_traverse(element, &shared_style_context);
    313 
    314    if !token.should_traverse() {
    315        return;
    316    }
    317 
    318    debug!("Traversing subtree from {:?}", element);
    319 
    320    let thread_pool_holder = &*STYLE_THREAD_POOL;
    321    let pool;
    322    let thread_pool = if traversal_flags.contains(TraversalFlags::ParallelTraversal) {
    323        pool = thread_pool_holder.pool();
    324        pool.as_ref()
    325    } else {
    326        None
    327    };
    328 
    329    let traversal = RecalcStyleOnly::new(shared_style_context);
    330    driver::traverse_dom(&traversal, token, thread_pool);
    331 }
    332 
    333 /// Traverses the subtree rooted at `root` for restyling.
    334 ///
    335 /// Returns whether the root was restyled. Whether anything else was restyled or
    336 /// not can be inferred from the dirty bits in the rest of the tree.
    337 #[no_mangle]
    338 pub extern "C" fn Servo_TraverseSubtree(
    339    root: &RawGeckoElement,
    340    raw_data: &PerDocumentStyleData,
    341    snapshots: *const ServoElementSnapshotTable,
    342    raw_flags: ServoTraversalFlags,
    343 ) -> bool {
    344    let traversal_flags = TraversalFlags::from_bits_retain(raw_flags);
    345    debug_assert!(!snapshots.is_null());
    346 
    347    let element = GeckoElement(root);
    348 
    349    debug!("Servo_TraverseSubtree (flags={:?})", traversal_flags);
    350    debug!("{:?}", ShowSubtreeData(element.as_node()));
    351 
    352    if cfg!(debug_assertions) {
    353        if let Some(parent) = element.traversal_parent() {
    354            let data = parent
    355                .borrow_data()
    356                .expect("Styling element with unstyled parent");
    357            assert!(
    358                !data.styles.is_display_none(),
    359                "Styling element with display: none parent"
    360            );
    361        }
    362    }
    363 
    364    let needs_animation_only_restyle =
    365        element.has_animation_only_dirty_descendants() || element.has_animation_restyle_hints();
    366 
    367    let per_doc_data = raw_data.borrow();
    368    debug_assert!(!per_doc_data.stylist.stylesheets_have_changed());
    369 
    370    let global_style_data = &*GLOBAL_STYLE_DATA;
    371    let guard = global_style_data.shared_lock.read();
    372 
    373    let was_initial_style = !element.has_data();
    374 
    375    if needs_animation_only_restyle {
    376        debug!(
    377            "Servo_TraverseSubtree doing animation-only restyle (aodd={})",
    378            element.has_animation_only_dirty_descendants()
    379        );
    380        traverse_subtree(
    381            element,
    382            &global_style_data,
    383            &per_doc_data,
    384            &guard,
    385            traversal_flags | TraversalFlags::AnimationOnly,
    386            unsafe { &*snapshots },
    387        );
    388    }
    389 
    390    traverse_subtree(
    391        element,
    392        &global_style_data,
    393        &per_doc_data,
    394        &guard,
    395        traversal_flags,
    396        unsafe { &*snapshots },
    397    );
    398 
    399    debug!(
    400        "Servo_TraverseSubtree complete (dd={}, aodd={}, lfcd={}, lfc={}, data={:?})",
    401        element.has_dirty_descendants(),
    402        element.has_animation_only_dirty_descendants(),
    403        element.descendants_need_frames(),
    404        element.needs_frame(),
    405        element.borrow_data().unwrap()
    406    );
    407 
    408    if was_initial_style {
    409        debug_assert!(!element.borrow_data().unwrap().contains_restyle_data());
    410        false
    411    } else {
    412        let element_was_restyled = element.borrow_data().unwrap().contains_restyle_data();
    413        element_was_restyled
    414    }
    415 }
    416 
    417 /// Checks whether the rule tree has crossed its threshold for unused nodes, and
    418 /// if so, frees them.
    419 #[no_mangle]
    420 pub extern "C" fn Servo_MaybeGCRuleTree(raw_data: &PerDocumentStyleData) {
    421    let per_doc_data = raw_data.borrow_mut();
    422    per_doc_data.stylist.rule_tree().maybe_gc();
    423 }
    424 
    425 #[no_mangle]
    426 pub extern "C" fn Servo_AnimationValues_Interpolate(
    427    from: &AnimationValue,
    428    to: &AnimationValue,
    429    progress: f64,
    430 ) -> Strong<AnimationValue> {
    431    if let Ok(value) = from.animate(to, Procedure::Interpolate { progress }) {
    432        Arc::new(value).into()
    433    } else {
    434        Strong::null()
    435    }
    436 }
    437 
    438 #[no_mangle]
    439 pub extern "C" fn Servo_AnimationValues_IsInterpolable(
    440    from: &AnimationValue,
    441    to: &AnimationValue,
    442 ) -> bool {
    443    from.interpolable_with(to)
    444 }
    445 
    446 #[no_mangle]
    447 pub extern "C" fn Servo_AnimationValues_Add(
    448    a: &AnimationValue,
    449    b: &AnimationValue,
    450 ) -> Strong<AnimationValue> {
    451    if let Ok(value) = a.animate(b, Procedure::Add) {
    452        Arc::new(value).into()
    453    } else {
    454        Strong::null()
    455    }
    456 }
    457 
    458 #[no_mangle]
    459 pub extern "C" fn Servo_AnimationValues_Accumulate(
    460    a: &AnimationValue,
    461    b: &AnimationValue,
    462    count: u64,
    463 ) -> Strong<AnimationValue> {
    464    if let Ok(value) = a.animate(b, Procedure::Accumulate { count }) {
    465        Arc::new(value).into()
    466    } else {
    467        Strong::null()
    468    }
    469 }
    470 
    471 #[no_mangle]
    472 pub extern "C" fn Servo_AnimationValues_GetZeroValue(
    473    value_to_match: &AnimationValue,
    474 ) -> Strong<AnimationValue> {
    475    if let Ok(zero_value) = value_to_match.to_animated_zero() {
    476        Arc::new(zero_value).into()
    477    } else {
    478        Strong::null()
    479    }
    480 }
    481 
    482 #[no_mangle]
    483 pub extern "C" fn Servo_AnimationValues_ComputeDistance(
    484    from: &AnimationValue,
    485    to: &AnimationValue,
    486 ) -> f64 {
    487    // If compute_squared_distance() failed, this function will return negative value
    488    // in order to check whether we support the specified paced animation values.
    489    from.compute_squared_distance(to).map_or(-1.0, |d| d.sqrt())
    490 }
    491 
    492 /// Compute one of the endpoints for the interpolation interval, compositing it with the
    493 /// underlying value if needed.
    494 /// An None returned value means, "Just use endpoint_value as-is."
    495 /// It is the responsibility of the caller to ensure that |underlying_value| is provided
    496 /// when it will be used.
    497 fn composite_endpoint(
    498    endpoint_value: Option<&AnimationValue>,
    499    composite: CompositeOperation,
    500    underlying_value: Option<&AnimationValue>,
    501 ) -> Option<AnimationValue> {
    502    match endpoint_value {
    503        Some(endpoint_value) => match composite {
    504            CompositeOperation::Add => underlying_value
    505                .expect("We should have an underlying_value")
    506                .animate(endpoint_value, Procedure::Add)
    507                .ok(),
    508            CompositeOperation::Accumulate => underlying_value
    509                .expect("We should have an underlying value")
    510                .animate(endpoint_value, Procedure::Accumulate { count: 1 })
    511                .ok(),
    512            _ => None,
    513        },
    514        None => underlying_value.map(|v| v.clone()),
    515    }
    516 }
    517 
    518 /// Accumulate one of the endpoints of the animation interval.
    519 /// A returned value of None means, "Just use endpoint_value as-is."
    520 fn accumulate_endpoint(
    521    endpoint_value: Option<&AnimationValue>,
    522    composited_value: Option<AnimationValue>,
    523    last_value: &AnimationValue,
    524    current_iteration: u64,
    525 ) -> Option<AnimationValue> {
    526    debug_assert!(
    527        endpoint_value.is_some() || composited_value.is_some(),
    528        "Should have a suitable value to use"
    529    );
    530 
    531    let count = current_iteration;
    532    match composited_value {
    533        Some(endpoint) => last_value
    534            .animate(&endpoint, Procedure::Accumulate { count })
    535            .ok()
    536            .or(Some(endpoint)),
    537        None => last_value
    538            .animate(endpoint_value.unwrap(), Procedure::Accumulate { count })
    539            .ok(),
    540    }
    541 }
    542 
    543 /// Compose the animation segment. We composite it with the underlying_value and last_value if
    544 /// needed.
    545 /// The caller is responsible for providing an underlying value and last value
    546 /// in all situations where there are needed.
    547 fn compose_animation_segment(
    548    segment: &structs::AnimationPropertySegment,
    549    underlying_value: Option<&AnimationValue>,
    550    last_value: Option<&AnimationValue>,
    551    iteration_composite: IterationCompositeOperation,
    552    current_iteration: u64,
    553    total_progress: f64,
    554    segment_progress: f64,
    555 ) -> AnimationValue {
    556    // Extract keyframe values.
    557    let keyframe_from_value = unsafe { segment.mFromValue.mServo.mRawPtr.as_ref() };
    558    let keyframe_to_value = unsafe { segment.mToValue.mServo.mRawPtr.as_ref() };
    559    let mut composited_from_value = composite_endpoint(
    560        keyframe_from_value,
    561        segment.mFromComposite,
    562        underlying_value,
    563    );
    564    let mut composited_to_value =
    565        composite_endpoint(keyframe_to_value, segment.mToComposite, underlying_value);
    566 
    567    debug_assert!(
    568        keyframe_from_value.is_some() || composited_from_value.is_some(),
    569        "Should have a suitable from value to use"
    570    );
    571    debug_assert!(
    572        keyframe_to_value.is_some() || composited_to_value.is_some(),
    573        "Should have a suitable to value to use"
    574    );
    575 
    576    // Apply iteration composite behavior.
    577    if iteration_composite == IterationCompositeOperation::Accumulate && current_iteration > 0 {
    578        let last_value = last_value
    579            .unwrap_or_else(|| underlying_value.expect("Should have a valid underlying value"));
    580 
    581        composited_from_value = accumulate_endpoint(
    582            keyframe_from_value,
    583            composited_from_value,
    584            last_value,
    585            current_iteration,
    586        );
    587        composited_to_value = accumulate_endpoint(
    588            keyframe_to_value,
    589            composited_to_value,
    590            last_value,
    591            current_iteration,
    592        );
    593    }
    594 
    595    // Use the composited value if there is one, otherwise, use the original keyframe value.
    596    let from = composited_from_value
    597        .as_ref()
    598        .unwrap_or_else(|| keyframe_from_value.unwrap());
    599    let to = composited_to_value
    600        .as_ref()
    601        .unwrap_or_else(|| keyframe_to_value.unwrap());
    602 
    603    if segment.mToKey == segment.mFromKey {
    604        return if total_progress < 0. {
    605            from.clone()
    606        } else {
    607            to.clone()
    608        };
    609    }
    610 
    611    match from.animate(
    612        to,
    613        Procedure::Interpolate {
    614            progress: segment_progress,
    615        },
    616    ) {
    617        Ok(value) => value,
    618        _ => {
    619            if segment_progress < 0.5 {
    620                from.clone()
    621            } else {
    622                to.clone()
    623            }
    624        },
    625    }
    626 }
    627 
    628 #[no_mangle]
    629 pub extern "C" fn Servo_ComposeAnimationSegment(
    630    segment: &structs::AnimationPropertySegment,
    631    underlying_value: Option<&AnimationValue>,
    632    last_value: Option<&AnimationValue>,
    633    iteration_composite: IterationCompositeOperation,
    634    progress: f64,
    635    current_iteration: u64,
    636 ) -> Strong<AnimationValue> {
    637    let result = compose_animation_segment(
    638        segment,
    639        underlying_value,
    640        last_value,
    641        iteration_composite,
    642        current_iteration,
    643        progress,
    644        progress,
    645    );
    646    Arc::new(result).into()
    647 }
    648 
    649 #[no_mangle]
    650 pub extern "C" fn Servo_AnimationCompose(
    651    value_map: &mut AnimationValueMap,
    652    base_values: &structs::RawServoAnimationValueTable,
    653    css_property: &structs::CSSPropertyId,
    654    segment: &structs::AnimationPropertySegment,
    655    last_segment: &structs::AnimationPropertySegment,
    656    computed_timing: &structs::ComputedTiming,
    657    iteration_composite: IterationCompositeOperation,
    658 ) {
    659    use style::gecko_bindings::bindings::Gecko_AnimationGetBaseStyle;
    660    use style::gecko_bindings::bindings::Gecko_GetPositionInSegment;
    661    use style::gecko_bindings::bindings::Gecko_GetProgressFromComputedTiming;
    662 
    663    let property = match OwnedPropertyDeclarationId::from_gecko_css_property_id(css_property) {
    664        Some(property) if property.as_borrowed().is_animatable() => property,
    665        _ => return,
    666    };
    667 
    668    // We will need an underlying value if either of the endpoints is null...
    669    let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
    670                                segment.mToValue.mServo.mRawPtr.is_null() ||
    671                                // ... or if they have a non-replace composite mode ...
    672                                segment.mFromComposite != CompositeOperation::Replace ||
    673                                segment.mToComposite != CompositeOperation::Replace ||
    674                                // ... or if we accumulate onto the last value and it is null.
    675                                (iteration_composite == IterationCompositeOperation::Accumulate &&
    676                                 computed_timing.mCurrentIteration > 0 &&
    677                                 last_segment.mToValue.mServo.mRawPtr.is_null());
    678 
    679    // If either of the segment endpoints are null, get the underlying value to
    680    // use from the current value in the values map (set by a lower-priority
    681    // effect), or, if there is no current value, look up the cached base value
    682    // for this property.
    683    let underlying_value = if need_underlying_value {
    684        let previous_composed_value = value_map.get(&property).map(|v| &*v);
    685        previous_composed_value
    686            .or_else(|| unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property).as_ref() })
    687    } else {
    688        None
    689    };
    690 
    691    if need_underlying_value && underlying_value.is_none() {
    692        warn!("Underlying value should be valid when we expect to use it");
    693        return;
    694    }
    695 
    696    let last_value = unsafe { last_segment.mToValue.mServo.mRawPtr.as_ref() };
    697    let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
    698    let position = if segment.mToKey == segment.mFromKey {
    699        // Note: compose_animation_segment doesn't use this value
    700        // if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine.
    701        progress
    702    } else {
    703        unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }
    704    };
    705 
    706    let result = compose_animation_segment(
    707        segment,
    708        underlying_value,
    709        last_value,
    710        iteration_composite,
    711        computed_timing.mCurrentIteration,
    712        progress,
    713        position,
    714    );
    715    value_map.insert(property, result);
    716 }
    717 
    718 macro_rules! get_property_id_from_noncustomcsspropertyid {
    719    ($property_id: ident, $ret: expr) => {{
    720        match PropertyId::from_noncustomcsspropertyid($property_id) {
    721            Some(property_id) => property_id,
    722            None => {
    723                return $ret;
    724            },
    725        }
    726    }};
    727 }
    728 
    729 macro_rules! get_property_id_from_csspropertyid {
    730    ($property_id: ident, $ret: expr) => {{
    731        match PropertyId::from_gecko_css_property_id($property_id) {
    732            Some(property_id) => property_id,
    733            None => {
    734                return $ret;
    735            },
    736        }
    737    }};
    738 }
    739 
    740 #[no_mangle]
    741 pub extern "C" fn Servo_AnimationValue_Serialize(
    742    value: &AnimationValue,
    743    property: &structs::CSSPropertyId,
    744    raw_data: &PerDocumentStyleData,
    745    buffer: &mut nsACString,
    746 ) {
    747    let uncomputed_value = value.uncompute();
    748    let data = raw_data.borrow();
    749    let rv = PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal)
    750        .single_value_to_css(
    751            &get_property_id_from_csspropertyid!(property, ()),
    752            buffer,
    753            None,
    754            &data.stylist,
    755        );
    756    debug_assert!(rv.is_ok());
    757 }
    758 
    759 /// Debug: MOZ_DBG for AnimationValue.
    760 #[no_mangle]
    761 pub extern "C" fn Servo_AnimationValue_Dump(value: &AnimationValue, result: &mut nsACString) {
    762    write!(result, "{:?}", value).unwrap();
    763 }
    764 
    765 #[no_mangle]
    766 pub extern "C" fn Servo_AnimationValue_GetColor(
    767    value: &AnimationValue,
    768    foreground_color: structs::nscolor,
    769 ) -> structs::nscolor {
    770    use style::gecko::values::{
    771        convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color,
    772    };
    773    use style::values::computed::color::Color as ComputedColor;
    774    match *value {
    775        AnimationValue::BackgroundColor(ref color) => {
    776            let computed: ComputedColor = color.clone();
    777            let foreground_color = convert_nscolor_to_absolute_color(foreground_color);
    778            convert_absolute_color_to_nscolor(&computed.resolve_to_absolute(&foreground_color))
    779        },
    780        _ => panic!("Other color properties are not supported yet"),
    781    }
    782 }
    783 
    784 #[no_mangle]
    785 pub extern "C" fn Servo_AnimationValue_IsCurrentColor(value: &AnimationValue) -> bool {
    786    match *value {
    787        AnimationValue::BackgroundColor(ref color) => color.is_currentcolor(),
    788        _ => {
    789            debug_assert!(false, "Other color properties are not supported yet");
    790            false
    791        },
    792    }
    793 }
    794 
    795 #[no_mangle]
    796 pub extern "C" fn Servo_AnimationValue_GetOpacity(value: &AnimationValue) -> f32 {
    797    if let AnimationValue::Opacity(opacity) = *value {
    798        opacity
    799    } else {
    800        panic!("The AnimationValue should be Opacity");
    801    }
    802 }
    803 
    804 #[no_mangle]
    805 pub extern "C" fn Servo_AnimationValue_Opacity(opacity: f32) -> Strong<AnimationValue> {
    806    Arc::new(AnimationValue::Opacity(opacity)).into()
    807 }
    808 
    809 #[no_mangle]
    810 pub extern "C" fn Servo_AnimationValue_Color(
    811    color_property: NonCustomCSSPropertyId,
    812    color: structs::nscolor,
    813 ) -> Strong<AnimationValue> {
    814    use style::gecko::values::convert_nscolor_to_absolute_color;
    815    use style::values::animated::color::Color;
    816 
    817    let property = LonghandId::from_noncustomcsspropertyid(color_property)
    818        .expect("We don't have shorthand property animation value");
    819 
    820    let animated = convert_nscolor_to_absolute_color(color);
    821 
    822    match property {
    823        LonghandId::BackgroundColor => {
    824            Arc::new(AnimationValue::BackgroundColor(Color::Absolute(animated))).into()
    825        },
    826        _ => panic!("Should be background-color property"),
    827    }
    828 }
    829 
    830 #[no_mangle]
    831 pub unsafe extern "C" fn Servo_AnimationValue_GetScale(
    832    value: &AnimationValue,
    833 ) -> *const computed::Scale {
    834    match *value {
    835        AnimationValue::Scale(ref value) => value,
    836        _ => unreachable!("Expected scale"),
    837    }
    838 }
    839 
    840 #[no_mangle]
    841 pub unsafe extern "C" fn Servo_AnimationValue_GetTranslate(
    842    value: &AnimationValue,
    843 ) -> *const computed::Translate {
    844    match *value {
    845        AnimationValue::Translate(ref value) => value,
    846        _ => unreachable!("Expected translate"),
    847    }
    848 }
    849 
    850 #[no_mangle]
    851 pub unsafe extern "C" fn Servo_AnimationValue_GetRotate(
    852    value: &AnimationValue,
    853 ) -> *const computed::Rotate {
    854    match *value {
    855        AnimationValue::Rotate(ref value) => value,
    856        _ => unreachable!("Expected rotate"),
    857    }
    858 }
    859 
    860 #[no_mangle]
    861 pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
    862    value: &AnimationValue,
    863 ) -> *const computed::Transform {
    864    match *value {
    865        AnimationValue::Transform(ref value) => value,
    866        _ => unreachable!("Unsupported transform animation value"),
    867    }
    868 }
    869 
    870 #[no_mangle]
    871 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPath(
    872    value: &AnimationValue,
    873    output: &mut computed::motion::OffsetPath,
    874 ) {
    875    use style::values::animated::ToAnimatedValue;
    876    match *value {
    877        AnimationValue::OffsetPath(ref value) => {
    878            *output = ToAnimatedValue::from_animated_value(value.clone())
    879        },
    880        _ => unreachable!("Expected offset-path"),
    881    }
    882 }
    883 
    884 #[no_mangle]
    885 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetDistance(
    886    value: &AnimationValue,
    887 ) -> *const computed::LengthPercentage {
    888    match *value {
    889        AnimationValue::OffsetDistance(ref value) => value,
    890        _ => unreachable!("Expected offset-distance"),
    891    }
    892 }
    893 
    894 #[no_mangle]
    895 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetRotate(
    896    value: &AnimationValue,
    897 ) -> *const computed::motion::OffsetRotate {
    898    match *value {
    899        AnimationValue::OffsetRotate(ref value) => value,
    900        _ => unreachable!("Expected offset-rotate"),
    901    }
    902 }
    903 
    904 #[no_mangle]
    905 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetAnchor(
    906    value: &AnimationValue,
    907 ) -> *const computed::position::PositionOrAuto {
    908    match *value {
    909        AnimationValue::OffsetAnchor(ref value) => value,
    910        _ => unreachable!("Expected offset-anchor"),
    911    }
    912 }
    913 
    914 #[no_mangle]
    915 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPosition(
    916    value: &AnimationValue,
    917 ) -> *const computed::motion::OffsetPosition {
    918    match *value {
    919        AnimationValue::OffsetPosition(ref value) => value,
    920        _ => unreachable!("Expected offset-position"),
    921    }
    922 }
    923 
    924 #[no_mangle]
    925 pub unsafe extern "C" fn Servo_AnimationValue_IsOffsetPathUrl(value: &AnimationValue) -> bool {
    926    use style::values::generics::motion::{GenericOffsetPath, GenericOffsetPathFunction};
    927    if let AnimationValue::OffsetPath(ref op) = value {
    928        if let GenericOffsetPath::OffsetPath { path, coord_box: _ } = op {
    929            return matches!(**path, GenericOffsetPathFunction::Url(_));
    930        }
    931    }
    932    false
    933 }
    934 
    935 #[no_mangle]
    936 pub unsafe extern "C" fn Servo_AnimationValue_Rotate(
    937    r: &computed::Rotate,
    938 ) -> Strong<AnimationValue> {
    939    Arc::new(AnimationValue::Rotate(r.clone())).into()
    940 }
    941 
    942 #[no_mangle]
    943 pub unsafe extern "C" fn Servo_AnimationValue_Translate(
    944    t: &computed::Translate,
    945 ) -> Strong<AnimationValue> {
    946    Arc::new(AnimationValue::Translate(t.clone())).into()
    947 }
    948 
    949 #[no_mangle]
    950 pub unsafe extern "C" fn Servo_AnimationValue_Scale(s: &computed::Scale) -> Strong<AnimationValue> {
    951    Arc::new(AnimationValue::Scale(s.clone())).into()
    952 }
    953 
    954 #[no_mangle]
    955 pub unsafe extern "C" fn Servo_AnimationValue_Transform(
    956    transform: &computed::Transform,
    957 ) -> Strong<AnimationValue> {
    958    Arc::new(AnimationValue::Transform(transform.clone())).into()
    959 }
    960 
    961 #[no_mangle]
    962 pub unsafe extern "C" fn Servo_AnimationValue_OffsetPath(
    963    p: &computed::OffsetPath,
    964 ) -> Strong<AnimationValue> {
    965    Arc::new(AnimationValue::OffsetPath(std::mem::transmute(p.clone()))).into()
    966 }
    967 
    968 #[no_mangle]
    969 pub unsafe extern "C" fn Servo_AnimationValue_OffsetDistance(
    970    d: &computed::LengthPercentage,
    971 ) -> Strong<AnimationValue> {
    972    Arc::new(AnimationValue::OffsetDistance(d.clone())).into()
    973 }
    974 
    975 #[no_mangle]
    976 pub unsafe extern "C" fn Servo_AnimationValue_OffsetRotate(
    977    r: &computed::motion::OffsetRotate,
    978 ) -> Strong<AnimationValue> {
    979    Arc::new(AnimationValue::OffsetRotate(*r)).into()
    980 }
    981 
    982 #[no_mangle]
    983 pub unsafe extern "C" fn Servo_AnimationValue_OffsetAnchor(
    984    p: &computed::position::PositionOrAuto,
    985 ) -> Strong<AnimationValue> {
    986    Arc::new(AnimationValue::OffsetAnchor(p.clone())).into()
    987 }
    988 
    989 #[no_mangle]
    990 pub unsafe extern "C" fn Servo_AnimationValue_OffsetPosition(
    991    p: &computed::motion::OffsetPosition,
    992 ) -> Strong<AnimationValue> {
    993    Arc::new(AnimationValue::OffsetPosition(p.clone())).into()
    994 }
    995 
    996 #[no_mangle]
    997 pub extern "C" fn Servo_AnimationValue_DeepEqual(
    998    this: &AnimationValue,
    999    other: &AnimationValue,
   1000 ) -> bool {
   1001    this == other
   1002 }
   1003 
   1004 #[no_mangle]
   1005 pub extern "C" fn Servo_AnimationValue_Uncompute(
   1006    value: &AnimationValue,
   1007 ) -> Strong<LockedDeclarationBlock> {
   1008    let global_style_data = &*GLOBAL_STYLE_DATA;
   1009    Arc::new(
   1010        global_style_data
   1011            .shared_lock
   1012            .wrap(PropertyDeclarationBlock::with_one(
   1013                value.uncompute(),
   1014                Importance::Normal,
   1015            )),
   1016    )
   1017    .into()
   1018 }
   1019 
   1020 ipdl_utils::define_ffi_serializer!(
   1021    computed::LengthPercentage,
   1022    Servo_LengthPercentage_Serialize,
   1023    Servo_LengthPercentage_Deserialize
   1024 );
   1025 
   1026 ipdl_utils::define_ffi_serializer!(
   1027    computed::transform::Rotate,
   1028    Servo_StyleRotate_Serialize,
   1029    Servo_StyleRotate_Deserialize
   1030 );
   1031 
   1032 ipdl_utils::define_ffi_serializer!(
   1033    computed::transform::Scale,
   1034    Servo_StyleScale_Serialize,
   1035    Servo_StyleScale_Deserialize
   1036 );
   1037 
   1038 ipdl_utils::define_ffi_serializer!(
   1039    computed::transform::Translate,
   1040    Servo_StyleTranslate_Serialize,
   1041    Servo_StyleTranslate_Deserialize
   1042 );
   1043 
   1044 ipdl_utils::define_ffi_serializer!(
   1045    computed::transform::Transform,
   1046    Servo_StyleTransform_Serialize,
   1047    Servo_StyleTransform_Deserialize
   1048 );
   1049 
   1050 ipdl_utils::define_ffi_serializer!(
   1051    computed::motion::OffsetPath,
   1052    Servo_StyleOffsetPath_Serialize,
   1053    Servo_StyleOffsetPath_Deserialize
   1054 );
   1055 
   1056 ipdl_utils::define_ffi_serializer!(
   1057    computed::motion::OffsetRotate,
   1058    Servo_StyleOffsetRotate_Serialize,
   1059    Servo_StyleOffsetRotate_Deserialize
   1060 );
   1061 
   1062 ipdl_utils::define_ffi_serializer!(
   1063    computed::position::PositionOrAuto,
   1064    Servo_StylePositionOrAuto_Serialize,
   1065    Servo_StylePositionOrAuto_Deserialize
   1066 );
   1067 
   1068 ipdl_utils::define_ffi_serializer!(
   1069    computed::motion::OffsetPosition,
   1070    Servo_StyleOffsetPosition_Serialize,
   1071    Servo_StyleOffsetPosition_Deserialize
   1072 );
   1073 
   1074 ipdl_utils::define_ffi_serializer!(
   1075    ComputedTimingFunction,
   1076    Servo_StyleComputedTimingFunction_Serialize,
   1077    Servo_StyleComputedTimingFunction_Deserialize
   1078 );
   1079 
   1080 // Return the ComputedValues by a base ComputedValues and the rules.
   1081 fn resolve_rules_for_element_with_context<'a>(
   1082    element: GeckoElement<'a>,
   1083    mut context: StyleContext<GeckoElement<'a>>,
   1084    rules: StrongRuleNode,
   1085    original_computed_values: &ComputedValues,
   1086 ) -> Arc<ComputedValues> {
   1087    use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};
   1088 
   1089    // This currently ignores visited styles, which seems acceptable, as
   1090    // existing browsers don't appear to animate visited styles.
   1091    let inputs = CascadeInputs {
   1092        rules: Some(rules),
   1093        visited_rules: None,
   1094        flags: original_computed_values.flags.for_cascade_inputs(),
   1095    };
   1096 
   1097    // Actually `PseudoElementResolution` doesn't matter.
   1098    let mut resolver = StyleResolverForElement::new(
   1099        element,
   1100        &mut context,
   1101        RuleInclusion::All,
   1102        PseudoElementResolution::IfApplicable,
   1103    );
   1104    resolver
   1105        .cascade_style_and_visited_with_default_parents(inputs)
   1106        .0
   1107 }
   1108 
   1109 #[no_mangle]
   1110 pub extern "C" fn Servo_AnimationValueMap_Create() -> *mut AnimationValueMap {
   1111    Box::into_raw(Box::default())
   1112 }
   1113 
   1114 #[no_mangle]
   1115 pub unsafe extern "C" fn Servo_AnimationValueMap_Drop(value_map: *mut AnimationValueMap) {
   1116    let _ = Box::from_raw(value_map);
   1117 }
   1118 
   1119 #[no_mangle]
   1120 pub extern "C" fn Servo_AnimationValueMap_GetValue(
   1121    value_map: &AnimationValueMap,
   1122    property_id: &structs::CSSPropertyId,
   1123 ) -> Strong<AnimationValue> {
   1124    let property = match OwnedPropertyDeclarationId::from_gecko_css_property_id(property_id) {
   1125        Some(property) => property,
   1126        None => return Strong::null(),
   1127    };
   1128    value_map
   1129        .get(&property)
   1130        .map_or(Strong::null(), |value| Arc::new(value.clone()).into())
   1131 }
   1132 
   1133 #[no_mangle]
   1134 pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
   1135    raw_style_set: &PerDocumentStyleData,
   1136    element: &RawGeckoElement,
   1137    computed_values: &ComputedValues,
   1138    snapshots: *const ServoElementSnapshotTable,
   1139 ) -> Strong<ComputedValues> {
   1140    debug_assert!(!snapshots.is_null());
   1141    let computed_values = unsafe { ArcBorrow::from_ref(computed_values) };
   1142 
   1143    let rules = match computed_values.rules {
   1144        None => return computed_values.clone_arc().into(),
   1145        Some(ref rules) => rules,
   1146    };
   1147 
   1148    let doc_data = raw_style_set.borrow();
   1149    let without_animations_rules = doc_data.stylist.rule_tree().remove_animation_rules(rules);
   1150    if without_animations_rules == *rules {
   1151        return computed_values.clone_arc().into();
   1152    }
   1153 
   1154    let element = GeckoElement(element);
   1155 
   1156    let global_style_data = &*GLOBAL_STYLE_DATA;
   1157    let guard = global_style_data.shared_lock.read();
   1158    let shared = create_shared_context(
   1159        &global_style_data,
   1160        &guard,
   1161        &doc_data.stylist,
   1162        TraversalFlags::empty(),
   1163        unsafe { &*snapshots },
   1164    );
   1165    let mut tlc = ThreadLocalStyleContext::new();
   1166    let context = StyleContext {
   1167        shared: &shared,
   1168        thread_local: &mut tlc,
   1169    };
   1170 
   1171    resolve_rules_for_element_with_context(
   1172        element,
   1173        context,
   1174        without_animations_rules,
   1175        &computed_values,
   1176    )
   1177    .into()
   1178 }
   1179 
   1180 #[repr(C)]
   1181 #[derive(Default)]
   1182 pub struct ShouldTransitionResult {
   1183    should_animate: bool,
   1184    old_transition_value_matches: bool,
   1185 }
   1186 
   1187 #[inline]
   1188 fn is_transitionable(prop: PropertyDeclarationId, behavior: computed::TransitionBehavior) -> bool {
   1189    if !prop.is_animatable() {
   1190        return false;
   1191    }
   1192    // TODO(bug 1885995): Return `false` in is_discrete_animatable for interpolatable custom
   1193    // property types.
   1194    if matches!(prop, PropertyDeclarationId::Custom(..)) {
   1195        return true;
   1196    }
   1197 
   1198    match behavior {
   1199        computed::TransitionBehavior::Normal => !prop.is_discrete_animatable(),
   1200        // If transition-behavior is allow-discrete, transitionable is the same as animatable.
   1201        computed::TransitionBehavior::AllowDiscrete => true,
   1202    }
   1203 }
   1204 
   1205 // Note: |new| is the after-change style; however, |old| is the computed values as of the previous
   1206 // style change event, and it includes the running transitions and animations.
   1207 #[no_mangle]
   1208 pub extern "C" fn Servo_ComputedValues_ShouldTransition(
   1209    old: &ComputedValues,
   1210    new: &ComputedValues,
   1211    prop: &structs::CSSPropertyId,
   1212    behavior: computed::TransitionBehavior,
   1213    old_transition_end_value: Option<&AnimationValue>,
   1214    current_start_value: Option<&AnimationValue>,
   1215    current_end_value: Option<&AnimationValue>,
   1216    progress: Option<&f64>,
   1217    start: &mut structs::RefPtr<AnimationValue>,
   1218    end: &mut structs::RefPtr<AnimationValue>,
   1219 ) -> ShouldTransitionResult {
   1220    let Some(prop) = OwnedPropertyDeclarationId::from_gecko_css_property_id(prop) else {
   1221        return Default::default();
   1222    };
   1223    let prop = prop.as_borrowed();
   1224    if !is_transitionable(prop, behavior) {
   1225        return Default::default();
   1226    }
   1227 
   1228    let Some(new_value) = AnimationValue::from_computed_values(prop, new) else {
   1229        return Default::default();
   1230    };
   1231 
   1232    // If the element has a running transition for the property, there is a matching
   1233    // transition-property value, and the end value of the running transition is not equal to the
   1234    // value of the property in the after-change style.
   1235    if let Some(old_transition_end_value) = old_transition_end_value {
   1236        if *old_transition_end_value == new_value {
   1237            return ShouldTransitionResult {
   1238                should_animate: false,
   1239                old_transition_value_matches: true,
   1240            };
   1241        }
   1242    }
   1243 
   1244    let Some(old_value) = AnimationValue::from_computed_values(prop, old) else {
   1245        return Default::default();
   1246    };
   1247 
   1248    // For main thread animations, it's fine to use |old_value| because it represents the value in
   1249    // before-change style [1] if not transitions and animations, or the current value [2] if it
   1250    // has a running transition.
   1251    //
   1252    // If this property is replacing a running or pending transition, we might want to compute a
   1253    // more accurate current value, to make sure the check of `current_or_old_value == new_value`
   1254    // below makes sense. |old_value| might be stale, even being the initial value of the
   1255    // transition (if we've throttled the animation on the main thread, due to it being off-screen,
   1256    // or a compositor animation). This prevents us from creating a reversing transition
   1257    // incorrectly.
   1258    //
   1259    // [1] https://drafts.csswg.org/css-transitions-1/#before-change-style
   1260    // [2] https://drafts.csswg.org/css-transitions-1/#current-value
   1261    let current_value = match (current_start_value, current_end_value, progress) {
   1262        (Some(from), Some(to), Some(p)) => {
   1263            // Compute the current value for the compositor animations.
   1264            from.animate(to, Procedure::Interpolate { progress: *p })
   1265                .ok()
   1266        },
   1267        _ => None,
   1268    };
   1269 
   1270    // Per spec (https://drafts.csswg.org/css-transitions-1/#starting):
   1271    // 1. If the element does not have a running transition for the property, we have to check if
   1272    //    the before-change style is different from the after-change style for that property, and
   1273    //    if the values for the property are transitionable.
   1274    // ...
   1275    // 4. If the element has a running transition for the property, there is a matching
   1276    //    transition-property value, and the end value is not equal to the value of the property
   1277    //    in the after-change style. Also, if the **current value** of the property in the
   1278    //    running transition is equal to the value of the property in the after-change style, or
   1279    //    if these two values are not transitionable. In this case, we don't create new transition
   1280    //    and we will cancel the running transition.
   1281    let current_or_old_value = current_value.unwrap_or(old_value);
   1282    if current_or_old_value == new_value
   1283        || matches!(behavior, computed::TransitionBehavior::Normal if !current_or_old_value.interpolable_with(&new_value))
   1284    {
   1285        return Default::default();
   1286    }
   1287 
   1288    start.set_arc(Arc::new(current_or_old_value));
   1289    end.set_arc(Arc::new(new_value));
   1290 
   1291    ShouldTransitionResult {
   1292        should_animate: true,
   1293        old_transition_value_matches: false,
   1294    }
   1295 }
   1296 
   1297 #[no_mangle]
   1298 pub extern "C" fn Servo_ComputedValues_TransitionValueMatches(
   1299    style: &ComputedValues,
   1300    prop: &structs::CSSPropertyId,
   1301    transition_value: &AnimationValue,
   1302 ) -> bool {
   1303    let Some(prop) = OwnedPropertyDeclarationId::from_gecko_css_property_id(prop) else {
   1304        return false;
   1305    };
   1306    // Note: the running transitions should be transitionable, so it is always allow-discrete.
   1307    let prop = prop.as_borrowed();
   1308    if !is_transitionable(prop, computed::TransitionBehavior::AllowDiscrete) {
   1309        return false;
   1310    }
   1311    let Some(value) = AnimationValue::from_computed_values(prop, style) else {
   1312        return false;
   1313    };
   1314    value == *transition_value
   1315 }
   1316 
   1317 #[no_mangle]
   1318 pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(
   1319    computed_values: &ComputedValues,
   1320    property_id: &structs::CSSPropertyId,
   1321 ) -> Strong<AnimationValue> {
   1322    let property = match OwnedPropertyDeclarationId::from_gecko_css_property_id(property_id) {
   1323        Some(property) => property,
   1324        None => return Strong::null(),
   1325    };
   1326    match AnimationValue::from_computed_values(property.as_borrowed(), &computed_values) {
   1327        Some(v) => Arc::new(v).into(),
   1328        None => Strong::null(),
   1329    }
   1330 }
   1331 
   1332 #[no_mangle]
   1333 pub extern "C" fn Servo_ResolveLogicalProperty(
   1334    property_id: NonCustomCSSPropertyId,
   1335    style: &ComputedValues,
   1336 ) -> NonCustomCSSPropertyId {
   1337    let longhand = LonghandId::from_noncustomcsspropertyid(property_id)
   1338        .expect("We shouldn't need to care about shorthands");
   1339 
   1340    longhand
   1341        .to_physical(style.writing_mode)
   1342        .to_noncustomcsspropertyid()
   1343 }
   1344 
   1345 #[no_mangle]
   1346 pub unsafe extern "C" fn Servo_Property_LookupEnabledForAllContent(
   1347    prop: &nsACString,
   1348 ) -> NonCustomCSSPropertyId {
   1349    match PropertyId::parse_enabled_for_all_content(prop.as_str_unchecked()) {
   1350        Ok(p) => p.to_noncustomcsspropertyid_resolving_aliases(),
   1351        Err(..) => NonCustomCSSPropertyId::eCSSProperty_UNKNOWN,
   1352    }
   1353 }
   1354 
   1355 #[no_mangle]
   1356 pub unsafe extern "C" fn Servo_Property_GetName(
   1357    prop: NonCustomCSSPropertyId,
   1358    out_length: *mut u32,
   1359 ) -> *const u8 {
   1360    let (ptr, len) = match NonCustomPropertyId::from_noncustomcsspropertyid(prop) {
   1361        Some(p) => {
   1362            let name = p.name();
   1363            (name.as_bytes().as_ptr(), name.len())
   1364        },
   1365        None => (ptr::null(), 0),
   1366    };
   1367 
   1368    *out_length = len as u32;
   1369    ptr
   1370 }
   1371 
   1372 macro_rules! parse_enabled_property_name {
   1373    ($prop_name:ident, $found:ident, $default:expr) => {{
   1374        let prop_name = $prop_name.as_str_unchecked();
   1375        match PropertyId::parse_enabled_for_all_content(prop_name) {
   1376            Ok(p) => {
   1377                *$found = true;
   1378                p
   1379            },
   1380            Err(..) => {
   1381                *$found = false;
   1382                return $default;
   1383            },
   1384        }
   1385    }};
   1386 }
   1387 
   1388 #[no_mangle]
   1389 pub unsafe extern "C" fn Servo_Property_IsShorthand(
   1390    prop_name: &nsACString,
   1391    found: *mut bool,
   1392 ) -> bool {
   1393    let prop_id = parse_enabled_property_name!(prop_name, found, false);
   1394    prop_id.is_shorthand()
   1395 }
   1396 
   1397 #[no_mangle]
   1398 pub unsafe extern "C" fn Servo_Property_IsInherited(
   1399    per_doc_data: &PerDocumentStyleData,
   1400    prop_name: &nsACString,
   1401 ) -> bool {
   1402    let prop_name = prop_name.as_str_unchecked();
   1403    let prop_id = match PropertyId::parse_enabled_for_all_content(prop_name) {
   1404        Ok(id) => id,
   1405        Err(_) => return false,
   1406    };
   1407    let longhand_id = match prop_id {
   1408        PropertyId::Custom(property_name) => {
   1409            let stylist = &per_doc_data.borrow().stylist;
   1410            return stylist
   1411                .get_custom_property_registration(&property_name)
   1412                .inherits();
   1413        },
   1414        PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
   1415            Ok(lh) => lh,
   1416            Err(sh) => sh.longhands().next().unwrap(),
   1417        },
   1418    };
   1419    longhand_id.inherited()
   1420 }
   1421 
   1422 #[no_mangle]
   1423 pub unsafe extern "C" fn Servo_Property_SupportsType(
   1424    prop_name: &nsACString,
   1425    ty: u8,
   1426    found: *mut bool,
   1427 ) -> bool {
   1428    let prop_id = parse_enabled_property_name!(prop_name, found, false);
   1429    prop_id.supports_type(ty)
   1430 }
   1431 
   1432 #[no_mangle]
   1433 pub unsafe extern "C" fn Servo_Property_GetCSSValuesForProperty(
   1434    prop_name: &nsACString,
   1435    found: *mut bool,
   1436    result: &mut nsTArray<nsString>,
   1437 ) {
   1438    let prop_id = parse_enabled_property_name!(prop_name, found, ());
   1439    // Use B-tree set for unique and sorted result.
   1440    let mut values = BTreeSet::<&'static str>::new();
   1441    prop_id.collect_property_completion_keywords(&mut |list| values.extend(list.iter()));
   1442 
   1443    if values.contains("transparent") {
   1444        // This is a special value devtools use to avoid inserting the
   1445        // long list of color keywords. We need to prepend it to values.
   1446        result.push("COLOR".into());
   1447    }
   1448 
   1449    for value in values {
   1450        result.push(value.into());
   1451    }
   1452 }
   1453 
   1454 #[no_mangle]
   1455 pub extern "C" fn Servo_Property_IsAnimatable(prop: &structs::CSSPropertyId) -> bool {
   1456    PropertyId::from_gecko_css_property_id(prop).map_or(false, |p| p.is_animatable())
   1457 }
   1458 
   1459 #[no_mangle]
   1460 pub extern "C" fn Servo_Property_IsDiscreteAnimatable(property: NonCustomCSSPropertyId) -> bool {
   1461    match LonghandId::from_noncustomcsspropertyid(property) {
   1462        Some(longhand) => longhand.is_discrete_animatable(),
   1463        None => return false,
   1464    }
   1465 }
   1466 
   1467 #[no_mangle]
   1468 pub extern "C" fn Servo_Element_ClearData(element: &RawGeckoElement) {
   1469    unsafe { GeckoElement(element).clear_data() };
   1470 }
   1471 
   1472 #[no_mangle]
   1473 pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(
   1474    malloc_size_of: GeckoMallocSizeOf,
   1475    malloc_enclosing_size_of: GeckoMallocSizeOf,
   1476    seen_ptrs: *mut SeenPtrs,
   1477    element: &RawGeckoElement,
   1478 ) -> usize {
   1479    let element = GeckoElement(element);
   1480    let borrow = element.borrow_data();
   1481    if let Some(data) = borrow {
   1482        let have_seen_ptr = move |ptr| unsafe { Gecko_HaveSeenPtr(seen_ptrs, ptr) };
   1483        let mut ops = MallocSizeOfOps::new(
   1484            malloc_size_of.unwrap(),
   1485            Some(malloc_enclosing_size_of.unwrap()),
   1486            Some(Box::new(have_seen_ptr)),
   1487        );
   1488        (*data).size_of_excluding_cvs(&mut ops)
   1489    } else {
   1490        0
   1491    }
   1492 }
   1493 
   1494 #[no_mangle]
   1495 pub extern "C" fn Servo_Element_GetMaybeOutOfDateStyle(
   1496    element: &RawGeckoElement,
   1497 ) -> *const ComputedValues {
   1498    let element = GeckoElement(element);
   1499    let data = match element.borrow_data() {
   1500        Some(d) => d,
   1501        None => return ptr::null(),
   1502    };
   1503    &**data.styles.primary() as *const _
   1504 }
   1505 
   1506 #[no_mangle]
   1507 pub extern "C" fn Servo_Element_GetMaybeOutOfDatePseudoStyle(
   1508    element: &RawGeckoElement,
   1509    index: usize,
   1510 ) -> *const ComputedValues {
   1511    let element = GeckoElement(element);
   1512    let data = match element.borrow_data() {
   1513        Some(d) => d,
   1514        None => return ptr::null(),
   1515    };
   1516    match data.styles.pseudos.as_array()[index].as_ref() {
   1517        Some(style) => &**style as *const _,
   1518        None => ptr::null(),
   1519    }
   1520 }
   1521 
   1522 // Some functions are so hot and main-thread-only that we have to bypass the AtomicRefCell.
   1523 //
   1524 // It would be nice to also assert that we're not in the servo traversal, but this function is
   1525 // called at various intermediate checkpoints when managing the traversal on the Gecko side.
   1526 #[cfg(debug_assertions)]
   1527 unsafe fn borrow_assert_main_thread<T>(
   1528    cell: &atomic_refcell::AtomicRefCell<T>,
   1529 ) -> atomic_refcell::AtomicRef<'_, T> {
   1530    debug_assert!(is_main_thread());
   1531    cell.borrow()
   1532 }
   1533 
   1534 #[cfg(not(debug_assertions))]
   1535 unsafe fn borrow_assert_main_thread<T>(cell: &atomic_refcell::AtomicRefCell<T>) -> &T {
   1536    unsafe { &*cell.as_ptr() }
   1537 }
   1538 
   1539 #[no_mangle]
   1540 pub extern "C" fn Servo_Element_IsDisplayNone(element: &RawGeckoElement) -> bool {
   1541    let element = GeckoElement(element);
   1542    let data = element
   1543        .get_data()
   1544        .expect("Invoking Servo_Element_IsDisplayNone on unstyled element");
   1545 
   1546    let is_display_none = unsafe { borrow_assert_main_thread(data) }
   1547        .styles
   1548        .is_display_none();
   1549    is_display_none
   1550 }
   1551 
   1552 #[no_mangle]
   1553 pub extern "C" fn Servo_Element_IsDisplayContents(element: &RawGeckoElement) -> bool {
   1554    let element = GeckoElement(element);
   1555    let data = element
   1556        .get_data()
   1557        .expect("Invoking Servo_Element_IsDisplayContents on unstyled element");
   1558 
   1559    let is_display_contents = unsafe { borrow_assert_main_thread(data) }
   1560        .styles
   1561        .primary()
   1562        .get_box()
   1563        .clone_display()
   1564        .is_contents();
   1565    is_display_contents
   1566 }
   1567 
   1568 #[no_mangle]
   1569 pub extern "C" fn Servo_Element_IsPrimaryStyleReusedViaRuleNode(element: &RawGeckoElement) -> bool {
   1570    let element = GeckoElement(element);
   1571    let data = element
   1572        .borrow_data()
   1573        .expect("Invoking Servo_Element_IsPrimaryStyleReusedViaRuleNode on unstyled element");
   1574    data.flags
   1575        .contains(data::ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE)
   1576 }
   1577 
   1578 #[no_mangle]
   1579 pub extern "C" fn Servo_Element_MayHaveStartingStyle(element: &RawGeckoElement) -> bool {
   1580    let element = GeckoElement(element);
   1581    let data = match element.borrow_data() {
   1582        Some(d) => d,
   1583        None => return false,
   1584    };
   1585    data.flags
   1586        .contains(data::ElementDataFlags::MAY_HAVE_STARTING_STYLE)
   1587 }
   1588 
   1589 fn mode_to_origin(mode: SheetParsingMode) -> Origin {
   1590    match mode {
   1591        SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
   1592        SheetParsingMode::eUserSheetFeatures => Origin::User,
   1593        SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
   1594    }
   1595 }
   1596 
   1597 #[no_mangle]
   1598 pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> Strong<StylesheetContents> {
   1599    let global_style_data = &*GLOBAL_STYLE_DATA;
   1600    let origin = mode_to_origin(mode);
   1601    let shared_lock = &global_style_data.shared_lock;
   1602    StylesheetContents::from_str(
   1603        "",
   1604        unsafe { dummy_url_data() }.clone(),
   1605        origin,
   1606        shared_lock,
   1607        /* loader = */ None,
   1608        None,
   1609        QuirksMode::NoQuirks,
   1610        AllowImportRules::Yes,
   1611        /* sanitization_data = */ None,
   1612    )
   1613    .into()
   1614 }
   1615 
   1616 /// Note: The load_data corresponds to this sheet, and is passed as the parent
   1617 /// load data for child sheet loads. It may be null for certain cases where we
   1618 /// know we won't have child loads.
   1619 #[no_mangle]
   1620 pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8Bytes(
   1621    loader: *mut Loader,
   1622    stylesheet: *mut DomStyleSheet,
   1623    load_data: *mut SheetLoadData,
   1624    bytes: &nsACString,
   1625    mode: SheetParsingMode,
   1626    extra_data: *mut URLExtraData,
   1627    quirks_mode: nsCompatibility,
   1628    reusable_sheets: *mut LoaderReusableStyleSheets,
   1629    allow_import_rules: AllowImportRules,
   1630    sanitization_kind: SanitizationKind,
   1631    sanitized_output: Option<&mut nsAString>,
   1632 ) -> Strong<StylesheetContents> {
   1633    let global_style_data = &*GLOBAL_STYLE_DATA;
   1634    let input = bytes.as_str_unchecked();
   1635 
   1636    let reporter = ErrorReporter::new(stylesheet, loader, extra_data);
   1637    let url_data = UrlExtraData::from_ptr_ref(&extra_data);
   1638    let loader = if loader.is_null() {
   1639        None
   1640    } else {
   1641        debug_assert!(
   1642            sanitized_output.is_none(),
   1643            "Shouldn't trigger @import loads for sanitization",
   1644        );
   1645        Some(StylesheetLoader::new(
   1646            loader,
   1647            stylesheet,
   1648            load_data,
   1649            reusable_sheets,
   1650        ))
   1651    };
   1652 
   1653    // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
   1654    let loader: Option<&dyn StyleStylesheetLoader> = match loader {
   1655        None => None,
   1656        Some(ref s) => Some(s),
   1657    };
   1658 
   1659    let mut sanitization_data = SanitizationData::new(sanitization_kind);
   1660 
   1661    let contents = StylesheetContents::from_str(
   1662        input,
   1663        url_data.clone(),
   1664        mode_to_origin(mode),
   1665        &global_style_data.shared_lock,
   1666        loader,
   1667        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
   1668        quirks_mode.into(),
   1669        allow_import_rules,
   1670        sanitization_data.as_mut(),
   1671    );
   1672 
   1673    if let Some(data) = sanitization_data {
   1674        sanitized_output
   1675            .unwrap()
   1676            .assign_utf8(data.take().as_bytes());
   1677    }
   1678 
   1679    contents.into()
   1680 }
   1681 
   1682 #[no_mangle]
   1683 pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
   1684    load_data: *mut SheetLoadDataHolder,
   1685    extra_data: *mut URLExtraData,
   1686    bytes: &nsACString,
   1687    mode: SheetParsingMode,
   1688    quirks_mode: nsCompatibility,
   1689    allow_import_rules: AllowImportRules,
   1690 ) {
   1691    let load_data = RefPtr::new(load_data);
   1692    let extra_data = UrlExtraData::new(extra_data);
   1693 
   1694    let mut sheet_bytes = nsCString::new();
   1695    sheet_bytes.assign(bytes);
   1696 
   1697    let async_parser = AsyncStylesheetParser::new(
   1698        load_data,
   1699        extra_data,
   1700        sheet_bytes,
   1701        mode_to_origin(mode),
   1702        quirks_mode.into(),
   1703        allow_import_rules,
   1704    );
   1705 
   1706    if let Some(thread_pool) = STYLE_THREAD_POOL.pool().as_ref() {
   1707        thread_pool.spawn(|| {
   1708            gecko_profiler_label!(Layout, CSSParsing);
   1709            async_parser.parse();
   1710        });
   1711    } else {
   1712        async_parser.parse();
   1713    }
   1714 }
   1715 
   1716 #[no_mangle]
   1717 pub unsafe extern "C" fn Servo_ShutdownThreadPool() {
   1718    debug_assert!(is_main_thread() && !is_in_servo_traversal());
   1719    StyleThreadPool::shutdown();
   1720 }
   1721 
   1722 #[no_mangle]
   1723 pub unsafe extern "C" fn Servo_ThreadPool_GetThreadHandles(
   1724    handles: &mut nsTArray<PlatformThreadHandle>,
   1725 ) {
   1726    StyleThreadPool::get_thread_handles(handles);
   1727 }
   1728 
   1729 #[no_mangle]
   1730 pub unsafe extern "C" fn Servo_StyleSheet_FromSharedData(
   1731    extra_data: *mut URLExtraData,
   1732    shared_rules: &LockedCssRules,
   1733 ) -> Strong<StylesheetContents> {
   1734    StylesheetContents::from_shared_data(
   1735        Arc::from_raw_addrefed(shared_rules),
   1736        Origin::UserAgent,
   1737        UrlExtraData::new(extra_data),
   1738        QuirksMode::NoQuirks,
   1739    )
   1740    .into()
   1741 }
   1742 
   1743 #[no_mangle]
   1744 pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
   1745    raw_data: &PerDocumentStyleData,
   1746    sheet: *const DomStyleSheet,
   1747 ) {
   1748    let global_style_data = &*GLOBAL_STYLE_DATA;
   1749    let mut data = raw_data.borrow_mut();
   1750    let data = &mut *data;
   1751    let guard = global_style_data.shared_lock.read();
   1752    let sheet = unsafe { GeckoStyleSheet::new(sheet) };
   1753    data.stylist.append_stylesheet(sheet, &guard);
   1754 }
   1755 
   1756 #[no_mangle]
   1757 pub extern "C" fn Servo_AuthorStyles_Create() -> *mut AuthorStyles {
   1758    Box::into_raw(Box::new(AuthorStyles::new()))
   1759 }
   1760 
   1761 #[no_mangle]
   1762 pub unsafe extern "C" fn Servo_AuthorStyles_Drop(styles: *mut AuthorStyles) {
   1763    let _ = Box::from_raw(styles);
   1764 }
   1765 
   1766 #[no_mangle]
   1767 pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet(
   1768    styles: &mut AuthorStyles,
   1769    sheet: *const DomStyleSheet,
   1770 ) {
   1771    let global_style_data = &*GLOBAL_STYLE_DATA;
   1772    let guard = global_style_data.shared_lock.read();
   1773    let sheet = GeckoStyleSheet::new(sheet);
   1774    styles
   1775        .stylesheets
   1776        .append_stylesheet(None, styles.data.custom_media_map(), sheet, &guard);
   1777 }
   1778 
   1779 #[no_mangle]
   1780 pub unsafe extern "C" fn Servo_AuthorStyles_InsertStyleSheetBefore(
   1781    styles: &mut AuthorStyles,
   1782    sheet: *const DomStyleSheet,
   1783    before_sheet: *const DomStyleSheet,
   1784 ) {
   1785    let global_style_data = &*GLOBAL_STYLE_DATA;
   1786    let guard = global_style_data.shared_lock.read();
   1787    styles.stylesheets.insert_stylesheet_before(
   1788        None,
   1789        styles.data.custom_media_map(),
   1790        GeckoStyleSheet::new(sheet),
   1791        GeckoStyleSheet::new(before_sheet),
   1792        &guard,
   1793    );
   1794 }
   1795 
   1796 #[no_mangle]
   1797 pub unsafe extern "C" fn Servo_AuthorStyles_RemoveStyleSheet(
   1798    styles: &mut AuthorStyles,
   1799    sheet: *const DomStyleSheet,
   1800 ) {
   1801    let global_style_data = &*GLOBAL_STYLE_DATA;
   1802    let guard = global_style_data.shared_lock.read();
   1803    styles.stylesheets.remove_stylesheet(
   1804        None,
   1805        styles.data.custom_media_map(),
   1806        GeckoStyleSheet::new(sheet),
   1807        &guard,
   1808    );
   1809 }
   1810 
   1811 #[no_mangle]
   1812 pub extern "C" fn Servo_AuthorStyles_ForceDirty(styles: &mut AuthorStyles) {
   1813    styles.stylesheets.force_dirty();
   1814 }
   1815 
   1816 #[no_mangle]
   1817 pub extern "C" fn Servo_AuthorStyles_IsDirty(styles: &AuthorStyles) -> bool {
   1818    styles.stylesheets.dirty()
   1819 }
   1820 
   1821 #[no_mangle]
   1822 pub unsafe extern "C" fn Servo_DeclarationBlock_SizeOfIncludingThis(
   1823    malloc_size_of: GeckoMallocSizeOf,
   1824    malloc_enclosing_size_of: GeckoMallocSizeOf,
   1825    declarations: &LockedDeclarationBlock,
   1826 ) -> usize {
   1827    use malloc_size_of::MallocSizeOf;
   1828    use malloc_size_of::MallocUnconditionalShallowSizeOf;
   1829 
   1830    let global_style_data = &*GLOBAL_STYLE_DATA;
   1831    let guard = global_style_data.shared_lock.read();
   1832 
   1833    let mut ops = MallocSizeOfOps::new(
   1834        malloc_size_of.unwrap(),
   1835        Some(malloc_enclosing_size_of.unwrap()),
   1836        None,
   1837    );
   1838 
   1839    ArcBorrow::from_ref(declarations).with_arc(|declarations| {
   1840        let mut n = 0;
   1841        n += declarations.unconditional_shallow_size_of(&mut ops);
   1842        n += declarations.read_with(&guard).size_of(&mut ops);
   1843        n
   1844    })
   1845 }
   1846 
   1847 #[no_mangle]
   1848 pub unsafe extern "C" fn Servo_AuthorStyles_SizeOfIncludingThis(
   1849    malloc_size_of: GeckoMallocSizeOf,
   1850    malloc_enclosing_size_of: GeckoMallocSizeOf,
   1851    styles: &AuthorStyles,
   1852 ) -> usize {
   1853    // We cannot `use` MallocSizeOf at the top level, otherwise the compiler
   1854    // would complain in `Servo_StyleSheet_SizeOfIncludingThis` for `size_of`
   1855    // there.
   1856    use malloc_size_of::MallocSizeOf;
   1857    let malloc_size_of = malloc_size_of.unwrap();
   1858    let malloc_size_of_this = malloc_size_of(styles as *const AuthorStyles as *const c_void);
   1859 
   1860    let mut ops = MallocSizeOfOps::new(
   1861        malloc_size_of,
   1862        Some(malloc_enclosing_size_of.unwrap()),
   1863        None,
   1864    );
   1865    malloc_size_of_this + styles.size_of(&mut ops)
   1866 }
   1867 
   1868 #[no_mangle]
   1869 pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
   1870    document_set: &PerDocumentStyleData,
   1871    non_document_styles: &mut nsTArray<&mut AuthorStyles>,
   1872    may_affect_default_style: bool,
   1873 ) -> structs::MediumFeaturesChangedResult {
   1874    let global_style_data = &*GLOBAL_STYLE_DATA;
   1875    let guard = global_style_data.shared_lock.read();
   1876 
   1877    // NOTE(emilio): We don't actually need to flush the stylist here and ensure
   1878    // it's up to date.
   1879    //
   1880    // In case it isn't we would trigger a rebuild + restyle as needed too.
   1881    //
   1882    // We need to ensure the default computed values are up to date though,
   1883    // because those can influence the result of media query evaluation.
   1884    let mut document_data = document_set.borrow_mut();
   1885 
   1886    if may_affect_default_style {
   1887        document_data.stylist.device_mut().reset_computed_values();
   1888    }
   1889    let guards = StylesheetGuards::same(&guard);
   1890 
   1891    let origins_in_which_rules_changed = document_data
   1892        .stylist
   1893        .media_features_change_changed_style(&guards, document_data.stylist.device());
   1894 
   1895    let affects_document_rules = !origins_in_which_rules_changed.is_empty();
   1896    if affects_document_rules {
   1897        document_data
   1898            .stylist
   1899            .force_stylesheet_origins_dirty(origins_in_which_rules_changed);
   1900    }
   1901 
   1902    let mut affects_non_document_rules = false;
   1903    for author_styles in &mut **non_document_styles {
   1904        let affected_style = author_styles.stylesheets.iter().any(|sheet| {
   1905            !author_styles.data.media_feature_affected_matches(
   1906                sheet,
   1907                &guards.author,
   1908                document_data.stylist.device(),
   1909                document_data.stylist.quirks_mode(),
   1910            )
   1911        });
   1912        if affected_style {
   1913            affects_non_document_rules = true;
   1914            author_styles.stylesheets.force_dirty();
   1915        }
   1916    }
   1917 
   1918    structs::MediumFeaturesChangedResult {
   1919        mAffectsDocumentRules: affects_document_rules,
   1920        mAffectsNonDocumentRules: affects_non_document_rules,
   1921    }
   1922 }
   1923 
   1924 #[no_mangle]
   1925 pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
   1926    raw_data: &PerDocumentStyleData,
   1927    sheet: *const DomStyleSheet,
   1928    before_sheet: *const DomStyleSheet,
   1929 ) {
   1930    let global_style_data = &*GLOBAL_STYLE_DATA;
   1931    let mut data = raw_data.borrow_mut();
   1932    let data = &mut *data;
   1933    let guard = global_style_data.shared_lock.read();
   1934    let sheet = unsafe { GeckoStyleSheet::new(sheet) };
   1935    data.stylist.insert_stylesheet_before(
   1936        sheet,
   1937        unsafe { GeckoStyleSheet::new(before_sheet) },
   1938        &guard,
   1939    );
   1940 }
   1941 
   1942 #[no_mangle]
   1943 pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(
   1944    raw_data: &PerDocumentStyleData,
   1945    sheet: *const DomStyleSheet,
   1946 ) {
   1947    let global_style_data = &*GLOBAL_STYLE_DATA;
   1948    let mut data = raw_data.borrow_mut();
   1949    let data = &mut *data;
   1950    let guard = global_style_data.shared_lock.read();
   1951    let sheet = unsafe { GeckoStyleSheet::new(sheet) };
   1952    data.stylist.remove_stylesheet(sheet, &guard);
   1953 }
   1954 
   1955 #[no_mangle]
   1956 pub unsafe extern "C" fn Servo_StyleSet_GetSheetAt(
   1957    raw_data: &PerDocumentStyleData,
   1958    origin: Origin,
   1959    index: usize,
   1960 ) -> *const DomStyleSheet {
   1961    let data = raw_data.borrow();
   1962    data.stylist
   1963        .sheet_at(origin, index)
   1964        .map_or(ptr::null(), |s| s.raw())
   1965 }
   1966 
   1967 #[no_mangle]
   1968 pub unsafe extern "C" fn Servo_StyleSet_GetSheetCount(
   1969    raw_data: &PerDocumentStyleData,
   1970    origin: Origin,
   1971 ) -> usize {
   1972    let data = raw_data.borrow();
   1973    data.stylist.sheet_count(origin)
   1974 }
   1975 
   1976 #[no_mangle]
   1977 pub unsafe extern "C" fn Servo_StyleSet_FlushStyleSheets(
   1978    raw_data: &PerDocumentStyleData,
   1979    doc_element: Option<&RawGeckoElement>,
   1980    snapshots: *const ServoElementSnapshotTable,
   1981    non_document_styles: &mut nsTArray<&mut AuthorStyles>,
   1982 ) {
   1983    let global_style_data = &*GLOBAL_STYLE_DATA;
   1984    let guard = global_style_data.shared_lock.read();
   1985    let mut data = raw_data.borrow_mut();
   1986    let doc_element = doc_element.map(GeckoElement);
   1987 
   1988    let mut invalidations = data.flush_stylesheets(&guard);
   1989    if !non_document_styles.is_empty() {
   1990        for author_styles in non_document_styles {
   1991            let shadow_invalidations = author_styles.flush(&mut data.stylist, &guard);
   1992            // TODO(emilio): For now we drop the style invalidations on the floor, relying on
   1993            // explicit invalidation from C++.
   1994            // TODO(emilio): Consider doing scoped cascade data invalidation, specially once we
   1995            // have tree-scoped names.
   1996            invalidations
   1997                .cascade_data_difference
   1998                .merge_with(shadow_invalidations.cascade_data_difference);
   1999        }
   2000        data.stylist.remove_unique_author_data_cache_entries();
   2001    }
   2002 
   2003    // TODO(emilio): consider merging the existing stylesheet invalidation machinery into the
   2004    // `CascadeDataDifference`.
   2005    let Some(doc_element) = doc_element else {
   2006        return;
   2007    };
   2008    if invalidations.process_style(doc_element, snapshots.as_ref()) {
   2009        // The style invalidation machinery propagates the bits up, but we still need to tell the
   2010        // Gecko restyle root machinery about it.
   2011        bindings::Gecko_NoteDirtySubtreeForInvalidation(doc_element.0);
   2012    }
   2013    let changed_position_try_names = &invalidations
   2014        .cascade_data_difference
   2015        .changed_position_try_names;
   2016    if !changed_position_try_names.is_empty() {
   2017        style::invalidation::stylesheets::invalidate_position_try(
   2018            doc_element,
   2019            &changed_position_try_names,
   2020            &mut |e, _data| unsafe {
   2021                bindings::Gecko_InvalidatePositionTry(e.0);
   2022            },
   2023            &mut |_| {},
   2024        );
   2025    }
   2026 }
   2027 
   2028 #[no_mangle]
   2029 pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
   2030    raw_data: &PerDocumentStyleData,
   2031    changed_origins: OriginFlags,
   2032 ) {
   2033    let mut data = raw_data.borrow_mut();
   2034    data.stylist
   2035        .force_stylesheet_origins_dirty(OriginSet::from(changed_origins));
   2036 }
   2037 
   2038 #[no_mangle]
   2039 pub extern "C" fn Servo_StyleSet_SetAuthorStyleDisabled(
   2040    raw_data: &PerDocumentStyleData,
   2041    author_style_disabled: bool,
   2042 ) {
   2043    let mut data = raw_data.borrow_mut();
   2044    let enabled = if author_style_disabled {
   2045        AuthorStylesEnabled::No
   2046    } else {
   2047        AuthorStylesEnabled::Yes
   2048    };
   2049    data.stylist.set_author_styles_enabled(enabled);
   2050 }
   2051 
   2052 #[no_mangle]
   2053 pub extern "C" fn Servo_StyleSet_UsesFontMetrics(raw_data: &PerDocumentStyleData) -> bool {
   2054    let doc_data = raw_data;
   2055    doc_data.borrow().stylist.device().used_font_metrics()
   2056 }
   2057 
   2058 #[no_mangle]
   2059 pub extern "C" fn Servo_StyleSet_UsesRootFontMetrics(raw_data: &PerDocumentStyleData) -> bool {
   2060    let doc_data = raw_data;
   2061    doc_data.borrow().stylist.device().used_root_font_metrics()
   2062 }
   2063 
   2064 #[no_mangle]
   2065 pub extern "C" fn Servo_StyleSheet_HasRules(raw_contents: &StylesheetContents) -> bool {
   2066    let global_style_data = &*GLOBAL_STYLE_DATA;
   2067    let guard = global_style_data.shared_lock.read();
   2068    !raw_contents.rules.read_with(&guard).0.is_empty()
   2069 }
   2070 
   2071 #[no_mangle]
   2072 pub extern "C" fn Servo_StyleSheet_UseCounters(raw_contents: &StylesheetContents) -> &UseCounters {
   2073    &raw_contents.use_counters
   2074 }
   2075 
   2076 #[no_mangle]
   2077 pub extern "C" fn Servo_StyleSheet_GetRules(sheet: &StylesheetContents) -> Strong<LockedCssRules> {
   2078    sheet.rules.clone().into()
   2079 }
   2080 
   2081 #[no_mangle]
   2082 pub extern "C" fn Servo_StyleSheet_Clone(
   2083    contents: &StylesheetContents,
   2084    data: *mut URLExtraData,
   2085 ) -> Strong<StylesheetContents> {
   2086    let global_style_data = &*GLOBAL_STYLE_DATA;
   2087    let guard = global_style_data.shared_lock.read();
   2088    let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
   2089    contents
   2090        .deep_clone(&global_style_data.shared_lock, Some(url_data), &guard)
   2091        .into()
   2092 }
   2093 
   2094 #[no_mangle]
   2095 pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
   2096    malloc_size_of: GeckoMallocSizeOf,
   2097    malloc_enclosing_size_of: GeckoMallocSizeOf,
   2098    sheet: &StylesheetContents,
   2099 ) -> usize {
   2100    let global_style_data = &*GLOBAL_STYLE_DATA;
   2101    let guard = global_style_data.shared_lock.read();
   2102    let mut ops = MallocSizeOfOps::new(
   2103        malloc_size_of.unwrap(),
   2104        Some(malloc_enclosing_size_of.unwrap()),
   2105        None,
   2106    );
   2107    // TODO(emilio): We're not measuring the size of the Arc<StylesheetContents>
   2108    // allocation itself here.
   2109    sheet.size_of(&guard, &mut ops)
   2110 }
   2111 
   2112 #[no_mangle]
   2113 pub extern "C" fn Servo_StyleSheet_GetOrigin(sheet: &StylesheetContents) -> Origin {
   2114    sheet.origin
   2115 }
   2116 
   2117 #[no_mangle]
   2118 pub extern "C" fn Servo_StyleSheet_GetSourceMapURL(
   2119    contents: &StylesheetContents,
   2120    result: &mut nsACString,
   2121 ) {
   2122    if let Some(ref url) = contents.source_map_url {
   2123        result.assign(url);
   2124    }
   2125 }
   2126 
   2127 #[no_mangle]
   2128 pub extern "C" fn Servo_StyleSheet_GetSourceURL(
   2129    contents: &StylesheetContents,
   2130    result: &mut nsACString,
   2131 ) {
   2132    if let Some(ref url) = contents.source_url {
   2133        result.assign(url);
   2134    }
   2135 }
   2136 
   2137 fn with_maybe_worker_shared_lock<R>(func: impl FnOnce(&SharedRwLock) -> R) -> R {
   2138    if is_dom_worker_thread() {
   2139        DOM_WORKER_RWLOCK.with(func)
   2140    } else {
   2141        func(&GLOBAL_STYLE_DATA.shared_lock)
   2142    }
   2143 }
   2144 
   2145 fn read_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
   2146 where
   2147    F: FnOnce(&T) -> R,
   2148 {
   2149    debug_assert!(!is_dom_worker_thread());
   2150    let global_style_data = &*GLOBAL_STYLE_DATA;
   2151    let guard = global_style_data.shared_lock.read();
   2152    func(raw.read_with(&guard))
   2153 }
   2154 
   2155 fn read_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
   2156 where
   2157    F: FnOnce(&T) -> R,
   2158 {
   2159    with_maybe_worker_shared_lock(|lock| {
   2160        let guard = lock.read();
   2161        func(raw.read_with(&guard))
   2162    })
   2163 }
   2164 
   2165 #[cfg(debug_assertions)]
   2166 unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
   2167 where
   2168    F: FnOnce(&T) -> R,
   2169 {
   2170    debug_assert!(is_main_thread() && !is_in_servo_traversal());
   2171    read_locked_arc(raw, func)
   2172 }
   2173 
   2174 #[cfg(not(debug_assertions))]
   2175 unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
   2176 where
   2177    F: FnOnce(&T) -> R,
   2178 {
   2179    debug_assert!(!is_dom_worker_thread());
   2180    func(raw.read_unchecked())
   2181 }
   2182 
   2183 fn write_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
   2184 where
   2185    F: FnOnce(&mut T) -> R,
   2186 {
   2187    debug_assert!(!is_dom_worker_thread());
   2188    let global_style_data = &*GLOBAL_STYLE_DATA;
   2189    let mut guard = global_style_data.shared_lock.write();
   2190    func(raw.write_with(&mut guard))
   2191 }
   2192 
   2193 fn write_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
   2194 where
   2195    F: FnOnce(&mut T) -> R,
   2196 {
   2197    with_maybe_worker_shared_lock(|lock| {
   2198        let mut guard = lock.write();
   2199        func(raw.write_with(&mut guard))
   2200    })
   2201 }
   2202 
   2203 #[no_mangle]
   2204 pub extern "C" fn Servo_CssRules_GetRuleCount(rules: &LockedCssRules) -> usize {
   2205    read_locked_arc(rules, |rules| rules.0.len())
   2206 }
   2207 
   2208 #[no_mangle]
   2209 pub extern "C" fn Servo_CssRules_GetRuleTypeAt(
   2210    rules: &LockedCssRules,
   2211    index: usize,
   2212 ) -> CssRuleType {
   2213    read_locked_arc(rules, |rules| rules.0[index].rule_type())
   2214 }
   2215 
   2216 #[no_mangle]
   2217 pub extern "C" fn Servo_CssRules_ListTypes(rules: &LockedCssRules, result: &mut nsTArray<usize>) {
   2218    read_locked_arc(rules, |rules: &CssRules| {
   2219        result.extend(rules.0.iter().map(|rule| rule.rule_type() as usize));
   2220    })
   2221 }
   2222 
   2223 #[no_mangle]
   2224 pub extern "C" fn Servo_CssRules_InsertRule(
   2225    rules: &LockedCssRules,
   2226    contents: &StylesheetContents,
   2227    rule: &nsACString,
   2228    index: u32,
   2229    containing_rule_types: u32,
   2230    parse_relative_rule_type: Option<&CssRuleType>,
   2231    loader: *mut Loader,
   2232    allow_import_rules: AllowImportRules,
   2233    gecko_stylesheet: *mut DomStyleSheet,
   2234    rule_type: &mut CssRuleType,
   2235 ) -> nsresult {
   2236    let loader = if loader.is_null() {
   2237        None
   2238    } else {
   2239        Some(StylesheetLoader::new(
   2240            loader,
   2241            gecko_stylesheet,
   2242            ptr::null_mut(),
   2243            ptr::null_mut(),
   2244        ))
   2245    };
   2246    let loader = loader
   2247        .as_ref()
   2248        .map(|loader| loader as &dyn StyleStylesheetLoader);
   2249    let rule = unsafe { rule.as_str_unchecked() };
   2250 
   2251    let global_style_data = &*GLOBAL_STYLE_DATA;
   2252    let index = index as usize;
   2253    let new_rule = {
   2254        let guard = global_style_data.shared_lock.read();
   2255        match rules.read_with(&guard).parse_rule_for_insert(
   2256            &global_style_data.shared_lock,
   2257            rule,
   2258            contents,
   2259            index,
   2260            CssRuleTypes::from_bits(containing_rule_types),
   2261            parse_relative_rule_type.cloned(),
   2262            loader,
   2263            allow_import_rules,
   2264        ) {
   2265            Ok(r) => r,
   2266            Err(e) => return e.into(),
   2267        }
   2268    };
   2269 
   2270    let mut write_guard = global_style_data.shared_lock.write();
   2271    *rule_type = new_rule.rule_type();
   2272    rules.write_with(&mut write_guard).0.insert(index, new_rule);
   2273    nsresult::NS_OK
   2274 }
   2275 
   2276 #[no_mangle]
   2277 pub extern "C" fn Servo_CssRules_DeleteRule(rules: &LockedCssRules, index: u32) -> nsresult {
   2278    write_locked_arc(rules, |rules: &mut CssRules| {
   2279        match rules.remove_rule(index as usize) {
   2280            Ok(_) => nsresult::NS_OK,
   2281            Err(err) => err.into(),
   2282        }
   2283    })
   2284 }
   2285 
   2286 trait MaybeLocked<Target> {
   2287    fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a Target;
   2288 }
   2289 
   2290 impl<T> MaybeLocked<T> for T {
   2291    fn maybe_locked_read<'a>(&'a self, _: &'a SharedRwLockReadGuard) -> &'a T {
   2292        self
   2293    }
   2294 }
   2295 
   2296 impl<T> MaybeLocked<T> for Locked<T> {
   2297    fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {
   2298        self.read_with(guard)
   2299    }
   2300 }
   2301 
   2302 macro_rules! impl_basic_rule_funcs_without_getter {
   2303    {
   2304        ($rule_type:ty, $maybe_locked_rule_type:ty),
   2305        debug: $debug:ident,
   2306        to_css: $to_css:ident,
   2307    } => {
   2308        #[cfg(debug_assertions)]
   2309        #[no_mangle]
   2310        pub extern "C" fn $debug(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
   2311            let global_style_data = &*GLOBAL_STYLE_DATA;
   2312            let guard = global_style_data.shared_lock.read();
   2313            let rule: &$rule_type = rule.maybe_locked_read(&guard);
   2314            write!(result, "{:?}", *rule).unwrap();
   2315        }
   2316 
   2317        #[cfg(not(debug_assertions))]
   2318        #[no_mangle]
   2319        pub extern "C" fn $debug(_: &$maybe_locked_rule_type, _: &mut nsACString) {
   2320            unreachable!()
   2321        }
   2322 
   2323        #[no_mangle]
   2324        pub extern "C" fn $to_css(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
   2325            let global_style_data = &*GLOBAL_STYLE_DATA;
   2326            let guard = global_style_data.shared_lock.read();
   2327            let rule: &$rule_type = rule.maybe_locked_read(&guard);
   2328            rule.to_css(&guard, result).unwrap();
   2329        }
   2330    }
   2331 }
   2332 
   2333 macro_rules! impl_basic_rule_funcs {
   2334    { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
   2335        getter: $getter:ident,
   2336        debug: $debug:ident,
   2337        to_css: $to_css:ident,
   2338        changed: $changed:ident,
   2339    } => {
   2340        #[no_mangle]
   2341        pub extern "C" fn $getter(
   2342            rules: &LockedCssRules,
   2343            index: u32,
   2344            line: &mut u32,
   2345            column: &mut u32,
   2346        ) -> Strong<$maybe_locked_rule_type> {
   2347            let global_style_data = &*GLOBAL_STYLE_DATA;
   2348            let guard = global_style_data.shared_lock.read();
   2349            let rules = rules.read_with(&guard);
   2350            let index = index as usize;
   2351 
   2352            if index >= rules.0.len() {
   2353                return Strong::null();
   2354            }
   2355 
   2356            match rules.0[index] {
   2357                CssRule::$name(ref arc) => {
   2358                    let rule: &$rule_type = (&**arc).maybe_locked_read(&guard);
   2359                    let location = rule.source_location;
   2360                    *line = location.line as u32;
   2361                    *column = location.column as u32;
   2362                    arc.clone().into()
   2363                },
   2364                _ => {
   2365                    Strong::null()
   2366                }
   2367            }
   2368        }
   2369 
   2370        #[no_mangle]
   2371        pub extern "C" fn $changed(
   2372            styleset: &PerDocumentStyleData,
   2373            rule: &$maybe_locked_rule_type,
   2374            sheet: &DomStyleSheet,
   2375            change_kind: RuleChangeKind,
   2376            ancestors: &nsTArray<CssRuleRef>,
   2377        ) {
   2378            let mut data = styleset.borrow_mut();
   2379            let data = &mut *data;
   2380            let global_style_data = &*GLOBAL_STYLE_DATA;
   2381            let guard = global_style_data.shared_lock.read();
   2382            // TODO(emilio): Would be nice not to deal with refcount bumps here,
   2383            // but it's probably not a huge deal.
   2384            let rule = unsafe { CssRule::$name(Arc::from_raw_addrefed(rule)) };
   2385            let sheet = unsafe { GeckoStyleSheet::new(sheet) };
   2386            data.stylist.rule_changed(&sheet, &rule, &guard, change_kind, ancestors.as_slice());
   2387        }
   2388 
   2389        impl_basic_rule_funcs_without_getter! {
   2390            ($rule_type, $maybe_locked_rule_type),
   2391            debug: $debug,
   2392            to_css: $to_css,
   2393        }
   2394    }
   2395 }
   2396 
   2397 macro_rules! impl_group_rule_funcs {
   2398    { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
   2399      get_rules: $get_rules:ident,
   2400      $($basic:tt)+
   2401    } => {
   2402        impl_basic_rule_funcs! { ($name, $rule_type, $maybe_locked_rule_type), $($basic)+ }
   2403 
   2404        #[no_mangle]
   2405        pub extern "C" fn $get_rules(rule: &$maybe_locked_rule_type) -> Strong<LockedCssRules> {
   2406            let global_style_data = &*GLOBAL_STYLE_DATA;
   2407            let guard = global_style_data.shared_lock.read();
   2408            let rule: &$rule_type = rule.maybe_locked_read(&guard);
   2409            rule.rules.clone().into()
   2410        }
   2411    }
   2412 }
   2413 
   2414 impl_basic_rule_funcs! { (Style, StyleRule, Locked<StyleRule>),
   2415    getter: Servo_CssRules_GetStyleRuleAt,
   2416    debug: Servo_StyleRule_Debug,
   2417    to_css: Servo_StyleRule_GetCssText,
   2418    changed: Servo_StyleSet_StyleRuleChanged,
   2419 }
   2420 
   2421 #[no_mangle]
   2422 pub extern "C" fn Servo_StyleRule_EnsureRules(
   2423    rule: &LockedStyleRule,
   2424    read_only: bool,
   2425 ) -> Strong<LockedCssRules> {
   2426    let global_style_data = &*GLOBAL_STYLE_DATA;
   2427    let lock = &global_style_data.shared_lock;
   2428    if read_only {
   2429        let guard = lock.read();
   2430        if let Some(ref rules) = rule.read_with(&guard).rules {
   2431            return rules.clone().into();
   2432        }
   2433        return CssRules::new(vec![], lock).into();
   2434    }
   2435    let mut guard = lock.write();
   2436    rule.write_with(&mut guard)
   2437        .rules
   2438        .get_or_insert_with(|| CssRules::new(vec![], lock))
   2439        .clone()
   2440        .into()
   2441 }
   2442 
   2443 impl_basic_rule_funcs! { (Import, ImportRule, Locked<ImportRule>),
   2444    getter: Servo_CssRules_GetImportRuleAt,
   2445    debug: Servo_ImportRule_Debug,
   2446    to_css: Servo_ImportRule_GetCssText,
   2447    changed: Servo_StyleSet_ImportRuleChanged,
   2448 }
   2449 
   2450 impl_basic_rule_funcs_without_getter! { (Keyframe, Locked<Keyframe>),
   2451    debug: Servo_Keyframe_Debug,
   2452    to_css: Servo_Keyframe_GetCssText,
   2453 }
   2454 
   2455 impl_basic_rule_funcs! { (Keyframes, KeyframesRule, Locked<KeyframesRule>),
   2456    getter: Servo_CssRules_GetKeyframesRuleAt,
   2457    debug: Servo_KeyframesRule_Debug,
   2458    to_css: Servo_KeyframesRule_GetCssText,
   2459    changed: Servo_StyleSet_KeyframesRuleChanged,
   2460 }
   2461 
   2462 impl_group_rule_funcs! { (Media, MediaRule, MediaRule),
   2463    get_rules: Servo_MediaRule_GetRules,
   2464    getter: Servo_CssRules_GetMediaRuleAt,
   2465    debug: Servo_MediaRule_Debug,
   2466    to_css: Servo_MediaRule_GetCssText,
   2467    changed: Servo_StyleSet_MediaRuleChanged,
   2468 }
   2469 
   2470 impl_basic_rule_funcs! { (Margin, MarginRule, MarginRule),
   2471    getter: Servo_CssRules_GetMarginRuleAt,
   2472    debug: Servo_MarginRule_Debug,
   2473    to_css: Servo_MarginRule_GetCssText,
   2474    changed: Servo_StyleSet_MarginRuleChanged,
   2475 }
   2476 
   2477 impl_basic_rule_funcs! { (CustomMedia, CustomMediaRule, CustomMediaRule),
   2478    getter: Servo_CssRules_GetCustomMediaRuleAt,
   2479    debug: Servo_CustomMediaRule_Debug,
   2480    to_css: Servo_CustomMediaRule_GetCssText,
   2481    changed: Servo_StyleSet_CustomMediaRuleChanged,
   2482 }
   2483 
   2484 impl_basic_rule_funcs! { (Namespace, NamespaceRule, NamespaceRule),
   2485    getter: Servo_CssRules_GetNamespaceRuleAt,
   2486    debug: Servo_NamespaceRule_Debug,
   2487    to_css: Servo_NamespaceRule_GetCssText,
   2488    changed: Servo_StyleSet_NamespaceRuleChanged,
   2489 }
   2490 
   2491 impl_group_rule_funcs! { (Page, PageRule, Locked<PageRule>),
   2492    get_rules: Servo_PageRule_GetRules,
   2493    getter: Servo_CssRules_GetPageRuleAt,
   2494    debug: Servo_PageRule_Debug,
   2495    to_css: Servo_PageRule_GetCssText,
   2496    changed: Servo_StyleSet_PageRuleChanged,
   2497 }
   2498 
   2499 impl_basic_rule_funcs! { (Property, PropertyRule, PropertyRule),
   2500    getter: Servo_CssRules_GetPropertyRuleAt,
   2501    debug: Servo_PropertyRule_Debug,
   2502    to_css: Servo_PropertyRule_GetCssText,
   2503    changed: Servo_StyleSet_PropertyRuleChanged,
   2504 }
   2505 
   2506 impl_group_rule_funcs! { (Supports, SupportsRule, SupportsRule),
   2507    get_rules: Servo_SupportsRule_GetRules,
   2508    getter: Servo_CssRules_GetSupportsRuleAt,
   2509    debug: Servo_SupportsRule_Debug,
   2510    to_css: Servo_SupportsRule_GetCssText,
   2511    changed: Servo_StyleSet_SupportsRuleChanged,
   2512 }
   2513 
   2514 impl_group_rule_funcs! { (Container, ContainerRule, ContainerRule),
   2515    get_rules: Servo_ContainerRule_GetRules,
   2516    getter: Servo_CssRules_GetContainerRuleAt,
   2517    debug: Servo_ContainerRule_Debug,
   2518    to_css: Servo_ContainerRule_GetCssText,
   2519    changed: Servo_StyleSet_ContainerRuleChanged,
   2520 }
   2521 
   2522 impl_group_rule_funcs! { (LayerBlock, LayerBlockRule, LayerBlockRule),
   2523    get_rules: Servo_LayerBlockRule_GetRules,
   2524    getter: Servo_CssRules_GetLayerBlockRuleAt,
   2525    debug: Servo_LayerBlockRule_Debug,
   2526    to_css: Servo_LayerBlockRule_GetCssText,
   2527    changed: Servo_StyleSet_LayerBlockRuleChanged,
   2528 }
   2529 
   2530 impl_basic_rule_funcs! { (LayerStatement, LayerStatementRule, LayerStatementRule),
   2531    getter: Servo_CssRules_GetLayerStatementRuleAt,
   2532    debug: Servo_LayerStatementRule_Debug,
   2533    to_css: Servo_LayerStatementRule_GetCssText,
   2534    changed: Servo_StyleSet_LayerStatementRuleChanged,
   2535 }
   2536 
   2537 impl_group_rule_funcs! { (Document, DocumentRule, DocumentRule),
   2538    get_rules: Servo_DocumentRule_GetRules,
   2539    getter: Servo_CssRules_GetDocumentRuleAt,
   2540    debug: Servo_DocumentRule_Debug,
   2541    to_css: Servo_DocumentRule_GetCssText,
   2542    changed: Servo_StyleSet_DocumentRuleChanged,
   2543 }
   2544 
   2545 impl_basic_rule_funcs! { (FontFeatureValues, FontFeatureValuesRule, FontFeatureValuesRule),
   2546    getter: Servo_CssRules_GetFontFeatureValuesRuleAt,
   2547    debug: Servo_FontFeatureValuesRule_Debug,
   2548    to_css: Servo_FontFeatureValuesRule_GetCssText,
   2549    changed: Servo_StyleSet_FontFeatureValuesRuleChanged,
   2550 }
   2551 
   2552 impl_basic_rule_funcs! { (FontPaletteValues, FontPaletteValuesRule, FontPaletteValuesRule),
   2553    getter: Servo_CssRules_GetFontPaletteValuesRuleAt,
   2554    debug: Servo_FontPaletteValuesRule_Debug,
   2555    to_css: Servo_FontPaletteValuesRule_GetCssText,
   2556    changed: Servo_StyleSet_FontPaletteValuesRuleChanged,
   2557 }
   2558 
   2559 impl_basic_rule_funcs! { (FontFace, FontFaceRule, Locked<FontFaceRule>),
   2560    getter: Servo_CssRules_GetFontFaceRuleAt,
   2561    debug: Servo_FontFaceRule_Debug,
   2562    to_css: Servo_FontFaceRule_GetCssText,
   2563    changed: Servo_StyleSet_FontFaceRuleChanged,
   2564 }
   2565 
   2566 impl_basic_rule_funcs! { (CounterStyle, CounterStyleRule, Locked<CounterStyleRule>),
   2567    getter: Servo_CssRules_GetCounterStyleRuleAt,
   2568    debug: Servo_CounterStyleRule_Debug,
   2569    to_css: Servo_CounterStyleRule_GetCssText,
   2570    changed: Servo_StyleSet_CounterStyleRuleChanged,
   2571 }
   2572 
   2573 impl_group_rule_funcs! { (Scope, ScopeRule, ScopeRule),
   2574    get_rules: Servo_ScopeRule_GetRules,
   2575    getter: Servo_CssRules_GetScopeRuleAt,
   2576    debug: Servo_ScopeRule_Debug,
   2577    to_css: Servo_ScopeRule_GetCssText,
   2578    changed: Servo_StyleSet_ScopeRuleChanged,
   2579 }
   2580 
   2581 impl_group_rule_funcs! { (StartingStyle, StartingStyleRule, StartingStyleRule),
   2582    get_rules: Servo_StartingStyleRule_GetRules,
   2583    getter: Servo_CssRules_GetStartingStyleRuleAt,
   2584    debug: Servo_StartingStyleRule_Debug,
   2585    to_css: Servo_StartingStyleRule_GetCssText,
   2586    changed: Servo_StyleSet_StartingStyleRuleChanged,
   2587 }
   2588 
   2589 impl_basic_rule_funcs! { (PositionTry, PositionTryRule, Locked<PositionTryRule>),
   2590    getter: Servo_CssRules_GetPositionTryRuleAt,
   2591    debug: Servo_PositionTryRule_Debug,
   2592    to_css: Servo_PositionTryRule_GetCssText,
   2593    changed: Servo_StyleSet_PositionTryRuleChanged,
   2594 }
   2595 
   2596 impl_basic_rule_funcs! { (NestedDeclarations, NestedDeclarationsRule, Locked<NestedDeclarationsRule>),
   2597    getter: Servo_CssRules_GetNestedDeclarationsRuleAt,
   2598    debug: Servo_NestedDeclarationsRule_Debug,
   2599    to_css: Servo_NestedDeclarationsRule_GetCssText,
   2600    changed: Servo_StyleSet_NestedDeclarationsRuleChanged,
   2601 }
   2602 
   2603 #[no_mangle]
   2604 pub extern "C" fn Servo_NestedDeclarationsRule_GetStyle(
   2605    rule: &LockedNestedDeclarationsRule,
   2606 ) -> Strong<LockedDeclarationBlock> {
   2607    read_locked_arc(rule, |rule: &NestedDeclarationsRule| {
   2608        rule.block.clone().into()
   2609    })
   2610 }
   2611 
   2612 #[no_mangle]
   2613 pub extern "C" fn Servo_NestedDeclarationsRule_SetStyle(
   2614    rule: &LockedNestedDeclarationsRule,
   2615    declarations: &LockedDeclarationBlock,
   2616 ) {
   2617    write_locked_arc(rule, |rule: &mut NestedDeclarationsRule| {
   2618        rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
   2619    })
   2620 }
   2621 
   2622 #[no_mangle]
   2623 pub extern "C" fn Servo_StyleRule_GetStyle(
   2624    rule: &LockedStyleRule,
   2625 ) -> Strong<LockedDeclarationBlock> {
   2626    read_locked_arc(rule, |rule: &StyleRule| rule.block.clone().into())
   2627 }
   2628 
   2629 #[no_mangle]
   2630 pub extern "C" fn Servo_StyleRule_SetStyle(
   2631    rule: &LockedStyleRule,
   2632    declarations: &LockedDeclarationBlock,
   2633 ) {
   2634    write_locked_arc(rule, |rule: &mut StyleRule| {
   2635        rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
   2636    })
   2637 }
   2638 
   2639 #[no_mangle]
   2640 pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: &LockedStyleRule, result: &mut nsACString) {
   2641    read_locked_arc(rule, |rule| rule.selectors.to_css(result).unwrap());
   2642 }
   2643 
   2644 fn desugared_selector_list(rules: &[&LockedStyleRule]) -> SelectorList {
   2645    let mut selectors: Option<SelectorList> = None;
   2646    for rule in rules.iter().rev() {
   2647        selectors = Some(read_locked_arc(rule, |rule| match selectors {
   2648            Some(ref s) => rule.selectors.replace_parent_selector(s),
   2649            None => rule.selectors.clone(),
   2650        }));
   2651    }
   2652    selectors.expect("Empty rule chain?")
   2653 }
   2654 
   2655 #[no_mangle]
   2656 pub extern "C" fn Servo_StyleRule_GetSelectorList(
   2657    rules: &nsTArray<&LockedStyleRule>,
   2658 ) -> *mut SelectorList {
   2659    Box::into_raw(Box::new(desugared_selector_list(rules)))
   2660 }
   2661 
   2662 #[no_mangle]
   2663 pub extern "C" fn Servo_StyleRule_GetSelectorDataAtIndex(
   2664    rules: &nsTArray<&LockedStyleRule>,
   2665    index: u32,
   2666    text: Option<&mut nsACString>,
   2667    specificity: Option<&mut u64>,
   2668 ) {
   2669    let selectors = desugared_selector_list(rules);
   2670    let Some(selector) = selectors.slice().get(index as usize) else {
   2671        return;
   2672    };
   2673    if let Some(text) = text {
   2674        selector.to_css(text).unwrap();
   2675    }
   2676    if let Some(specificity) = specificity {
   2677        *specificity = selector.specificity() as u64;
   2678    }
   2679 }
   2680 
   2681 #[no_mangle]
   2682 pub extern "C" fn Servo_StyleRule_GetSelectorCount(rule: &LockedStyleRule) -> u32 {
   2683    read_locked_arc(rule, |rule| rule.selectors.len() as u32)
   2684 }
   2685 
   2686 struct DesugaredScopeData {
   2687    conditions: SmallVec<[ScopeConditionReference; 2]>,
   2688    subject_map: ScopeSubjectMap,
   2689 }
   2690 
   2691 fn desugared_selector_list_with_scope(
   2692    quirks_mode: QuirksMode,
   2693    rules: &[&LockedStyleRule],
   2694    scope_data: &nsTArray<ScopeRuleData>,
   2695 ) -> (SelectorList, Option<DesugaredScopeData>) {
   2696    if scope_data.is_empty() {
   2697        return (desugared_selector_list(rules), None);
   2698    }
   2699 
   2700    fn desugared_scope_rule(
   2701        quirks_mode: QuirksMode,
   2702        id: u16,
   2703        scope_data: &ScopeRuleData,
   2704        selectors: &[&LockedStyleRule],
   2705        subject_map: &mut ScopeSubjectMap,
   2706    ) -> ScopeConditionReference {
   2707        debug_assert!(id > 0, "ID corresponds to none?");
   2708        let (start, end) = unsafe {
   2709            let rule = scope_data
   2710                .scope_rule
   2711                .as_ref()
   2712                .expect("Ill-formed scope data?");
   2713            (
   2714                if selectors.is_empty() {
   2715                    rule.bounds.start.clone()
   2716                } else {
   2717                    let desugared = desugared_selector_list(selectors);
   2718                    rule.bounds
   2719                        .start
   2720                        .as_ref()
   2721                        .map(|s| s.replace_parent_selector(&desugared))
   2722                },
   2723                rule.bounds
   2724                    .end
   2725                    .as_ref()
   2726                    .map(|s| replace_parent_selector_with_implicit_scope(s)),
   2727            )
   2728        };
   2729        // Building the subject map is likey worth it, because we traverse up as much as possible to gather all possible candidates.
   2730        start
   2731            .as_ref()
   2732            .map(|s| subject_map.add_bound_start(s, quirks_mode));
   2733        ScopeConditionReference::new(
   2734            ScopeConditionId::new(id - 1),
   2735            // Don't bother with hashing - we don't compute ancestor hashes anyway.
   2736            Some(ScopeBoundsWithHashes::new_no_hash(start, end)),
   2737            unsafe { GeckoStyleSheet::new(scope_data.sheet).implicit_scope_root() }
   2738                .unwrap_or(ImplicitScopeRoot::DocumentElement),
   2739            false,
   2740        )
   2741    }
   2742 
   2743    let innermost_scope_valid_til = scope_data[0].valid_til;
   2744    // Take style rules up to the innermost scope rule, and desugar it.
   2745    let desugared_style = desugared_selector_list(if innermost_scope_valid_til > rules.len() - 1 {
   2746        &rules[0..]
   2747    } else {
   2748        &rules[0..=innermost_scope_valid_til]
   2749    });
   2750 
   2751    // Now, desugar the scope rule(s) with any intervening nested style rule(s).
   2752    let mut conditions = SmallVec::with_capacity(scope_data.len());
   2753    let mut subject_map = ScopeSubjectMap::default();
   2754    conditions.push(ScopeConditionReference::none());
   2755    let mut end_at = rules.len() - 1;
   2756    for data in scope_data.iter().rev() {
   2757        conditions.push(desugared_scope_rule(
   2758            quirks_mode,
   2759            conditions.len() as u16,
   2760            data,
   2761            &rules[data.valid_til + 1..=end_at],
   2762            &mut subject_map,
   2763        ));
   2764        end_at = data.valid_til;
   2765    }
   2766 
   2767    (
   2768        desugared_style,
   2769        Some(DesugaredScopeData {
   2770            conditions,
   2771            subject_map,
   2772        }),
   2773    )
   2774 }
   2775 
   2776 /// Additional scope rule data for matching a style rule.
   2777 #[repr(C)]
   2778 pub struct ScopeRuleData {
   2779    /// Scope rule applicable at this nesting level.
   2780    pub scope_rule: *const ScopeRule,
   2781    /// Stylesheet this scope rule comes from.
   2782    pub sheet: *const DomStyleSheet,
   2783    /// Index to the last style rule in an array this scope applies to.
   2784    pub valid_til: usize,
   2785 }
   2786 
   2787 fn selector_matches_element<F, R>(
   2788    rules: &nsTArray<&LockedStyleRule>,
   2789    scope_rules: &nsTArray<ScopeRuleData>,
   2790    element: GeckoElement,
   2791    index: u32,
   2792    host: Option<&RawGeckoElement>,
   2793    pseudo_type: PseudoStyleType,
   2794    pseudo_id: *const nsAtom,
   2795    relevant_link_visited: bool,
   2796    on_match: F,
   2797 ) -> Option<R>
   2798 where
   2799    F: FnOnce(Option<&ScopeRootCandidate>) -> R,
   2800 {
   2801    use selectors::matching::{
   2802        matches_selector, IncludeStartingStyle, MatchingContext, MatchingMode, NeedsSelectorFlags,
   2803        VisitedHandlingMode,
   2804    };
   2805 
   2806    let quirks_mode = element.as_node().owner_doc().quirks_mode();
   2807    let (selectors, scopes) = desugared_selector_list_with_scope(quirks_mode, rules, scope_rules);
   2808    let Some(selector) = selectors.slice().get(index as usize) else {
   2809        return None;
   2810    };
   2811    let mut matching_mode = MatchingMode::Normal;
   2812    let pseudo_id = if pseudo_id.is_null() {
   2813        None
   2814    } else {
   2815        Some(AtomIdent::new(unsafe {
   2816            Atom::from_raw(pseudo_id as *mut nsAtom)
   2817        }))
   2818    };
   2819    match PseudoElement::from_pseudo_type(pseudo_type, pseudo_id) {
   2820        Some(pseudo) => {
   2821            // We need to make sure that the requested pseudo element type
   2822            // matches the selector pseudo element type before proceeding.
   2823            let selector_pseudo = selector.pseudo_element()?;
   2824            // The element here is used to get the active view transition (for
   2825            // view-transition-class), so passing the originating element is fine here.
   2826            if !pseudo.matches(selector_pseudo, &element) {
   2827                return None;
   2828            }
   2829            matching_mode = MatchingMode::ForStatelessPseudoElement;
   2830        },
   2831        None => {
   2832            // Do not attempt to match if a pseudo element is requested and
   2833            // this is not a pseudo element selector, or vice versa.
   2834            if selector.has_pseudo_element() {
   2835                return None;
   2836            }
   2837        },
   2838    };
   2839 
   2840    let host = host.map(GeckoElement);
   2841    let mut selector_caches = SelectorCaches::default();
   2842    let visited_mode = if relevant_link_visited {
   2843        VisitedHandlingMode::RelevantLinkVisited
   2844    } else {
   2845        VisitedHandlingMode::AllLinksUnvisited
   2846    };
   2847    let mut ctx = MatchingContext::new_for_visited(
   2848        matching_mode,
   2849        /* bloom_filter = */ None,
   2850        &mut selector_caches,
   2851        visited_mode,
   2852        IncludeStartingStyle::No,
   2853        quirks_mode,
   2854        NeedsSelectorFlags::No,
   2855        MatchingForInvalidation::No,
   2856    );
   2857    ctx.with_shadow_host(host, |ctx| match scopes.as_ref() {
   2858        None => matches_selector(selector, 0, None, &element, ctx).then(|| on_match(None)),
   2859        Some(s) => {
   2860            let id = ScopeConditionId::new((s.conditions.len() - 1) as u16);
   2861            let candidates = scope_root_candidates(
   2862                &s.conditions,
   2863                id,
   2864                &element,
   2865                selector.is_part(),
   2866                &s.subject_map,
   2867                ctx,
   2868            );
   2869            let scope_root = candidates.candidates.iter().find_map(|candidate| {
   2870                ctx.nest_for_scope(Some(candidate.root), |ctx| {
   2871                    matches_selector(selector, 0, None, &element, ctx).then(|| candidate)
   2872                })
   2873            });
   2874            scope_root.map(|root| on_match(Some(root)))
   2875        },
   2876    })
   2877 }
   2878 
   2879 #[no_mangle]
   2880 pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(
   2881    rules: &nsTArray<&LockedStyleRule>,
   2882    scope_rules: &nsTArray<ScopeRuleData>,
   2883    element: &RawGeckoElement,
   2884    index: u32,
   2885    host: Option<&RawGeckoElement>,
   2886    pseudo_type: PseudoStyleType,
   2887    pseudo_id: *const nsAtom,
   2888    relevant_link_visited: bool,
   2889 ) -> bool {
   2890    selector_matches_element(
   2891        rules,
   2892        scope_rules,
   2893        GeckoElement(element),
   2894        index,
   2895        host,
   2896        pseudo_type,
   2897        pseudo_id,
   2898        relevant_link_visited,
   2899        |_| true,
   2900    )
   2901    .unwrap_or(false)
   2902 }
   2903 
   2904 #[no_mangle]
   2905 pub extern "C" fn Servo_StyleRule_GetScopeRootFor(
   2906    rules: &nsTArray<&LockedStyleRule>,
   2907    scope_rules: &nsTArray<ScopeRuleData>,
   2908    element: &RawGeckoElement,
   2909    index: u32,
   2910    host: Option<&RawGeckoElement>,
   2911    pseudo_type: PseudoStyleType,
   2912    pseudo_id: *const nsAtom,
   2913    relevant_link_visited: bool,
   2914 ) -> *const RawGeckoElement {
   2915    let element = GeckoElement(element);
   2916    selector_matches_element(
   2917        rules,
   2918        scope_rules,
   2919        element,
   2920        index,
   2921        host,
   2922        pseudo_type,
   2923        pseudo_id,
   2924        relevant_link_visited,
   2925        |candidate| {
   2926            candidate
   2927                .map(|c| c.get_scope_root_element(element))
   2928                .flatten()
   2929        },
   2930    )
   2931    .flatten()
   2932    .map_or(ptr::null(), |e| e.0)
   2933 }
   2934 
   2935 pub type SelectorList = selectors::SelectorList<style::gecko::selector_parser::SelectorImpl>;
   2936 
   2937 #[no_mangle]
   2938 pub extern "C" fn Servo_StyleRule_SetSelectorText(
   2939    contents: &StylesheetContents,
   2940    rule: &LockedStyleRule,
   2941    text: &nsACString,
   2942    parse_relative_rule_type: Option<&CssRuleType>,
   2943 ) -> bool {
   2944    let value_str = unsafe { text.as_str_unchecked() };
   2945 
   2946    write_locked_arc(rule, |rule: &mut StyleRule| {
   2947        use selectors::parser::ParseRelative;
   2948        use style::selector_parser::SelectorParser;
   2949 
   2950        let namespaces = &contents.namespaces;
   2951        let url_data = &contents.url_data;
   2952        let parser = SelectorParser {
   2953            stylesheet_origin: contents.origin,
   2954            namespaces: &namespaces,
   2955            url_data: &url_data,
   2956            for_supports_rule: false,
   2957        };
   2958 
   2959        let parse_relative = match parse_relative_rule_type {
   2960            Some(CssRuleType::Style) => ParseRelative::ForNesting,
   2961            Some(CssRuleType::Scope) => ParseRelative::ForScope,
   2962            _ => ParseRelative::No,
   2963        };
   2964        let mut parser_input = ParserInput::new(&value_str);
   2965        match SelectorList::parse(&parser, &mut Parser::new(&mut parser_input), parse_relative) {
   2966            Ok(selectors) => {
   2967                rule.selectors = selectors;
   2968                true
   2969            },
   2970            Err(_) => false,
   2971        }
   2972    })
   2973 }
   2974 
   2975 #[no_mangle]
   2976 pub unsafe extern "C" fn Servo_SelectorList_Closest(
   2977    element: &RawGeckoElement,
   2978    selectors: &SelectorList,
   2979 ) -> *const RawGeckoElement {
   2980    use style::dom_apis;
   2981 
   2982    let element = GeckoElement(element);
   2983    let quirks_mode = element.as_node().owner_doc().quirks_mode();
   2984    dom_apis::element_closest(element, &selectors, quirks_mode).map_or(ptr::null(), |e| e.0)
   2985 }
   2986 
   2987 #[no_mangle]
   2988 pub unsafe extern "C" fn Servo_SelectorList_Matches(
   2989    element: &RawGeckoElement,
   2990    selectors: &SelectorList,
   2991 ) -> bool {
   2992    use style::dom_apis;
   2993 
   2994    let element = GeckoElement(element);
   2995    let quirks_mode = element.as_node().owner_doc().quirks_mode();
   2996    dom_apis::element_matches(&element, &selectors, quirks_mode)
   2997 }
   2998 
   2999 #[no_mangle]
   3000 pub unsafe extern "C" fn Servo_SelectorList_QueryFirst(
   3001    node: &RawGeckoNode,
   3002    selectors: &SelectorList,
   3003    may_use_invalidation: bool,
   3004 ) -> *const RawGeckoElement {
   3005    use style::dom_apis::{self, MayUseInvalidation, QueryFirst};
   3006 
   3007    let node = GeckoNode(node);
   3008    let mut result = None;
   3009 
   3010    let may_use_invalidation = if may_use_invalidation {
   3011        MayUseInvalidation::Yes
   3012    } else {
   3013        MayUseInvalidation::No
   3014    };
   3015 
   3016    dom_apis::query_selector::<GeckoElement, QueryFirst>(
   3017        node,
   3018        &selectors,
   3019        &mut result,
   3020        may_use_invalidation,
   3021    );
   3022 
   3023    result.map_or(ptr::null(), |e| e.0)
   3024 }
   3025 
   3026 #[no_mangle]
   3027 pub unsafe extern "C" fn Servo_SelectorList_QueryAll(
   3028    node: &RawGeckoNode,
   3029    selectors: &SelectorList,
   3030    content_list: *mut structs::nsSimpleContentList,
   3031    may_use_invalidation: bool,
   3032 ) {
   3033    use style::dom_apis::{self, MayUseInvalidation, QueryAll};
   3034 
   3035    let node = GeckoNode(node);
   3036    let mut result = SmallVec::new();
   3037 
   3038    let may_use_invalidation = if may_use_invalidation {
   3039        MayUseInvalidation::Yes
   3040    } else {
   3041        MayUseInvalidation::No
   3042    };
   3043 
   3044    dom_apis::query_selector::<GeckoElement, QueryAll>(
   3045        node,
   3046        &selectors,
   3047        &mut result,
   3048        may_use_invalidation,
   3049    );
   3050 
   3051    if !result.is_empty() {
   3052        // NOTE(emilio): This relies on a slice of GeckoElement having the same
   3053        // memory representation than a slice of element pointers.
   3054        bindings::Gecko_ContentList_AppendAll(
   3055            content_list,
   3056            result.as_ptr() as *mut *const _,
   3057            result.len(),
   3058        )
   3059    }
   3060 }
   3061 
   3062 #[no_mangle]
   3063 pub unsafe extern "C" fn Servo_SelectorList_QueryAllWithScope(
   3064    node: &RawGeckoNode,
   3065    rules: &nsTArray<&LockedStyleRule>,
   3066    scope_rules: &nsTArray<ScopeRuleData>,
   3067    content_list: *mut structs::nsSimpleContentList,
   3068 ) {
   3069    let root = GeckoNode(node);
   3070    let quirks_mode = root.owner_doc().quirks_mode();
   3071    let (selectors, scopes) = desugared_selector_list_with_scope(quirks_mode, rules, scope_rules);
   3072    // This replicates the slow path of `querySelectorAll`.
   3073    // TODO(dshin): A tigher integration could be nicer - but it'd be for
   3074    // a very specific (DevTools-only) use case, since normal `querySelectorAll`
   3075    // JS calls can't use @scope.
   3076    let mut selector_caches = SelectorCaches::default();
   3077    let mut ctx = MatchingContext::new(
   3078        MatchingMode::Normal,
   3079        /* bloom_filter = */ None,
   3080        &mut selector_caches,
   3081        quirks_mode,
   3082        NeedsSelectorFlags::No,
   3083        MatchingForInvalidation::No,
   3084    );
   3085    let root_element = root.as_element();
   3086    ctx.current_host = match root_element {
   3087        Some(root) => root.containing_shadow_host().map(|host| host.opaque()),
   3088        None => root.as_shadow_root().map(|root| root.host().opaque()),
   3089    };
   3090    let mut result: SmallVec<[GeckoElement; 128]> = Default::default();
   3091    let may_match_shadow_host_for_part = selectors.slice().iter().any(|s| s.is_part());
   3092    for node in root.dom_descendants() {
   3093        let Some(element) = node.as_element() else {
   3094            continue;
   3095        };
   3096        let matches = match scopes.as_ref() {
   3097            None => matches_selector_list(&selectors, &element, &mut ctx),
   3098            Some(s) => {
   3099                let id = ScopeConditionId::new((s.conditions.len() - 1) as u16);
   3100                let candidates = scope_root_candidates(
   3101                    &s.conditions,
   3102                    id,
   3103                    &element,
   3104                    may_match_shadow_host_for_part,
   3105                    &s.subject_map,
   3106                    &mut ctx,
   3107                );
   3108                candidates.candidates.iter().any(|candidate| {
   3109                    ctx.nest_for_scope(Some(candidate.root), |ctx| {
   3110                        matches_selector_list(&selectors, &element, ctx)
   3111                    })
   3112                })
   3113            },
   3114        };
   3115        if matches {
   3116            result.push(element);
   3117        }
   3118    }
   3119 
   3120    if !result.is_empty() {
   3121        // NOTE(emilio): This relies on a slice of GeckoElement having the same
   3122        // memory representation than a slice of element pointers.
   3123        bindings::Gecko_ContentList_AppendAll(
   3124            content_list,
   3125            result.as_ptr() as *mut *const _,
   3126            result.len(),
   3127        )
   3128    }
   3129 }
   3130 
   3131 #[no_mangle]
   3132 pub extern "C" fn Servo_ImportRule_GetHref(rule: &LockedImportRule, result: &mut nsAString) {
   3133    read_locked_arc(rule, |rule: &ImportRule| {
   3134        write!(result, "{}", rule.url.as_str()).unwrap();
   3135    })
   3136 }
   3137 
   3138 #[no_mangle]
   3139 pub extern "C" fn Servo_ImportRule_GetLayerName(rule: &LockedImportRule, result: &mut nsACString) {
   3140    // https://w3c.github.io/csswg-drafts/cssom/#dom-cssimportrule-layername
   3141    read_locked_arc(rule, |rule: &ImportRule| match rule.layer {
   3142        ImportLayer::Named(ref name) => name.to_css(&mut CssWriter::new(result)).unwrap(), // "return the layer name declared in the at-rule itself"
   3143        ImportLayer::Anonymous => {}, // "or an empty string if the layer is anonymous"
   3144        ImportLayer::None => result.set_is_void(true), // "or null if the at-rule does not declare a layer"
   3145    })
   3146 }
   3147 
   3148 #[no_mangle]
   3149 pub extern "C" fn Servo_ImportRule_GetSupportsText(
   3150    rule: &LockedImportRule,
   3151    result: &mut nsACString,
   3152 ) {
   3153    read_locked_arc(rule, |rule: &ImportRule| match rule.supports {
   3154        Some(ref supports) => supports
   3155            .condition
   3156            .to_css(&mut CssWriter::new(result))
   3157            .unwrap(),
   3158        None => result.set_is_void(true),
   3159    })
   3160 }
   3161 
   3162 #[no_mangle]
   3163 pub extern "C" fn Servo_ImportRule_GetSheet(rule: &LockedImportRule) -> *const DomStyleSheet {
   3164    read_locked_arc(rule, |rule: &ImportRule| {
   3165        rule.stylesheet
   3166            .as_sheet()
   3167            .map_or(ptr::null(), |s| s.raw() as *const DomStyleSheet)
   3168    })
   3169 }
   3170 
   3171 #[no_mangle]
   3172 pub unsafe extern "C" fn Servo_ImportRule_SetSheet(
   3173    rule: &LockedImportRule,
   3174    sheet: *mut DomStyleSheet,
   3175 ) {
   3176    write_locked_arc(rule, |rule: &mut ImportRule| {
   3177        rule.stylesheet = ImportSheet::new(GeckoStyleSheet::new(sheet));
   3178    })
   3179 }
   3180 
   3181 #[no_mangle]
   3182 pub extern "C" fn Servo_Keyframe_GetKeyText(keyframe: &LockedKeyframe, result: &mut nsACString) {
   3183    read_locked_arc(keyframe, |keyframe: &Keyframe| {
   3184        keyframe
   3185            .selector
   3186            .to_css(&mut CssWriter::new(result))
   3187            .unwrap()
   3188    })
   3189 }
   3190 
   3191 #[no_mangle]
   3192 pub extern "C" fn Servo_Keyframe_SetKeyText(keyframe: &LockedKeyframe, text: &nsACString) -> bool {
   3193    let text = unsafe { text.as_str_unchecked() };
   3194    let mut input = ParserInput::new(&text);
   3195    if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) {
   3196        write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
   3197            keyframe.selector = selector;
   3198        });
   3199        true
   3200    } else {
   3201        false
   3202    }
   3203 }
   3204 
   3205 #[no_mangle]
   3206 pub extern "C" fn Servo_Keyframe_GetStyle(
   3207    keyframe: &LockedKeyframe,
   3208 ) -> Strong<LockedDeclarationBlock> {
   3209    read_locked_arc(keyframe, |keyframe: &Keyframe| {
   3210        keyframe.block.clone().into()
   3211    })
   3212 }
   3213 
   3214 #[no_mangle]
   3215 pub extern "C" fn Servo_Keyframe_SetStyle(
   3216    keyframe: &LockedKeyframe,
   3217    declarations: &LockedDeclarationBlock,
   3218 ) {
   3219    write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
   3220        keyframe.block = unsafe { Arc::from_raw_addrefed(declarations) };
   3221    })
   3222 }
   3223 
   3224 #[no_mangle]
   3225 pub extern "C" fn Servo_KeyframesRule_GetName(rule: &LockedKeyframesRule) -> *mut nsAtom {
   3226    read_locked_arc(rule, |rule: &KeyframesRule| rule.name.as_atom().as_ptr())
   3227 }
   3228 
   3229 #[no_mangle]
   3230 pub unsafe extern "C" fn Servo_KeyframesRule_SetName(
   3231    rule: &LockedKeyframesRule,
   3232    name: *mut nsAtom,
   3233 ) {
   3234    write_locked_arc(rule, |rule: &mut KeyframesRule| {
   3235        rule.name = KeyframesName::from_atom(Atom::from_addrefed(name));
   3236    })
   3237 }
   3238 
   3239 #[no_mangle]
   3240 pub extern "C" fn Servo_KeyframesRule_GetCount(rule: &LockedKeyframesRule) -> u32 {
   3241    read_locked_arc(rule, |rule: &KeyframesRule| rule.keyframes.len() as u32)
   3242 }
   3243 
   3244 #[no_mangle]
   3245 pub extern "C" fn Servo_KeyframesRule_GetKeyframeAt(
   3246    rule: &LockedKeyframesRule,
   3247    index: u32,
   3248    line: &mut u32,
   3249    column: &mut u32,
   3250 ) -> Strong<LockedKeyframe> {
   3251    let global_style_data = &*GLOBAL_STYLE_DATA;
   3252    let guard = global_style_data.shared_lock.read();
   3253    let key = rule.read_with(&guard).keyframes[index as usize].clone();
   3254    let location = key.read_with(&guard).source_location;
   3255    *line = location.line as u32;
   3256    *column = location.column as u32;
   3257    key.into()
   3258 }
   3259 
   3260 #[no_mangle]
   3261 pub extern "C" fn Servo_KeyframesRule_FindRule(
   3262    rule: &LockedKeyframesRule,
   3263    key: &nsACString,
   3264 ) -> u32 {
   3265    let key = unsafe { key.as_str_unchecked() };
   3266    let global_style_data = &*GLOBAL_STYLE_DATA;
   3267    let guard = global_style_data.shared_lock.read();
   3268    rule.read_with(&guard)
   3269        .find_rule(&guard, key)
   3270        .map(|index| index as u32)
   3271        .unwrap_or(u32::max_value())
   3272 }
   3273 
   3274 #[no_mangle]
   3275 pub extern "C" fn Servo_KeyframesRule_AppendRule(
   3276    rule: &LockedKeyframesRule,
   3277    contents: &StylesheetContents,
   3278    css: &nsACString,
   3279 ) -> bool {
   3280    let css = unsafe { css.as_str_unchecked() };
   3281    let global_style_data = &*GLOBAL_STYLE_DATA;
   3282 
   3283    match Keyframe::parse(css, &contents, &global_style_data.shared_lock) {
   3284        Ok(keyframe) => {
   3285            write_locked_arc(rule, |rule: &mut KeyframesRule| {
   3286                rule.keyframes.push(keyframe);
   3287            });
   3288            true
   3289        },
   3290        Err(..) => false,
   3291    }
   3292 }
   3293 
   3294 #[no_mangle]
   3295 pub extern "C" fn Servo_KeyframesRule_DeleteRule(rule: &LockedKeyframesRule, index: u32) {
   3296    write_locked_arc(rule, |rule: &mut KeyframesRule| {
   3297        rule.keyframes.remove(index as usize);
   3298    })
   3299 }
   3300 
   3301 #[no_mangle]
   3302 pub extern "C" fn Servo_MediaRule_GetMedia(rule: &MediaRule) -> Strong<LockedMediaList> {
   3303    rule.media_queries.clone().into()
   3304 }
   3305 
   3306 /// If the condition is null, the true/false gets communicated via the out-param
   3307 #[no_mangle]
   3308 pub extern "C" fn Servo_CustomMediaRule_GetCondition(
   3309    rule: &CustomMediaRule,
   3310    value: Option<&mut bool>,
   3311 ) -> Strong<LockedMediaList> {
   3312    let fixed_value = match rule.condition {
   3313        CustomMediaCondition::True => true,
   3314        CustomMediaCondition::False => false,
   3315        CustomMediaCondition::MediaList(ref list) => return list.clone().into(),
   3316    };
   3317    if let Some(value) = value {
   3318        *value = fixed_value;
   3319    }
   3320    Strong::null()
   3321 }
   3322 
   3323 #[no_mangle]
   3324 pub extern "C" fn Servo_NamespaceRule_GetPrefix(rule: &NamespaceRule) -> *mut nsAtom {
   3325    rule.prefix
   3326        .as_ref()
   3327        .map_or(atom!("").as_ptr(), |a| a.as_ptr())
   3328 }
   3329 
   3330 #[no_mangle]
   3331 pub extern "C" fn Servo_NamespaceRule_GetURI(rule: &NamespaceRule) -> *mut nsAtom {
   3332    rule.url.0.as_ptr()
   3333 }
   3334 
   3335 #[no_mangle]
   3336 pub extern "C" fn Servo_MarginRule_GetStyle(rule: &MarginRule) -> Strong<LockedDeclarationBlock> {
   3337    rule.block.clone().into()
   3338 }
   3339 
   3340 #[no_mangle]
   3341 pub extern "C" fn Servo_MarginRule_GetName(rule: &MarginRule, out: &mut nsACString) {
   3342    out.assign(rule.name());
   3343 }
   3344 
   3345 #[no_mangle]
   3346 pub extern "C" fn Servo_CustomMediaRule_GetName(rule: &CustomMediaRule) -> *mut nsAtom {
   3347    rule.name.0.as_ptr()
   3348 }
   3349 
   3350 #[no_mangle]
   3351 pub extern "C" fn Servo_PageRule_GetStyle(rule: &LockedPageRule) -> Strong<LockedDeclarationBlock> {
   3352    read_locked_arc(rule, |rule: &PageRule| rule.block.clone().into())
   3353 }
   3354 
   3355 #[no_mangle]
   3356 pub extern "C" fn Servo_PageRule_SetStyle(
   3357    rule: &LockedPageRule,
   3358    declarations: &LockedDeclarationBlock,
   3359 ) {
   3360    write_locked_arc(rule, |rule: &mut PageRule| {
   3361        rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
   3362    })
   3363 }
   3364 
   3365 #[no_mangle]
   3366 pub extern "C" fn Servo_PageRule_GetSelectorText(rule: &LockedPageRule, result: &mut nsACString) {
   3367    read_locked_arc(rule, |rule: &PageRule| {
   3368        rule.selectors.to_css(&mut CssWriter::new(result)).unwrap();
   3369    })
   3370 }
   3371 
   3372 #[no_mangle]
   3373 pub extern "C" fn Servo_PageRule_SetSelectorText(
   3374    contents: &StylesheetContents,
   3375    rule: &LockedPageRule,
   3376    text: &nsACString,
   3377 ) -> bool {
   3378    let value_str = unsafe { text.as_str_unchecked() };
   3379 
   3380    write_locked_arc(rule, |rule: &mut PageRule| {
   3381        use style::stylesheets::PageSelectors;
   3382 
   3383        let mut parser_input = ParserInput::new(&value_str);
   3384        let mut parser = Parser::new(&mut parser_input);
   3385 
   3386        // Ensure that a blank input results in empty page selectors
   3387        if parser.is_exhausted() {
   3388            rule.selectors = PageSelectors::default();
   3389            return true;
   3390        }
   3391 
   3392        let url_data = &contents.url_data;
   3393        let context = ParserContext::new(
   3394            Origin::Author,
   3395            &url_data,
   3396            None,
   3397            ParsingMode::DEFAULT,
   3398            QuirksMode::NoQuirks,
   3399            /* namespaces = */ Default::default(),
   3400            None,
   3401            None,
   3402        );
   3403 
   3404        match parser.parse_entirely(|i| PageSelectors::parse(&context, i)) {
   3405            Ok(selectors) => {
   3406                rule.selectors = selectors;
   3407                true
   3408            },
   3409            Err(_) => false,
   3410        }
   3411    })
   3412 }
   3413 
   3414 #[no_mangle]
   3415 pub extern "C" fn Servo_PropertyRule_GetName(rule: &PropertyRule, result: &mut nsACString) {
   3416    write!(result, "--{}", rule.name.0).unwrap();
   3417 }
   3418 
   3419 #[no_mangle]
   3420 pub extern "C" fn Servo_PropertyRule_GetSyntax(rule: &PropertyRule, result: &mut nsACString) {
   3421    if let Some(syntax) = rule.data.syntax.specified_string() {
   3422        result.assign(syntax);
   3423    } else {
   3424        debug_assert!(false, "Rule without specified syntax?");
   3425    }
   3426 }
   3427 
   3428 #[no_mangle]
   3429 pub extern "C" fn Servo_PropertyRule_GetInherits(rule: &PropertyRule) -> bool {
   3430    rule.inherits()
   3431 }
   3432 
   3433 #[no_mangle]
   3434 pub extern "C" fn Servo_PropertyRule_GetInitialValue(
   3435    rule: &PropertyRule,
   3436    result: &mut nsACString,
   3437 ) -> bool {
   3438    rule.data
   3439        .initial_value
   3440        .to_css(&mut CssWriter::new(result))
   3441        .unwrap();
   3442    rule.data.initial_value.is_some()
   3443 }
   3444 
   3445 #[no_mangle]
   3446 pub extern "C" fn Servo_SupportsRule_GetConditionText(
   3447    rule: &SupportsRule,
   3448    result: &mut nsACString,
   3449 ) {
   3450    rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
   3451 }
   3452 
   3453 #[no_mangle]
   3454 pub extern "C" fn Servo_ContainerRule_GetConditionText(
   3455    rule: &ContainerRule,
   3456    result: &mut nsACString,
   3457 ) {
   3458    rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
   3459 }
   3460 
   3461 #[no_mangle]
   3462 pub extern "C" fn Servo_ContainerRule_GetContainerQuery(
   3463    rule: &ContainerRule,
   3464    result: &mut nsACString,
   3465 ) {
   3466    rule.query_condition()
   3467        .to_css(&mut CssWriter::new(result))
   3468        .unwrap();
   3469 }
   3470 
   3471 #[no_mangle]
   3472 pub extern "C" fn Servo_ContainerRule_QueryContainerFor(
   3473    rule: &ContainerRule,
   3474    element: &RawGeckoElement,
   3475 ) -> *const RawGeckoElement {
   3476    rule.condition
   3477        .find_container(GeckoElement(element), None)
   3478        .map_or(ptr::null(), |result| result.element.0)
   3479 }
   3480 
   3481 #[no_mangle]
   3482 pub extern "C" fn Servo_ContainerRule_GetContainerName(
   3483    rule: &ContainerRule,
   3484    result: &mut nsACString,
   3485 ) {
   3486    let name = rule.container_name();
   3487    if !name.is_none() {
   3488        name.to_css(&mut CssWriter::new(result)).unwrap();
   3489    }
   3490 }
   3491 
   3492 #[no_mangle]
   3493 pub extern "C" fn Servo_DocumentRule_GetConditionText(
   3494    rule: &DocumentRule,
   3495    result: &mut nsACString,
   3496 ) {
   3497    rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
   3498 }
   3499 
   3500 #[no_mangle]
   3501 pub extern "C" fn Servo_FontFeatureValuesRule_GetFontFamily(
   3502    rule: &FontFeatureValuesRule,
   3503    result: &mut nsACString,
   3504 ) {
   3505    rule.family_names
   3506        .to_css(&mut CssWriter::new(result))
   3507        .unwrap();
   3508 }
   3509 
   3510 #[no_mangle]
   3511 pub extern "C" fn Servo_FontFeatureValuesRule_GetValueText(
   3512    rule: &FontFeatureValuesRule,
   3513    result: &mut nsACString,
   3514 ) {
   3515    rule.value_to_css(&mut CssWriter::new(result)).unwrap();
   3516 }
   3517 
   3518 #[no_mangle]
   3519 pub extern "C" fn Servo_FontPaletteValuesRule_GetName(
   3520    rule: &FontPaletteValuesRule,
   3521    result: &mut nsACString,
   3522 ) {
   3523    rule.name.to_css(&mut CssWriter::new(result)).unwrap()
   3524 }
   3525 
   3526 #[no_mangle]
   3527 pub extern "C" fn Servo_FontPaletteValuesRule_GetFontFamily(
   3528    rule: &FontPaletteValuesRule,
   3529    result: &mut nsACString,
   3530 ) {
   3531    if !rule.family_names.is_empty() {
   3532        rule.family_names
   3533            .to_css(&mut CssWriter::new(result))
   3534            .unwrap()
   3535    }
   3536 }
   3537 
   3538 #[no_mangle]
   3539 pub extern "C" fn Servo_FontPaletteValuesRule_GetBasePalette(
   3540    rule: &FontPaletteValuesRule,
   3541    result: &mut nsACString,
   3542 ) {
   3543    rule.base_palette
   3544        .to_css(&mut CssWriter::new(result))
   3545        .unwrap()
   3546 }
   3547 
   3548 #[no_mangle]
   3549 pub extern "C" fn Servo_FontPaletteValuesRule_GetOverrideColors(
   3550    rule: &FontPaletteValuesRule,
   3551    result: &mut nsACString,
   3552 ) {
   3553    if !rule.override_colors.is_empty() {
   3554        rule.override_colors
   3555            .to_css(&mut CssWriter::new(result))
   3556            .unwrap()
   3557    }
   3558 }
   3559 
   3560 #[no_mangle]
   3561 pub extern "C" fn Servo_FontFaceRule_CreateEmpty() -> Strong<LockedFontFaceRule> {
   3562    // XXX This is not great. We should split FontFace descriptor data
   3563    // from the rule, so that we don't need to create the rule like this
   3564    // and the descriptor data itself can be hold in UniquePtr from the
   3565    // Gecko side. See bug 1450904.
   3566    with_maybe_worker_shared_lock(|lock| {
   3567        Arc::new(lock.wrap(FontFaceRule::empty(SourceLocation { line: 0, column: 0 }))).into()
   3568    })
   3569 }
   3570 
   3571 #[no_mangle]
   3572 pub unsafe extern "C" fn Servo_FontFaceRule_Clone(
   3573    rule: &LockedFontFaceRule,
   3574 ) -> Strong<LockedFontFaceRule> {
   3575    let clone = read_locked_arc_worker(rule, |rule: &FontFaceRule| rule.clone());
   3576    with_maybe_worker_shared_lock(|lock| Arc::new(lock.wrap(clone)).into())
   3577 }
   3578 
   3579 #[no_mangle]
   3580 pub unsafe extern "C" fn Servo_FontFaceRule_Equals(
   3581    a: &LockedFontFaceRule,
   3582    b: &LockedFontFaceRule,
   3583 ) -> bool {
   3584    if a as *const _ == b as *const _ {
   3585        return true;
   3586    }
   3587    read_locked_arc_worker(a, |a: &FontFaceRule| {
   3588        read_locked_arc_worker(b, |b: &FontFaceRule| a == b)
   3589    })
   3590 }
   3591 
   3592 #[no_mangle]
   3593 pub unsafe extern "C" fn Servo_FontFaceRule_GetSourceLocation(
   3594    rule: &LockedFontFaceRule,
   3595    line: *mut u32,
   3596    column: *mut u32,
   3597 ) {
   3598    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3599        let location = rule.source_location;
   3600        *line.as_mut().unwrap() = location.line as u32;
   3601        *column.as_mut().unwrap() = location.column as u32;
   3602    });
   3603 }
   3604 
   3605 macro_rules! apply_font_desc_list {
   3606    ($apply_macro:ident) => {
   3607        $apply_macro! {
   3608            valid: [
   3609                eCSSFontDesc_Family => family,
   3610                eCSSFontDesc_Style => style,
   3611                eCSSFontDesc_Weight => weight,
   3612                eCSSFontDesc_Stretch => stretch,
   3613                eCSSFontDesc_Src => sources,
   3614                eCSSFontDesc_UnicodeRange => unicode_range,
   3615                eCSSFontDesc_FontFeatureSettings => feature_settings,
   3616                eCSSFontDesc_FontVariationSettings => variation_settings,
   3617                eCSSFontDesc_FontLanguageOverride => language_override,
   3618                eCSSFontDesc_Display => display,
   3619                eCSSFontDesc_AscentOverride => ascent_override,
   3620                eCSSFontDesc_DescentOverride => descent_override,
   3621                eCSSFontDesc_LineGapOverride => line_gap_override,
   3622                eCSSFontDesc_SizeAdjust => size_adjust,
   3623            ]
   3624            invalid: [
   3625                eCSSFontDesc_UNKNOWN,
   3626                eCSSFontDesc_COUNT,
   3627            ]
   3628        }
   3629    };
   3630 }
   3631 
   3632 #[no_mangle]
   3633 pub unsafe extern "C" fn Servo_FontFaceRule_Length(rule: &LockedFontFaceRule) -> u32 {
   3634    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3635        let mut result = 0;
   3636        macro_rules! count_values {
   3637            (
   3638                valid: [$($v_enum_name:ident => $field:ident,)*]
   3639                invalid: [$($i_enum_name:ident,)*]
   3640            ) => {
   3641                $(if rule.$field.is_some() {
   3642                    result += 1;
   3643                })*
   3644            }
   3645        }
   3646        apply_font_desc_list!(count_values);
   3647        result
   3648    })
   3649 }
   3650 
   3651 #[no_mangle]
   3652 pub unsafe extern "C" fn Servo_FontFaceRule_IndexGetter(
   3653    rule: &LockedFontFaceRule,
   3654    index: u32,
   3655 ) -> nsCSSFontDesc {
   3656    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3657        let mut count = 0;
   3658        macro_rules! lookup_index {
   3659            (
   3660                valid: [$($v_enum_name:ident => $field:ident,)*]
   3661                invalid: [$($i_enum_name:ident,)*]
   3662            ) => {
   3663                $(if rule.$field.is_some() {
   3664                    count += 1;
   3665                    if count - 1 == index {
   3666                        return nsCSSFontDesc::$v_enum_name;
   3667                    }
   3668                })*
   3669            }
   3670        }
   3671        apply_font_desc_list!(lookup_index);
   3672        return nsCSSFontDesc::eCSSFontDesc_UNKNOWN;
   3673    })
   3674 }
   3675 
   3676 #[no_mangle]
   3677 pub unsafe extern "C" fn Servo_FontFaceRule_GetDeclCssText(
   3678    rule: &LockedFontFaceRule,
   3679    result: &mut nsACString,
   3680 ) {
   3681    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3682        rule.decl_to_css(result).unwrap();
   3683    })
   3684 }
   3685 
   3686 macro_rules! simple_font_descriptor_getter_impl {
   3687    ($rule:ident, $out:ident, $field:ident, $compute:ident) => {
   3688        read_locked_arc_worker($rule, |rule: &FontFaceRule| {
   3689            match rule.$field {
   3690                None => return false,
   3691                Some(ref f) => *$out = f.$compute(),
   3692            }
   3693            true
   3694        })
   3695    };
   3696 }
   3697 
   3698 #[no_mangle]
   3699 pub extern "C" fn Servo_FontFaceRule_GetFontWeight(
   3700    rule: &LockedFontFaceRule,
   3701    out: &mut font_face::ComputedFontWeightRange,
   3702 ) -> bool {
   3703    simple_font_descriptor_getter_impl!(rule, out, weight, compute)
   3704 }
   3705 
   3706 #[no_mangle]
   3707 pub extern "C" fn Servo_FontFaceRule_GetFontStretch(
   3708    rule: &LockedFontFaceRule,
   3709    out: &mut font_face::ComputedFontStretchRange,
   3710 ) -> bool {
   3711    simple_font_descriptor_getter_impl!(rule, out, stretch, compute)
   3712 }
   3713 
   3714 #[no_mangle]
   3715 pub extern "C" fn Servo_FontFaceRule_GetFontStyle(
   3716    rule: &LockedFontFaceRule,
   3717    out: &mut font_face::ComputedFontStyleDescriptor,
   3718 ) -> bool {
   3719    simple_font_descriptor_getter_impl!(rule, out, style, compute)
   3720 }
   3721 
   3722 #[no_mangle]
   3723 pub extern "C" fn Servo_FontFaceRule_GetFontDisplay(
   3724    rule: &LockedFontFaceRule,
   3725    out: &mut font_face::FontDisplay,
   3726 ) -> bool {
   3727    simple_font_descriptor_getter_impl!(rule, out, display, clone)
   3728 }
   3729 
   3730 #[no_mangle]
   3731 pub extern "C" fn Servo_FontFaceRule_GetFontLanguageOverride(
   3732    rule: &LockedFontFaceRule,
   3733    out: &mut computed::FontLanguageOverride,
   3734 ) -> bool {
   3735    simple_font_descriptor_getter_impl!(rule, out, language_override, clone)
   3736 }
   3737 
   3738 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
   3739 // rather than an actual percentage value.
   3740 #[no_mangle]
   3741 pub extern "C" fn Servo_FontFaceRule_GetAscentOverride(
   3742    rule: &LockedFontFaceRule,
   3743    out: &mut computed::Percentage,
   3744 ) -> bool {
   3745    simple_font_descriptor_getter_impl!(rule, out, ascent_override, compute)
   3746 }
   3747 
   3748 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
   3749 // rather than an actual percentage value.
   3750 #[no_mangle]
   3751 pub extern "C" fn Servo_FontFaceRule_GetDescentOverride(
   3752    rule: &LockedFontFaceRule,
   3753    out: &mut computed::Percentage,
   3754 ) -> bool {
   3755    simple_font_descriptor_getter_impl!(rule, out, descent_override, compute)
   3756 }
   3757 
   3758 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
   3759 // rather than an actual percentage value.
   3760 #[no_mangle]
   3761 pub extern "C" fn Servo_FontFaceRule_GetLineGapOverride(
   3762    rule: &LockedFontFaceRule,
   3763    out: &mut computed::Percentage,
   3764 ) -> bool {
   3765    simple_font_descriptor_getter_impl!(rule, out, line_gap_override, compute)
   3766 }
   3767 
   3768 #[no_mangle]
   3769 pub extern "C" fn Servo_FontFaceRule_GetSizeAdjust(
   3770    rule: &LockedFontFaceRule,
   3771    out: &mut computed::Percentage,
   3772 ) -> bool {
   3773    simple_font_descriptor_getter_impl!(rule, out, size_adjust, compute)
   3774 }
   3775 
   3776 #[no_mangle]
   3777 pub unsafe extern "C" fn Servo_FontFaceRule_GetFamilyName(
   3778    rule: &LockedFontFaceRule,
   3779 ) -> *mut nsAtom {
   3780    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3781        // TODO(emilio): font-family is a mandatory descriptor, can't we unwrap
   3782        // here, and remove the null-checks in Gecko?
   3783        rule.family
   3784            .as_ref()
   3785            .map_or(ptr::null_mut(), |f| f.name.as_ptr())
   3786    })
   3787 }
   3788 
   3789 #[no_mangle]
   3790 pub unsafe extern "C" fn Servo_FontFaceRule_GetUnicodeRanges(
   3791    rule: &LockedFontFaceRule,
   3792    out_len: *mut usize,
   3793 ) -> *const UnicodeRange {
   3794    *out_len = 0;
   3795    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3796        let ranges = match rule.unicode_range {
   3797            Some(ref ranges) => ranges,
   3798            None => return ptr::null(),
   3799        };
   3800        *out_len = ranges.len();
   3801        ranges.as_ptr() as *const _
   3802    })
   3803 }
   3804 
   3805 #[no_mangle]
   3806 pub unsafe extern "C" fn Servo_FontFaceRule_GetSources(
   3807    rule: &LockedFontFaceRule,
   3808    out: &mut nsTArray<FontFaceSourceListComponent>,
   3809 ) {
   3810    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3811        let sources = match rule.sources {
   3812            Some(ref s) => s,
   3813            None => return,
   3814        };
   3815 
   3816        for source in sources.0.iter() {
   3817            match *source {
   3818                Source::Url(ref url) => {
   3819                    out.push(FontFaceSourceListComponent::Url(&url.url));
   3820                    if let Some(hint) = &url.format_hint {
   3821                        match hint {
   3822                            FontFaceSourceFormat::Keyword(kw) => {
   3823                                out.push(FontFaceSourceListComponent::FormatHintKeyword(*kw))
   3824                            },
   3825                            FontFaceSourceFormat::String(s) => {
   3826                                out.push(FontFaceSourceListComponent::FormatHintString {
   3827                                    length: s.len(),
   3828                                    utf8_bytes: s.as_ptr(),
   3829                                })
   3830                            },
   3831                        }
   3832                    }
   3833                    if !url.tech_flags.is_empty() {
   3834                        out.push(FontFaceSourceListComponent::TechFlags(url.tech_flags));
   3835                    }
   3836                },
   3837                Source::Local(ref name) => {
   3838                    out.push(FontFaceSourceListComponent::Local(name.name.as_ptr()));
   3839                },
   3840            }
   3841        }
   3842    })
   3843 }
   3844 
   3845 #[no_mangle]
   3846 pub unsafe extern "C" fn Servo_FontFaceRule_GetVariationSettings(
   3847    rule: &LockedFontFaceRule,
   3848    variations: &mut nsTArray<structs::gfxFontVariation>,
   3849 ) {
   3850    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3851        let source_variations = match rule.variation_settings {
   3852            Some(ref v) => v,
   3853            None => return,
   3854        };
   3855 
   3856        variations.extend(
   3857            source_variations
   3858                .0
   3859                .iter()
   3860                .map(|source| structs::gfxFontVariation {
   3861                    mTag: source.tag.0,
   3862                    mValue: source.value.get(),
   3863                }),
   3864        );
   3865    });
   3866 }
   3867 
   3868 #[no_mangle]
   3869 pub unsafe extern "C" fn Servo_FontFaceRule_GetFeatureSettings(
   3870    rule: &LockedFontFaceRule,
   3871    features: &mut nsTArray<structs::gfxFontFeature>,
   3872 ) {
   3873    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3874        let source_features = match rule.feature_settings {
   3875            Some(ref v) => v,
   3876            None => return,
   3877        };
   3878 
   3879        features.extend(
   3880            source_features
   3881                .0
   3882                .iter()
   3883                .map(|source| structs::gfxFontFeature {
   3884                    mTag: source.tag.0,
   3885                    mValue: source.value.value() as u32,
   3886                }),
   3887        );
   3888    });
   3889 }
   3890 
   3891 #[no_mangle]
   3892 pub extern "C" fn Servo_FontFaceRule_GetDescriptorCssText(
   3893    rule: &LockedFontFaceRule,
   3894    desc: nsCSSFontDesc,
   3895    result: &mut nsACString,
   3896 ) {
   3897    read_locked_arc_worker(rule, |rule: &FontFaceRule| {
   3898        let mut writer = CssWriter::new(result);
   3899        macro_rules! to_css_text {
   3900            (
   3901                valid: [$($v_enum_name:ident => $field:ident,)*]
   3902                invalid: [$($i_enum_name:ident,)*]
   3903            ) => {
   3904                match desc {
   3905                    $(
   3906                        nsCSSFontDesc::$v_enum_name => {
   3907                            if let Some(ref value) = rule.$field {
   3908                                value.to_css(&mut writer).unwrap();
   3909                            }
   3910                        }
   3911                    )*
   3912                    $(
   3913                        nsCSSFontDesc::$i_enum_name => {
   3914                            debug_assert!(false, "not a valid font descriptor");
   3915                        }
   3916                    )*
   3917                }
   3918            }
   3919        }
   3920        apply_font_desc_list!(to_css_text)
   3921    })
   3922 }
   3923 
   3924 #[no_mangle]
   3925 pub unsafe extern "C" fn Servo_FontFaceRule_SetDescriptor(
   3926    rule: &LockedFontFaceRule,
   3927    desc: nsCSSFontDesc,
   3928    value: &nsACString,
   3929    data: *mut URLExtraData,
   3930    out_changed: *mut bool,
   3931 ) -> bool {
   3932    let value = value.as_str_unchecked();
   3933    let mut input = ParserInput::new(&value);
   3934    let mut parser = Parser::new(&mut input);
   3935    let url_data = UrlExtraData::from_ptr_ref(&data);
   3936    let context = ParserContext::new(
   3937        Origin::Author,
   3938        url_data,
   3939        Some(CssRuleType::FontFace),
   3940        ParsingMode::DEFAULT,
   3941        QuirksMode::NoQuirks,
   3942        /* namespaces = */ Default::default(),
   3943        None,
   3944        None,
   3945    );
   3946 
   3947    write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
   3948        macro_rules! to_css_text {
   3949            (
   3950                valid: [$($v_enum_name:ident => $field:ident,)*]
   3951                invalid: [$($i_enum_name:ident,)*]
   3952            ) => {
   3953                match desc {
   3954                    $(
   3955                        nsCSSFontDesc::$v_enum_name => {
   3956                            if let Ok(value) = parser.parse_entirely(|i| Parse::parse(&context, i)) {
   3957                                let result = Some(value);
   3958                                *out_changed = result != rule.$field;
   3959                                rule.$field = result;
   3960                                true
   3961                            } else {
   3962                                false
   3963                            }
   3964                        }
   3965                    )*
   3966                    $(
   3967                        nsCSSFontDesc::$i_enum_name => {
   3968                            debug_assert!(false, "not a valid font descriptor");
   3969                            false
   3970                        }
   3971                    )*
   3972                }
   3973            }
   3974        }
   3975        apply_font_desc_list!(to_css_text)
   3976    })
   3977 }
   3978 
   3979 #[no_mangle]
   3980 pub unsafe extern "C" fn Servo_FontFaceRule_ResetDescriptor(
   3981    rule: &LockedFontFaceRule,
   3982    desc: nsCSSFontDesc,
   3983 ) {
   3984    write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
   3985        macro_rules! reset_desc {
   3986            (
   3987                valid: [$($v_enum_name:ident => $field:ident,)*]
   3988                invalid: [$($i_enum_name:ident,)*]
   3989            ) => {
   3990                match desc {
   3991                    $(nsCSSFontDesc::$v_enum_name => rule.$field = None,)*
   3992                    $(nsCSSFontDesc::$i_enum_name => debug_assert!(false, "not a valid font descriptor"),)*
   3993                }
   3994            }
   3995        }
   3996        apply_font_desc_list!(reset_desc)
   3997    })
   3998 }
   3999 
   4000 #[no_mangle]
   4001 pub unsafe extern "C" fn Servo_CounterStyleRule_GetName(
   4002    rule: &LockedCounterStyleRule,
   4003 ) -> *mut nsAtom {
   4004    read_locked_arc(rule, |rule: &CounterStyleRule| rule.name().0.as_ptr())
   4005 }
   4006 
   4007 #[no_mangle]
   4008 pub unsafe extern "C" fn Servo_CounterStyleRule_SetName(
   4009    rule: &LockedCounterStyleRule,
   4010    value: &nsACString,
   4011 ) -> bool {
   4012    let value = value.as_str_unchecked();
   4013    let mut input = ParserInput::new(&value);
   4014    let mut parser = Parser::new(&mut input);
   4015    match parser.parse_entirely(counter_style::parse_counter_style_name_definition) {
   4016        Ok(name) => {
   4017            write_locked_arc(rule, |rule: &mut CounterStyleRule| rule.set_name(name));
   4018            true
   4019        },
   4020        Err(_) => false,
   4021    }
   4022 }
   4023 
   4024 #[no_mangle]
   4025 pub unsafe extern "C" fn Servo_CounterStyleRule_GetGeneration(
   4026    rule: &LockedCounterStyleRule,
   4027 ) -> u32 {
   4028    read_locked_arc(rule, |rule: &CounterStyleRule| rule.generation())
   4029 }
   4030 
   4031 fn symbol_to_string(s: &counter_style::Symbol) -> nsString {
   4032    match *s {
   4033        counter_style::Symbol::String(ref s) => nsString::from(&**s),
   4034        counter_style::Symbol::Ident(ref i) => nsString::from(i.0.as_slice()),
   4035    }
   4036 }
   4037 
   4038 // TODO(emilio): Cbindgen could be used to simplify a bunch of code here.
   4039 #[no_mangle]
   4040 pub unsafe extern "C" fn Servo_CounterStyleRule_GetPad(
   4041    rule: &LockedCounterStyleRule,
   4042    width: &mut i32,
   4043    symbol: &mut nsString,
   4044 ) -> bool {
   4045    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4046        let pad = match rule.pad() {
   4047            Some(pad) => pad,
   4048            None => return false,
   4049        };
   4050        *width = pad.0.value();
   4051        *symbol = symbol_to_string(&pad.1);
   4052        true
   4053    })
   4054 }
   4055 
   4056 fn get_symbol(s: Option<&counter_style::Symbol>, out: &mut nsString) -> bool {
   4057    let s = match s {
   4058        Some(s) => s,
   4059        None => return false,
   4060    };
   4061    *out = symbol_to_string(s);
   4062    true
   4063 }
   4064 
   4065 #[no_mangle]
   4066 pub unsafe extern "C" fn Servo_CounterStyleRule_GetPrefix(
   4067    rule: &LockedCounterStyleRule,
   4068    out: &mut nsString,
   4069 ) -> bool {
   4070    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4071        get_symbol(rule.prefix(), out)
   4072    })
   4073 }
   4074 
   4075 #[no_mangle]
   4076 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSuffix(
   4077    rule: &LockedCounterStyleRule,
   4078    out: &mut nsString,
   4079 ) -> bool {
   4080    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4081        get_symbol(rule.suffix(), out)
   4082    })
   4083 }
   4084 
   4085 #[no_mangle]
   4086 pub unsafe extern "C" fn Servo_CounterStyleRule_GetNegative(
   4087    rule: &LockedCounterStyleRule,
   4088    prefix: &mut nsString,
   4089    suffix: &mut nsString,
   4090 ) -> bool {
   4091    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4092        let negative = match rule.negative() {
   4093            Some(n) => n,
   4094            None => return false,
   4095        };
   4096        *prefix = symbol_to_string(&negative.0);
   4097        *suffix = match negative.1 {
   4098            Some(ref s) => symbol_to_string(s),
   4099            None => nsString::new(),
   4100        };
   4101        true
   4102    })
   4103 }
   4104 
   4105 #[repr(u8)]
   4106 pub enum IsOrdinalInRange {
   4107    Auto,
   4108    InRange,
   4109    NotInRange,
   4110    NoOrdinalSpecified,
   4111 }
   4112 
   4113 #[no_mangle]
   4114 pub unsafe extern "C" fn Servo_CounterStyleRule_IsInRange(
   4115    rule: &LockedCounterStyleRule,
   4116    ordinal: i32,
   4117 ) -> IsOrdinalInRange {
   4118    use style::counter_style::CounterBound;
   4119    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4120        let range = match rule.range() {
   4121            Some(r) => r,
   4122            None => return IsOrdinalInRange::NoOrdinalSpecified,
   4123        };
   4124 
   4125        if range.0.is_empty() {
   4126            return IsOrdinalInRange::Auto;
   4127        }
   4128 
   4129        let in_range = range.0.iter().any(|r| {
   4130            if let CounterBound::Integer(start) = r.start {
   4131                if start.value() > ordinal {
   4132                    return false;
   4133                }
   4134            }
   4135 
   4136            if let CounterBound::Integer(end) = r.end {
   4137                if end.value() < ordinal {
   4138                    return false;
   4139                }
   4140            }
   4141 
   4142            true
   4143        });
   4144 
   4145        if in_range {
   4146            IsOrdinalInRange::InRange
   4147        } else {
   4148            IsOrdinalInRange::NotInRange
   4149        }
   4150    })
   4151 }
   4152 
   4153 #[no_mangle]
   4154 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSymbols(
   4155    rule: &LockedCounterStyleRule,
   4156    count: &mut usize,
   4157 ) -> *const counter_style::Symbol {
   4158    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4159        let symbols = match rule.symbols() {
   4160            Some(s) => &*s.0,
   4161            None => &[],
   4162        };
   4163        *count = symbols.len();
   4164        symbols.as_ptr()
   4165    })
   4166 }
   4167 
   4168 #[repr(C)]
   4169 pub struct AdditiveSymbol {
   4170    pub weight: i32,
   4171    pub symbol: nsString,
   4172 }
   4173 
   4174 #[no_mangle]
   4175 pub unsafe extern "C" fn Servo_CounterStyleRule_GetAdditiveSymbols(
   4176    rule: &LockedCounterStyleRule,
   4177    symbols: &mut style::OwnedSlice<AdditiveSymbol>,
   4178 ) {
   4179    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4180        *symbols = match rule.additive_symbols() {
   4181            Some(s) => {
   4182                s.0.iter()
   4183                    .map(|s| AdditiveSymbol {
   4184                        weight: s.weight.value(),
   4185                        symbol: symbol_to_string(&s.symbol),
   4186                    })
   4187                    .collect()
   4188            },
   4189            None => style::OwnedSlice::default(),
   4190        };
   4191    })
   4192 }
   4193 
   4194 #[repr(C, u8)]
   4195 pub enum CounterSpeakAs {
   4196    None,
   4197    Auto,
   4198    Bullets,
   4199    Numbers,
   4200    Words,
   4201    Ident(*mut nsAtom),
   4202 }
   4203 
   4204 #[no_mangle]
   4205 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSpeakAs(
   4206    rule: &LockedCounterStyleRule,
   4207    out: &mut CounterSpeakAs,
   4208 ) {
   4209    use style::counter_style::SpeakAs;
   4210    *out = read_locked_arc(rule, |rule: &CounterStyleRule| {
   4211        let speak_as = match rule.speak_as() {
   4212            Some(s) => s,
   4213            None => return CounterSpeakAs::None,
   4214        };
   4215        match *speak_as {
   4216            SpeakAs::Auto => CounterSpeakAs::Auto,
   4217            SpeakAs::Bullets => CounterSpeakAs::Bullets,
   4218            SpeakAs::Numbers => CounterSpeakAs::Numbers,
   4219            SpeakAs::Words => CounterSpeakAs::Words,
   4220            SpeakAs::Other(ref other) => CounterSpeakAs::Ident(other.0.as_ptr()),
   4221        }
   4222    });
   4223 }
   4224 
   4225 #[repr(u8)]
   4226 pub enum CounterSystem {
   4227    Cyclic = 0,
   4228    Numeric,
   4229    Alphabetic,
   4230    Symbolic,
   4231    Additive,
   4232    Fixed,
   4233    Extends,
   4234 }
   4235 
   4236 #[no_mangle]
   4237 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSystem(
   4238    rule: &LockedCounterStyleRule,
   4239 ) -> CounterSystem {
   4240    use style::counter_style::System;
   4241    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4242        match *rule.resolved_system() {
   4243            System::Cyclic => CounterSystem::Cyclic,
   4244            System::Numeric => CounterSystem::Numeric,
   4245            System::Alphabetic => CounterSystem::Alphabetic,
   4246            System::Symbolic => CounterSystem::Symbolic,
   4247            System::Additive => CounterSystem::Additive,
   4248            System::Fixed { .. } => CounterSystem::Fixed,
   4249            System::Extends(_) => CounterSystem::Extends,
   4250        }
   4251    })
   4252 }
   4253 
   4254 #[no_mangle]
   4255 pub unsafe extern "C" fn Servo_CounterStyleRule_GetExtended(
   4256    rule: &LockedCounterStyleRule,
   4257 ) -> *mut nsAtom {
   4258    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4259        match *rule.resolved_system() {
   4260            counter_style::System::Extends(ref name) => name.0.as_ptr(),
   4261            _ => {
   4262                debug_assert!(false, "Not extends system");
   4263                ptr::null_mut()
   4264            },
   4265        }
   4266    })
   4267 }
   4268 
   4269 #[no_mangle]
   4270 pub unsafe extern "C" fn Servo_CounterStyleRule_GetFixedFirstValue(
   4271    rule: &LockedCounterStyleRule,
   4272 ) -> i32 {
   4273    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4274        match *rule.resolved_system() {
   4275            counter_style::System::Fixed { first_symbol_value } => {
   4276                first_symbol_value.map_or(1, |v| v.value())
   4277            },
   4278            _ => {
   4279                debug_assert!(false, "Not fixed system");
   4280                0
   4281            },
   4282        }
   4283    })
   4284 }
   4285 
   4286 #[no_mangle]
   4287 pub unsafe extern "C" fn Servo_CounterStyleRule_GetFallback(
   4288    rule: &LockedCounterStyleRule,
   4289 ) -> *mut nsAtom {
   4290    read_locked_arc(rule, |rule: &CounterStyleRule| {
   4291        rule.fallback().map_or(ptr::null_mut(), |i| i.0 .0.as_ptr())
   4292    })
   4293 }
   4294 
   4295 macro_rules! counter_style_descriptors {
   4296    {
   4297        valid: [
   4298            $($desc:ident => $getter:ident / $setter:ident,)+
   4299        ]
   4300        invalid: [
   4301            $($i_desc:ident,)+
   4302        ]
   4303    } => {
   4304        #[no_mangle]
   4305        pub unsafe extern "C" fn Servo_CounterStyleRule_GetDescriptorCssText(
   4306            rule: &LockedCounterStyleRule,
   4307            desc: nsCSSCounterDesc,
   4308            result: &mut nsACString,
   4309        ) {
   4310            let mut writer = CssWriter::new(result);
   4311            read_locked_arc(rule, |rule: &CounterStyleRule| {
   4312                match desc {
   4313                    $(nsCSSCounterDesc::$desc => {
   4314                        if let Some(value) = rule.$getter() {
   4315                            value.to_css(&mut writer).unwrap();
   4316                        }
   4317                    })+
   4318                    $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
   4319                }
   4320            });
   4321        }
   4322 
   4323        #[no_mangle]
   4324        pub unsafe extern "C" fn Servo_CounterStyleRule_SetDescriptor(
   4325            rule: &LockedCounterStyleRule,
   4326            desc: nsCSSCounterDesc,
   4327            value: &nsACString,
   4328        ) -> bool {
   4329            let value = value.as_str_unchecked();
   4330            let mut input = ParserInput::new(&value);
   4331            let mut parser = Parser::new(&mut input);
   4332            let url_data = dummy_url_data();
   4333            let context = ParserContext::new(
   4334                Origin::Author,
   4335                url_data,
   4336                Some(CssRuleType::CounterStyle),
   4337                ParsingMode::DEFAULT,
   4338                QuirksMode::NoQuirks,
   4339                /* namespaces = */ Default::default(),
   4340                None,
   4341                None,
   4342            );
   4343 
   4344            write_locked_arc(rule, |rule: &mut CounterStyleRule| {
   4345                match desc {
   4346                    $(nsCSSCounterDesc::$desc => {
   4347                        match parser.parse_entirely(|i| Parse::parse(&context, i)) {
   4348                            Ok(value) => rule.$setter(value),
   4349                            Err(_) => false,
   4350                        }
   4351                    })+
   4352                    $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
   4353                }
   4354            })
   4355        }
   4356    }
   4357 }
   4358 
   4359 counter_style_descriptors! {
   4360    valid: [
   4361        eCSSCounterDesc_System => system / set_system,
   4362        eCSSCounterDesc_Symbols => symbols / set_symbols,
   4363        eCSSCounterDesc_AdditiveSymbols => additive_symbols / set_additive_symbols,
   4364        eCSSCounterDesc_Negative => negative / set_negative,
   4365        eCSSCounterDesc_Prefix => prefix / set_prefix,
   4366        eCSSCounterDesc_Suffix => suffix / set_suffix,
   4367        eCSSCounterDesc_Range => range / set_range,
   4368        eCSSCounterDesc_Pad => pad / set_pad,
   4369        eCSSCounterDesc_Fallback => fallback / set_fallback,
   4370        eCSSCounterDesc_SpeakAs => speak_as / set_speak_as,
   4371    ]
   4372    invalid: [
   4373        eCSSCounterDesc_UNKNOWN,
   4374        eCSSCounterDesc_COUNT,
   4375    ]
   4376 }
   4377 
   4378 #[no_mangle]
   4379 pub unsafe extern "C" fn Servo_PositionTryRule_GetName(
   4380    rule: &LockedPositionTryRule,
   4381    result: &mut nsACString,
   4382 ) {
   4383    read_locked_arc(rule, |rule: &PositionTryRule| {
   4384        rule.name.to_css(&mut CssWriter::new(result)).unwrap()
   4385    });
   4386 }
   4387 
   4388 #[no_mangle]
   4389 pub extern "C" fn Servo_PositionTryRule_GetStyle(
   4390    rule: &LockedPositionTryRule,
   4391 ) -> Strong<LockedDeclarationBlock> {
   4392    read_locked_arc(rule, |rule: &PositionTryRule| rule.block.clone().into())
   4393 }
   4394 
   4395 #[no_mangle]
   4396 pub extern "C" fn Servo_PositionTryRule_SetStyle(
   4397    rule: &LockedPositionTryRule,
   4398    declarations: &LockedDeclarationBlock,
   4399 ) {
   4400    write_locked_arc(rule, |rule: &mut PositionTryRule| {
   4401        rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
   4402    })
   4403 }
   4404 
   4405 #[no_mangle]
   4406 pub unsafe extern "C" fn Servo_ComputedValues_GetForPageContent(
   4407    raw_data: &PerDocumentStyleData,
   4408    page_name: *const nsAtom,
   4409    pseudos: PagePseudoClassFlags,
   4410 ) -> Strong<ComputedValues> {
   4411    let global_style_data = &*GLOBAL_STYLE_DATA;
   4412    let guard = global_style_data.shared_lock.read();
   4413    let guards = StylesheetGuards::same(&guard);
   4414    let data = raw_data.borrow_mut();
   4415    let cascade_data = data.stylist.cascade_data();
   4416 
   4417    let mut extra_declarations = vec![];
   4418    let iter = data.stylist.iter_extra_data_origins_rev();
   4419    let name = if !page_name.is_null() {
   4420        Some(Atom::from_raw(page_name as *mut nsAtom))
   4421    } else {
   4422        None
   4423    };
   4424    for (data, origin) in iter {
   4425        data.pages.match_and_append_rules(
   4426            &mut extra_declarations,
   4427            origin,
   4428            &guards,
   4429            cascade_data,
   4430            &name,
   4431            pseudos,
   4432        );
   4433    }
   4434 
   4435    let rule_node = data.stylist.rule_node_for_precomputed_pseudo(
   4436        &guards,
   4437        &PseudoElement::PageContent,
   4438        extra_declarations,
   4439    );
   4440 
   4441    data.stylist
   4442        .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
   4443            &guards,
   4444            &PseudoElement::PageContent,
   4445            None,
   4446            rule_node,
   4447        )
   4448        .into()
   4449 }
   4450 
   4451 #[no_mangle]
   4452 pub unsafe extern "C" fn Servo_ComputedValues_GetForPositionTry(
   4453    raw_data: &PerDocumentStyleData,
   4454    style: &ComputedValues,
   4455    element: &RawGeckoElement,
   4456    fallback_item: &PositionTryFallbacksItem,
   4457 ) -> Strong<ComputedValues> {
   4458    let global_style_data = &*GLOBAL_STYLE_DATA;
   4459    let guard = global_style_data.shared_lock.read();
   4460    let guards = StylesheetGuards::same(&guard);
   4461    let element = GeckoElement(element);
   4462    let data = raw_data.borrow();
   4463    data.stylist
   4464        .resolve_position_try(style, &guards, element, fallback_item)
   4465        .into()
   4466 }
   4467 
   4468 #[no_mangle]
   4469 pub unsafe extern "C" fn Servo_ComputedValues_GetForAnonymousBox(
   4470    parent_style_or_null: Option<&ComputedValues>,
   4471    pseudo: PseudoStyleType,
   4472    raw_data: &PerDocumentStyleData,
   4473 ) -> Strong<ComputedValues> {
   4474    let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
   4475    debug_assert!(pseudo.is_anon_box());
   4476    debug_assert_ne!(pseudo, PseudoElement::PageContent);
   4477    let global_style_data = &*GLOBAL_STYLE_DATA;
   4478    let guard = global_style_data.shared_lock.read();
   4479    let guards = StylesheetGuards::same(&guard);
   4480    let data = raw_data.borrow_mut();
   4481    let rule_node = data
   4482        .stylist
   4483        .rule_node_for_precomputed_pseudo(&guards, &pseudo, vec![]);
   4484 
   4485    data.stylist
   4486        .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
   4487            &guards,
   4488            &pseudo,
   4489            parent_style_or_null.map(|x| &*x),
   4490            rule_node,
   4491        )
   4492        .into()
   4493 }
   4494 
   4495 fn get_functional_pseudo_parameter_atom(
   4496    functional_pseudo_parameter: *mut nsAtom,
   4497 ) -> Option<AtomIdent> {
   4498    if functional_pseudo_parameter.is_null() {
   4499        None
   4500    } else {
   4501        Some(AtomIdent::new(unsafe {
   4502            Atom::from_raw(functional_pseudo_parameter)
   4503        }))
   4504    }
   4505 }
   4506 
   4507 #[no_mangle]
   4508 pub extern "C" fn Servo_ResolvePseudoStyle(
   4509    element: &RawGeckoElement,
   4510    pseudo_type: PseudoStyleType,
   4511    functional_pseudo_parameter: *mut nsAtom,
   4512    is_probe: bool,
   4513    inherited_style: Option<&ComputedValues>,
   4514    raw_data: &PerDocumentStyleData,
   4515 ) -> Strong<ComputedValues> {
   4516    let element = GeckoElement(element);
   4517    let doc_data = raw_data.borrow();
   4518 
   4519    debug!(
   4520        "Servo_ResolvePseudoStyle: {:?} {:?}, is_probe: {}",
   4521        element,
   4522        PseudoElement::from_pseudo_type(
   4523            pseudo_type,
   4524            get_functional_pseudo_parameter_atom(functional_pseudo_parameter)
   4525        ),
   4526        is_probe
   4527    );
   4528 
   4529    let data = element.borrow_data();
   4530 
   4531    let data = match data.as_ref() {
   4532        Some(data) if data.has_styles() => data,
   4533        _ => {
   4534            // FIXME(bholley, emilio): Assert against this.
   4535            //
   4536            // Known offender is nsMathMLmoFrame::MarkIntrinsicISizesDirty,
   4537            // which goes and does a bunch of work involving style resolution.
   4538            //
   4539            // Bug 1403865 tracks fixing it, and potentially adding an assert
   4540            // here instead.
   4541            warn!("Calling Servo_ResolvePseudoStyle on unstyled element");
   4542            return if is_probe {
   4543                Strong::null()
   4544            } else {
   4545                doc_data.default_computed_values().clone().into()
   4546            };
   4547        },
   4548    };
   4549 
   4550    let pseudo_element = PseudoElement::from_pseudo_type(
   4551        pseudo_type,
   4552        get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
   4553    )
   4554    .expect("ResolvePseudoStyle with a non-pseudo?");
   4555 
   4556    let matching_fn = |pseudo: &PseudoElement| *pseudo == pseudo_element;
   4557 
   4558    let global_style_data = &*GLOBAL_STYLE_DATA;
   4559    let guard = global_style_data.shared_lock.read();
   4560    let style = get_pseudo_style(
   4561        &guard,
   4562        element,
   4563        &pseudo_element,
   4564        RuleInclusion::All,
   4565        &data.styles,
   4566        inherited_style,
   4567        &doc_data.stylist,
   4568        is_probe,
   4569        /* matching_func = */
   4570        if pseudo_element.is_highlight() {
   4571            Some(&matching_fn)
   4572        } else {
   4573            None
   4574        },
   4575    );
   4576 
   4577    match style {
   4578        Some(s) => s.into(),
   4579        None => {
   4580            debug_assert!(is_probe);
   4581            Strong::null()
   4582        },
   4583    }
   4584 }
   4585 
   4586 fn debug_atom_array(atoms: &nsTArray<structs::RefPtr<nsAtom>>) -> String {
   4587    let mut result = String::from("[");
   4588    for atom in atoms.iter() {
   4589        if atom.mRawPtr.is_null() {
   4590            result += "(null), ";
   4591        } else {
   4592            let atom = unsafe { WeakAtom::new(atom.mRawPtr) };
   4593            write!(result, "{}, ", atom).unwrap();
   4594        }
   4595    }
   4596    result.push(']');
   4597    result
   4598 }
   4599 
   4600 #[no_mangle]
   4601 pub extern "C" fn Servo_ComputedValues_ResolveXULTreePseudoStyle(
   4602    element: &RawGeckoElement,
   4603    pseudo_tag: *mut nsAtom,
   4604    inherited_style: &ComputedValues,
   4605    input_word: &nsTArray<structs::RefPtr<nsAtom>>,
   4606    raw_data: &PerDocumentStyleData,
   4607 ) -> Strong<ComputedValues> {
   4608    let element = GeckoElement(element);
   4609    let data = element
   4610        .borrow_data()
   4611        .expect("Calling ResolveXULTreePseudoStyle on unstyled element?");
   4612 
   4613    let pseudo = unsafe {
   4614        Atom::with(pseudo_tag, |atom| {
   4615            PseudoElement::from_tree_pseudo_atom(atom, Box::new([]))
   4616        })
   4617        .expect("ResolveXULTreePseudoStyle with a non-tree pseudo?")
   4618    };
   4619    let doc_data = raw_data.borrow();
   4620 
   4621    debug!(
   4622        "ResolveXULTreePseudoStyle: {:?} {:?} {}",
   4623        element,
   4624        pseudo,
   4625        debug_atom_array(input_word)
   4626    );
   4627 
   4628    let matching_fn = |pseudo: &PseudoElement| {
   4629        let args = pseudo
   4630            .tree_pseudo_args()
   4631            .expect("Not a tree pseudo-element?");
   4632        args.iter()
   4633            .all(|atom| input_word.iter().any(|item| atom.as_ptr() == item.mRawPtr))
   4634    };
   4635 
   4636    let global_style_data = &*GLOBAL_STYLE_DATA;
   4637    let guard = global_style_data.shared_lock.read();
   4638    get_pseudo_style(
   4639        &guard,
   4640        element,
   4641        &pseudo,
   4642        RuleInclusion::All,
   4643        &data.styles,
   4644        Some(inherited_style),
   4645        &doc_data.stylist,
   4646        /* is_probe = */ false,
   4647        Some(&matching_fn),
   4648    )
   4649    .unwrap()
   4650    .into()
   4651 }
   4652 
   4653 #[no_mangle]
   4654 pub extern "C" fn Servo_SetExplicitStyle(element: &RawGeckoElement, style: &ComputedValues) {
   4655    let element = GeckoElement(element);
   4656    debug!("Servo_SetExplicitStyle: {:?}", element);
   4657    // We only support this API for initial styling. There's no reason it couldn't
   4658    // work for other things, we just haven't had a reason to do so.
   4659    debug_assert!(!element.has_data());
   4660    let mut data = unsafe { element.ensure_data() };
   4661    data.styles.primary = Some(unsafe { Arc::from_raw_addrefed(style) });
   4662 }
   4663 
   4664 fn get_pseudo_style(
   4665    guard: &SharedRwLockReadGuard,
   4666    element: GeckoElement,
   4667    pseudo: &PseudoElement,
   4668    rule_inclusion: RuleInclusion,
   4669    styles: &ElementStyles,
   4670    inherited_styles: Option<&ComputedValues>,
   4671    stylist: &Stylist,
   4672    is_probe: bool,
   4673    matching_func: Option<&dyn Fn(&PseudoElement) -> bool>,
   4674 ) -> Option<Arc<ComputedValues>> {
   4675    let style = match pseudo.cascade_type() {
   4676        PseudoElementCascadeType::Eager => {
   4677            match *pseudo {
   4678                PseudoElement::FirstLetter => {
   4679                    styles.pseudos.get(&pseudo).map(|pseudo_styles| {
   4680                        // inherited_styles can be None when doing lazy resolution
   4681                        // (e.g. for computed style) or when probing.  In that case
   4682                        // we just inherit from our element, which is what Gecko
   4683                        // does in that situation.  What should actually happen in
   4684                        // the computed style case is a bit unclear.
   4685                        let inherited_styles = inherited_styles.unwrap_or(styles.primary());
   4686                        let guards = StylesheetGuards::same(guard);
   4687                        let inputs = CascadeInputs::new_from_style(pseudo_styles);
   4688                        stylist.compute_pseudo_element_style_with_inputs(
   4689                            inputs,
   4690                            pseudo,
   4691                            &guards,
   4692                            Some(inherited_styles),
   4693                            Some(element),
   4694                        )
   4695                    })
   4696                },
   4697                _ => {
   4698                    // Unfortunately, we can't assert that inherited_styles, if
   4699                    // present, is pointer-equal to styles.primary(), or even
   4700                    // equal in any meaningful way.  The way it can fail is as
   4701                    // follows.  Say we append an element with a ::before,
   4702                    // ::after, or ::first-line to a parent with a ::first-line,
   4703                    // such that the element ends up on the first line of the
   4704                    // parent (e.g. it's an inline-block in the case it has a
   4705                    // ::first-line, or any container in the ::before/::after
   4706                    // cases).  Then gecko will update its frame's style to
   4707                    // inherit from the parent's ::first-line.  The next time we
   4708                    // try to get the ::before/::after/::first-line style for
   4709                    // the kid, we'll likely pass in the frame's style as
   4710                    // inherited_styles, but that's not pointer-identical to
   4711                    // styles.primary(), because it got reparented.
   4712                    //
   4713                    // Now in practice this turns out to be OK, because all the
   4714                    // cases in which there's a mismatch go ahead and reparent
   4715                    // styles again as needed to make sure the ::first-line
   4716                    // affects all the things it should affect.  But it makes it
   4717                    // impossible to assert anything about the two styles
   4718                    // matching here, unfortunately.
   4719                    styles.pseudos.get(&pseudo).cloned()
   4720                },
   4721            }
   4722        },
   4723        PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
   4724        PseudoElementCascadeType::Lazy => {
   4725            debug_assert!(
   4726                inherited_styles.is_none()
   4727                    || ptr::eq(inherited_styles.unwrap(), &**styles.primary())
   4728            );
   4729            let originating_element_style = styles.primary();
   4730            let guards = StylesheetGuards::same(guard);
   4731            stylist.lazily_compute_pseudo_element_style(
   4732                &guards,
   4733                element,
   4734                &pseudo,
   4735                rule_inclusion,
   4736                originating_element_style,
   4737                is_probe,
   4738                matching_func,
   4739            )
   4740        },
   4741    };
   4742 
   4743    if is_probe {
   4744        return style;
   4745    }
   4746 
   4747    Some(style.unwrap_or_else(|| {
   4748        StyleBuilder::for_inheritance(
   4749            stylist.device(),
   4750            Some(stylist),
   4751            Some(styles.primary()),
   4752            Some(pseudo),
   4753        )
   4754        .build()
   4755    }))
   4756 }
   4757 
   4758 #[no_mangle]
   4759 pub unsafe extern "C" fn Servo_ComputedValues_Inherit(
   4760    raw_data: &PerDocumentStyleData,
   4761    pseudo: PseudoStyleType,
   4762    parent_style_context: Option<&ComputedValues>,
   4763    target: structs::InheritTarget,
   4764 ) -> Strong<ComputedValues> {
   4765    let data = raw_data.borrow();
   4766 
   4767    let for_text = target == structs::InheritTarget::Text;
   4768    let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
   4769    debug_assert!(pseudo.is_anon_box());
   4770 
   4771    let mut style = StyleBuilder::for_inheritance(
   4772        data.stylist.device(),
   4773        Some(&data.stylist),
   4774        parent_style_context,
   4775        Some(&pseudo),
   4776    );
   4777 
   4778    if for_text {
   4779        StyleAdjuster::new(&mut style).adjust_for_text();
   4780    }
   4781 
   4782    style.build().into()
   4783 }
   4784 
   4785 #[no_mangle]
   4786 pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(
   4787    values: &ComputedValues,
   4788 ) -> bool {
   4789    let ui = values.get_ui();
   4790    ui.specifies_animations() || ui.specifies_transitions()
   4791 }
   4792 
   4793 #[repr(u8)]
   4794 pub enum MatchingDeclarationBlockOrigin {
   4795    UserAgent,
   4796    User,
   4797    Author,
   4798    PresHints,
   4799    PositionFallback,
   4800    Animations,
   4801    Transitions,
   4802    SMIL,
   4803 }
   4804 
   4805 #[repr(C)]
   4806 pub struct MatchingDeclarationBlock {
   4807    block: *const LockedDeclarationBlock,
   4808    origin: MatchingDeclarationBlockOrigin,
   4809 }
   4810 
   4811 #[no_mangle]
   4812 pub extern "C" fn Servo_ComputedValues_GetMatchingDeclarations(
   4813    values: &ComputedValues,
   4814    rules: &mut nsTArray<MatchingDeclarationBlock>,
   4815 ) {
   4816    use style::rule_tree::CascadeLevel;
   4817    let rule_node = match values.rules {
   4818        Some(ref r) => r,
   4819        None => return,
   4820    };
   4821 
   4822    for node in rule_node.self_and_ancestors() {
   4823        // For the rules with any important declaration, we insert them into
   4824        // rule tree twice, one for normal level and another for important
   4825        // level. So, we skip the important one to keep the specificity order of
   4826        // rules.
   4827        if node.importance().important() {
   4828            continue;
   4829        }
   4830 
   4831        let Some(source) = node.style_source() else {
   4832            continue;
   4833        };
   4834 
   4835        let origin = match node.cascade_level() {
   4836            CascadeLevel::UANormal | CascadeLevel::UAImportant => {
   4837                MatchingDeclarationBlockOrigin::UserAgent
   4838            },
   4839            CascadeLevel::UserNormal | CascadeLevel::UserImportant => {
   4840                MatchingDeclarationBlockOrigin::User
   4841            },
   4842            CascadeLevel::AuthorNormal { .. } | CascadeLevel::AuthorImportant { .. } => {
   4843                MatchingDeclarationBlockOrigin::Author
   4844            },
   4845            CascadeLevel::PositionFallback => MatchingDeclarationBlockOrigin::PositionFallback,
   4846            CascadeLevel::PresHints => MatchingDeclarationBlockOrigin::PresHints,
   4847            CascadeLevel::Animations => MatchingDeclarationBlockOrigin::Animations,
   4848            CascadeLevel::Transitions => MatchingDeclarationBlockOrigin::Transitions,
   4849            CascadeLevel::SMILOverride => MatchingDeclarationBlockOrigin::SMIL,
   4850        };
   4851 
   4852        rules.push(MatchingDeclarationBlock {
   4853            block: &**source.get(),
   4854            origin,
   4855        });
   4856    }
   4857 }
   4858 
   4859 /// println_stderr!() calls Gecko's printf_stderr(), which, unlike eprintln!(),
   4860 /// will funnel output to Android logcat.
   4861 #[cfg(feature = "gecko_debug")]
   4862 macro_rules! println_stderr {
   4863    ($($e:expr),+) => {
   4864        {
   4865            let mut s = nsCString::new();
   4866            write!(s, $($e),+).unwrap();
   4867            s.write_char('\n').unwrap();
   4868            unsafe { bindings::Gecko_PrintfStderr(&s); }
   4869        }
   4870    }
   4871 }
   4872 
   4873 #[cfg(feature = "gecko_debug")]
   4874 fn dump_properties_and_rules(cv: &ComputedValues, properties: &LonghandIdSet) {
   4875    println_stderr!("  Properties:");
   4876    for p in properties.iter() {
   4877        let mut v = nsCString::new();
   4878        cv.computed_or_resolved_value(p, None, &mut v).unwrap();
   4879        println_stderr!("    {:?}: {}", p, v);
   4880    }
   4881    dump_rules(cv);
   4882 }
   4883 
   4884 #[cfg(feature = "gecko_debug")]
   4885 fn dump_rules(cv: &ComputedValues) {
   4886    println_stderr!("  Rules({:?}):", cv.pseudo());
   4887    if let Some(rules) = cv.rules.as_ref() {
   4888        for rn in rules.self_and_ancestors() {
   4889            if rn.importance().important() {
   4890                continue;
   4891            }
   4892            if let Some(d) = rn.style_source() {
   4893                println_stderr!("    {:?}", d.get());
   4894            }
   4895        }
   4896    }
   4897 }
   4898 
   4899 #[cfg(feature = "gecko_debug")]
   4900 #[no_mangle]
   4901 pub extern "C" fn Servo_ComputedValues_EqualForCachedAnonymousContentStyle(
   4902    a: &ComputedValues,
   4903    b: &ComputedValues,
   4904 ) -> bool {
   4905    let mut differing_properties = a.differing_properties(b);
   4906 
   4907    // Ignore any difference in -x-lang, which we can't override in the rules in scrollbars.css,
   4908    // but which makes no difference for the anonymous content subtrees we cache style for.
   4909    differing_properties.remove(LonghandId::XLang);
   4910    // Similarly, -x-lang can influence the font-family fallback we have for the initial
   4911    // font-family so remove it as well.
   4912    differing_properties.remove(LonghandId::FontFamily);
   4913    // We reset font-size to an explicit pixel value, and thus it can get affected by our inherited
   4914    // effective zoom. But we don't care about it for the same reason as above.
   4915    differing_properties.remove(LonghandId::FontSize);
   4916 
   4917    // Ignore any difference in pref-controlled, inherited properties.  These properties may or may
   4918    // not be set by the 'all' declaration in scrollbars.css, depending on whether the pref was
   4919    // enabled at the time the UA sheets were parsed.
   4920    //
   4921    // If you add a new pref-controlled, inherited property, it must be defined with
   4922    // `has_effect_on_gecko_scrollbars=False` to declare that different values of this property on
   4923    // a <scrollbar> element or its descendant scrollbar part elements should have no effect on
   4924    // their rendering and behavior.
   4925    //
   4926    // If you do need a pref-controlled, inherited property to have an effect on these elements,
   4927    // then you will need to add some checks to the
   4928    // nsIAnonymousContentCreator::CreateAnonymousContent implementations of ScrollContainerFrame
   4929    // and nsScrollbarFrame to clear the AnonymousContentKey if a non-initial value is used.
   4930    differing_properties.remove_all(&LonghandIdSet::has_no_effect_on_gecko_scrollbars());
   4931 
   4932    if !differing_properties.is_empty() {
   4933        println_stderr!("Actual style:");
   4934        dump_properties_and_rules(a, &differing_properties);
   4935        println_stderr!("Expected style:");
   4936        dump_properties_and_rules(b, &differing_properties);
   4937    }
   4938 
   4939    differing_properties.is_empty()
   4940 }
   4941 
   4942 #[cfg(feature = "gecko_debug")]
   4943 #[no_mangle]
   4944 pub extern "C" fn Servo_ComputedValues_DumpMatchedRules(s: &ComputedValues) {
   4945    dump_rules(s);
   4946 }
   4947 
   4948 #[no_mangle]
   4949 pub extern "C" fn Servo_ComputedValues_BlockifiedDisplay(
   4950    style: &ComputedValues,
   4951    is_root_element: bool,
   4952 ) -> u16 {
   4953    let display = style.get_box().clone_display();
   4954    let blockified_display = display.equivalent_block_display(is_root_element);
   4955    blockified_display.to_u16()
   4956 }
   4957 
   4958 #[no_mangle]
   4959 pub extern "C" fn Servo_StyleSet_Init(doc: &structs::Document) -> *mut PerDocumentStyleData {
   4960    let data = Box::new(PerDocumentStyleData::new(doc));
   4961 
   4962    // Do this here rather than in Servo_Initialize since we need a document to
   4963    // get the default computed values from.
   4964    style::properties::generated::gecko::assert_initial_values_match(&data);
   4965 
   4966    Box::into_raw(data) as *mut PerDocumentStyleData
   4967 }
   4968 
   4969 #[no_mangle]
   4970 pub unsafe extern "C" fn Servo_StyleSet_Drop(data: *mut PerDocumentStyleData) {
   4971    let _ = Box::from_raw(data);
   4972 }
   4973 
   4974 #[no_mangle]
   4975 pub extern "C" fn Servo_StyleSet_RebuildCachedData(raw_data: &PerDocumentStyleData) {
   4976    let mut data = raw_data.borrow_mut();
   4977    data.stylist.device_mut().rebuild_cached_data();
   4978    data.undisplayed_style_cache.clear();
   4979 }
   4980 
   4981 #[no_mangle]
   4982 pub unsafe extern "C" fn Servo_StyleSet_CompatModeChanged(raw_data: &PerDocumentStyleData) {
   4983    let mut data = raw_data.borrow_mut();
   4984    let quirks_mode = data.stylist.device().document().mCompatMode;
   4985    data.stylist.set_quirks_mode(quirks_mode.into());
   4986 }
   4987 
   4988 fn parse_property_into(
   4989    declarations: &mut SourcePropertyDeclaration,
   4990    property_id: PropertyId,
   4991    value: &nsACString,
   4992    origin: Origin,
   4993    url_data: &UrlExtraData,
   4994    parsing_mode: ParsingMode,
   4995    quirks_mode: QuirksMode,
   4996    rule_type: CssRuleType,
   4997    reporter: Option<&dyn ParseErrorReporter>,
   4998 ) -> Result<(), ()> {
   4999    let value = unsafe { value.as_str_unchecked() };
   5000 
   5001    if let Some(non_custom) = property_id.non_custom_id() {
   5002        if !non_custom.allowed_in_rule(rule_type.into()) {
   5003            return Err(());
   5004        }
   5005    }
   5006 
   5007    parse_one_declaration_into(
   5008        declarations,
   5009        property_id,
   5010        value,
   5011        origin,
   5012        url_data,
   5013        reporter,
   5014        parsing_mode,
   5015        quirks_mode,
   5016        rule_type,
   5017    )
   5018 }
   5019 
   5020 #[no_mangle]
   5021 pub unsafe extern "C" fn Servo_ParseProperty(
   5022    property: &structs::CSSPropertyId,
   5023    value: &nsACString,
   5024    data: *mut URLExtraData,
   5025    parsing_mode: ParsingMode,
   5026    quirks_mode: nsCompatibility,
   5027    loader: *mut Loader,
   5028    rule_type: CssRuleType,
   5029 ) -> Strong<LockedDeclarationBlock> {
   5030    let id = get_property_id_from_csspropertyid!(property, Strong::null());
   5031    let mut declarations = SourcePropertyDeclaration::default();
   5032    let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
   5033    let data = UrlExtraData::from_ptr_ref(&data);
   5034    let result = parse_property_into(
   5035        &mut declarations,
   5036        id,
   5037        value,
   5038        Origin::Author,
   5039        data,
   5040        parsing_mode,
   5041        quirks_mode.into(),
   5042        rule_type,
   5043        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
   5044    );
   5045 
   5046    match result {
   5047        Ok(()) => {
   5048            let global_style_data = &*GLOBAL_STYLE_DATA;
   5049            let mut block = PropertyDeclarationBlock::new();
   5050            block.extend(declarations.drain(), Importance::Normal);
   5051            Arc::new(global_style_data.shared_lock.wrap(block)).into()
   5052        },
   5053        Err(_) => Strong::null(),
   5054    }
   5055 }
   5056 
   5057 #[no_mangle]
   5058 pub extern "C" fn Servo_ParseEasing(
   5059    easing: &nsACString,
   5060    output: &mut ComputedTimingFunction,
   5061 ) -> bool {
   5062    use style::properties::longhands::transition_timing_function;
   5063 
   5064    let context = ParserContext::new(
   5065        Origin::Author,
   5066        unsafe { dummy_url_data() },
   5067        Some(CssRuleType::Style),
   5068        ParsingMode::DEFAULT,
   5069        QuirksMode::NoQuirks,
   5070        /* namespaces = */ Default::default(),
   5071        None,
   5072        None,
   5073    );
   5074    let easing = easing.to_string();
   5075    let mut input = ParserInput::new(&easing);
   5076    let mut parser = Parser::new(&mut input);
   5077    let result =
   5078        parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
   5079    match result {
   5080        Ok(parsed_easing) => {
   5081            *output = parsed_easing.to_computed_value_without_context();
   5082            true
   5083        },
   5084        Err(_) => false,
   5085    }
   5086 }
   5087 
   5088 #[no_mangle]
   5089 pub extern "C" fn Servo_SerializeEasing(easing: &ComputedTimingFunction, output: &mut nsACString) {
   5090    easing.to_css(&mut CssWriter::new(output)).unwrap();
   5091 }
   5092 
   5093 #[no_mangle]
   5094 pub extern "C" fn Servo_GetProperties_Overriding_Animation(
   5095    element: &RawGeckoElement,
   5096    list: &nsTArray<NonCustomCSSPropertyId>,
   5097    set: &mut structs::nsCSSPropertyIDSet,
   5098 ) {
   5099    let element = GeckoElement(element);
   5100    let element_data = match element.borrow_data() {
   5101        Some(data) => data,
   5102        None => return,
   5103    };
   5104    let global_style_data = &*GLOBAL_STYLE_DATA;
   5105    let guard = global_style_data.shared_lock.read();
   5106    let guards = StylesheetGuards::same(&guard);
   5107    let (overridden, custom) = element_data
   5108        .styles
   5109        .primary()
   5110        .rules()
   5111        .get_properties_overriding_animations(&guards);
   5112    for p in list.iter() {
   5113        match NonCustomPropertyId::from_noncustomcsspropertyid(*p) {
   5114            Some(property) => {
   5115                if let Some(id) = property.as_longhand() {
   5116                    if overridden.contains(id) {
   5117                        unsafe { Gecko_AddPropertyToSet(set, *p) };
   5118                    }
   5119                }
   5120            },
   5121            None => {
   5122                if *p == NonCustomCSSPropertyId::eCSSPropertyExtra_variable && custom {
   5123                    unsafe { Gecko_AddPropertyToSet(set, *p) };
   5124                }
   5125            },
   5126        }
   5127    }
   5128 }
   5129 
   5130 #[no_mangle]
   5131 pub extern "C" fn Servo_MatrixTransform_Operate(
   5132    interpolate: bool,
   5133    from: &structs::Matrix4x4Components,
   5134    to: &structs::Matrix4x4Components,
   5135    progress: f64,
   5136    output: &mut structs::Matrix4x4Components,
   5137 ) {
   5138    use style::values::computed::transform::Matrix3D;
   5139 
   5140    let from = Matrix3D::from(from);
   5141    let to = Matrix3D::from(to);
   5142    let proc = if interpolate {
   5143        Procedure::Interpolate { progress }
   5144    } else {
   5145        Procedure::Accumulate {
   5146            count: progress as u64,
   5147        }
   5148    };
   5149    let result = from.animate(&to, proc);
   5150    if let Ok(result) = result {
   5151        *output = result.into();
   5152    } else if progress < 0.5 {
   5153        *output = from.clone().into();
   5154    } else {
   5155        *output = to.clone().into();
   5156    }
   5157 }
   5158 
   5159 #[no_mangle]
   5160 pub unsafe extern "C" fn Servo_ParseStyleAttribute(
   5161    data: &nsACString,
   5162    raw_extra_data: *mut URLExtraData,
   5163    quirks_mode: nsCompatibility,
   5164    loader: *mut Loader,
   5165    rule_type: CssRuleType,
   5166 ) -> Strong<LockedDeclarationBlock> {
   5167    let global_style_data = &*GLOBAL_STYLE_DATA;
   5168    let value = data.as_str_unchecked();
   5169    let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
   5170    let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
   5171    Arc::new(global_style_data.shared_lock.wrap(parse_style_attribute(
   5172        value,
   5173        url_data,
   5174        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
   5175        quirks_mode.into(),
   5176        rule_type,
   5177    )))
   5178    .into()
   5179 }
   5180 
   5181 #[no_mangle]
   5182 pub extern "C" fn Servo_ParsePseudoElement(
   5183    data: &nsAString,
   5184    request: &mut structs::PseudoStyleRequest, /* output */
   5185 ) -> bool {
   5186    let string = data.to_string();
   5187    let mut input = ParserInput::new(&string);
   5188    let mut parser = Parser::new(&mut input);
   5189    // This is unspecced, but we'd like to match other browsers' behavior, so we reject the
   5190    // preceding whitespaces and trailing whitespaces.
   5191    // FIXME: Bug 1845712. Figure out if it is necessary to reject preceding and trailing
   5192    // whitespaces.
   5193    if parser.try_parse(|i| i.expect_whitespace()).is_ok() {
   5194        return false;
   5195    }
   5196    let Ok(pseudo) = PseudoElement::parse_ignore_enabled_state(&mut parser) else {
   5197        return false;
   5198    };
   5199    // The trailing tokens are not allowed, including whitespaces.
   5200    if parser.next_including_whitespace().is_ok() {
   5201        return false;
   5202    }
   5203 
   5204    let (pseudo_type, name) = pseudo.pseudo_type_and_argument();
   5205    let name_ptr = name.map_or(std::ptr::null_mut(), |name| name.as_ptr());
   5206    request.mType = pseudo_type;
   5207    request.mIdentifier = unsafe { RefPtr::new(name_ptr).forget() };
   5208 
   5209    true
   5210 }
   5211 
   5212 #[no_mangle]
   5213 pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> Strong<LockedDeclarationBlock> {
   5214    let global_style_data = &*GLOBAL_STYLE_DATA;
   5215    Arc::new(
   5216        global_style_data
   5217            .shared_lock
   5218            .wrap(PropertyDeclarationBlock::new()),
   5219    )
   5220    .into()
   5221 }
   5222 
   5223 #[no_mangle]
   5224 pub extern "C" fn Servo_DeclarationBlock_Clear(declarations: &LockedDeclarationBlock) {
   5225    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   5226        decls.clear();
   5227    });
   5228 }
   5229 
   5230 #[no_mangle]
   5231 pub extern "C" fn Servo_DeclarationBlock_Clone(
   5232    declarations: &LockedDeclarationBlock,
   5233 ) -> Strong<LockedDeclarationBlock> {
   5234    let global_style_data = &*GLOBAL_STYLE_DATA;
   5235    let guard = global_style_data.shared_lock.read();
   5236    Arc::new(
   5237        global_style_data
   5238            .shared_lock
   5239            .wrap(declarations.read_with(&guard).clone()),
   5240    )
   5241    .into()
   5242 }
   5243 
   5244 #[no_mangle]
   5245 pub extern "C" fn Servo_DeclarationBlock_Equals(
   5246    a: &LockedDeclarationBlock,
   5247    b: &LockedDeclarationBlock,
   5248 ) -> bool {
   5249    let global_style_data = &*GLOBAL_STYLE_DATA;
   5250    let guard = global_style_data.shared_lock.read();
   5251    a.read_with(&guard).declarations() == b.read_with(&guard).declarations()
   5252 }
   5253 
   5254 #[no_mangle]
   5255 pub extern "C" fn Servo_DeclarationBlock_GetCssText(
   5256    declarations: &LockedDeclarationBlock,
   5257    result: &mut nsACString,
   5258 ) {
   5259    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5260        decls.to_css(result).unwrap()
   5261    })
   5262 }
   5263 
   5264 #[no_mangle]
   5265 pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue(
   5266    decls: &LockedDeclarationBlock,
   5267    property_id: &structs::CSSPropertyId,
   5268    buffer: &mut nsACString,
   5269    computed_values: Option<&ComputedValues>,
   5270    data: &PerDocumentStyleData,
   5271 ) {
   5272    let property_id = get_property_id_from_csspropertyid!(property_id, ());
   5273 
   5274    let global_style_data = &*GLOBAL_STYLE_DATA;
   5275    let guard = global_style_data.shared_lock.read();
   5276    let data = data.borrow();
   5277    let rv = decls.read_with(&guard).single_value_to_css(
   5278        &property_id,
   5279        buffer,
   5280        computed_values,
   5281        &data.stylist,
   5282    );
   5283    debug_assert!(rv.is_ok());
   5284 }
   5285 
   5286 #[no_mangle]
   5287 pub unsafe extern "C" fn Servo_SerializeFontValueForCanvas(
   5288    declarations: &LockedDeclarationBlock,
   5289    buffer: &mut nsACString,
   5290 ) {
   5291    use style::properties::shorthands::font;
   5292    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5293        let longhands = match font::LonghandsToSerialize::from_iter(decls.declarations().iter()) {
   5294            Ok(l) => l,
   5295            Err(()) => {
   5296                warn!("Unexpected property!");
   5297                return;
   5298            },
   5299        };
   5300 
   5301        let rv = longhands.to_css(&mut CssWriter::new(buffer));
   5302        debug_assert!(rv.is_ok());
   5303    })
   5304 }
   5305 
   5306 #[no_mangle]
   5307 pub extern "C" fn Servo_DeclarationBlock_Count(declarations: &LockedDeclarationBlock) -> u32 {
   5308    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5309        decls.declarations().len() as u32
   5310    })
   5311 }
   5312 
   5313 #[no_mangle]
   5314 pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(
   5315    declarations: &LockedDeclarationBlock,
   5316    index: u32,
   5317    result: &mut nsACString,
   5318 ) -> bool {
   5319    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5320        if let Some(decl) = decls.declarations().get(index as usize) {
   5321            result.assign(&decl.id().name());
   5322            true
   5323        } else {
   5324            false
   5325        }
   5326    })
   5327 }
   5328 
   5329 macro_rules! get_property_id_from_property {
   5330    ($property: ident, $ret: expr) => {{
   5331        let property = $property.as_str_unchecked();
   5332        match PropertyId::parse_enabled_for_all_content(property) {
   5333            Ok(property_id) => property_id,
   5334            Err(_) => return $ret,
   5335        }
   5336    }};
   5337 }
   5338 
   5339 unsafe fn get_property_value(
   5340    declarations: &LockedDeclarationBlock,
   5341    property_id: PropertyId,
   5342    value: &mut nsACString,
   5343 ) {
   5344    // This callsite is hot enough that the lock acquisition shows up in profiles.
   5345    // Using an unchecked read here improves our performance by ~10% on the
   5346    // microbenchmark in bug 1355599.
   5347    read_locked_arc_unchecked(declarations, |decls: &PropertyDeclarationBlock| {
   5348        decls.property_value_to_css(&property_id, value).unwrap();
   5349    })
   5350 }
   5351 
   5352 #[no_mangle]
   5353 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValue(
   5354    declarations: &LockedDeclarationBlock,
   5355    property: &nsACString,
   5356    value: &mut nsACString,
   5357 ) {
   5358    get_property_value(
   5359        declarations,
   5360        get_property_id_from_property!(property, ()),
   5361        value,
   5362    )
   5363 }
   5364 
   5365 #[no_mangle]
   5366 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValueByNonCustomId(
   5367    declarations: &LockedDeclarationBlock,
   5368    property_id: NonCustomCSSPropertyId,
   5369    value: &mut nsACString,
   5370 ) {
   5371    get_property_value(
   5372        declarations,
   5373        get_property_id_from_noncustomcsspropertyid!(property_id, ()),
   5374        value,
   5375    )
   5376 }
   5377 
   5378 #[no_mangle]
   5379 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValueById(
   5380    declarations: &LockedDeclarationBlock,
   5381    property_id: &structs::CSSPropertyId,
   5382    value: &mut nsACString,
   5383 ) {
   5384    get_property_value(
   5385        declarations,
   5386        get_property_id_from_csspropertyid!(property_id, ()),
   5387        value,
   5388    )
   5389 }
   5390 
   5391 #[no_mangle]
   5392 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(
   5393    declarations: &LockedDeclarationBlock,
   5394    property: &nsACString,
   5395 ) -> bool {
   5396    let property_id = get_property_id_from_property!(property, false);
   5397    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5398        decls.property_priority(&property_id).important()
   5399    })
   5400 }
   5401 
   5402 /// A FFI-friendly counterpart to [`PropertyTypedValue`].
   5403 ///
   5404 /// This type is returned across the Rust <-> C++ boundary. It mirrors
   5405 /// [`PropertyTypedValue`], but with one important difference:
   5406 ///
   5407 /// * Internally, [`PropertyTypedValue::Unsupported`] is just a marker.
   5408 /// * Here, `PropertyTypedValueResult::Unsupported` carries a
   5409 ///   `Strong<LockedDeclarationBlock>`.
   5410 /// This is mostly because `cbindgen` currently cannot generate right bindings
   5411 /// for `Arc<Locked<PropertyDeclarationBlock>>` inside the Rust enum.
   5412 #[repr(C)]
   5413 pub enum PropertyTypedValueResult {
   5414    /// The property is not present in the declaration block.
   5415    None,
   5416 
   5417    /// The property exists but cannot be expressed as a `TypedValue`.
   5418    ///
   5419    /// Used for shorthands and other unrepresentable cases. In this case, the
   5420    /// full declaration block is returned so that a corresponding
   5421    /// `CSSUnsupportedValue` object can be created and tied to the property.
   5422    Unsupported(Strong<LockedDeclarationBlock>),
   5423 
   5424    /// The property was successfully reified into a `TypedValue`.
   5425    Typed(TypedValue),
   5426 }
   5427 
   5428 #[no_mangle]
   5429 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyTypedValue(
   5430    declarations: &LockedDeclarationBlock,
   5431    property: &nsACString,
   5432    result: *mut PropertyTypedValueResult,
   5433 ) -> bool {
   5434    let property_id = get_property_id_from_property!(property, false);
   5435 
   5436    *result = read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5437        let property_typed_value = decls.property_value_to_typed(&property_id);
   5438 
   5439        match property_typed_value {
   5440            PropertyTypedValue::None => PropertyTypedValueResult::None,
   5441 
   5442            PropertyTypedValue::Unsupported => {
   5443                let global_style_data = &*GLOBAL_STYLE_DATA;
   5444                PropertyTypedValueResult::Unsupported(
   5445                    Arc::new(global_style_data.shared_lock.wrap(decls.clone())).into(),
   5446                )
   5447            },
   5448 
   5449            PropertyTypedValue::Typed(typed_value) => PropertyTypedValueResult::Typed(typed_value),
   5450        }
   5451    });
   5452 
   5453    true
   5454 }
   5455 
   5456 #[inline(always)]
   5457 fn set_property_to_declarations(
   5458    non_custom_property_id: Option<NonCustomPropertyId>,
   5459    block: &LockedDeclarationBlock,
   5460    parsed_declarations: &mut SourcePropertyDeclaration,
   5461    before_change_closure: DeclarationBlockMutationClosure,
   5462    importance: Importance,
   5463 ) -> bool {
   5464    let mut updates = Default::default();
   5465    let will_change = read_locked_arc(block, |decls: &PropertyDeclarationBlock| {
   5466        decls.prepare_for_update(&parsed_declarations, importance, &mut updates)
   5467    });
   5468    if !will_change {
   5469        return false;
   5470    }
   5471 
   5472    before_change_closure.invoke(non_custom_property_id);
   5473    write_locked_arc(block, |decls: &mut PropertyDeclarationBlock| {
   5474        decls.update(parsed_declarations.drain(), importance, &mut updates)
   5475    });
   5476    true
   5477 }
   5478 
   5479 fn set_property(
   5480    declarations: &LockedDeclarationBlock,
   5481    property_id: PropertyId,
   5482    value: &nsACString,
   5483    is_important: bool,
   5484    data: &UrlExtraData,
   5485    parsing_mode: ParsingMode,
   5486    quirks_mode: QuirksMode,
   5487    loader: *mut Loader,
   5488    rule_type: CssRuleType,
   5489    before_change_closure: DeclarationBlockMutationClosure,
   5490 ) -> bool {
   5491    let mut source_declarations = SourcePropertyDeclaration::default();
   5492    let reporter = ErrorReporter::new(ptr::null_mut(), loader, data.ptr());
   5493    let non_custom_property_id = property_id.non_custom_id();
   5494    let result = parse_property_into(
   5495        &mut source_declarations,
   5496        property_id,
   5497        value,
   5498        Origin::Author,
   5499        data,
   5500        parsing_mode,
   5501        quirks_mode,
   5502        rule_type,
   5503        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
   5504    );
   5505 
   5506    if result.is_err() {
   5507        return false;
   5508    }
   5509 
   5510    let importance = if is_important {
   5511        Importance::Important
   5512    } else {
   5513        Importance::Normal
   5514    };
   5515 
   5516    set_property_to_declarations(
   5517        non_custom_property_id,
   5518        declarations,
   5519        &mut source_declarations,
   5520        before_change_closure,
   5521        importance,
   5522    )
   5523 }
   5524 
   5525 #[no_mangle]
   5526 pub unsafe extern "C" fn Servo_DeclarationBlock_SanitizeForCanvas(
   5527    declarations: &LockedDeclarationBlock,
   5528 ) {
   5529    use style::properties::PropertyDeclaration;
   5530    use style::values::specified::{LineHeight, XTextScale, Zoom};
   5531    // From canvas spec, force to set line-height property to 'normal' font property.
   5532    // Also, for compat, disable text scaling and CSS zoom.
   5533    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   5534        decls.push(
   5535            PropertyDeclaration::LineHeight(LineHeight::Normal),
   5536            Importance::Normal,
   5537        );
   5538        decls.push(
   5539            PropertyDeclaration::Zoom(Zoom::Document),
   5540            Importance::Normal,
   5541        );
   5542        decls.push(
   5543            PropertyDeclaration::XTextScale(XTextScale::None),
   5544            Importance::Normal,
   5545        );
   5546    });
   5547 }
   5548 
   5549 #[no_mangle]
   5550 pub unsafe extern "C" fn Servo_DeclarationBlock_SetProperty(
   5551    declarations: &LockedDeclarationBlock,
   5552    property: &nsACString,
   5553    value: &nsACString,
   5554    is_important: bool,
   5555    data: *mut URLExtraData,
   5556    parsing_mode: ParsingMode,
   5557    quirks_mode: nsCompatibility,
   5558    loader: *mut Loader,
   5559    rule_type: CssRuleType,
   5560    before_change_closure: DeclarationBlockMutationClosure,
   5561 ) -> bool {
   5562    set_property(
   5563        declarations,
   5564        get_property_id_from_property!(property, false),
   5565        value,
   5566        is_important,
   5567        UrlExtraData::from_ptr_ref(&data),
   5568        parsing_mode,
   5569        quirks_mode.into(),
   5570        loader,
   5571        rule_type,
   5572        before_change_closure,
   5573    )
   5574 }
   5575 
   5576 #[no_mangle]
   5577 pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue(
   5578    declarations: &LockedDeclarationBlock,
   5579    animation_value: &AnimationValue,
   5580    before_change_closure: DeclarationBlockMutationClosure,
   5581 ) -> bool {
   5582    let non_custom_property_id = match animation_value.id() {
   5583        PropertyDeclarationId::Longhand(id) => Some(id.into()),
   5584        PropertyDeclarationId::Custom(_) => None,
   5585    };
   5586    let mut source_declarations = SourcePropertyDeclaration::with_one(animation_value.uncompute());
   5587 
   5588    set_property_to_declarations(
   5589        non_custom_property_id,
   5590        declarations,
   5591        &mut source_declarations,
   5592        before_change_closure,
   5593        Importance::Normal,
   5594    )
   5595 }
   5596 
   5597 #[no_mangle]
   5598 pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyById(
   5599    declarations: &LockedDeclarationBlock,
   5600    property: NonCustomCSSPropertyId,
   5601    value: &nsACString,
   5602    is_important: bool,
   5603    data: *mut URLExtraData,
   5604    parsing_mode: ParsingMode,
   5605    quirks_mode: nsCompatibility,
   5606    loader: *mut Loader,
   5607    rule_type: CssRuleType,
   5608    before_change_closure: DeclarationBlockMutationClosure,
   5609 ) -> bool {
   5610    set_property(
   5611        declarations,
   5612        get_property_id_from_noncustomcsspropertyid!(property, false),
   5613        value,
   5614        is_important,
   5615        UrlExtraData::from_ptr_ref(&data),
   5616        parsing_mode,
   5617        quirks_mode.into(),
   5618        loader,
   5619        rule_type,
   5620        before_change_closure,
   5621    )
   5622 }
   5623 
   5624 fn remove_property(
   5625    declarations: &LockedDeclarationBlock,
   5626    property_id: PropertyId,
   5627    before_change_closure: DeclarationBlockMutationClosure,
   5628 ) -> bool {
   5629    let first_declaration = read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5630        decls.first_declaration_to_remove(&property_id)
   5631    });
   5632 
   5633    let first_declaration = match first_declaration {
   5634        Some(i) => i,
   5635        None => return false,
   5636    };
   5637 
   5638    before_change_closure.invoke(property_id.non_custom_id());
   5639    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   5640        decls.remove_property(&property_id, first_declaration)
   5641    });
   5642 
   5643    true
   5644 }
   5645 
   5646 #[no_mangle]
   5647 pub unsafe extern "C" fn Servo_DeclarationBlock_RemoveProperty(
   5648    declarations: &LockedDeclarationBlock,
   5649    property: &nsACString,
   5650    before_change_closure: DeclarationBlockMutationClosure,
   5651 ) -> bool {
   5652    remove_property(
   5653        declarations,
   5654        get_property_id_from_property!(property, false),
   5655        before_change_closure,
   5656    )
   5657 }
   5658 
   5659 #[no_mangle]
   5660 pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(
   5661    declarations: &LockedDeclarationBlock,
   5662    property: NonCustomCSSPropertyId,
   5663    before_change_closure: DeclarationBlockMutationClosure,
   5664 ) -> bool {
   5665    remove_property(
   5666        declarations,
   5667        get_property_id_from_noncustomcsspropertyid!(property, false),
   5668        before_change_closure,
   5669    )
   5670 }
   5671 
   5672 #[no_mangle]
   5673 pub extern "C" fn Servo_MediaList_Create() -> Strong<LockedMediaList> {
   5674    let global_style_data = &*GLOBAL_STYLE_DATA;
   5675    Arc::new(global_style_data.shared_lock.wrap(MediaList::empty())).into()
   5676 }
   5677 
   5678 #[no_mangle]
   5679 pub extern "C" fn Servo_MediaList_DeepClone(list: &LockedMediaList) -> Strong<LockedMediaList> {
   5680    let global_style_data = &*GLOBAL_STYLE_DATA;
   5681    read_locked_arc(list, |list: &MediaList| {
   5682        Arc::new(global_style_data.shared_lock.wrap(list.clone())).into()
   5683    })
   5684 }
   5685 
   5686 #[no_mangle]
   5687 pub extern "C" fn Servo_MediaList_Matches(
   5688    list: &LockedMediaList,
   5689    raw_data: &PerDocumentStyleData,
   5690 ) -> bool {
   5691    let per_doc_data = raw_data.borrow();
   5692    read_locked_arc(list, |list: &MediaList| {
   5693        list.evaluate(
   5694            per_doc_data.stylist.device(),
   5695            per_doc_data.stylist.quirks_mode(),
   5696            &mut CustomMediaEvaluator::none(),
   5697        )
   5698    })
   5699 }
   5700 
   5701 #[no_mangle]
   5702 pub extern "C" fn Servo_DeclarationBlock_HasCSSWideKeyword(
   5703    declarations: &LockedDeclarationBlock,
   5704    property: NonCustomCSSPropertyId,
   5705 ) -> bool {
   5706    let property_id = get_property_id_from_noncustomcsspropertyid!(property, false);
   5707    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5708        decls.has_css_wide_keyword(&property_id)
   5709    })
   5710 }
   5711 
   5712 #[no_mangle]
   5713 pub extern "C" fn Servo_MediaList_GetText(list: &LockedMediaList, result: &mut nsACString) {
   5714    read_locked_arc(list, |list: &MediaList| {
   5715        list.to_css(&mut CssWriter::new(result)).unwrap();
   5716    })
   5717 }
   5718 
   5719 #[no_mangle]
   5720 pub unsafe extern "C" fn Servo_MediaList_SetText(
   5721    list: &LockedMediaList,
   5722    text: &nsACString,
   5723    caller_type: CallerType,
   5724 ) {
   5725    let text = text.as_str_unchecked();
   5726 
   5727    let mut input = ParserInput::new(&text);
   5728    let mut parser = Parser::new(&mut input);
   5729    let url_data = dummy_url_data();
   5730 
   5731    // TODO(emilio): If the need for `CallerType` appears in more places,
   5732    // consider adding an explicit member in `ParserContext` instead of doing
   5733    // this (or adding a dummy "chrome://" url data).
   5734    //
   5735    // For media query parsing it's effectively the same, so for now...
   5736    let origin = match caller_type {
   5737        CallerType::System => Origin::UserAgent,
   5738        CallerType::NonSystem => Origin::Author,
   5739    };
   5740 
   5741    let context = ParserContext::new(
   5742        origin,
   5743        url_data,
   5744        Some(CssRuleType::Media),
   5745        ParsingMode::DEFAULT,
   5746        QuirksMode::NoQuirks,
   5747        /* namespaces = */ Default::default(),
   5748        // TODO(emilio): Looks like error reporting could be useful here?
   5749        None,
   5750        None,
   5751    );
   5752 
   5753    write_locked_arc(list, |list: &mut MediaList| {
   5754        *list = MediaList::parse(&context, &mut parser);
   5755    })
   5756 }
   5757 
   5758 #[no_mangle]
   5759 pub extern "C" fn Servo_MediaList_IsViewportDependent(list: &LockedMediaList) -> bool {
   5760    read_locked_arc(list, |list: &MediaList| list.is_viewport_dependent())
   5761 }
   5762 
   5763 #[no_mangle]
   5764 pub extern "C" fn Servo_MediaList_GetLength(list: &LockedMediaList) -> u32 {
   5765    read_locked_arc(list, |list: &MediaList| list.media_queries.len() as u32)
   5766 }
   5767 
   5768 #[no_mangle]
   5769 pub extern "C" fn Servo_MediaList_GetMediumAt(
   5770    list: &LockedMediaList,
   5771    index: u32,
   5772    result: &mut nsACString,
   5773 ) -> bool {
   5774    read_locked_arc(list, |list: &MediaList| {
   5775        let media_query = match list.media_queries.get(index as usize) {
   5776            Some(mq) => mq,
   5777            None => return false,
   5778        };
   5779        media_query.to_css(&mut CssWriter::new(result)).unwrap();
   5780        true
   5781    })
   5782 }
   5783 
   5784 #[no_mangle]
   5785 pub extern "C" fn Servo_MediaList_AppendMedium(list: &LockedMediaList, new_medium: &nsACString) {
   5786    let new_medium = unsafe { new_medium.as_str_unchecked() };
   5787    let url_data = unsafe { dummy_url_data() };
   5788    let context = ParserContext::new(
   5789        Origin::Author,
   5790        url_data,
   5791        Some(CssRuleType::Media),
   5792        ParsingMode::DEFAULT,
   5793        QuirksMode::NoQuirks,
   5794        /* namespaces = */ Default::default(),
   5795        None,
   5796        None,
   5797    );
   5798    write_locked_arc(list, |list: &mut MediaList| {
   5799        list.append_medium(&context, new_medium);
   5800    })
   5801 }
   5802 
   5803 #[no_mangle]
   5804 pub extern "C" fn Servo_MediaList_DeleteMedium(
   5805    list: &LockedMediaList,
   5806    old_medium: &nsACString,
   5807 ) -> bool {
   5808    let old_medium = unsafe { old_medium.as_str_unchecked() };
   5809    let url_data = unsafe { dummy_url_data() };
   5810    let context = ParserContext::new(
   5811        Origin::Author,
   5812        url_data,
   5813        Some(CssRuleType::Media),
   5814        ParsingMode::DEFAULT,
   5815        QuirksMode::NoQuirks,
   5816        /* namespaces = */ Default::default(),
   5817        None,
   5818        None,
   5819    );
   5820    write_locked_arc(list, |list: &mut MediaList| {
   5821        list.delete_medium(&context, old_medium)
   5822    })
   5823 }
   5824 
   5825 #[no_mangle]
   5826 pub extern "C" fn Servo_MediaList_SizeOfIncludingThis(
   5827    malloc_size_of: GeckoMallocSizeOf,
   5828    malloc_enclosing_size_of: GeckoMallocSizeOf,
   5829    list: &LockedMediaList,
   5830 ) -> usize {
   5831    use malloc_size_of::MallocSizeOf;
   5832    use malloc_size_of::MallocUnconditionalShallowSizeOf;
   5833 
   5834    let global_style_data = &*GLOBAL_STYLE_DATA;
   5835    let guard = global_style_data.shared_lock.read();
   5836 
   5837    let mut ops = MallocSizeOfOps::new(
   5838        malloc_size_of.unwrap(),
   5839        Some(malloc_enclosing_size_of.unwrap()),
   5840        None,
   5841    );
   5842 
   5843    unsafe { ArcBorrow::from_ref(list) }.with_arc(|list| {
   5844        let mut n = 0;
   5845        n += list.unconditional_shallow_size_of(&mut ops);
   5846        n += list.read_with(&guard).size_of(&mut ops);
   5847        n
   5848    })
   5849 }
   5850 
   5851 macro_rules! get_longhand_from_id {
   5852    ($id:expr) => {
   5853        match LonghandId::from_noncustomcsspropertyid($id) {
   5854            Some(lh) => lh,
   5855            _ => panic!("stylo: unknown presentation property with id"),
   5856        }
   5857    };
   5858 }
   5859 
   5860 macro_rules! match_wrap_declared {
   5861    ($longhand:ident, $($property:ident => $inner:expr,)*) => (
   5862        match $longhand {
   5863            $(
   5864                LonghandId::$property => PropertyDeclaration::$property($inner),
   5865            )*
   5866            _ => {
   5867                panic!("stylo: Don't know how to handle presentation property");
   5868            }
   5869        }
   5870    )
   5871 }
   5872 
   5873 #[no_mangle]
   5874 pub extern "C" fn Servo_DeclarationBlock_PropertyIsSet(
   5875    declarations: &LockedDeclarationBlock,
   5876    property: NonCustomCSSPropertyId,
   5877 ) -> bool {
   5878    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5879        decls.contains(PropertyDeclarationId::Longhand(get_longhand_from_id!(
   5880            property
   5881        )))
   5882    })
   5883 }
   5884 
   5885 #[no_mangle]
   5886 pub unsafe extern "C" fn Servo_DeclarationBlock_HasLonghandProperty(
   5887    declarations: &LockedDeclarationBlock,
   5888    property: &nsACString,
   5889 ) -> bool {
   5890    read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
   5891        let prop_name = property.as_str_unchecked();
   5892        if let Ok(property_id) = PropertyId::parse_unchecked(prop_name, None) {
   5893            if let Err(longhand_or_custom) = property_id.as_shorthand() {
   5894                return decls.contains(longhand_or_custom);
   5895            }
   5896        }
   5897 
   5898        false
   5899    })
   5900 }
   5901 
   5902 #[no_mangle]
   5903 pub unsafe extern "C" fn Servo_DeclarationBlock_SetIdentStringValue(
   5904    declarations: &LockedDeclarationBlock,
   5905    property: NonCustomCSSPropertyId,
   5906    value: *mut nsAtom,
   5907 ) {
   5908    use style::properties::longhands::_x_lang::computed_value::T as Lang;
   5909    use style::properties::PropertyDeclaration;
   5910 
   5911    let long = get_longhand_from_id!(property);
   5912    let prop = match_wrap_declared! { long,
   5913        XLang => Lang(Atom::from_raw(value)),
   5914    };
   5915    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   5916        decls.push(prop, Importance::Normal);
   5917    })
   5918 }
   5919 
   5920 #[no_mangle]
   5921 #[allow(unreachable_code)]
   5922 pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(
   5923    declarations: &LockedDeclarationBlock,
   5924    property: NonCustomCSSPropertyId,
   5925    value: i32,
   5926 ) -> bool {
   5927    use num_traits::FromPrimitive;
   5928    use style::properties::longhands;
   5929    use style::properties::PropertyDeclaration;
   5930    use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword};
   5931    use style::values::generics::font::FontStyle;
   5932    use style::values::specified::{
   5933        table::CaptionSide, BorderStyle, Clear, Display, Float, TextAlign, TextEmphasisPosition,
   5934        TextTransform,
   5935    };
   5936 
   5937    fn get_from_computed<T>(value: u32) -> T
   5938    where
   5939        T: ToComputedValue,
   5940        T::ComputedValue: FromPrimitive,
   5941    {
   5942        T::from_computed_value(&T::ComputedValue::from_u32(value).unwrap())
   5943    }
   5944 
   5945    let long = get_longhand_from_id!(property);
   5946    let value = value as u32;
   5947 
   5948    let prop = match_wrap_declared! { long,
   5949        Direction => get_from_computed::<longhands::direction::SpecifiedValue>(value),
   5950        Display => get_from_computed::<Display>(value),
   5951        Float => get_from_computed::<Float>(value),
   5952        Clear => get_from_computed::<Clear>(value),
   5953        VerticalAlign => VerticalAlign::Keyword(VerticalAlignKeyword::from_u32(value).unwrap()),
   5954        TextAlign => get_from_computed::<TextAlign>(value),
   5955        TextEmphasisPosition => TextEmphasisPosition::from_bits_retain(value as u8),
   5956        FontSize => {
   5957            // We rely on Gecko passing in font-size values (0...7) here.
   5958            longhands::font_size::SpecifiedValue::from_html_size(value as u8)
   5959        },
   5960        FontStyle => {
   5961            specified::FontStyle::Specified(if value == structs::NS_FONT_STYLE_ITALIC {
   5962                FontStyle::Italic
   5963            } else {
   5964                debug_assert_eq!(value, structs::NS_FONT_STYLE_NORMAL);
   5965                FontStyle::normal()
   5966            })
   5967        },
   5968        FontWeight => longhands::font_weight::SpecifiedValue::from_gecko_keyword(value),
   5969        ListStyleType => longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value),
   5970        MathStyle => longhands::math_style::SpecifiedValue::from_gecko_keyword(value),
   5971        MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value),
   5972        WhiteSpaceCollapse => get_from_computed::<longhands::white_space_collapse::SpecifiedValue>(value),
   5973        TextWrapMode => get_from_computed::<longhands::text_wrap_mode::SpecifiedValue>(value),
   5974        CaptionSide => get_from_computed::<CaptionSide>(value),
   5975        BorderTopStyle => get_from_computed::<BorderStyle>(value),
   5976        BorderRightStyle => get_from_computed::<BorderStyle>(value),
   5977        BorderBottomStyle => get_from_computed::<BorderStyle>(value),
   5978        BorderLeftStyle => get_from_computed::<BorderStyle>(value),
   5979        TextTransform => {
   5980            debug_assert_eq!(value, 0);
   5981            TextTransform::NONE
   5982        },
   5983        WritingMode => get_from_computed::<longhands::writing_mode::SpecifiedValue>(value),
   5984        ContentVisibility => get_from_computed::<ContentVisibility>(value),
   5985        TextOrientation => get_from_computed::<longhands::text_orientation::SpecifiedValue>(value),
   5986        MixBlendMode => get_from_computed::<longhands::mix_blend_mode::SpecifiedValue>(value),
   5987    };
   5988    let mut source_declarations = SourcePropertyDeclaration::with_one(prop);
   5989    set_property_to_declarations(
   5990        Some(long.into()),
   5991        declarations,
   5992        &mut source_declarations,
   5993        NO_MUTATION_CLOSURE,
   5994        Importance::Normal,
   5995    )
   5996 }
   5997 
   5998 #[no_mangle]
   5999 pub extern "C" fn Servo_DeclarationBlock_SetIntValue(
   6000    declarations: &LockedDeclarationBlock,
   6001    property: NonCustomCSSPropertyId,
   6002    value: i32,
   6003 ) {
   6004    use style::properties::PropertyDeclaration;
   6005    use style::values::specified::Integer;
   6006 
   6007    let long = get_longhand_from_id!(property);
   6008    let prop = match_wrap_declared! { long,
   6009        XSpan => Integer::new(value),
   6010    };
   6011    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6012        decls.push(prop, Importance::Normal);
   6013    })
   6014 }
   6015 
   6016 #[no_mangle]
   6017 pub extern "C" fn Servo_DeclarationBlock_SetMathDepthValue(
   6018    declarations: &LockedDeclarationBlock,
   6019    value: i32,
   6020    is_relative: bool,
   6021 ) {
   6022    use style::properties::longhands::math_depth::SpecifiedValue as MathDepth;
   6023    use style::properties::PropertyDeclaration;
   6024 
   6025    let integer_value = style::values::specified::Integer::new(value);
   6026    let prop = PropertyDeclaration::MathDepth(if is_relative {
   6027        MathDepth::Add(integer_value)
   6028    } else {
   6029        MathDepth::Absolute(integer_value)
   6030    });
   6031    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6032        decls.push(prop, Importance::Normal);
   6033    })
   6034 }
   6035 
   6036 #[no_mangle]
   6037 pub extern "C" fn Servo_DeclarationBlock_SetCounterResetListItem(
   6038    declarations: &LockedDeclarationBlock,
   6039    counter_value: i32,
   6040    is_reversed: bool,
   6041 ) {
   6042    use style::properties::PropertyDeclaration;
   6043    use style::values::generics::counters::{CounterPair, CounterReset};
   6044 
   6045    let prop = PropertyDeclaration::CounterReset(CounterReset::new(vec![CounterPair {
   6046        name: CustomIdent(atom!("list-item")),
   6047        value: style::values::specified::Integer::new(counter_value),
   6048        is_reversed,
   6049    }]));
   6050    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6051        decls.push(prop, Importance::Normal);
   6052    })
   6053 }
   6054 
   6055 #[no_mangle]
   6056 pub extern "C" fn Servo_DeclarationBlock_SetCounterSetListItem(
   6057    declarations: &LockedDeclarationBlock,
   6058    counter_value: i32,
   6059 ) {
   6060    use style::properties::PropertyDeclaration;
   6061    use style::values::generics::counters::{CounterPair, CounterSet};
   6062 
   6063    let prop = PropertyDeclaration::CounterSet(CounterSet::new(vec![CounterPair {
   6064        name: CustomIdent(atom!("list-item")),
   6065        value: style::values::specified::Integer::new(counter_value),
   6066        is_reversed: false,
   6067    }]));
   6068    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6069        decls.push(prop, Importance::Normal);
   6070    })
   6071 }
   6072 
   6073 #[no_mangle]
   6074 pub extern "C" fn Servo_DeclarationBlock_SetPixelValue(
   6075    declarations: &LockedDeclarationBlock,
   6076    property: NonCustomCSSPropertyId,
   6077    value: f32,
   6078 ) {
   6079    use style::properties::longhands::border_spacing::SpecifiedValue as BorderSpacing;
   6080    use style::properties::PropertyDeclaration;
   6081    use style::values::generics::length::{GenericMargin, Size};
   6082    use style::values::generics::NonNegative;
   6083    use style::values::specified::length::{
   6084        LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
   6085    };
   6086    use style::values::specified::{BorderCornerRadius, BorderSideWidth};
   6087 
   6088    let long = get_longhand_from_id!(property);
   6089    let nocalc = NoCalcLength::from_px(value);
   6090    let lp = LengthPercentage::Length(nocalc);
   6091    let margin = GenericMargin::LengthPercentage(lp.clone());
   6092    let prop = match_wrap_declared! { long,
   6093        Height => Size::LengthPercentage(NonNegative(lp)),
   6094        Width => Size::LengthPercentage(NonNegative(lp)),
   6095        BorderTopWidth => BorderSideWidth::from_px(value),
   6096        BorderRightWidth => BorderSideWidth::from_px(value),
   6097        BorderBottomWidth => BorderSideWidth::from_px(value),
   6098        BorderLeftWidth => BorderSideWidth::from_px(value),
   6099        MarginTop => margin,
   6100        MarginRight => margin,
   6101        MarginBottom => margin,
   6102        MarginLeft => margin,
   6103        PaddingTop => NonNegative(lp),
   6104        PaddingRight => NonNegative(lp),
   6105        PaddingBottom => NonNegative(lp),
   6106        PaddingLeft => NonNegative(lp),
   6107        BorderSpacing => {
   6108            let v = NonNegativeLength::from(nocalc);
   6109            Box::new(BorderSpacing::new(v.clone(), v))
   6110        },
   6111        BorderTopLeftRadius => {
   6112            let length = NonNegativeLengthPercentage::from(nocalc);
   6113            Box::new(BorderCornerRadius::new(length.clone(), length))
   6114        },
   6115        BorderTopRightRadius => {
   6116            let length = NonNegativeLengthPercentage::from(nocalc);
   6117            Box::new(BorderCornerRadius::new(length.clone(), length))
   6118        },
   6119        BorderBottomLeftRadius => {
   6120            let length = NonNegativeLengthPercentage::from(nocalc);
   6121            Box::new(BorderCornerRadius::new(length.clone(), length))
   6122        },
   6123        BorderBottomRightRadius => {
   6124            let length = NonNegativeLengthPercentage::from(nocalc);
   6125            Box::new(BorderCornerRadius::new(length.clone(), length))
   6126        },
   6127    };
   6128    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6129        decls.push(prop, Importance::Normal);
   6130    })
   6131 }
   6132 
   6133 #[no_mangle]
   6134 pub extern "C" fn Servo_DeclarationBlock_SetLengthValue(
   6135    declarations: &LockedDeclarationBlock,
   6136    property: NonCustomCSSPropertyId,
   6137    value: f32,
   6138    unit: structs::nsCSSUnit,
   6139 ) -> bool {
   6140    use style::properties::PropertyDeclaration;
   6141    use style::values::generics::length::{LengthPercentageOrAuto, Size};
   6142    use style::values::generics::NonNegative;
   6143    use style::values::specified::length::{
   6144        FontRelativeLength, LengthPercentage, ViewportPercentageLength,
   6145    };
   6146    use style::values::specified::FontSize;
   6147 
   6148    let long = get_longhand_from_id!(property);
   6149    let nocalc = match unit {
   6150        structs::nsCSSUnit::eCSSUnit_EM => {
   6151            NoCalcLength::FontRelative(FontRelativeLength::Em(value))
   6152        },
   6153        structs::nsCSSUnit::eCSSUnit_XHeight => {
   6154            NoCalcLength::FontRelative(FontRelativeLength::Ex(value))
   6155        },
   6156        structs::nsCSSUnit::eCSSUnit_RootEM => {
   6157            NoCalcLength::FontRelative(FontRelativeLength::Rem(value))
   6158        },
   6159        structs::nsCSSUnit::eCSSUnit_Char => {
   6160            NoCalcLength::FontRelative(FontRelativeLength::Ch(value))
   6161        },
   6162        structs::nsCSSUnit::eCSSUnit_Ideographic => {
   6163            NoCalcLength::FontRelative(FontRelativeLength::Ic(value))
   6164        },
   6165        structs::nsCSSUnit::eCSSUnit_CapHeight => {
   6166            NoCalcLength::FontRelative(FontRelativeLength::Cap(value))
   6167        },
   6168        structs::nsCSSUnit::eCSSUnit_LineHeight => {
   6169            NoCalcLength::FontRelative(FontRelativeLength::Lh(value))
   6170        },
   6171        structs::nsCSSUnit::eCSSUnit_RootLineHeight => {
   6172            NoCalcLength::FontRelative(FontRelativeLength::Rlh(value))
   6173        },
   6174        structs::nsCSSUnit::eCSSUnit_Pixel => NoCalcLength::Absolute(AbsoluteLength::Px(value)),
   6175        structs::nsCSSUnit::eCSSUnit_Inch => NoCalcLength::Absolute(AbsoluteLength::In(value)),
   6176        structs::nsCSSUnit::eCSSUnit_Centimeter => {
   6177            NoCalcLength::Absolute(AbsoluteLength::Cm(value))
   6178        },
   6179        structs::nsCSSUnit::eCSSUnit_Millimeter => {
   6180            NoCalcLength::Absolute(AbsoluteLength::Mm(value))
   6181        },
   6182        structs::nsCSSUnit::eCSSUnit_Point => NoCalcLength::Absolute(AbsoluteLength::Pt(value)),
   6183        structs::nsCSSUnit::eCSSUnit_Pica => NoCalcLength::Absolute(AbsoluteLength::Pc(value)),
   6184        structs::nsCSSUnit::eCSSUnit_Quarter => NoCalcLength::Absolute(AbsoluteLength::Q(value)),
   6185        structs::nsCSSUnit::eCSSUnit_VW => {
   6186            NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value))
   6187        },
   6188        structs::nsCSSUnit::eCSSUnit_VH => {
   6189            NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value))
   6190        },
   6191        structs::nsCSSUnit::eCSSUnit_VMin => {
   6192            NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value))
   6193        },
   6194        structs::nsCSSUnit::eCSSUnit_VMax => {
   6195            NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value))
   6196        },
   6197        _ => unreachable!("Unknown unit passed to SetLengthValue"),
   6198    };
   6199 
   6200    let mut source_declarations = SourcePropertyDeclaration::with_one(match_wrap_declared! { long,
   6201        Width => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
   6202        Height => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
   6203        X =>  LengthPercentage::Length(nocalc),
   6204        Y =>  LengthPercentage::Length(nocalc),
   6205        Cx => LengthPercentage::Length(nocalc),
   6206        Cy => LengthPercentage::Length(nocalc),
   6207        R =>  NonNegative(LengthPercentage::Length(nocalc)),
   6208        Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
   6209        Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
   6210        FontSize => FontSize::Length(LengthPercentage::Length(nocalc)),
   6211    });
   6212    set_property_to_declarations(
   6213        Some(long.into()),
   6214        declarations,
   6215        &mut source_declarations,
   6216        NO_MUTATION_CLOSURE,
   6217        Importance::Normal,
   6218    )
   6219 }
   6220 
   6221 #[no_mangle]
   6222 pub extern "C" fn Servo_DeclarationBlock_SetTransform(
   6223    declarations: &LockedDeclarationBlock,
   6224    property: NonCustomCSSPropertyId,
   6225    ops: &nsTArray<computed::TransformOperation>,
   6226 ) -> bool {
   6227    use style::properties::PropertyDeclaration;
   6228    use style::values::generics::transform::GenericTransform;
   6229    let long = get_longhand_from_id!(property);
   6230    let v = GenericTransform(
   6231        ops.iter()
   6232            .map(ToComputedValue::from_computed_value)
   6233            .collect(),
   6234    );
   6235    let prop = match_wrap_declared! { long,
   6236        Transform => v,
   6237    };
   6238    let mut source_declarations = SourcePropertyDeclaration::with_one(prop);
   6239    set_property_to_declarations(
   6240        Some(long.into()),
   6241        declarations,
   6242        &mut source_declarations,
   6243        NO_MUTATION_CLOSURE,
   6244        Importance::Normal,
   6245    )
   6246 }
   6247 
   6248 #[no_mangle]
   6249 pub extern "C" fn Servo_DeclarationBlock_SetBackdropFilter(
   6250    declarations: &LockedDeclarationBlock,
   6251    property: NonCustomCSSPropertyId,
   6252    filters: &style::OwnedSlice<Filter>,
   6253 ) -> bool {
   6254    use style::properties::longhands::backdrop_filter::SpecifiedValue as BackdropFilters;
   6255    use style::properties::PropertyDeclaration;
   6256    let long = get_longhand_from_id!(property);
   6257    let v = BackdropFilters(
   6258        filters
   6259            .iter()
   6260            .map(ToComputedValue::from_computed_value)
   6261            .collect(),
   6262    );
   6263    let prop = match_wrap_declared! { long,
   6264        BackdropFilter => v,
   6265    };
   6266    let mut source_declarations = SourcePropertyDeclaration::with_one(prop);
   6267    set_property_to_declarations(
   6268        Some(long.into()),
   6269        declarations,
   6270        &mut source_declarations,
   6271        NO_MUTATION_CLOSURE,
   6272        Importance::Normal,
   6273    )
   6274 }
   6275 
   6276 #[no_mangle]
   6277 pub extern "C" fn Servo_DeclarationBlock_SetColorScheme(
   6278    declarations: &LockedDeclarationBlock,
   6279    property: NonCustomCSSPropertyId,
   6280    color_scheme: &style::values::computed::ColorScheme,
   6281 ) -> bool {
   6282    use style::properties::PropertyDeclaration;
   6283    use style::values::specified::ColorScheme;
   6284    let long = get_longhand_from_id!(property);
   6285    let prop = match_wrap_declared! { long,
   6286        ColorScheme => ColorScheme::from_computed_value(color_scheme),
   6287    };
   6288    let mut source_declarations = SourcePropertyDeclaration::with_one(prop);
   6289    set_property_to_declarations(
   6290        Some(long.into()),
   6291        declarations,
   6292        &mut source_declarations,
   6293        NO_MUTATION_CLOSURE,
   6294        Importance::Normal,
   6295    )
   6296 }
   6297 
   6298 #[no_mangle]
   6299 pub extern "C" fn Servo_DeclarationBlock_SetPathValue(
   6300    declarations: &LockedDeclarationBlock,
   6301    property: NonCustomCSSPropertyId,
   6302    path: &specified::SVGPathData,
   6303 ) {
   6304    use style::properties::PropertyDeclaration;
   6305    use style::values::specified::DProperty;
   6306 
   6307    // 2. Set decoded path into style.
   6308    let long = get_longhand_from_id!(property);
   6309    let prop = match_wrap_declared! { long,
   6310        D => if path.0.is_empty() { DProperty::None } else { DProperty::Path(path.clone()) },
   6311    };
   6312    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6313        decls.push(prop, Importance::Normal);
   6314    })
   6315 }
   6316 
   6317 #[no_mangle]
   6318 pub extern "C" fn Servo_CreatePathDataFromCommands(
   6319    path_commands: &mut nsTArray<PathCommand>,
   6320    dest: &mut specified::SVGPathData,
   6321 ) {
   6322    let path = specified::SVGPathData(style_traits::arc_slice::ArcSlice::from_iter(
   6323        path_commands.drain(..),
   6324    ));
   6325    *dest = path;
   6326 }
   6327 
   6328 #[no_mangle]
   6329 pub extern "C" fn Servo_SVGPathData_Add(
   6330    dest: &mut specified::SVGPathData,
   6331    to_add: &specified::SVGPathData,
   6332    count: u32,
   6333 ) -> bool {
   6334    match dest.animate(
   6335        to_add,
   6336        Procedure::Accumulate {
   6337            count: count as u64,
   6338        },
   6339    ) {
   6340        Ok(r) => {
   6341            *dest = r;
   6342            true
   6343        },
   6344        Err(..) => false,
   6345    }
   6346 }
   6347 
   6348 #[no_mangle]
   6349 pub extern "C" fn Servo_SVGPathData_Parse(
   6350    input: &nsACString,
   6351    dest: &mut specified::SVGPathData,
   6352 ) -> bool {
   6353    let (path, ret) = specified::SVGPathData::parse_bytes(input.as_ref());
   6354    *dest = path;
   6355    ret
   6356 }
   6357 
   6358 #[no_mangle]
   6359 pub extern "C" fn Servo_SVGPathData_NormalizeAndReduce(
   6360    input: &specified::SVGPathData,
   6361    dest: &mut specified::SVGPathData,
   6362 ) {
   6363    let path = input.normalize(true);
   6364    *dest = path;
   6365 }
   6366 
   6367 #[no_mangle]
   6368 pub extern "C" fn Servo_SVGPathData_ToString(path: &specified::SVGPathData, dest: &mut nsACString) {
   6369    path.to_css(&mut CssWriter::new(dest), /* quote = */ false)
   6370        .unwrap();
   6371 }
   6372 
   6373 #[no_mangle]
   6374 pub extern "C" fn Servo_SVGPathData_Interpolate(
   6375    left: Option<&specified::SVGPathData>,
   6376    right: &specified::SVGPathData,
   6377    progress: f64,
   6378    dest: &mut specified::SVGPathData,
   6379 ) -> bool {
   6380    let zero;
   6381    let left = match left {
   6382        Some(l) => l,
   6383        None => {
   6384            zero = match right.to_animated_zero() {
   6385                Ok(z) => z,
   6386                Err(..) => {
   6387                    debug_assert!(false, "how?");
   6388                    return false;
   6389                },
   6390            };
   6391            &zero
   6392        },
   6393    };
   6394 
   6395    match left.animate(right, Procedure::Interpolate { progress }) {
   6396        Ok(r) => {
   6397            *dest = r;
   6398            true
   6399        },
   6400        Err(..) => false,
   6401    }
   6402 }
   6403 
   6404 #[no_mangle]
   6405 pub extern "C" fn Servo_DeclarationBlock_SetPercentValue(
   6406    declarations: &LockedDeclarationBlock,
   6407    property: NonCustomCSSPropertyId,
   6408    value: f32,
   6409 ) {
   6410    use style::properties::PropertyDeclaration;
   6411    use style::values::computed::Percentage;
   6412    use style::values::generics::length::{GenericMargin, LengthPercentageOrAuto, Size};
   6413    use style::values::generics::NonNegative;
   6414    use style::values::specified::length::LengthPercentage;
   6415    use style::values::specified::FontSize;
   6416 
   6417    let long = get_longhand_from_id!(property);
   6418    let pc = Percentage(value);
   6419    let lp = LengthPercentage::Percentage(pc);
   6420    let margin = GenericMargin::LengthPercentage(lp.clone());
   6421 
   6422    let prop = match_wrap_declared! { long,
   6423        Height => Size::LengthPercentage(NonNegative(lp)),
   6424        Width => Size::LengthPercentage(NonNegative(lp)),
   6425        X =>  lp,
   6426        Y =>  lp,
   6427        Cx => lp,
   6428        Cy => lp,
   6429        R =>  NonNegative(lp),
   6430        Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
   6431        Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
   6432        MarginTop => margin,
   6433        MarginRight => margin,
   6434        MarginBottom => margin,
   6435        MarginLeft => margin,
   6436        FontSize => FontSize::Length(LengthPercentage::Percentage(pc)),
   6437    };
   6438    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6439        decls.push(prop, Importance::Normal);
   6440    })
   6441 }
   6442 
   6443 #[no_mangle]
   6444 pub extern "C" fn Servo_DeclarationBlock_SetAutoValue(
   6445    declarations: &LockedDeclarationBlock,
   6446    property: NonCustomCSSPropertyId,
   6447 ) {
   6448    use style::properties::PropertyDeclaration;
   6449    use style::values::generics::length::{GenericMargin, Size};
   6450 
   6451    let long = get_longhand_from_id!(property);
   6452    let auto = GenericMargin::Auto;
   6453 
   6454    let prop = match_wrap_declared! { long,
   6455        Height => Size::auto(),
   6456        Width => Size::auto(),
   6457        MarginTop => auto,
   6458        MarginRight => auto,
   6459        MarginBottom => auto,
   6460        MarginLeft => auto,
   6461        AspectRatio => specified::AspectRatio::auto(),
   6462    };
   6463    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6464        decls.push(prop, Importance::Normal);
   6465    })
   6466 }
   6467 
   6468 #[no_mangle]
   6469 pub extern "C" fn Servo_DeclarationBlock_SetCurrentColor(
   6470    declarations: &LockedDeclarationBlock,
   6471    property: NonCustomCSSPropertyId,
   6472 ) {
   6473    use style::properties::PropertyDeclaration;
   6474    use style::values::specified::Color;
   6475 
   6476    let long = get_longhand_from_id!(property);
   6477    let cc = Color::currentcolor();
   6478 
   6479    let prop = match_wrap_declared! { long,
   6480        BorderTopColor => cc,
   6481        BorderRightColor => cc,
   6482        BorderBottomColor => cc,
   6483        BorderLeftColor => cc,
   6484    };
   6485    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6486        decls.push(prop, Importance::Normal);
   6487    })
   6488 }
   6489 
   6490 #[no_mangle]
   6491 pub extern "C" fn Servo_DeclarationBlock_SetColorValue(
   6492    declarations: &LockedDeclarationBlock,
   6493    property: NonCustomCSSPropertyId,
   6494    value: structs::nscolor,
   6495 ) {
   6496    use style::gecko::values::convert_nscolor_to_absolute_color;
   6497    use style::properties::longhands;
   6498    use style::properties::PropertyDeclaration;
   6499    use style::values::specified::Color;
   6500 
   6501    let long = get_longhand_from_id!(property);
   6502    let rgba = convert_nscolor_to_absolute_color(value);
   6503    let color = Color::from_absolute_color(rgba);
   6504 
   6505    let prop = match_wrap_declared! { long,
   6506        BorderTopColor => color,
   6507        BorderRightColor => color,
   6508        BorderBottomColor => color,
   6509        BorderLeftColor => color,
   6510        Color => longhands::color::SpecifiedValue(color),
   6511        BackgroundColor => color,
   6512    };
   6513    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6514        decls.push(prop, Importance::Normal);
   6515    })
   6516 }
   6517 
   6518 #[no_mangle]
   6519 pub unsafe extern "C" fn Servo_DeclarationBlock_SetFontFamily(
   6520    declarations: &LockedDeclarationBlock,
   6521    value: &nsACString,
   6522 ) {
   6523    use style::properties::longhands::font_family::SpecifiedValue as FontFamily;
   6524    use style::properties::PropertyDeclaration;
   6525 
   6526    let string = value.as_str_unchecked();
   6527    let mut input = ParserInput::new(&string);
   6528    let mut parser = Parser::new(&mut input);
   6529    let context = ParserContext::new(
   6530        Origin::Author,
   6531        dummy_url_data(),
   6532        Some(CssRuleType::Style),
   6533        ParsingMode::DEFAULT,
   6534        QuirksMode::NoQuirks,
   6535        /* namespaces = */ Default::default(),
   6536        None,
   6537        None,
   6538    );
   6539    let result = FontFamily::parse(&context, &mut parser);
   6540    if let Ok(family) = result {
   6541        if parser.is_exhausted() {
   6542            let decl = PropertyDeclaration::FontFamily(family);
   6543            write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6544                decls.push(decl, Importance::Normal);
   6545            })
   6546        }
   6547    }
   6548 }
   6549 
   6550 #[no_mangle]
   6551 pub unsafe extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(
   6552    declarations: &LockedDeclarationBlock,
   6553    value: &nsACString,
   6554    raw_extra_data: *mut URLExtraData,
   6555 ) {
   6556    use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage;
   6557    use style::properties::PropertyDeclaration;
   6558    use style::stylesheets::CorsMode;
   6559    use style::values::generics::image::Image;
   6560    use style::values::specified::url::SpecifiedUrl;
   6561 
   6562    let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
   6563    let string = value.as_str_unchecked();
   6564    let context = ParserContext::new(
   6565        Origin::Author,
   6566        url_data,
   6567        Some(CssRuleType::Style),
   6568        ParsingMode::DEFAULT,
   6569        QuirksMode::NoQuirks,
   6570        /* namespaces = */ Default::default(),
   6571        None,
   6572        None,
   6573    );
   6574    let url = SpecifiedUrl::parse_from_string(string.into(), &context, CorsMode::None);
   6575    let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(vec![Image::Url(url)].into()));
   6576    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6577        decls.push(decl, Importance::Normal);
   6578    });
   6579 }
   6580 
   6581 #[no_mangle]
   6582 pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(
   6583    declarations: &LockedDeclarationBlock,
   6584 ) {
   6585    use style::properties::PropertyDeclaration;
   6586    use style::values::specified::text::TextDecorationLine;
   6587 
   6588    let decoration = TextDecorationLine::COLOR_OVERRIDE;
   6589    let decl = PropertyDeclaration::TextDecorationLine(decoration);
   6590    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6591        decls.push(decl, Importance::Normal);
   6592    })
   6593 }
   6594 
   6595 #[no_mangle]
   6596 pub extern "C" fn Servo_DeclarationBlock_SetAspectRatio(
   6597    declarations: &LockedDeclarationBlock,
   6598    width: f32,
   6599    height: f32,
   6600 ) {
   6601    use style::properties::PropertyDeclaration;
   6602    use style::values::generics::position::AspectRatio;
   6603 
   6604    let decl = PropertyDeclaration::AspectRatio(AspectRatio::from_mapped_ratio(width, height));
   6605    write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
   6606        decls.push(decl, Importance::Normal);
   6607    })
   6608 }
   6609 
   6610 #[no_mangle]
   6611 pub extern "C" fn Servo_CSSSupports2(property: &nsACString, value: &nsACString) -> bool {
   6612    let id = unsafe { get_property_id_from_property!(property, false) };
   6613 
   6614    let mut declarations = SourcePropertyDeclaration::default();
   6615    parse_property_into(
   6616        &mut declarations,
   6617        id,
   6618        value,
   6619        Origin::Author,
   6620        unsafe { dummy_url_data() },
   6621        ParsingMode::DEFAULT,
   6622        QuirksMode::NoQuirks,
   6623        CssRuleType::Style,
   6624        None,
   6625    )
   6626    .is_ok()
   6627 }
   6628 
   6629 #[no_mangle]
   6630 pub extern "C" fn Servo_CSSSupports(
   6631    cond: &nsACString,
   6632    ua_origin: bool,
   6633    chrome_sheet: bool,
   6634    quirks: bool,
   6635 ) -> bool {
   6636    let condition = unsafe { cond.as_str_unchecked() };
   6637    let mut input = ParserInput::new(&condition);
   6638    let mut input = Parser::new(&mut input);
   6639    let cond = match input.parse_entirely(parse_condition_or_declaration) {
   6640        Ok(c) => c,
   6641        Err(..) => return false,
   6642    };
   6643 
   6644    let origin = if ua_origin {
   6645        Origin::UserAgent
   6646    } else {
   6647        Origin::Author
   6648    };
   6649    let url_data = unsafe {
   6650        if chrome_sheet {
   6651            dummy_chrome_url_data()
   6652        } else {
   6653            dummy_url_data()
   6654        }
   6655    };
   6656    let quirks_mode = if quirks {
   6657        QuirksMode::Quirks
   6658    } else {
   6659        QuirksMode::NoQuirks
   6660    };
   6661 
   6662    // NOTE(emilio): The supports API is not associated to any stylesheet,
   6663    // so the fact that there is no namespace map here is fine.
   6664    let context = ParserContext::new(
   6665        origin,
   6666        url_data,
   6667        Some(CssRuleType::Style),
   6668        ParsingMode::DEFAULT,
   6669        quirks_mode,
   6670        /* namespaces = */ Default::default(),
   6671        None,
   6672        None,
   6673    );
   6674 
   6675    cond.eval(&context)
   6676 }
   6677 
   6678 #[no_mangle]
   6679 pub extern "C" fn Servo_CSSSupportsForImport(after_rule: &nsACString) -> bool {
   6680    let condition = unsafe { after_rule.as_str_unchecked() };
   6681    let mut input = ParserInput::new(&condition);
   6682    let mut input = Parser::new(&mut input);
   6683 
   6684    // NOTE(emilio): The supports API is not associated to any stylesheet,
   6685    // so the fact that there is no namespace map here is fine.
   6686    let mut context = ParserContext::new(
   6687        Origin::Author,
   6688        unsafe { dummy_url_data() },
   6689        Some(CssRuleType::Style),
   6690        ParsingMode::DEFAULT,
   6691        QuirksMode::NoQuirks,
   6692        /* namespaces = */ Default::default(),
   6693        None,
   6694        None,
   6695    );
   6696 
   6697    let (_layer, supports) = ImportRule::parse_layer_and_supports(&mut input, &mut context);
   6698 
   6699    supports.map_or(true, |s| s.enabled)
   6700 }
   6701 
   6702 #[no_mangle]
   6703 pub unsafe extern "C" fn Servo_NoteExplicitHints(
   6704    element: &RawGeckoElement,
   6705    restyle_hint: RestyleHint,
   6706    change_hint: nsChangeHint,
   6707 ) {
   6708    GeckoElement(element).note_explicit_hints(restyle_hint, change_hint);
   6709 }
   6710 
   6711 #[no_mangle]
   6712 pub extern "C" fn Servo_TakeChangeHint(element: &RawGeckoElement, was_restyled: &mut bool) -> u32 {
   6713    let element = GeckoElement(element);
   6714 
   6715    let damage = match element.mutate_data() {
   6716        Some(mut data) => {
   6717            *was_restyled = data.is_restyle();
   6718 
   6719            let damage = data.damage;
   6720            data.clear_restyle_state();
   6721            damage
   6722        },
   6723        None => {
   6724            warn!("Trying to get change hint from unstyled element");
   6725            *was_restyled = false;
   6726            GeckoRestyleDamage::empty()
   6727        },
   6728    };
   6729 
   6730    debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage);
   6731    // We'd like to return `nsChangeHint` here, but bindgen bitfield enums don't
   6732    // work as return values with the Linux 32-bit ABI at the moment because
   6733    // they wrap the value in a struct, so for now just unwrap it.
   6734    damage.as_change_hint().0
   6735 }
   6736 
   6737 #[no_mangle]
   6738 pub extern "C" fn Servo_ResolveStyle(element: &RawGeckoElement) -> Strong<ComputedValues> {
   6739    let element = GeckoElement(element);
   6740    debug!("Servo_ResolveStyle: {:?}", element);
   6741    let data = element
   6742        .borrow_data()
   6743        .expect("Resolving style on unstyled element");
   6744 
   6745    debug_assert!(
   6746        element.has_current_styles(&*data),
   6747        "Resolving style on {:?} without current styles: {:?}",
   6748        element,
   6749        data
   6750    );
   6751    data.styles.primary().clone().into()
   6752 }
   6753 
   6754 #[no_mangle]
   6755 pub extern "C" fn Servo_ResolveStyleLazily(
   6756    element: &RawGeckoElement,
   6757    pseudo_type: PseudoStyleType,
   6758    functional_pseudo_parameter: *mut nsAtom,
   6759    rule_inclusion: StyleRuleInclusion,
   6760    snapshots: *const ServoElementSnapshotTable,
   6761    cache_generation: u64,
   6762    can_use_cache: bool,
   6763    raw_data: &PerDocumentStyleData,
   6764 ) -> Strong<ComputedValues> {
   6765    debug_assert!(!snapshots.is_null());
   6766    let global_style_data = &*GLOBAL_STYLE_DATA;
   6767    let guard = global_style_data.shared_lock.read();
   6768    let element = GeckoElement(element);
   6769    let mut data = raw_data.borrow_mut();
   6770    let data = &mut *data;
   6771    let rule_inclusion = RuleInclusion::from(rule_inclusion);
   6772    let pseudo_element = PseudoElement::from_pseudo_type(
   6773        pseudo_type,
   6774        get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
   6775    );
   6776 
   6777    let matching_fn = |pseudo_selector: &PseudoElement| match pseudo_element {
   6778        Some(ref p) => p.matches(pseudo_selector, &element),
   6779        _ => false,
   6780    };
   6781 
   6782    if cache_generation != data.undisplayed_style_cache_generation {
   6783        data.undisplayed_style_cache.clear();
   6784        data.undisplayed_style_cache_generation = cache_generation;
   6785    }
   6786 
   6787    let stylist = &data.stylist;
   6788    let finish = |styles: &ElementStyles, is_probe: bool| -> Option<Arc<ComputedValues>> {
   6789        match pseudo_element {
   6790            Some(ref pseudo) => {
   6791                get_pseudo_style(
   6792                    &guard,
   6793                    element,
   6794                    pseudo,
   6795                    rule_inclusion,
   6796                    styles,
   6797                    /* inherited_styles = */ None,
   6798                    &stylist,
   6799                    is_probe,
   6800                    if pseudo.is_highlight() || pseudo.is_named_view_transition() {
   6801                        Some(&matching_fn)
   6802                    } else {
   6803                        None
   6804                    },
   6805                )
   6806            },
   6807            None => Some(styles.primary().clone()),
   6808        }
   6809    };
   6810 
   6811    let is_before_or_after = pseudo_element
   6812        .as_ref()
   6813        .map_or(false, |p| p.is_before_or_after());
   6814 
   6815    // In the common case we already have the style. Check that before setting
   6816    // up all the computation machinery.
   6817    //
   6818    // Also, only probe in the ::before or ::after case, since their styles may
   6819    // not be in the `ElementData`, given they may exist but not be applicable
   6820    // to generate an actual pseudo-element (like, having a `content: none`).
   6821    if rule_inclusion == RuleInclusion::All {
   6822        let styles = element.borrow_data().and_then(|d| {
   6823            if d.has_styles() {
   6824                finish(&d.styles, is_before_or_after)
   6825            } else {
   6826                None
   6827            }
   6828        });
   6829        if let Some(result) = styles {
   6830            return result.into();
   6831        }
   6832        if pseudo_element.is_none() && can_use_cache {
   6833            if let Some(style) = data.undisplayed_style_cache.get(&element.opaque()) {
   6834                return style.clone().into();
   6835            }
   6836        }
   6837    }
   6838 
   6839    // We don't have the style ready. Go ahead and compute it as necessary.
   6840    let shared = create_shared_context(
   6841        &global_style_data,
   6842        &guard,
   6843        &stylist,
   6844        TraversalFlags::empty(),
   6845        unsafe { &*snapshots },
   6846    );
   6847    let mut tlc = ThreadLocalStyleContext::new();
   6848    let mut context = StyleContext {
   6849        shared: &shared,
   6850        thread_local: &mut tlc,
   6851    };
   6852 
   6853    let styles = resolve_style(
   6854        &mut context,
   6855        element,
   6856        rule_inclusion,
   6857        pseudo_element.as_ref(),
   6858        if can_use_cache {
   6859            Some(&mut data.undisplayed_style_cache)
   6860        } else {
   6861            None
   6862        },
   6863    );
   6864 
   6865    finish(&styles, /* is_probe = */ false)
   6866        .expect("We're not probing, so we should always get a style back")
   6867        .into()
   6868 }
   6869 
   6870 #[no_mangle]
   6871 pub extern "C" fn Servo_ResolveStartingStyle(
   6872    element: &RawGeckoElement,
   6873    snapshots: *const ServoElementSnapshotTable,
   6874    raw_data: &PerDocumentStyleData,
   6875 ) -> Strong<ComputedValues> {
   6876    use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};
   6877 
   6878    let doc_data = raw_data.borrow();
   6879    let global_style_data = &*GLOBAL_STYLE_DATA;
   6880    let guard = global_style_data.shared_lock.read();
   6881    let shared = create_shared_context(
   6882        &global_style_data,
   6883        &guard,
   6884        &doc_data.stylist,
   6885        TraversalFlags::empty(),
   6886        unsafe { &*snapshots },
   6887    );
   6888    let mut tlc = ThreadLocalStyleContext::new();
   6889    let mut context = StyleContext {
   6890        shared: &shared,
   6891        thread_local: &mut tlc,
   6892    };
   6893 
   6894    let element = GeckoElement(element);
   6895    context.thread_local.bloom_filter.rebuild(element);
   6896 
   6897    let mut resolver = StyleResolverForElement::new(
   6898        element,
   6899        &mut context,
   6900        RuleInclusion::All,
   6901        PseudoElementResolution::IfApplicable,
   6902    );
   6903 
   6904    let starting_style = resolver.resolve_starting_style();
   6905    starting_style.style.0.into()
   6906 }
   6907 
   6908 #[no_mangle]
   6909 pub extern "C" fn Servo_ReparentStyle(
   6910    style_to_reparent: &ComputedValues,
   6911    parent_style: &ComputedValues,
   6912    layout_parent_style: &ComputedValues,
   6913    element: Option<&RawGeckoElement>,
   6914    raw_data: &PerDocumentStyleData,
   6915 ) -> Strong<ComputedValues> {
   6916    use style::properties::FirstLineReparenting;
   6917 
   6918    let global_style_data = &*GLOBAL_STYLE_DATA;
   6919    let guard = global_style_data.shared_lock.read();
   6920    let doc_data = raw_data.borrow();
   6921    let inputs = CascadeInputs::new_from_style(style_to_reparent);
   6922    let element = element.map(GeckoElement);
   6923    // We need the pseudo-type to reparent stuff like anonymous boxes, but we don't store pseudo
   6924    // identifiers in the style itself, so if there's no pseudo try the element as well.
   6925    let pseudo = style_to_reparent
   6926        .pseudo()
   6927        .or_else(|| element.and_then(|e| e.implemented_pseudo_element()));
   6928 
   6929    doc_data
   6930        .stylist
   6931        .cascade_style_and_visited(
   6932            element,
   6933            pseudo.as_ref(),
   6934            inputs,
   6935            &StylesheetGuards::same(&guard),
   6936            Some(parent_style),
   6937            Some(layout_parent_style),
   6938            FirstLineReparenting::Yes { style_to_reparent },
   6939            /* try_tactic = */ &Default::default(),
   6940            /* rule_cache = */ None,
   6941            &mut RuleCacheConditions::default(),
   6942        )
   6943        .into()
   6944 }
   6945 
   6946 #[cfg(feature = "gecko_debug")]
   6947 fn simulate_compute_values_failure(property: &PropertyValuePair) -> bool {
   6948    let p = &property.mProperty;
   6949    let id = get_property_id_from_csspropertyid!(p, false);
   6950    id.as_shorthand().is_ok() && property.mSimulateComputeValuesFailure
   6951 }
   6952 
   6953 #[cfg(not(feature = "gecko_debug"))]
   6954 fn simulate_compute_values_failure(_: &PropertyValuePair) -> bool {
   6955    false
   6956 }
   6957 
   6958 fn create_context_for_animation<'a>(
   6959    per_doc_data: &'a PerDocumentStyleDataImpl,
   6960    style: &'a ComputedValues,
   6961    parent_style: Option<&'a ComputedValues>,
   6962    for_smil_animation: bool,
   6963    rule_cache_conditions: &'a mut RuleCacheConditions,
   6964    container_size_query: ContainerSizeQuery<'a>,
   6965 ) -> Context<'a> {
   6966    Context::new_for_animation(
   6967        StyleBuilder::for_derived_style(
   6968            per_doc_data.stylist.device(),
   6969            Some(&per_doc_data.stylist),
   6970            style,
   6971            parent_style,
   6972        ),
   6973        for_smil_animation,
   6974        per_doc_data.stylist.quirks_mode(),
   6975        rule_cache_conditions,
   6976        container_size_query,
   6977    )
   6978 }
   6979 
   6980 struct PropertyAndIndex {
   6981    property: PropertyId,
   6982    index: usize,
   6983 }
   6984 
   6985 struct PrioritizedPropertyIter<'a> {
   6986    properties: &'a [PropertyValuePair],
   6987    sorted_property_indices: Box<[PropertyAndIndex]>,
   6988    curr: usize,
   6989 }
   6990 
   6991 impl<'a> PrioritizedPropertyIter<'a> {
   6992    fn new(properties: &'a [PropertyValuePair]) -> Self {
   6993        use style::values::animated::compare_property_priority;
   6994 
   6995        // If we fail to convert a NonCustomCSSPropertyId into a PropertyId we
   6996        // shouldn't fail outright but instead by treating that property as the
   6997        // 'all' property we make it sort last.
   6998        let mut sorted_property_indices: Box<[PropertyAndIndex]> = properties
   6999            .iter()
   7000            .enumerate()
   7001            .map(|(index, pair)| {
   7002                let property = PropertyId::from_gecko_css_property_id(&pair.mProperty)
   7003                    .unwrap_or(PropertyId::NonCustom(ShorthandId::All.into()));
   7004                PropertyAndIndex { property, index }
   7005            })
   7006            .collect();
   7007        sorted_property_indices.sort_by(|a, b| compare_property_priority(&a.property, &b.property));
   7008 
   7009        PrioritizedPropertyIter {
   7010            properties,
   7011            sorted_property_indices,
   7012            curr: 0,
   7013        }
   7014    }
   7015 
   7016    fn reset(&mut self) {
   7017        self.curr = 0;
   7018    }
   7019 }
   7020 
   7021 impl<'a> Iterator for PrioritizedPropertyIter<'a> {
   7022    type Item = &'a PropertyValuePair;
   7023 
   7024    fn next(&mut self) -> Option<&'a PropertyValuePair> {
   7025        if self.curr >= self.sorted_property_indices.len() {
   7026            return None;
   7027        }
   7028        self.curr += 1;
   7029        Some(&self.properties[self.sorted_property_indices[self.curr - 1].index])
   7030    }
   7031 }
   7032 
   7033 #[no_mangle]
   7034 pub extern "C" fn Servo_GetComputedKeyframeValues(
   7035    keyframes: &nsTArray<structs::Keyframe>,
   7036    element: &RawGeckoElement,
   7037    pseudo_type: PseudoStyleType,
   7038    style: &ComputedValues,
   7039    raw_data: &PerDocumentStyleData,
   7040    computed_keyframes: &mut nsTArray<structs::ComputedKeyframeValues>,
   7041 ) {
   7042    use style::applicable_declarations::CascadePriority;
   7043    use style::custom_properties::CustomPropertiesBuilder;
   7044    use style::properties::PropertyDeclaration;
   7045    let data = raw_data.borrow();
   7046    let element = GeckoElement(element);
   7047    let pseudo = PseudoElement::from_pseudo_type(pseudo_type, None);
   7048    let parent_element = if pseudo.is_none() {
   7049        element.inheritance_parent()
   7050    } else {
   7051        Some(element)
   7052    };
   7053    let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
   7054    let parent_style = parent_data
   7055        .as_ref()
   7056        .map(|d| d.styles.primary())
   7057        .map(|x| &**x);
   7058 
   7059    let container_size_query =
   7060        ContainerSizeQuery::for_element(element, parent_style, pseudo.is_some());
   7061    let mut conditions = Default::default();
   7062    let mut context = create_context_for_animation(
   7063        &data,
   7064        &style,
   7065        parent_style,
   7066        /* for_smil_animation = */ false,
   7067        &mut conditions,
   7068        container_size_query,
   7069    );
   7070 
   7071    let restriction = pseudo.and_then(|p| p.property_restriction());
   7072 
   7073    let global_style_data = &*GLOBAL_STYLE_DATA;
   7074    let guard = global_style_data.shared_lock.read();
   7075    let default_values = data.default_computed_values();
   7076 
   7077    for (index, keyframe) in keyframes.iter().enumerate() {
   7078        let ref mut animation_values = computed_keyframes[index];
   7079 
   7080        let mut seen = PropertyDeclarationIdSet::default();
   7081        let mut iter = PrioritizedPropertyIter::new(&keyframe.mPropertyValues);
   7082 
   7083        // FIXME (bug 1883255): This is pretty much a hack. Instead, the AnimatedValue should be
   7084        // better integrated in the cascade.
   7085        {
   7086            let mut builder = CustomPropertiesBuilder::new_with_properties(
   7087                &data.stylist,
   7088                style.custom_properties().clone(),
   7089                &mut context,
   7090            );
   7091            let priority = CascadePriority::same_tree_author_normal_at_root_layer();
   7092            for property in &mut iter {
   7093                let is_custom = match PropertyId::from_gecko_css_property_id(&property.mProperty) {
   7094                    Some(PropertyId::Custom(..)) => true,
   7095                    _ => false,
   7096                };
   7097                if !is_custom {
   7098                    break; // Custom props are guaranteed to sort earlier.
   7099                }
   7100                if property.mServoDeclarationBlock.mRawPtr.is_null() {
   7101                    continue;
   7102                }
   7103                let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
   7104                let guard = declarations.read_with(&guard);
   7105                for decl in guard.normal_declaration_iter() {
   7106                    if let PropertyDeclaration::Custom(ref declaration) = *decl {
   7107                        builder.cascade(declaration, priority, &element);
   7108                    }
   7109                }
   7110            }
   7111            iter.reset();
   7112            let _deferred = builder.build(DeferFontRelativeCustomPropertyResolution::No, &element);
   7113            debug_assert!(
   7114                _deferred.is_none(),
   7115                "Custom property processing deferred despite specifying otherwise?"
   7116            );
   7117        };
   7118 
   7119        let mut property_index = 0;
   7120        for property in iter {
   7121            if simulate_compute_values_failure(property) {
   7122                continue;
   7123            }
   7124 
   7125            let mut maybe_append_animation_value =
   7126                |property: PropertyDeclarationId, value: Option<AnimationValue>| {
   7127                    debug_assert!(!property.is_logical());
   7128                    debug_assert!(property.is_animatable());
   7129 
   7130                    // 'display' is only animatable from SMIL
   7131                    if property == PropertyDeclarationId::Longhand(LonghandId::Display) {
   7132                        return;
   7133                    }
   7134 
   7135                    // Skip restricted properties
   7136                    if restriction.map_or(false, |r| !property.flags().contains(r)) {
   7137                        return;
   7138                    }
   7139 
   7140                    if seen.contains(property) {
   7141                        return;
   7142                    }
   7143                    seen.insert(property);
   7144 
   7145                    animation_values.push(structs::PropertyStyleAnimationValuePair {
   7146                        mProperty: property.to_gecko_css_property_id(),
   7147                        mValue: structs::AnimationValue {
   7148                            mServo: value.map_or(structs::RefPtr::null(), |v| {
   7149                                structs::RefPtr::from_arc(Arc::new(v))
   7150                            }),
   7151                        },
   7152                    });
   7153                    property_index += 1;
   7154                };
   7155 
   7156            if property.mServoDeclarationBlock.mRawPtr.is_null() {
   7157                if let Some(prop) =
   7158                    OwnedPropertyDeclarationId::from_gecko_css_property_id(&property.mProperty)
   7159                {
   7160                    maybe_append_animation_value(prop.as_borrowed(), None);
   7161                }
   7162                continue;
   7163            }
   7164 
   7165            let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
   7166            let guard = declarations.read_with(&guard);
   7167            let iter = guard.to_animation_value_iter(&mut context, style, &default_values);
   7168 
   7169            for value in iter {
   7170                maybe_append_animation_value(value.id(), Some(value.clone()));
   7171            }
   7172        }
   7173    }
   7174 }
   7175 
   7176 #[no_mangle]
   7177 pub extern "C" fn Servo_GetAnimationValues(
   7178    declarations: &LockedDeclarationBlock,
   7179    element: &RawGeckoElement,
   7180    style: &ComputedValues,
   7181    raw_data: &PerDocumentStyleData,
   7182    animation_values: &mut nsTArray<structs::RefPtr<AnimationValue>>,
   7183 ) {
   7184    let data = raw_data.borrow();
   7185    let element = GeckoElement(element);
   7186    let parent_element = element.inheritance_parent();
   7187    let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
   7188    let parent_style = parent_data
   7189        .as_ref()
   7190        .map(|d| d.styles.primary())
   7191        .map(|x| &**x);
   7192 
   7193    let container_size_query =
   7194        ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
   7195    let mut conditions = Default::default();
   7196    let mut context = create_context_for_animation(
   7197        &data,
   7198        &style,
   7199        parent_style,
   7200        /* for_smil_animation = */ true,
   7201        &mut conditions,
   7202        container_size_query,
   7203    );
   7204 
   7205    let default_values = data.default_computed_values();
   7206    let global_style_data = &*GLOBAL_STYLE_DATA;
   7207    let guard = global_style_data.shared_lock.read();
   7208 
   7209    let guard = declarations.read_with(&guard);
   7210    let iter = guard.to_animation_value_iter(&mut context, style, &default_values);
   7211    animation_values.extend(iter.map(|v| structs::RefPtr::from_arc(Arc::new(v))));
   7212 }
   7213 
   7214 #[no_mangle]
   7215 pub extern "C" fn Servo_AnimationValue_GetPropertyId(
   7216    value: &AnimationValue,
   7217    property_id: &mut structs::CSSPropertyId,
   7218 ) {
   7219    *property_id = value.id().to_gecko_css_property_id();
   7220 }
   7221 
   7222 #[no_mangle]
   7223 pub extern "C" fn Servo_AnimationValue_Compute(
   7224    element: &RawGeckoElement,
   7225    declarations: &LockedDeclarationBlock,
   7226    style: &ComputedValues,
   7227    raw_data: &PerDocumentStyleData,
   7228 ) -> Strong<AnimationValue> {
   7229    let data = raw_data.borrow();
   7230 
   7231    let element = GeckoElement(element);
   7232    let parent_element = element.inheritance_parent();
   7233    let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
   7234    let parent_style = parent_data
   7235        .as_ref()
   7236        .map(|d| d.styles.primary())
   7237        .map(|x| &**x);
   7238 
   7239    let container_size_query =
   7240        ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
   7241    let mut conditions = Default::default();
   7242    let mut context = create_context_for_animation(
   7243        &data,
   7244        style,
   7245        parent_style,
   7246        /* for_smil_animation = */ false,
   7247        &mut conditions,
   7248        container_size_query,
   7249    );
   7250 
   7251    let default_values = data.default_computed_values();
   7252    let global_style_data = &*GLOBAL_STYLE_DATA;
   7253    let guard = global_style_data.shared_lock.read();
   7254    // We only compute the first element in declarations.
   7255    match declarations
   7256        .read_with(&guard)
   7257        .declaration_importance_iter()
   7258        .next()
   7259    {
   7260        Some((decl, imp)) if imp == Importance::Normal => {
   7261            let animation = AnimationValue::from_declaration(
   7262                decl,
   7263                &mut context,
   7264                style,
   7265                default_values,
   7266                &element,
   7267            );
   7268            animation.map_or(Strong::null(), |value| Arc::new(value).into())
   7269        },
   7270        _ => Strong::null(),
   7271    }
   7272 }
   7273 
   7274 #[no_mangle]
   7275 pub extern "C" fn Servo_AssertTreeIsClean(root: &RawGeckoElement) {
   7276    if !cfg!(feature = "gecko_debug") {
   7277        panic!("Calling Servo_AssertTreeIsClean in release build");
   7278    }
   7279 
   7280    let root = GeckoElement(root);
   7281    debug!("Servo_AssertTreeIsClean: ");
   7282    debug!("{:?}", ShowSubtreeData(root.as_node()));
   7283 
   7284    fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) {
   7285        debug_assert!(
   7286            !el.has_dirty_descendants() && !el.has_animation_only_dirty_descendants(),
   7287            "{:?} has still dirty bit {:?} or animation-only dirty bit {:?}",
   7288            el,
   7289            el.has_dirty_descendants(),
   7290            el.has_animation_only_dirty_descendants()
   7291        );
   7292        for child in el.traversal_children() {
   7293            if let Some(child) = child.as_element() {
   7294                assert_subtree_is_clean(child);
   7295            }
   7296        }
   7297    }
   7298 
   7299    assert_subtree_is_clean(root);
   7300 }
   7301 
   7302 #[no_mangle]
   7303 pub extern "C" fn Servo_IsWorkerThread() -> bool {
   7304    thread_state::get().is_worker()
   7305 }
   7306 
   7307 enum Offset {
   7308    Zero,
   7309    One,
   7310 }
   7311 
   7312 fn property_value_pair_for(id: &PropertyDeclarationId) -> structs::PropertyValuePair {
   7313    structs::PropertyValuePair {
   7314        mProperty: id.to_gecko_css_property_id(),
   7315        mServoDeclarationBlock: structs::RefPtr::null(),
   7316        #[cfg(feature = "gecko_debug")]
   7317        mSimulateComputeValuesFailure: false,
   7318    }
   7319 }
   7320 
   7321 fn fill_in_missing_keyframe_values(
   7322    all_properties: &PropertyDeclarationIdSet,
   7323    timing_function: &ComputedTimingFunction,
   7324    properties_at_offset: &PropertyDeclarationIdSet,
   7325    offset: Offset,
   7326    keyframes: &mut nsTArray<structs::Keyframe>,
   7327 ) {
   7328    // Return early if all animated properties are already set.
   7329    if properties_at_offset.contains_all(all_properties) {
   7330        return;
   7331    }
   7332 
   7333    // Use auto for missing keyframes.
   7334    // FIXME: This may be a spec issue in css-animations-2 because the spec says the default
   7335    // keyframe-specific composite is replace, but web-animations-1 uses auto. Use auto now so we
   7336    // use the value of animation-composition of the element, for missing keyframes.
   7337    // https://github.com/w3c/csswg-drafts/issues/7476
   7338    let composition = structs::CompositeOperationOrAuto::Auto;
   7339    let keyframe = match offset {
   7340        Offset::Zero => unsafe {
   7341            &mut *bindings::Gecko_GetOrCreateInitialKeyframe(
   7342                keyframes,
   7343                timing_function,
   7344                composition,
   7345            )
   7346        },
   7347        Offset::One => unsafe {
   7348            &mut *bindings::Gecko_GetOrCreateFinalKeyframe(keyframes, timing_function, composition)
   7349        },
   7350    };
   7351 
   7352    // Append properties that have not been set at this offset.
   7353    for property in all_properties.iter() {
   7354        if !properties_at_offset.contains(property) {
   7355            keyframe
   7356                .mPropertyValues
   7357                .push(property_value_pair_for(&property));
   7358        }
   7359    }
   7360 }
   7361 
   7362 #[no_mangle]
   7363 pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName(
   7364    raw_data: &PerDocumentStyleData,
   7365    element: &RawGeckoElement,
   7366    style: &ComputedValues,
   7367    name: *mut nsAtom,
   7368    inherited_timing_function: &ComputedTimingFunction,
   7369    keyframes: &mut nsTArray<structs::Keyframe>,
   7370 ) -> bool {
   7371    use style::gecko_bindings::structs::CompositeOperationOrAuto;
   7372    use style::values::computed::AnimationComposition;
   7373 
   7374    debug_assert!(keyframes.len() == 0, "keyframes should be initially empty");
   7375 
   7376    let element = GeckoElement(element);
   7377    let data = raw_data.borrow();
   7378    let name = Atom::from_raw(name);
   7379 
   7380    let animation = match data.stylist.lookup_keyframes(&name, element) {
   7381        Some(animation) => animation,
   7382        None => return false,
   7383    };
   7384 
   7385    let global_style_data = &*GLOBAL_STYLE_DATA;
   7386    let guard = global_style_data.shared_lock.read();
   7387 
   7388    let mut properties_set_at_current_offset = PropertyDeclarationIdSet::default();
   7389    let mut properties_set_at_start = PropertyDeclarationIdSet::default();
   7390    let mut properties_set_at_end = PropertyDeclarationIdSet::default();
   7391    let mut has_complete_initial_keyframe = false;
   7392    let mut has_complete_final_keyframe = false;
   7393    let mut current_offset = -1.;
   7394 
   7395    let writing_mode = style.writing_mode;
   7396 
   7397    // Iterate over the keyframe rules backwards so we can drop overridden
   7398    // properties (since declarations in later rules override those in earlier
   7399    // ones).
   7400    for step in animation.steps.iter().rev() {
   7401        if step.start_percentage.0 != current_offset {
   7402            properties_set_at_current_offset.clear();
   7403            current_offset = step.start_percentage.0;
   7404        }
   7405 
   7406        // Override timing_function if the keyframe has an animation-timing-function.
   7407        let timing_function = match step.get_animation_timing_function(&guard) {
   7408            Some(val) => val.to_computed_value_without_context(),
   7409            None => (*inherited_timing_function).clone(),
   7410        };
   7411 
   7412        // Override composite operation if the keyframe has an animation-composition.
   7413        let composition =
   7414            step.get_animation_composition(&guard)
   7415                .map_or(CompositeOperationOrAuto::Auto, |val| match val {
   7416                    AnimationComposition::Replace => CompositeOperationOrAuto::Replace,
   7417                    AnimationComposition::Add => CompositeOperationOrAuto::Add,
   7418                    AnimationComposition::Accumulate => CompositeOperationOrAuto::Accumulate,
   7419                });
   7420 
   7421        // Look for an existing keyframe with the same offset, timing function, and compsition, or
   7422        // else add a new keyframe at the beginning of the keyframe array.
   7423        let keyframe = &mut *bindings::Gecko_GetOrCreateKeyframeAtStart(
   7424            keyframes,
   7425            step.start_percentage.0 as f32,
   7426            &timing_function,
   7427            composition,
   7428        );
   7429 
   7430        match step.value {
   7431            KeyframesStepValue::ComputedValues => {
   7432                // In KeyframesAnimation::from_keyframes if there is no 0% or
   7433                // 100% keyframe at all, we will create a 'ComputedValues' step
   7434                // to represent that all properties animated by the keyframes
   7435                // animation should be set to the underlying computed value for
   7436                // that keyframe.
   7437                let mut seen = PropertyDeclarationIdSet::default();
   7438                for property in animation.properties_changed.iter() {
   7439                    let property = property.to_physical(writing_mode);
   7440                    if seen.contains(property) {
   7441                        continue;
   7442                    }
   7443                    seen.insert(property);
   7444                    keyframe
   7445                        .mPropertyValues
   7446                        .push(property_value_pair_for(&property));
   7447                }
   7448                if current_offset == 0.0 {
   7449                    has_complete_initial_keyframe = true;
   7450                } else if current_offset == 1.0 {
   7451                    has_complete_final_keyframe = true;
   7452                }
   7453            },
   7454            KeyframesStepValue::Declarations { ref block } => {
   7455                let guard = block.read_with(&guard);
   7456 
   7457                // Filter out non-animatable properties and properties with
   7458                // !important.
   7459                //
   7460                // Also, iterate in reverse to respect the source order in case
   7461                // there are logical and physical longhands in the same block.
   7462                for declaration in guard.normal_declaration_iter().rev() {
   7463                    let id = declaration.id().to_physical(writing_mode);
   7464 
   7465                    // Skip non-animatable properties, including the 'display' property because
   7466                    // although it is animatable from SMIL, it should not be animatable from CSS
   7467                    // Animations.
   7468                    if !id.is_animatable()
   7469                        || id == PropertyDeclarationId::Longhand(LonghandId::Display)
   7470                    {
   7471                        continue;
   7472                    }
   7473 
   7474                    if properties_set_at_current_offset.contains(id) {
   7475                        continue;
   7476                    }
   7477 
   7478                    let mut pair = property_value_pair_for(&id);
   7479                    pair.mServoDeclarationBlock.set_arc(Arc::new(
   7480                        global_style_data
   7481                            .shared_lock
   7482                            .wrap(PropertyDeclarationBlock::with_one(
   7483                                declaration.to_physical(writing_mode),
   7484                                Importance::Normal,
   7485                            )),
   7486                    ));
   7487                    keyframe.mPropertyValues.push(pair);
   7488 
   7489                    if current_offset == 0.0 {
   7490                        properties_set_at_start.insert(id);
   7491                    } else if current_offset == 1.0 {
   7492                        properties_set_at_end.insert(id);
   7493                    }
   7494                    properties_set_at_current_offset.insert(id);
   7495                }
   7496            },
   7497        }
   7498    }
   7499 
   7500    let mut properties_changed = PropertyDeclarationIdSet::default();
   7501    for property in animation.properties_changed.iter() {
   7502        properties_changed.insert(property.to_physical(writing_mode));
   7503    }
   7504 
   7505    // Append property values that are missing in the initial or the final keyframes.
   7506    if !has_complete_initial_keyframe {
   7507        fill_in_missing_keyframe_values(
   7508            &properties_changed,
   7509            inherited_timing_function,
   7510            &properties_set_at_start,
   7511            Offset::Zero,
   7512            keyframes,
   7513        );
   7514    }
   7515    if !has_complete_final_keyframe {
   7516        fill_in_missing_keyframe_values(
   7517            &properties_changed,
   7518            inherited_timing_function,
   7519            &properties_set_at_end,
   7520            Offset::One,
   7521            keyframes,
   7522        );
   7523    }
   7524    true
   7525 }
   7526 
   7527 #[no_mangle]
   7528 pub extern "C" fn Servo_StyleSet_GetFontFaceRules(
   7529    raw_data: &PerDocumentStyleData,
   7530    rules: &mut nsTArray<structs::nsFontFaceRuleContainer>,
   7531 ) {
   7532    let data = raw_data.borrow();
   7533    debug_assert_eq!(rules.len(), 0);
   7534 
   7535    // Reversed iterator because Gecko expects rules to appear sorted
   7536    // UserAgent first, Author last.
   7537    let font_face_iter = data
   7538        .stylist
   7539        .iter_extra_data_origins_rev()
   7540        .flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
   7541 
   7542    rules.extend(font_face_iter.map(|(&(ref rule, _layer_id), origin)| {
   7543        structs::nsFontFaceRuleContainer {
   7544            mRule: structs::RefPtr::from_arc(rule.clone()),
   7545            mOrigin: origin,
   7546        }
   7547    }))
   7548 }
   7549 
   7550 // XXX Ideally this should return a Option<&LockedCounterStyleRule>,
   7551 // but we cannot, because the value from AtomicRefCell::borrow() can only
   7552 // live in this function, and thus anything derived from it cannot get the
   7553 // same lifetime as raw_data in parameter. See bug 1451543.
   7554 #[no_mangle]
   7555 pub unsafe extern "C" fn Servo_StyleSet_GetCounterStyleRule(
   7556    raw_data: &PerDocumentStyleData,
   7557    name: *mut nsAtom,
   7558 ) -> *const LockedCounterStyleRule {
   7559    let data = raw_data.borrow();
   7560    Atom::with(name, |name| {
   7561        data.stylist
   7562            .iter_extra_data_origins()
   7563            .find_map(|(d, _)| d.counter_styles.get(name))
   7564            .map_or(ptr::null(), |rule| &**rule as *const _)
   7565    })
   7566 }
   7567 
   7568 #[no_mangle]
   7569 pub extern "C" fn Servo_StyleSet_BuildFontFeatureValueSet(
   7570    raw_data: &PerDocumentStyleData,
   7571 ) -> *mut gfxFontFeatureValueSet {
   7572    let data = raw_data.borrow();
   7573 
   7574    let has_rule = data
   7575        .stylist
   7576        .iter_extra_data_origins()
   7577        .any(|(d, _)| !d.font_feature_values.is_empty());
   7578 
   7579    if !has_rule {
   7580        return ptr::null_mut();
   7581    }
   7582 
   7583    let font_feature_values_iter = data
   7584        .stylist
   7585        .iter_extra_data_origins_rev()
   7586        .flat_map(|(d, _)| d.font_feature_values.iter());
   7587 
   7588    let set = unsafe { Gecko_ConstructFontFeatureValueSet() };
   7589    for &(ref rule, _) in font_feature_values_iter {
   7590        rule.set_at_rules(set);
   7591    }
   7592    set
   7593 }
   7594 
   7595 #[no_mangle]
   7596 pub extern "C" fn Servo_StyleSet_BuildFontPaletteValueSet(
   7597    raw_data: &PerDocumentStyleData,
   7598 ) -> *mut FontPaletteValueSet {
   7599    let data = raw_data.borrow();
   7600 
   7601    let has_rule = data
   7602        .stylist
   7603        .iter_extra_data_origins()
   7604        .any(|(d, _)| !d.font_palette_values.is_empty());
   7605 
   7606    if !has_rule {
   7607        return ptr::null_mut();
   7608    }
   7609 
   7610    let font_palette_values_iter = data
   7611        .stylist
   7612        .iter_extra_data_origins_rev()
   7613        .flat_map(|(d, _)| d.font_palette_values.iter());
   7614 
   7615    let set = unsafe { Gecko_ConstructFontPaletteValueSet() };
   7616    for &(ref rule, _) in font_palette_values_iter {
   7617        rule.to_gecko_palette_value_set(set);
   7618    }
   7619    set
   7620 }
   7621 
   7622 #[no_mangle]
   7623 pub extern "C" fn Servo_StyleSet_ResolveForDeclarations(
   7624    raw_data: &PerDocumentStyleData,
   7625    parent_style_context: Option<&ComputedValues>,
   7626    declarations: &LockedDeclarationBlock,
   7627 ) -> Strong<ComputedValues> {
   7628    let doc_data = raw_data.borrow();
   7629    let global_style_data = &*GLOBAL_STYLE_DATA;
   7630    let guard = global_style_data.shared_lock.read();
   7631    let guards = StylesheetGuards::same(&guard);
   7632 
   7633    let parent_style = match parent_style_context {
   7634        Some(parent) => &*parent,
   7635        None => doc_data.default_computed_values(),
   7636    };
   7637 
   7638    doc_data
   7639        .stylist
   7640        .compute_for_declarations::<GeckoElement>(&guards, parent_style, unsafe {
   7641            Arc::from_raw_addrefed(declarations)
   7642        })
   7643        .into()
   7644 }
   7645 
   7646 #[no_mangle]
   7647 pub extern "C" fn Servo_StyleSet_AddSizeOfExcludingThis(
   7648    malloc_size_of: GeckoMallocSizeOf,
   7649    malloc_enclosing_size_of: GeckoMallocSizeOf,
   7650    sizes: *mut ServoStyleSetSizes,
   7651    raw_data: &PerDocumentStyleData,
   7652 ) {
   7653    let data = raw_data.borrow_mut();
   7654    let mut ops = MallocSizeOfOps::new(
   7655        malloc_size_of.unwrap(),
   7656        Some(malloc_enclosing_size_of.unwrap()),
   7657        None,
   7658    );
   7659    let sizes = unsafe { sizes.as_mut() }.unwrap();
   7660    data.add_size_of(&mut ops, sizes);
   7661 }
   7662 
   7663 #[no_mangle]
   7664 pub extern "C" fn Servo_UACache_AddSizeOf(
   7665    malloc_size_of: GeckoMallocSizeOf,
   7666    malloc_enclosing_size_of: GeckoMallocSizeOf,
   7667    sizes: *mut ServoStyleSetSizes,
   7668 ) {
   7669    let mut ops = MallocSizeOfOps::new(
   7670        malloc_size_of.unwrap(),
   7671        Some(malloc_enclosing_size_of.unwrap()),
   7672        None,
   7673    );
   7674    let sizes = unsafe { sizes.as_mut() }.unwrap();
   7675    add_size_of_ua_cache(&mut ops, sizes);
   7676 }
   7677 
   7678 #[no_mangle]
   7679 pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(
   7680    raw_data: &PerDocumentStyleData,
   7681    element: &RawGeckoElement,
   7682    local_name: *mut nsAtom,
   7683 ) -> bool {
   7684    let data = raw_data.borrow();
   7685    let element = GeckoElement(element);
   7686 
   7687    unsafe {
   7688        AtomIdent::with(local_name, |atom| {
   7689            data.stylist.any_applicable_rule_data(element, |data| {
   7690                data.might_have_attribute_dependency(atom)
   7691            })
   7692        })
   7693    }
   7694 }
   7695 
   7696 #[no_mangle]
   7697 pub extern "C" fn Servo_StyleSet_MightHaveNthOfIDDependency(
   7698    raw_data: &PerDocumentStyleData,
   7699    element: &RawGeckoElement,
   7700    old_id: *mut nsAtom,
   7701    new_id: *mut nsAtom,
   7702 ) -> bool {
   7703    let data = raw_data.borrow();
   7704    let element = GeckoElement(element);
   7705 
   7706    data.stylist.any_applicable_rule_data(element, |data| {
   7707        [old_id, new_id]
   7708            .iter()
   7709            .filter(|id| !id.is_null())
   7710            .any(|id| unsafe {
   7711                AtomIdent::with(*id, |atom| data.might_have_nth_of_id_dependency(atom))
   7712            })
   7713            || data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("id")))
   7714    })
   7715 }
   7716 
   7717 #[no_mangle]
   7718 pub extern "C" fn Servo_StyleSet_MightHaveNthOfClassDependency(
   7719    raw_data: &PerDocumentStyleData,
   7720    element: &RawGeckoElement,
   7721    snapshots: &ServoElementSnapshotTable,
   7722 ) -> bool {
   7723    let data = raw_data.borrow();
   7724    let element = GeckoElement(element);
   7725 
   7726    data.stylist.any_applicable_rule_data(element, |data| {
   7727        classes_changed(&element, snapshots)
   7728            .iter()
   7729            .any(|atom| data.might_have_nth_of_class_dependency(atom))
   7730            || data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("class")))
   7731    })
   7732 }
   7733 
   7734 #[no_mangle]
   7735 pub extern "C" fn Servo_StyleSet_MightHaveNthOfAttributeDependency(
   7736    raw_data: &PerDocumentStyleData,
   7737    element: &RawGeckoElement,
   7738    local_name: *mut nsAtom,
   7739 ) -> bool {
   7740    let data = raw_data.borrow();
   7741    let element = GeckoElement(element);
   7742 
   7743    unsafe {
   7744        AtomIdent::with(local_name, |atom| {
   7745            data.stylist.any_applicable_rule_data(element, |data| {
   7746                data.might_have_nth_of_attribute_dependency(atom)
   7747            })
   7748        })
   7749    }
   7750 }
   7751 
   7752 fn on_siblings_invalidated(element: GeckoElement) {
   7753    let parent = element
   7754        .traversal_parent()
   7755        .expect("How could we invalidate siblings without a common parent?");
   7756    unsafe {
   7757        parent.set_dirty_descendants();
   7758        bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
   7759    }
   7760 }
   7761 
   7762 fn restyle_for_nth_of(element: GeckoElement, flags: ElementSelectorFlags) {
   7763    debug_assert!(
   7764        !flags.is_empty(),
   7765        "Calling restyle for nth but no relevant flag is set."
   7766    );
   7767    fn invalidate_siblings_of(
   7768        element: GeckoElement,
   7769        get_sibling: fn(GeckoElement) -> Option<GeckoElement>,
   7770    ) {
   7771        let mut sibling = get_sibling(element);
   7772        while let Some(sib) = sibling {
   7773            if let Some(mut data) = sib.mutate_data() {
   7774                data.hint.insert(RestyleHint::restyle_subtree());
   7775            }
   7776            sibling = get_sibling(sib);
   7777        }
   7778    }
   7779 
   7780    if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
   7781        invalidate_siblings_of(element, |e| e.prev_sibling_element());
   7782    }
   7783    if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
   7784        invalidate_siblings_of(element, |e| e.next_sibling_element());
   7785    }
   7786    on_siblings_invalidated(element);
   7787 }
   7788 
   7789 fn relative_selector_invalidated_at(element: GeckoElement, result: &InvalidationResult) {
   7790    if result.has_invalidated_siblings() {
   7791        on_siblings_invalidated(element);
   7792    } else if result.has_invalidated_descendants() {
   7793        unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
   7794    } else if result.has_invalidated_self() {
   7795        unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
   7796        let flags = element
   7797            .parent_element()
   7798            .map_or(ElementSelectorFlags::empty(), |e| e.slow_selector_flags());
   7799        // We invalidated up to the anchor, and it has a flag for nth-of invalidation.
   7800        if !flags.is_empty() {
   7801            restyle_for_nth_of(element, flags);
   7802        }
   7803    }
   7804 }
   7805 
   7806 fn add_relative_selector_attribute_dependency<'a>(
   7807    element: &GeckoElement<'a>,
   7808    scope: &Option<OpaqueElement>,
   7809    invalidation_map: &'a InvalidationMap,
   7810    attribute: &AtomIdent,
   7811    collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
   7812 ) {
   7813    match invalidation_map
   7814        .other_attribute_affecting_selectors
   7815        .get(attribute)
   7816    {
   7817        Some(v) => {
   7818            for dependency in v {
   7819                collector.add_dependency(dependency, *element, *scope);
   7820            }
   7821        },
   7822        None => (),
   7823    };
   7824 }
   7825 
   7826 fn inherit_relative_selector_search_direction(
   7827    parent: Option<GeckoElement>,
   7828    prev_sibling: Option<GeckoElement>,
   7829 ) -> ElementSelectorFlags {
   7830    let mut inherited = ElementSelectorFlags::empty();
   7831    if let Some(parent) = parent {
   7832        inherited |= parent
   7833            .relative_selector_search_direction()
   7834            .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR);
   7835    }
   7836    if let Some(sibling) = prev_sibling {
   7837        // Inherit both, for e.g. a sibling with `:has(~.sibling .descendant)`
   7838        inherited |= sibling.relative_selector_search_direction().intersection(
   7839            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING,
   7840        );
   7841    }
   7842    inherited
   7843 }
   7844 
   7845 #[no_mangle]
   7846 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorIDDependency(
   7847    raw_data: &PerDocumentStyleData,
   7848    element: &RawGeckoElement,
   7849    old_id: *mut nsAtom,
   7850    new_id: *mut nsAtom,
   7851    snapshots: &ServoElementSnapshotTable,
   7852 ) {
   7853    let data = raw_data.borrow();
   7854    let element = GeckoElement(element);
   7855 
   7856    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   7857    let invalidator = RelativeSelectorInvalidator {
   7858        element,
   7859        quirks_mode,
   7860        snapshot_table: Some(snapshots),
   7861        invalidated: relative_selector_invalidated_at,
   7862        sibling_traversal_map: SiblingTraversalMap::default(),
   7863        _marker: std::marker::PhantomData,
   7864    };
   7865 
   7866    invalidator.invalidate_relative_selectors_for_this(
   7867        &data.stylist,
   7868        |element, scope, data, quirks_mode, collector| {
   7869            let invalidation_map = data.relative_selector_invalidation_map();
   7870            relative_selector_dependencies_for_id(
   7871                old_id,
   7872                new_id,
   7873                element,
   7874                scope,
   7875                quirks_mode,
   7876                &invalidation_map,
   7877                collector,
   7878            );
   7879            add_relative_selector_attribute_dependency(
   7880                element,
   7881                &scope,
   7882                invalidation_map,
   7883                &AtomIdent(atom!("id")),
   7884                collector,
   7885            );
   7886        },
   7887    );
   7888 }
   7889 
   7890 #[no_mangle]
   7891 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorClassDependency(
   7892    raw_data: &PerDocumentStyleData,
   7893    element: &RawGeckoElement,
   7894    snapshots: &ServoElementSnapshotTable,
   7895 ) {
   7896    let data = raw_data.borrow();
   7897    let element = GeckoElement(element);
   7898    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   7899    let invalidator = RelativeSelectorInvalidator {
   7900        element,
   7901        quirks_mode,
   7902        snapshot_table: Some(snapshots),
   7903        invalidated: relative_selector_invalidated_at,
   7904        sibling_traversal_map: SiblingTraversalMap::default(),
   7905        _marker: std::marker::PhantomData,
   7906    };
   7907 
   7908    invalidator.invalidate_relative_selectors_for_this(
   7909        &data.stylist,
   7910        |element, scope, data, quirks_mode, mut collector| {
   7911            let invalidation_map = data.relative_selector_invalidation_map();
   7912 
   7913            relative_selector_dependencies_for_class(
   7914                &classes_changed(element, snapshots),
   7915                &element,
   7916                scope,
   7917                quirks_mode,
   7918                invalidation_map,
   7919                collector,
   7920            );
   7921            add_relative_selector_attribute_dependency(
   7922                element,
   7923                &scope,
   7924                invalidation_map,
   7925                &AtomIdent(atom!("class")),
   7926                &mut collector,
   7927            );
   7928        },
   7929    );
   7930 }
   7931 
   7932 #[no_mangle]
   7933 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorAttributeDependency(
   7934    raw_data: &PerDocumentStyleData,
   7935    element: &RawGeckoElement,
   7936    local_name: *mut nsAtom,
   7937    snapshots: &ServoElementSnapshotTable,
   7938 ) {
   7939    let data = raw_data.borrow();
   7940    let element = GeckoElement(element);
   7941 
   7942    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   7943    unsafe {
   7944        AtomIdent::with(local_name, |atom| {
   7945            let invalidator = RelativeSelectorInvalidator {
   7946                element,
   7947                quirks_mode,
   7948                snapshot_table: Some(snapshots),
   7949                invalidated: relative_selector_invalidated_at,
   7950                sibling_traversal_map: SiblingTraversalMap::default(),
   7951                _marker: std::marker::PhantomData,
   7952            };
   7953 
   7954            invalidator.invalidate_relative_selectors_for_this(
   7955                &data.stylist,
   7956                |element, scope, data, _quirks_mode, mut collector| {
   7957                    add_relative_selector_attribute_dependency(
   7958                        element,
   7959                        &scope,
   7960                        data.relative_selector_invalidation_map(),
   7961                        atom,
   7962                        &mut collector,
   7963                    );
   7964                },
   7965            );
   7966        })
   7967    }
   7968 }
   7969 
   7970 #[no_mangle]
   7971 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorStateDependency(
   7972    raw_data: &PerDocumentStyleData,
   7973    element: &RawGeckoElement,
   7974    state: u64,
   7975    snapshots: &ServoElementSnapshotTable,
   7976 ) {
   7977    let element = GeckoElement(element);
   7978 
   7979    let state = match ElementState::from_bits(state) {
   7980        Some(state) => state,
   7981        None => return,
   7982    };
   7983    let data = raw_data.borrow();
   7984    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   7985 
   7986    let invalidator = RelativeSelectorInvalidator {
   7987        element,
   7988        quirks_mode,
   7989        snapshot_table: Some(snapshots),
   7990        invalidated: relative_selector_invalidated_at,
   7991        sibling_traversal_map: SiblingTraversalMap::default(),
   7992        _marker: std::marker::PhantomData,
   7993    };
   7994 
   7995    invalidator.invalidate_relative_selectors_for_this(
   7996        &data.stylist,
   7997        |element, scope, data, quirks_mode, collector| {
   7998            let invalidation_map = data.relative_selector_invalidation_map();
   7999            invalidation_map
   8000                .state_affecting_selectors
   8001                .lookup_with_additional(*element, quirks_mode, None, &[], state, |dependency| {
   8002                    if !dependency.state.intersects(state) {
   8003                        return true;
   8004                    }
   8005                    collector.add_dependency(&dependency.dep, *element, scope);
   8006                    true
   8007                });
   8008        },
   8009    );
   8010 }
   8011 
   8012 #[no_mangle]
   8013 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorCustomStateDependency(
   8014    raw_data: &PerDocumentStyleData,
   8015    element: &RawGeckoElement,
   8016    state: *mut nsAtom,
   8017    snapshots: &ServoElementSnapshotTable,
   8018 ) {
   8019    let data = raw_data.borrow();
   8020    let element = GeckoElement(element);
   8021 
   8022    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   8023    let invalidator = RelativeSelectorInvalidator {
   8024        element,
   8025        quirks_mode,
   8026        snapshot_table: Some(snapshots),
   8027        invalidated: relative_selector_invalidated_at,
   8028        sibling_traversal_map: SiblingTraversalMap::default(),
   8029        _marker: std::marker::PhantomData,
   8030    };
   8031 
   8032    invalidator.invalidate_relative_selectors_for_this(
   8033        &data.stylist,
   8034        |element, scope, data, _quirks_mode, collector| {
   8035            let invalidation_map = data.relative_selector_invalidation_map();
   8036            relative_selector_dependencies_for_custom_state(
   8037                state,
   8038                *element,
   8039                scope,
   8040                &invalidation_map,
   8041                collector,
   8042            );
   8043        },
   8044    );
   8045 }
   8046 
   8047 fn invalidate_relative_selector_prev_sibling_side_effect(
   8048    prev_sibling: GeckoElement,
   8049    quirks_mode: QuirksMode,
   8050    sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
   8051    stylist: &Stylist,
   8052 ) {
   8053    let invalidator = RelativeSelectorInvalidator {
   8054        element: prev_sibling,
   8055        quirks_mode,
   8056        snapshot_table: None,
   8057        invalidated: relative_selector_invalidated_at,
   8058        sibling_traversal_map,
   8059        _marker: std::marker::PhantomData,
   8060    };
   8061    invalidator.invalidate_relative_selectors_for_dom_mutation(
   8062        false,
   8063        &stylist,
   8064        ElementSelectorFlags::empty(),
   8065        DomMutationOperation::SideEffectPrevSibling,
   8066    );
   8067 }
   8068 
   8069 fn invalidate_relative_selector_next_sibling_side_effect(
   8070    next_sibling: GeckoElement,
   8071    quirks_mode: QuirksMode,
   8072    sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
   8073    stylist: &Stylist,
   8074 ) {
   8075    let invalidator = RelativeSelectorInvalidator {
   8076        element: next_sibling,
   8077        quirks_mode,
   8078        snapshot_table: None,
   8079        invalidated: relative_selector_invalidated_at,
   8080        sibling_traversal_map,
   8081        _marker: std::marker::PhantomData,
   8082    };
   8083    invalidator.invalidate_relative_selectors_for_dom_mutation(
   8084        false,
   8085        &stylist,
   8086        ElementSelectorFlags::empty(),
   8087        DomMutationOperation::SideEffectNextSibling,
   8088    );
   8089 }
   8090 
   8091 fn invalidate_relative_selector_ts_dependency(
   8092    stylist: &Stylist,
   8093    element: GeckoElement,
   8094    state: TSStateForInvalidation,
   8095 ) {
   8096    let quirks_mode = stylist.quirks_mode();
   8097 
   8098    let invalidator = RelativeSelectorInvalidator {
   8099        element,
   8100        quirks_mode,
   8101        snapshot_table: None,
   8102        invalidated: relative_selector_invalidated_at,
   8103        sibling_traversal_map: SiblingTraversalMap::default(),
   8104        _marker: std::marker::PhantomData,
   8105    };
   8106 
   8107    invalidator.invalidate_relative_selectors_for_this(
   8108        stylist,
   8109        |element, scope, data, quirks_mode, collector| {
   8110            let invalidation_map_attributes = data.relative_invalidation_map_attributes();
   8111            invalidation_map_attributes
   8112                .ts_state_to_selector
   8113                .lookup_with_additional(
   8114                    *element,
   8115                    quirks_mode,
   8116                    None,
   8117                    &[],
   8118                    ElementState::empty(),
   8119                    |dependency| {
   8120                        if !dependency.state.intersects(state) {
   8121                            return true;
   8122                        }
   8123                        collector.add_dependency(&dependency.dep, *element, scope);
   8124                        true
   8125                    },
   8126                );
   8127        },
   8128    );
   8129 }
   8130 
   8131 #[no_mangle]
   8132 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorEmptyDependency(
   8133    raw_data: &PerDocumentStyleData,
   8134    element: &RawGeckoElement,
   8135 ) {
   8136    invalidate_relative_selector_ts_dependency(
   8137        &raw_data.borrow().stylist,
   8138        GeckoElement(element),
   8139        TSStateForInvalidation::EMPTY,
   8140    );
   8141 }
   8142 
   8143 /// Which edge side should the invalidation run for?
   8144 #[repr(u8)]
   8145 pub enum RelativeSelectorNthEdgeInvalidateFor {
   8146    First,
   8147    Last,
   8148 }
   8149 
   8150 #[no_mangle]
   8151 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthEdgeDependency(
   8152    raw_data: &PerDocumentStyleData,
   8153    element: &RawGeckoElement,
   8154    invalidate_for: RelativeSelectorNthEdgeInvalidateFor,
   8155 ) {
   8156    invalidate_relative_selector_ts_dependency(
   8157        &raw_data.borrow().stylist,
   8158        GeckoElement(element),
   8159        match invalidate_for {
   8160            RelativeSelectorNthEdgeInvalidateFor::First => TSStateForInvalidation::NTH_EDGE_FIRST,
   8161            RelativeSelectorNthEdgeInvalidateFor::Last => TSStateForInvalidation::NTH_EDGE_LAST,
   8162        },
   8163    );
   8164 }
   8165 
   8166 #[no_mangle]
   8167 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthDependencyFromSibling(
   8168    raw_data: &PerDocumentStyleData,
   8169    element: &RawGeckoElement,
   8170    force: bool,
   8171 ) {
   8172    let mut element = Some(GeckoElement(element));
   8173    let data = unsafe { borrow_assert_main_thread(raw_data) };
   8174 
   8175    // Short of doing the actual matching, any of the siblings can match the selector, so we
   8176    // have to try invalidating against all of them.
   8177    while let Some(sibling) = element {
   8178        if force {
   8179            unsafe { sibling.note_explicit_hints(RestyleHint::restyle_subtree(), nsChangeHint(0)) };
   8180        }
   8181        invalidate_relative_selector_ts_dependency(
   8182            &data.stylist,
   8183            sibling,
   8184            TSStateForInvalidation::NTH,
   8185        );
   8186        element = sibling.next_sibling_element();
   8187    }
   8188 }
   8189 
   8190 #[no_mangle]
   8191 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForInsertion(
   8192    raw_data: &PerDocumentStyleData,
   8193    element: &RawGeckoElement,
   8194 ) {
   8195    let element = GeckoElement(element);
   8196    let data = raw_data.borrow();
   8197    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   8198 
   8199    let inherited = inherit_relative_selector_search_direction(
   8200        element.parent_element(),
   8201        element.prev_sibling_element(),
   8202    );
   8203    // Technically, we're not handling breakouts, where the anchor is a (later-sibling) descendant.
   8204    // For descendant case, we're ok since it's a descendant of an element yet to be styled.
   8205    // For later-sibling descendant, `HAS_SLOW_SELECTOR_LATER_SIBLINGS` is set anyway.
   8206    if inherited.is_empty() {
   8207        return;
   8208    }
   8209 
   8210    // Ok, we could've been inserted between two sibling elements that were connected
   8211    // through next sibling. This can happen in two ways:
   8212    // * `.a:has(+ .b)`
   8213    // * `:has(.. .a + .b ..)`
   8214    // Note that the previous sibling may be the anchor, and not part of the invalidation chain.
   8215    // Either way, there must be siblings to both sides of the element being inserted
   8216    // to consider it.
   8217    match (
   8218        element.prev_sibling_element(),
   8219        element.next_sibling_element(),
   8220    ) {
   8221        (Some(prev_sibling), Some(next_sibling)) => 'sibling: {
   8222            // If the prev sibling is not on the sibling search path, skip.
   8223            if !prev_sibling
   8224                .relative_selector_search_direction()
   8225                .intersects(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING)
   8226            {
   8227                break 'sibling;
   8228            }
   8229            element.apply_selector_flags(
   8230                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
   8231            );
   8232            invalidate_relative_selector_prev_sibling_side_effect(
   8233                prev_sibling,
   8234                quirks_mode,
   8235                SiblingTraversalMap::new(
   8236                    prev_sibling,
   8237                    prev_sibling.prev_sibling_element(),
   8238                    element.next_sibling_element(),
   8239                ), // Pretend this inserted element isn't here.
   8240                &data.stylist,
   8241            );
   8242            invalidate_relative_selector_next_sibling_side_effect(
   8243                next_sibling,
   8244                quirks_mode,
   8245                SiblingTraversalMap::new(
   8246                    next_sibling,
   8247                    Some(prev_sibling),
   8248                    next_sibling.next_sibling_element(),
   8249                ),
   8250                &data.stylist,
   8251            );
   8252        },
   8253        _ => (),
   8254    };
   8255 
   8256    let invalidator = RelativeSelectorInvalidator {
   8257        element,
   8258        quirks_mode,
   8259        snapshot_table: None,
   8260        invalidated: relative_selector_invalidated_at,
   8261        sibling_traversal_map: SiblingTraversalMap::default(),
   8262        _marker: std::marker::PhantomData,
   8263    };
   8264 
   8265    invalidator.invalidate_relative_selectors_for_dom_mutation(
   8266        true,
   8267        &data.stylist,
   8268        inherited,
   8269        DomMutationOperation::Insert,
   8270    );
   8271 }
   8272 
   8273 #[no_mangle]
   8274 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForAppend(
   8275    raw_data: &PerDocumentStyleData,
   8276    first_node: &RawGeckoNode,
   8277 ) {
   8278    let first_node = GeckoNode(first_node);
   8279    let inherited = inherit_relative_selector_search_direction(
   8280        first_node.parent_element(),
   8281        first_node.prev_sibling_element(),
   8282    );
   8283    if inherited.is_empty() {
   8284        return;
   8285    }
   8286    let first_element = if let Some(e) = first_node.as_element() {
   8287        e
   8288    } else if let Some(e) = first_node.next_sibling_element() {
   8289        e
   8290    } else {
   8291        return;
   8292    };
   8293    let data = raw_data.borrow();
   8294    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   8295 
   8296    let mut element = Some(first_element);
   8297    while let Some(e) = element {
   8298        let invalidator = RelativeSelectorInvalidator {
   8299            element: e,
   8300            quirks_mode,
   8301            snapshot_table: None,
   8302            sibling_traversal_map: SiblingTraversalMap::default(),
   8303            invalidated: relative_selector_invalidated_at,
   8304            _marker: std::marker::PhantomData,
   8305        };
   8306        invalidator.invalidate_relative_selectors_for_dom_mutation(
   8307            true,
   8308            &data.stylist,
   8309            inherited,
   8310            DomMutationOperation::Append,
   8311        );
   8312        element = e.next_sibling_element();
   8313    }
   8314 }
   8315 
   8316 #[no_mangle]
   8317 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForRemoval(
   8318    raw_data: &PerDocumentStyleData,
   8319    element: &RawGeckoElement,
   8320 ) {
   8321    let element = GeckoElement(element);
   8322 
   8323    // This element was in-tree, so we can safely say that if it was not on
   8324    // the relative selector search path, its removal will not invalidate any
   8325    // relative selector.
   8326    if element.relative_selector_search_direction().is_empty() {
   8327        return;
   8328    }
   8329    let node = element.as_node();
   8330    let (prev_sibling, next_sibling) = (node.prev_sibling_element(), node.next_sibling_element());
   8331 
   8332    let inherited =
   8333        inherit_relative_selector_search_direction(element.parent_element(), prev_sibling);
   8334    if inherited.is_empty() {
   8335        return;
   8336    }
   8337 
   8338    let data = raw_data.borrow();
   8339    let quirks_mode = data.stylist.quirks_mode();
   8340    // Same comment as insertion applies.
   8341    match (prev_sibling, next_sibling) {
   8342        (Some(prev_sibling), Some(next_sibling)) => {
   8343            // Pretend the element isn't there.
   8344            invalidate_relative_selector_prev_sibling_side_effect(
   8345                prev_sibling,
   8346                quirks_mode,
   8347                SiblingTraversalMap::new(
   8348                    prev_sibling,
   8349                    prev_sibling.prev_sibling_element(),
   8350                    Some(next_sibling),
   8351                ),
   8352                &data.stylist,
   8353            );
   8354            invalidate_relative_selector_next_sibling_side_effect(
   8355                next_sibling,
   8356                quirks_mode,
   8357                SiblingTraversalMap::new(
   8358                    next_sibling,
   8359                    Some(prev_sibling),
   8360                    next_sibling.next_sibling_element(),
   8361                ),
   8362                &data.stylist,
   8363            );
   8364        },
   8365        _ => (),
   8366    };
   8367    let invalidator = RelativeSelectorInvalidator {
   8368        element,
   8369        quirks_mode,
   8370        snapshot_table: None,
   8371        sibling_traversal_map: SiblingTraversalMap::default(),
   8372        invalidated: relative_selector_invalidated_at,
   8373        _marker: std::marker::PhantomData,
   8374    };
   8375    invalidator.invalidate_relative_selectors_for_dom_mutation(
   8376        true,
   8377        &data.stylist,
   8378        inherited,
   8379        DomMutationOperation::Remove,
   8380    );
   8381 }
   8382 
   8383 #[no_mangle]
   8384 pub extern "C" fn Servo_StyleSet_HasStateDependency(
   8385    raw_data: &PerDocumentStyleData,
   8386    element: &RawGeckoElement,
   8387    state: u64,
   8388 ) -> bool {
   8389    let element = GeckoElement(element);
   8390 
   8391    let state = ElementState::from_bits_retain(state);
   8392    let data = raw_data.borrow();
   8393 
   8394    data.stylist
   8395        .any_applicable_rule_data(element, |data| data.has_state_dependency(state))
   8396 }
   8397 
   8398 #[no_mangle]
   8399 pub extern "C" fn Servo_StyleSet_HasNthOfCustomStateDependency(
   8400    raw_data: &PerDocumentStyleData,
   8401    element: &RawGeckoElement,
   8402    state: *mut nsAtom,
   8403 ) -> bool {
   8404    let element = GeckoElement(element);
   8405    let data = raw_data.borrow();
   8406    data.stylist
   8407        .any_applicable_rule_data(element, |data| unsafe {
   8408            AtomIdent::with(state, |atom| data.has_nth_of_custom_state_dependency(atom))
   8409        })
   8410 }
   8411 
   8412 #[no_mangle]
   8413 pub extern "C" fn Servo_StyleSet_HasNthOfStateDependency(
   8414    raw_data: &PerDocumentStyleData,
   8415    element: &RawGeckoElement,
   8416    state: u64,
   8417 ) -> bool {
   8418    let element = GeckoElement(element);
   8419 
   8420    let state = ElementState::from_bits_retain(state);
   8421    let data = raw_data.borrow();
   8422 
   8423    data.stylist
   8424        .any_applicable_rule_data(element, |data| data.has_nth_of_state_dependency(state))
   8425 }
   8426 
   8427 #[no_mangle]
   8428 pub extern "C" fn Servo_StyleSet_RestyleSiblingsForNthOf(element: &RawGeckoElement, flags: u32) {
   8429    let flags = slow_selector_flags_from_node_selector_flags(flags);
   8430    let element = GeckoElement(element);
   8431    restyle_for_nth_of(element, flags);
   8432 }
   8433 
   8434 #[no_mangle]
   8435 pub extern "C" fn Servo_StyleSet_HasDocumentStateDependency(
   8436    raw_data: &PerDocumentStyleData,
   8437    state: u64,
   8438 ) -> bool {
   8439    let state = DocumentState::from_bits_retain(state);
   8440    let data = raw_data.borrow();
   8441 
   8442    data.stylist.has_document_state_dependency(state)
   8443 }
   8444 
   8445 fn computed_or_resolved_value(
   8446    style: &ComputedValues,
   8447    prop: NonCustomPropertyId,
   8448    mut context: Option<&mut resolved::Context>,
   8449    value: &mut nsACString,
   8450 ) {
   8451    let shorthand = match prop.longhand_or_shorthand() {
   8452        Ok(longhand) => {
   8453            return style
   8454                .computed_or_resolved_value(longhand, context, value)
   8455                .unwrap()
   8456        },
   8457        Err(shorthand) => shorthand,
   8458    };
   8459 
   8460    let mut block = PropertyDeclarationBlock::new();
   8461    for longhand in shorthand.longhands() {
   8462        block.push(
   8463            style.computed_or_resolved_declaration(longhand, context.as_deref_mut()),
   8464            Importance::Normal,
   8465        );
   8466    }
   8467    block.shorthand_to_css(shorthand, value).unwrap();
   8468 }
   8469 
   8470 #[no_mangle]
   8471 pub unsafe extern "C" fn Servo_GetComputedValue(
   8472    style: &ComputedValues,
   8473    prop: NonCustomCSSPropertyId,
   8474    value: &mut nsACString,
   8475 ) {
   8476    let prop = NonCustomPropertyId::from_noncustomcsspropertyid(prop).unwrap();
   8477    computed_or_resolved_value(style, prop, None, value)
   8478 }
   8479 
   8480 #[no_mangle]
   8481 pub unsafe extern "C" fn Servo_GetResolvedValue(
   8482    style: &ComputedValues,
   8483    prop: NonCustomCSSPropertyId,
   8484    raw_data: &PerDocumentStyleData,
   8485    element: &RawGeckoElement,
   8486    value: &mut nsACString,
   8487 ) {
   8488    let data = raw_data.borrow();
   8489    let device = data.stylist.device();
   8490    let prop = NonCustomPropertyId::from_noncustomcsspropertyid(prop).unwrap();
   8491    let mut context = resolved::Context {
   8492        style,
   8493        device,
   8494        element_info: resolved::ResolvedElementInfo {
   8495            element: GeckoElement(element),
   8496        },
   8497        for_property: prop,
   8498        current_longhand: None,
   8499    };
   8500 
   8501    computed_or_resolved_value(style, prop, Some(&mut context), value)
   8502 }
   8503 
   8504 #[no_mangle]
   8505 pub unsafe extern "C" fn Servo_GetComputedTypedValue(
   8506    style: &ComputedValues,
   8507    property: &nsACString,
   8508    result: *mut PropertyTypedValueResult,
   8509 ) -> bool {
   8510    let property_id = get_property_id_from_property!(property, false);
   8511 
   8512    let non_custom_property_id = match property_id.non_custom_id() {
   8513        Some(id) => id,
   8514        // XXX Handle custom properties here. Tracked in bug 1990426.
   8515        None => return false,
   8516    };
   8517 
   8518    let property_typed_value = match non_custom_property_id.longhand_or_shorthand() {
   8519        Ok(longhand) => style
   8520            .computed_typed_value(longhand)
   8521            .map_or(PropertyTypedValue::Unsupported, PropertyTypedValue::Typed),
   8522        Err(_) => PropertyTypedValue::Unsupported,
   8523    };
   8524 
   8525    *result = match property_typed_value {
   8526        PropertyTypedValue::None => PropertyTypedValueResult::None,
   8527 
   8528        PropertyTypedValue::Unsupported => {
   8529            let global_style_data = &*GLOBAL_STYLE_DATA;
   8530 
   8531            let mut block = PropertyDeclarationBlock::new();
   8532 
   8533            match non_custom_property_id.longhand_or_shorthand() {
   8534                Ok(longhand) => {
   8535                    block.push(
   8536                        style.computed_or_resolved_declaration(longhand, None),
   8537                        Importance::Normal,
   8538                    );
   8539                },
   8540                Err(shorthand) => {
   8541                    for longhand in shorthand.longhands() {
   8542                        block.push(
   8543                            style.computed_or_resolved_declaration(longhand, None),
   8544                            Importance::Normal,
   8545                        );
   8546                    }
   8547                },
   8548            };
   8549 
   8550            PropertyTypedValueResult::Unsupported(
   8551                Arc::new(global_style_data.shared_lock.wrap(block)).into(),
   8552            )
   8553        },
   8554 
   8555        PropertyTypedValue::Typed(typed_value) => PropertyTypedValueResult::Typed(typed_value),
   8556    };
   8557 
   8558    true
   8559 }
   8560 
   8561 #[no_mangle]
   8562 pub unsafe extern "C" fn Servo_GetCustomPropertyValue(
   8563    style: &ComputedValues,
   8564    name: &nsACString,
   8565    raw_data: &PerDocumentStyleData,
   8566    value: &mut nsACString,
   8567 ) -> bool {
   8568    let data = raw_data.borrow();
   8569    let name = Atom::from(name.as_str_unchecked());
   8570    let custom_registration = data.stylist.get_custom_property_registration(&name);
   8571    let computed_value = style.custom_properties.get(custom_registration, &name);
   8572    let computed_value = match computed_value {
   8573        Some(v) => v,
   8574        None => return false,
   8575    };
   8576    // TODO(emilio): This might want to return resolved colors and so on for example, see
   8577    // https://github.com/w3c/csswg-drafts/issues/10371.
   8578    computed_value.to_css(&mut CssWriter::new(value)).unwrap();
   8579    true
   8580 }
   8581 
   8582 #[no_mangle]
   8583 pub extern "C" fn Servo_GetCustomPropertiesCount(computed_values: &ComputedValues) -> u32 {
   8584    // Just expose the custom property items from custom_properties.inherited
   8585    // and custom_properties.non_inherited.
   8586    let properties = computed_values.custom_properties();
   8587    properties.inherited.len() as u32 + properties.non_inherited.len() as u32
   8588 }
   8589 
   8590 #[no_mangle]
   8591 pub extern "C" fn Servo_GetCustomPropertyNameAt(
   8592    computed_values: &ComputedValues,
   8593    index: u32,
   8594 ) -> *mut nsAtom {
   8595    match &computed_values
   8596        .custom_properties
   8597        .property_at(index as usize)
   8598    {
   8599        Some((name, _value)) => name.as_ptr(),
   8600        None => ptr::null_mut(),
   8601    }
   8602 }
   8603 
   8604 #[no_mangle]
   8605 pub extern "C" fn Servo_CssUrl_IsLocalRef(url: &url::CssUrl) -> bool {
   8606    url.is_fragment()
   8607 }
   8608 
   8609 fn relative_selector_dependencies_for_id<'a>(
   8610    old_id: *const nsAtom,
   8611    new_id: *const nsAtom,
   8612    element: &GeckoElement<'a>,
   8613    scope: Option<OpaqueElement>,
   8614    quirks_mode: QuirksMode,
   8615    invalidation_map: &'a InvalidationMap,
   8616    collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
   8617 ) {
   8618    [old_id, new_id]
   8619        .iter()
   8620        .filter(|id| !id.is_null())
   8621        .for_each(|id| unsafe {
   8622            AtomIdent::with(*id, |atom| {
   8623                match invalidation_map.id_to_selector.get(atom, quirks_mode) {
   8624                    Some(v) => {
   8625                        for dependency in v {
   8626                            collector.add_dependency(dependency, *element, scope);
   8627                        }
   8628                    },
   8629                    None => (),
   8630                };
   8631            })
   8632        });
   8633 }
   8634 
   8635 fn relative_selector_dependencies_for_class<'a>(
   8636    classes_changed: &SmallVec<[Atom; 8]>,
   8637    element: &GeckoElement<'a>,
   8638    scope: Option<OpaqueElement>,
   8639    quirks_mode: QuirksMode,
   8640    invalidation_map: &'a InvalidationMap,
   8641    collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
   8642 ) {
   8643    classes_changed.iter().for_each(|atom| {
   8644        match invalidation_map.class_to_selector.get(atom, quirks_mode) {
   8645            Some(v) => {
   8646                for dependency in v {
   8647                    collector.add_dependency(dependency, *element, scope);
   8648                }
   8649            },
   8650            None => (),
   8651        };
   8652    });
   8653 }
   8654 
   8655 fn relative_selector_dependencies_for_custom_state<'a>(
   8656    state: *const nsAtom,
   8657    element: GeckoElement<'a>,
   8658    scope: Option<OpaqueElement>,
   8659    invalidation_map: &'a InvalidationMap,
   8660    collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
   8661 ) {
   8662    unsafe {
   8663        AtomIdent::with(state, |atom| {
   8664            match invalidation_map.custom_state_affecting_selectors.get(atom) {
   8665                Some(v) => {
   8666                    for dependency in v {
   8667                        collector.add_dependency(dependency, element, scope);
   8668                    }
   8669                },
   8670                None => (),
   8671            };
   8672        })
   8673    }
   8674 }
   8675 
   8676 fn process_relative_selector_invalidations(
   8677    element: &GeckoElement,
   8678    snapshot_table: &ServoElementSnapshotTable,
   8679    data: &PerDocumentStyleDataImpl,
   8680 ) {
   8681    let snapshot = match snapshot_table.get(element) {
   8682        None => return,
   8683        Some(s) => s,
   8684    };
   8685    let mut states = None;
   8686    let mut classes = None;
   8687 
   8688    let quirks_mode: QuirksMode = data.stylist.quirks_mode();
   8689    let invalidator = RelativeSelectorInvalidator {
   8690        element: *element,
   8691        quirks_mode,
   8692        invalidated: relative_selector_invalidated_at,
   8693        sibling_traversal_map: SiblingTraversalMap::default(),
   8694        snapshot_table: Some(snapshot_table),
   8695        _marker: std::marker::PhantomData,
   8696    };
   8697 
   8698    invalidator.invalidate_relative_selectors_for_this(
   8699        &data.stylist,
   8700        |element, scope, data, quirks_mode, collector| {
   8701            let invalidation_map = data.relative_selector_invalidation_map();
   8702            let states = *states.get_or_insert_with(|| {
   8703                ElementWrapper::new(*element, snapshot_table).state_changes()
   8704            });
   8705            let classes = classes.get_or_insert_with(|| classes_changed(element, snapshot_table));
   8706            if snapshot.id_changed() {
   8707                relative_selector_dependencies_for_id(
   8708                    element
   8709                        .id()
   8710                        .map(|id| id.as_ptr().cast_const())
   8711                        .unwrap_or(ptr::null()),
   8712                    snapshot
   8713                        .id_attr()
   8714                        .map(|id| id.as_ptr().cast_const())
   8715                        .unwrap_or(ptr::null()),
   8716                    element,
   8717                    scope,
   8718                    quirks_mode,
   8719                    invalidation_map,
   8720                    collector,
   8721                );
   8722            }
   8723            relative_selector_dependencies_for_class(
   8724                &classes,
   8725                element,
   8726                scope,
   8727                quirks_mode,
   8728                invalidation_map,
   8729                collector,
   8730            );
   8731            snapshot.each_attr_changed(|attr| {
   8732                add_relative_selector_attribute_dependency(
   8733                    element,
   8734                    &scope,
   8735                    invalidation_map,
   8736                    attr,
   8737                    collector,
   8738                )
   8739            });
   8740            invalidation_map
   8741                .state_affecting_selectors
   8742                .lookup_with_additional(*element, quirks_mode, None, &[], states, |dependency| {
   8743                    if !dependency.state.intersects(states) {
   8744                        return true;
   8745                    }
   8746                    collector.add_dependency(&dependency.dep, *element, scope);
   8747                    true
   8748                });
   8749        },
   8750    );
   8751 }
   8752 
   8753 #[no_mangle]
   8754 pub extern "C" fn Servo_ProcessInvalidations(
   8755    set: &PerDocumentStyleData,
   8756    element: &RawGeckoElement,
   8757    snapshots: *const ServoElementSnapshotTable,
   8758 ) {
   8759    debug_assert!(!snapshots.is_null());
   8760 
   8761    let element = GeckoElement(element);
   8762    debug_assert!(element.has_snapshot());
   8763    debug_assert!(!element.handled_snapshot());
   8764 
   8765    let snapshot_table = unsafe { &*snapshots };
   8766    let per_doc_data = set.borrow();
   8767    process_relative_selector_invalidations(&element, snapshot_table, &per_doc_data);
   8768 
   8769    let mut data = element.mutate_data();
   8770    if data.is_none() {
   8771        // Snapshot for unstyled element is really only meant for relative selector
   8772        // invalidation, so this is fine.
   8773        return;
   8774    }
   8775 
   8776    let global_style_data = &*GLOBAL_STYLE_DATA;
   8777    let guard = global_style_data.shared_lock.read();
   8778    let per_doc_data = set.borrow();
   8779    let shared_style_context = create_shared_context(
   8780        &global_style_data,
   8781        &guard,
   8782        &per_doc_data.stylist,
   8783        TraversalFlags::empty(),
   8784        snapshot_table,
   8785    );
   8786    let mut data = data.as_mut().map(|d| &mut **d);
   8787 
   8788    let mut selector_caches = SelectorCaches::default();
   8789    if let Some(ref mut data) = data {
   8790        // FIXME(emilio): Ideally we could share the nth-index-cache across all
   8791        // the elements?
   8792        let result = data.invalidate_style_if_needed(
   8793            element,
   8794            &shared_style_context,
   8795            None,
   8796            &mut selector_caches,
   8797        );
   8798 
   8799        if result.has_invalidated_siblings() {
   8800            let parent = element
   8801                .traversal_parent()
   8802                .expect("How could we invalidate siblings without a common parent?");
   8803            unsafe {
   8804                parent.set_dirty_descendants();
   8805                bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
   8806            }
   8807        } else if result.has_invalidated_descendants() {
   8808            unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
   8809        } else if result.has_invalidated_self() {
   8810            unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
   8811        }
   8812    }
   8813 }
   8814 
   8815 #[no_mangle]
   8816 pub extern "C" fn Servo_HasPendingRestyleAncestor(
   8817    element: &RawGeckoElement,
   8818    may_need_to_flush_layout: bool,
   8819 ) -> bool {
   8820    let mut has_yet_to_be_styled = false;
   8821    let mut element = Some(GeckoElement(element));
   8822    while let Some(e) = element {
   8823        if e.has_any_animation() {
   8824            return true;
   8825        }
   8826 
   8827        // If the element needs a frame, it means that we haven't styled it yet
   8828        // after it got inserted in the document, and thus we may need to do
   8829        // that for transitions and animations to trigger.
   8830        //
   8831        // This is a fast path in the common case, but `has_yet_to_be_styled` is
   8832        // the real check for this.
   8833        if e.needs_frame() {
   8834            return true;
   8835        }
   8836 
   8837        let data = e.borrow_data();
   8838        if let Some(ref data) = data {
   8839            if !data.hint.is_empty() {
   8840                return true;
   8841            }
   8842            if has_yet_to_be_styled && !data.styles.is_display_none() {
   8843                return true;
   8844            }
   8845            // Ideally, DOM mutations wouldn't affect layout trees of siblings.
   8846            //
   8847            // In practice, this can happen because Gecko deals pretty badly
   8848            // with some kinds of content insertion and removals.
   8849            //
   8850            // If we may need to flush layout, we need frames to accurately
   8851            // determine whether we'll actually flush, so if we have to
   8852            // reconstruct we need to flush style, which is what will take care
   8853            // of ensuring that frames are constructed, even if the style itself
   8854            // is up-to-date.
   8855            if may_need_to_flush_layout && data.damage.contains(GeckoRestyleDamage::reconstruct()) {
   8856                return true;
   8857            }
   8858        }
   8859        has_yet_to_be_styled = data.is_none();
   8860 
   8861        element = e.traversal_parent();
   8862    }
   8863    false
   8864 }
   8865 
   8866 #[no_mangle]
   8867 pub unsafe extern "C" fn Servo_SelectorList_Parse(
   8868    selector_list: &nsACString,
   8869    is_chrome: bool,
   8870 ) -> *mut SelectorList {
   8871    use style::selector_parser::SelectorParser;
   8872 
   8873    let url_data = if is_chrome {
   8874        dummy_chrome_url_data()
   8875    } else {
   8876        dummy_url_data()
   8877    };
   8878 
   8879    let input = selector_list.as_str_unchecked();
   8880    let selector_list = match SelectorParser::parse_author_origin_no_namespace(&input, url_data) {
   8881        Ok(selector_list) => selector_list,
   8882        Err(..) => return ptr::null_mut(),
   8883    };
   8884 
   8885    Box::into_raw(Box::new(selector_list))
   8886 }
   8887 
   8888 #[no_mangle]
   8889 pub unsafe extern "C" fn Servo_SelectorList_Drop(list: *mut SelectorList) {
   8890    let _ = Box::from_raw(list);
   8891 }
   8892 
   8893 #[no_mangle]
   8894 pub unsafe extern "C" fn Servo_IsValidCSSColor(value: &nsACString) -> bool {
   8895    let mut input = ParserInput::new(value.as_str_unchecked());
   8896    let mut input = Parser::new(&mut input);
   8897    let context = ParserContext::new(
   8898        Origin::Author,
   8899        dummy_url_data(),
   8900        Some(CssRuleType::Style),
   8901        ParsingMode::DEFAULT,
   8902        QuirksMode::NoQuirks,
   8903        /* namespaces = */ Default::default(),
   8904        None,
   8905        None,
   8906    );
   8907    specified::Color::is_valid(&context, &mut input)
   8908 }
   8909 
   8910 struct ComputeColorResult {
   8911    result_color: AbsoluteColor,
   8912    was_current_color: bool,
   8913 }
   8914 
   8915 unsafe fn compute_color(
   8916    raw_data: Option<&PerDocumentStyleData>,
   8917    current_color: &AbsoluteColor,
   8918    value: &nsACString,
   8919    loader: *mut Loader,
   8920 ) -> Option<ComputeColorResult> {
   8921    let mut input = ParserInput::new(value.as_str_unchecked());
   8922    let mut input = Parser::new(&mut input);
   8923    let reporter = loader.as_mut().and_then(|loader| {
   8924        // Make an ErrorReporter that will report errors as being "from DOM".
   8925        ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
   8926    });
   8927 
   8928    let context = ParserContext::new(
   8929        Origin::Author,
   8930        dummy_url_data(),
   8931        Some(CssRuleType::Style),
   8932        ParsingMode::DEFAULT,
   8933        QuirksMode::NoQuirks,
   8934        /* namespaces = */ Default::default(),
   8935        reporter.as_ref().map(|e| e as &dyn ParseErrorReporter),
   8936        None,
   8937    );
   8938 
   8939    let data;
   8940    let device = match raw_data {
   8941        Some(d) => {
   8942            data = d.borrow();
   8943            Some(data.stylist.device())
   8944        },
   8945        None => None,
   8946    };
   8947 
   8948    let computed = specified::Color::parse_and_compute(&context, &mut input, device)?;
   8949 
   8950    let result_color = computed.resolve_to_absolute(current_color);
   8951    let was_current_color = computed.is_currentcolor();
   8952 
   8953    Some(ComputeColorResult {
   8954        result_color,
   8955        was_current_color,
   8956    })
   8957 }
   8958 
   8959 #[no_mangle]
   8960 pub unsafe extern "C" fn Servo_ComputeColor(
   8961    raw_data: Option<&PerDocumentStyleData>,
   8962    current_color: structs::nscolor,
   8963    value: &nsACString,
   8964    result_color: &mut structs::nscolor,
   8965    was_current_color: *mut bool,
   8966    loader: *mut Loader,
   8967 ) -> bool {
   8968    let current_color = style::gecko::values::convert_nscolor_to_absolute_color(current_color);
   8969    let Some(result) = compute_color(raw_data, &current_color, value, loader) else {
   8970        return false;
   8971    };
   8972 
   8973    *result_color = style::gecko::values::convert_absolute_color_to_nscolor(&result.result_color);
   8974    if !was_current_color.is_null() {
   8975        *was_current_color = result.was_current_color
   8976    }
   8977    true
   8978 }
   8979 
   8980 // This implements https://html.spec.whatwg.org/#update-a-color-well-control-color,
   8981 // except the actual serialization steps in step 6 of "serialize a color well control color".
   8982 #[no_mangle]
   8983 pub unsafe extern "C" fn Servo_ComputeColorWellControlColor(
   8984    raw_data: Option<&PerDocumentStyleData>,
   8985    value: &nsACString,
   8986    to_color_space: ColorSpace,
   8987    result_color: &mut AbsoluteColor,
   8988 ) -> bool {
   8989    if let Some(color) = compute_color(raw_data, &AbsoluteColor::BLACK, value, ptr::null_mut()) {
   8990        *result_color = color.result_color.to_color_space(to_color_space);
   8991        true
   8992    } else {
   8993        false
   8994    }
   8995 }
   8996 
   8997 #[no_mangle]
   8998 pub unsafe extern "C" fn Servo_ColorTo(
   8999    from_color: &nsACString,
   9000    to_color_space: &nsACString,
   9001    result_color: &mut nsACString,
   9002    result_components: &mut nsTArray<f32>,
   9003    result_adjusted: &mut bool,
   9004    loader: *mut Loader,
   9005 ) -> bool {
   9006    // Figure out the color space.
   9007    let mut input = ParserInput::new(to_color_space.as_str_unchecked());
   9008    let mut input = Parser::new(&mut input);
   9009    let to_color_space = match ColorSpace::parse(&mut input) {
   9010        Ok(color_space) => color_space,
   9011        Err(_) => {
   9012            // Can't parse the color space? Fail the conversion.
   9013            return false;
   9014        },
   9015    };
   9016 
   9017    let mut input = ParserInput::new(from_color.as_str_unchecked());
   9018    let mut input = Parser::new(&mut input);
   9019 
   9020    let reporter = loader.as_mut().and_then(|loader| {
   9021        // Make an ErrorReporter that will report errors as being "from DOM".
   9022        ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
   9023    });
   9024 
   9025    let context = ParserContext::new(
   9026        Origin::Author,
   9027        dummy_url_data(),
   9028        Some(CssRuleType::Style),
   9029        ParsingMode::DEFAULT,
   9030        QuirksMode::NoQuirks,
   9031        /* namespaces = */ Default::default(),
   9032        reporter.as_ref().map(|e| e as &dyn ParseErrorReporter),
   9033        None,
   9034    );
   9035 
   9036    let specified = match specified::Color::parse(&context, &mut input) {
   9037        Ok(color) => color,
   9038        Err(_) => return false,
   9039    };
   9040 
   9041    let color = match specified {
   9042        specified::Color::Absolute(ref absolute) => &absolute.color,
   9043        _ => {
   9044            // Can't do anything with a non-absolute color from here, so we
   9045            // fail the conversion.
   9046            return false;
   9047        },
   9048    };
   9049 
   9050    let color = color.to_color_space(to_color_space);
   9051    let mut s = String::new();
   9052    color
   9053        .write_author_preferred_value(&mut CssWriter::new(&mut s))
   9054        .unwrap();
   9055    result_color.assign(&s);
   9056 
   9057    result_components.extend(color.raw_components().iter().copied());
   9058 
   9059    // For now we don't do gamut mapping, so always false.
   9060    *result_adjusted = false;
   9061 
   9062    true
   9063 }
   9064 
   9065 #[no_mangle]
   9066 pub extern "C" fn Servo_ResolveColor(
   9067    color: &computed::Color,
   9068    foreground: &style::color::AbsoluteColor,
   9069 ) -> style::color::AbsoluteColor {
   9070    color.resolve_to_absolute(foreground)
   9071 }
   9072 
   9073 #[no_mangle]
   9074 pub extern "C" fn Servo_ResolveCalcLengthPercentage(
   9075    calc: &computed::length_percentage::CalcLengthPercentage,
   9076    basis: f32,
   9077 ) -> f32 {
   9078    calc.resolve(computed::Length::new(basis)).px()
   9079 }
   9080 
   9081 /// Result of resolving a math function node potentially containing
   9082 /// anchor positioning function.
   9083 #[repr(u8)]
   9084 pub enum CalcAnchorPositioningFunctionResolution {
   9085    /// Anchor positioning function is used, but at least one of them
   9086    /// did not resolve to a valid reference - Property using this
   9087    /// expression is now invalid at computed time.
   9088    Invalid,
   9089    /// Anchor positioning function is used, and all of them resolved
   9090    /// to valid references, or specified a fallback.
   9091    Valid(computed::LengthPercentage),
   9092 }
   9093 
   9094 #[no_mangle]
   9095 pub extern "C" fn Servo_ResolveAnchorFunctionsInCalcPercentage(
   9096    calc: &computed::length_percentage::CalcLengthPercentage,
   9097    allowed: &AllowAnchorPosResolutionInCalcPercentage,
   9098    params: &AnchorPosOffsetResolutionParams,
   9099    out: &mut CalcAnchorPositioningFunctionResolution,
   9100 ) {
   9101    let resolved = calc.resolve_anchor(*allowed, params);
   9102 
   9103    match resolved {
   9104        Err(()) => *out = CalcAnchorPositioningFunctionResolution::Invalid,
   9105        Ok((node, clamping_mode)) => {
   9106            *out = CalcAnchorPositioningFunctionResolution::Valid(
   9107                computed::LengthPercentage::new_calc(node, clamping_mode),
   9108            )
   9109        },
   9110    };
   9111 }
   9112 
   9113 #[no_mangle]
   9114 pub extern "C" fn Servo_ConvertColorSpace(
   9115    color: &AbsoluteColor,
   9116    color_space: ColorSpace,
   9117 ) -> AbsoluteColor {
   9118    color.to_color_space(color_space)
   9119 }
   9120 
   9121 #[no_mangle]
   9122 pub unsafe extern "C" fn Servo_IntersectionObserverMargin_Parse(
   9123    value: &nsACString,
   9124    result: *mut IntersectionObserverMargin,
   9125 ) -> bool {
   9126    let value = value.as_str_unchecked();
   9127    let result = result.as_mut().unwrap();
   9128 
   9129    let mut input = ParserInput::new(&value);
   9130    let mut parser = Parser::new(&mut input);
   9131 
   9132    let url_data = dummy_url_data();
   9133    let context = ParserContext::new(
   9134        Origin::Author,
   9135        url_data,
   9136        Some(CssRuleType::Style),
   9137        ParsingMode::DEFAULT,
   9138        QuirksMode::NoQuirks,
   9139        /* namespaces = */ Default::default(),
   9140        None,
   9141        None,
   9142    );
   9143 
   9144    let margin = parser.parse_entirely(|p| IntersectionObserverMargin::parse(&context, p));
   9145    match margin {
   9146        Ok(margin) => {
   9147            *result = margin;
   9148            true
   9149        },
   9150        Err(..) => false,
   9151    }
   9152 }
   9153 
   9154 #[no_mangle]
   9155 pub extern "C" fn Servo_IntersectionObserverMargin_ToString(
   9156    root_margin: &IntersectionObserverMargin,
   9157    result: &mut nsACString,
   9158 ) {
   9159    let mut writer = CssWriter::new(result);
   9160    root_margin.to_css(&mut writer).unwrap();
   9161 }
   9162 
   9163 #[no_mangle]
   9164 pub extern "C" fn Servo_ParseTransformIntoMatrix(
   9165    value: &nsACString,
   9166    contain_3d: &mut bool,
   9167    result: &mut structs::Matrix4x4Components,
   9168 ) -> bool {
   9169    use style::properties::longhands::transform;
   9170 
   9171    let string = unsafe { value.as_str_unchecked() };
   9172    let mut input = ParserInput::new(&string);
   9173    let mut parser = Parser::new(&mut input);
   9174    let context = ParserContext::new(
   9175        Origin::Author,
   9176        unsafe { dummy_url_data() },
   9177        Some(CssRuleType::Style),
   9178        ParsingMode::DEFAULT,
   9179        QuirksMode::NoQuirks,
   9180        /* namespaces = */ Default::default(),
   9181        None,
   9182        None,
   9183    );
   9184 
   9185    let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
   9186        Ok(t) => t,
   9187        Err(..) => return false,
   9188    };
   9189 
   9190    let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
   9191        Ok(result) => result,
   9192        Err(..) => return false,
   9193    };
   9194 
   9195    *result = m.to_array();
   9196    *contain_3d = is_3d;
   9197    true
   9198 }
   9199 
   9200 #[no_mangle]
   9201 pub extern "C" fn Servo_ParseFilters(
   9202    value: &nsACString,
   9203    ignore_urls: bool,
   9204    data: *mut URLExtraData,
   9205    out: &mut style::OwnedSlice<Filter>,
   9206 ) -> bool {
   9207    use style::values::specified::effects::SpecifiedFilter;
   9208 
   9209    let string = unsafe { value.as_str_unchecked() };
   9210    let mut input = ParserInput::new(&string);
   9211    let mut parser = Parser::new(&mut input);
   9212    let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
   9213    let context = ParserContext::new(
   9214        Origin::Author,
   9215        url_data,
   9216        None,
   9217        ParsingMode::DEFAULT,
   9218        QuirksMode::NoQuirks,
   9219        /* namespaces = */ Default::default(),
   9220        None,
   9221        None,
   9222    );
   9223 
   9224    let mut filters = vec![];
   9225 
   9226    if parser
   9227        .try_parse(|i| i.expect_ident_matching("none"))
   9228        .is_ok()
   9229    {
   9230        return parser.expect_exhausted().is_ok();
   9231    }
   9232 
   9233    if parser.is_exhausted() {
   9234        return false;
   9235    }
   9236 
   9237    while !parser.is_exhausted() {
   9238        let specified_filter = match SpecifiedFilter::parse(&context, &mut parser) {
   9239            Ok(f) => f,
   9240            Err(..) => return false,
   9241        };
   9242 
   9243        let filter = match specified_filter.to_computed_value_without_context() {
   9244            Ok(f) => f,
   9245            Err(..) => return false,
   9246        };
   9247 
   9248        if ignore_urls && matches!(filter, Filter::Url(_)) {
   9249            continue;
   9250        }
   9251 
   9252        filters.push(filter);
   9253    }
   9254 
   9255    *out = style::OwnedSlice::from(filters);
   9256    true
   9257 }
   9258 
   9259 #[no_mangle]
   9260 pub unsafe extern "C" fn Servo_ParseFontShorthandForMatching(
   9261    value: &nsACString,
   9262    data: *mut URLExtraData,
   9263    family: &mut FontFamilyList,
   9264    style: &mut FontStyle,
   9265    stretch: &mut FontStretch,
   9266    weight: &mut FontWeight,
   9267    size: Option<&mut f32>,
   9268    small_caps: Option<&mut bool>,
   9269 ) -> bool {
   9270    use style::properties::shorthands::font;
   9271    use style::values::generics::font::FontStyle as GenericFontStyle;
   9272    use style::values::specified::font as specified;
   9273 
   9274    let string = value.as_str_unchecked();
   9275    let mut input = ParserInput::new(&string);
   9276    let mut parser = Parser::new(&mut input);
   9277    let url_data = UrlExtraData::from_ptr_ref(&data);
   9278    let context = ParserContext::new(
   9279        Origin::Author,
   9280        url_data,
   9281        Some(CssRuleType::FontFace),
   9282        ParsingMode::DEFAULT,
   9283        QuirksMode::NoQuirks,
   9284        /* namespaces = */ Default::default(),
   9285        None,
   9286        None,
   9287    );
   9288 
   9289    let font = match parser.parse_entirely(|f| font::parse_value(&context, f)) {
   9290        Ok(f) => f,
   9291        Err(..) => return false,
   9292    };
   9293 
   9294    // The system font is not acceptable, so we return false.
   9295    match font.font_family {
   9296        specified::FontFamily::Values(list) => *family = list,
   9297        specified::FontFamily::System(_) => return false,
   9298    }
   9299 
   9300    let specified_font_style = match font.font_style {
   9301        specified::FontStyle::Specified(ref s) => s,
   9302        specified::FontStyle::System(_) => return false,
   9303    };
   9304 
   9305    *style = match *specified_font_style {
   9306        GenericFontStyle::Italic => FontStyle::ITALIC,
   9307        GenericFontStyle::Oblique(ref angle) => FontStyle::oblique(angle.degrees()),
   9308    };
   9309 
   9310    *stretch = match font.font_stretch {
   9311        specified::FontStretch::Keyword(ref k) => k.compute(),
   9312        specified::FontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()),
   9313        specified::FontStretch::System(_) => return false,
   9314    };
   9315 
   9316    *weight = match font.font_weight {
   9317        specified::FontWeight::Absolute(w) => w.compute(),
   9318        // Resolve relative font weights against the initial of font-weight
   9319        // (normal, which is equivalent to 400).
   9320        specified::FontWeight::Bolder => FontWeight::normal().bolder(),
   9321        specified::FontWeight::Lighter => FontWeight::normal().lighter(),
   9322        specified::FontWeight::System(_) => return false,
   9323    };
   9324 
   9325    // XXX This is unfinished; see values::specified::FontSize::ToComputedValue
   9326    // for a more complete implementation (but we can't use it as-is).
   9327    if let Some(size) = size {
   9328        *size = match font.font_size {
   9329            specified::FontSize::Length(lp) => {
   9330                use style::values::generics::transform::ToAbsoluteLength;
   9331                match lp.to_pixel_length(None) {
   9332                    Ok(len) => len,
   9333                    Err(..) => return false,
   9334                }
   9335            },
   9336            specified::FontSize::Keyword(info) => {
   9337                let keyword = if info.kw != specified::FontSizeKeyword::Math {
   9338                    info.kw
   9339                } else {
   9340                    specified::FontSizeKeyword::Medium
   9341                };
   9342                // Map absolute-size keywords to sizes.
   9343                // TODO: Maybe get a meaningful quirks / base size from the caller?
   9344                let quirks_mode = QuirksMode::NoQuirks;
   9345                keyword
   9346                    .to_length_without_context(
   9347                        quirks_mode,
   9348                        computed::Length::new(specified::FONT_MEDIUM_PX),
   9349                    )
   9350                    .0
   9351                    .px()
   9352            },
   9353            // smaller, larger not currently supported
   9354            specified::FontSize::Smaller
   9355            | specified::FontSize::Larger
   9356            | specified::FontSize::System(_) => {
   9357                return false;
   9358            },
   9359        };
   9360    }
   9361 
   9362    if let Some(small_caps) = small_caps {
   9363        use style::computed_values::font_variant_caps::T::SmallCaps;
   9364        *small_caps = font.font_variant_caps == SmallCaps;
   9365    }
   9366 
   9367    true
   9368 }
   9369 
   9370 #[no_mangle]
   9371 pub unsafe extern "C" fn Servo_SourceSizeList_Parse(value: &nsACString) -> *mut SourceSizeList {
   9372    let value = value.as_str_unchecked();
   9373    let mut input = ParserInput::new(value);
   9374    let mut parser = Parser::new(&mut input);
   9375 
   9376    let context = ParserContext::new(
   9377        Origin::Author,
   9378        dummy_url_data(),
   9379        Some(CssRuleType::Style),
   9380        ParsingMode::DEFAULT,
   9381        QuirksMode::NoQuirks,
   9382        /* namespaces = */ Default::default(),
   9383        None,
   9384        None,
   9385    );
   9386 
   9387    // NB: Intentionally not calling parse_entirely.
   9388    let list = SourceSizeList::parse(&context, &mut parser);
   9389    Box::into_raw(Box::new(list))
   9390 }
   9391 
   9392 #[no_mangle]
   9393 pub unsafe extern "C" fn Servo_SourceSizeList_Evaluate(
   9394    raw_data: &PerDocumentStyleData,
   9395    list: Option<&SourceSizeList>,
   9396 ) -> i32 {
   9397    let doc_data = raw_data.borrow();
   9398    let device = doc_data.stylist.device();
   9399    let quirks_mode = doc_data.stylist.quirks_mode();
   9400 
   9401    let result = match list {
   9402        Some(list) => list.evaluate(device, quirks_mode),
   9403        None => SourceSizeList::empty().evaluate(device, quirks_mode),
   9404    };
   9405 
   9406    result.0
   9407 }
   9408 
   9409 #[no_mangle]
   9410 pub unsafe extern "C" fn Servo_SourceSizeList_Drop(list: *mut SourceSizeList) {
   9411    let _ = Box::from_raw(list);
   9412 }
   9413 
   9414 #[no_mangle]
   9415 pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges(
   9416    root: &RawGeckoElement,
   9417    document_style: &PerDocumentStyleData,
   9418    non_document_styles: &nsTArray<&AuthorStyles>,
   9419    states_changed: u64,
   9420 ) {
   9421    use style::invalidation::element::document_state::DocumentStateInvalidationProcessor;
   9422    use style::invalidation::element::invalidator::TreeStyleInvalidator;
   9423 
   9424    let document_data = document_style.borrow();
   9425 
   9426    let iter = document_data
   9427        .stylist
   9428        .iter_origins()
   9429        .map(|(data, _origin)| data)
   9430        .chain(
   9431            non_document_styles
   9432                .iter()
   9433                .map(|author_styles| &*author_styles.data),
   9434        );
   9435 
   9436    let mut selector_caches = SelectorCaches::default();
   9437    let root = GeckoElement(root);
   9438    let mut processor = DocumentStateInvalidationProcessor::new(
   9439        iter,
   9440        DocumentState::from_bits_retain(states_changed),
   9441        &mut selector_caches,
   9442        root.as_node().owner_doc().quirks_mode(),
   9443    );
   9444 
   9445    let result =
   9446        TreeStyleInvalidator::new(root, /* stack_limit_checker = */ None, &mut processor)
   9447            .invalidate();
   9448 
   9449    debug_assert!(!result.has_invalidated_siblings(), "How in the world?");
   9450    if result.has_invalidated_descendants() {
   9451        bindings::Gecko_NoteDirtySubtreeForInvalidation(root.0);
   9452    } else if result.has_invalidated_self() {
   9453        bindings::Gecko_NoteDirtyElement(root.0);
   9454    }
   9455 }
   9456 
   9457 #[no_mangle]
   9458 pub unsafe extern "C" fn Servo_PseudoClass_GetStates(name: &nsACString) -> u64 {
   9459    let name = name.as_str_unchecked();
   9460    match NonTSPseudoClass::parse_non_functional(name) {
   9461        None => 0,
   9462        // Ignore :any-link since it contains both visited and unvisited state.
   9463        Some(NonTSPseudoClass::AnyLink) => 0,
   9464        Some(pseudo_class) => pseudo_class.state_flag().bits(),
   9465    }
   9466 }
   9467 
   9468 #[no_mangle]
   9469 pub unsafe extern "C" fn Servo_UseCounters_Create() -> *mut UseCounters {
   9470    Box::into_raw(Box::<UseCounters>::default())
   9471 }
   9472 
   9473 #[no_mangle]
   9474 pub unsafe extern "C" fn Servo_UseCounters_Drop(c: *mut UseCounters) {
   9475    let _ = Box::from_raw(c);
   9476 }
   9477 
   9478 #[no_mangle]
   9479 pub unsafe extern "C" fn Servo_UseCounters_Merge(
   9480    doc_counters: &UseCounters,
   9481    sheet_counters: &UseCounters,
   9482 ) {
   9483    doc_counters.merge(sheet_counters)
   9484 }
   9485 
   9486 #[no_mangle]
   9487 pub unsafe extern "C" fn Servo_IsPropertyIdRecordedInUseCounter(
   9488    use_counters: &UseCounters,
   9489    id: NonCustomCSSPropertyId,
   9490 ) -> bool {
   9491    let id = NonCustomPropertyId::from_noncustomcsspropertyid(id).unwrap();
   9492    use_counters.non_custom_properties.recorded(id)
   9493 }
   9494 
   9495 #[no_mangle]
   9496 pub unsafe extern "C" fn Servo_IsUnknownPropertyRecordedInUseCounter(
   9497    use_counters: &UseCounters,
   9498    p: CountedUnknownProperty,
   9499 ) -> bool {
   9500    use_counters.counted_unknown_properties.recorded(p)
   9501 }
   9502 
   9503 #[no_mangle]
   9504 pub unsafe extern "C" fn Servo_IsCustomUseCounterRecorded(
   9505    use_counters: &UseCounters,
   9506    c: CustomUseCounter,
   9507 ) -> bool {
   9508    use_counters.custom.recorded(c)
   9509 }
   9510 
   9511 #[no_mangle]
   9512 pub unsafe extern "C" fn Servo_IsCssPropertyRecordedInUseCounter(
   9513    use_counters: &UseCounters,
   9514    property: &nsACString,
   9515    known_prop: *mut bool,
   9516 ) -> bool {
   9517    *known_prop = false;
   9518 
   9519    let prop_name = property.as_str_unchecked();
   9520    if let Ok(p) = PropertyId::parse_unchecked_for_testing(prop_name) {
   9521        if let Some(id) = p.non_custom_id() {
   9522            *known_prop = true;
   9523            return use_counters.non_custom_properties.recorded(id);
   9524        }
   9525    }
   9526 
   9527    if let Some(p) = CountedUnknownProperty::parse_for_testing(prop_name) {
   9528        *known_prop = true;
   9529        return use_counters.counted_unknown_properties.recorded(p);
   9530    }
   9531 
   9532    false
   9533 }
   9534 
   9535 #[no_mangle]
   9536 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Create(
   9537    buffer: *mut u8,
   9538    len: usize,
   9539 ) -> *mut SharedMemoryBuilder {
   9540    Box::into_raw(Box::new(SharedMemoryBuilder::new(buffer, len)))
   9541 }
   9542 
   9543 #[no_mangle]
   9544 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_AddStylesheet(
   9545    builder: &mut SharedMemoryBuilder,
   9546    contents: &StylesheetContents,
   9547    error_message: &mut nsACString,
   9548 ) -> *const LockedCssRules {
   9549    // Assert some things we assume when we create a style sheet from shared
   9550    // memory.
   9551    debug_assert_eq!(contents.quirks_mode, QuirksMode::NoQuirks);
   9552    debug_assert!(contents.source_map_url.is_none());
   9553    debug_assert!(contents.source_url.is_none());
   9554 
   9555    match builder.write(&contents.rules) {
   9556        Ok(rules_ptr) => &**rules_ptr,
   9557        Err(message) => {
   9558            error_message.assign(&message);
   9559            ptr::null()
   9560        },
   9561    }
   9562 }
   9563 
   9564 #[no_mangle]
   9565 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_GetLength(
   9566    builder: &SharedMemoryBuilder,
   9567 ) -> usize {
   9568    builder.len()
   9569 }
   9570 
   9571 #[no_mangle]
   9572 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Drop(builder: *mut SharedMemoryBuilder) {
   9573    let _ = Box::from_raw(builder);
   9574 }
   9575 
   9576 #[no_mangle]
   9577 pub unsafe extern "C" fn Servo_StyleArcSlice_EmptyPtr() -> *mut c_void {
   9578    style_traits::arc_slice::ArcSlice::<u64>::leaked_empty_ptr()
   9579 }
   9580 
   9581 #[no_mangle]
   9582 pub unsafe extern "C" fn Servo_LoadData_GetLazy(
   9583    source: &url::LoadDataSource,
   9584 ) -> *const url::LoadData {
   9585    source.get()
   9586 }
   9587 
   9588 #[no_mangle]
   9589 pub extern "C" fn Servo_LengthPercentage_ToCss(
   9590    lp: &computed::LengthPercentage,
   9591    result: &mut nsACString,
   9592 ) {
   9593    lp.to_css(&mut CssWriter::new(result)).unwrap();
   9594 }
   9595 
   9596 #[no_mangle]
   9597 pub extern "C" fn Servo_FontStyle_ToCss(s: &FontStyle, result: &mut nsACString) {
   9598    s.to_css(&mut CssWriter::new(result)).unwrap()
   9599 }
   9600 
   9601 #[no_mangle]
   9602 pub extern "C" fn Servo_FontWeight_ToCss(w: &FontWeight, result: &mut nsACString) {
   9603    w.to_css(&mut CssWriter::new(result)).unwrap()
   9604 }
   9605 
   9606 #[no_mangle]
   9607 pub extern "C" fn Servo_FontStretch_ToCss(s: &FontStretch, result: &mut nsACString) {
   9608    s.to_css(&mut CssWriter::new(result)).unwrap()
   9609 }
   9610 
   9611 #[no_mangle]
   9612 pub extern "C" fn Servo_FontStretch_SerializeKeyword(
   9613    s: &FontStretch,
   9614    result: &mut nsACString,
   9615 ) -> bool {
   9616    let kw = match s.as_keyword() {
   9617        Some(kw) => kw,
   9618        None => return false,
   9619    };
   9620    kw.to_css(&mut CssWriter::new(result)).unwrap();
   9621    true
   9622 }
   9623 
   9624 #[no_mangle]
   9625 pub unsafe extern "C" fn Servo_CursorKind_Parse(
   9626    cursor: &nsACString,
   9627    result: &mut computed::ui::CursorKind,
   9628 ) -> bool {
   9629    match computed::ui::CursorKind::from_ident(cursor.as_str_unchecked()) {
   9630        Ok(c) => {
   9631            *result = c;
   9632            true
   9633        },
   9634        Err(..) => false,
   9635    }
   9636 }
   9637 
   9638 #[no_mangle]
   9639 pub extern "C" fn Servo_FontFamily_Generic(generic: GenericFontFamily) -> &'static FontFamily {
   9640    FontFamily::generic(generic)
   9641 }
   9642 
   9643 #[no_mangle]
   9644 pub extern "C" fn Servo_FontFamily_ForSystemFont(name: &nsACString, out: &mut FontFamily) {
   9645    *out = FontFamily::for_system_font(&name.to_utf8());
   9646 }
   9647 
   9648 #[no_mangle]
   9649 pub extern "C" fn Servo_FontFamilyList_WithNames(
   9650    names: &nsTArray<computed::font::SingleFontFamily>,
   9651    out: &mut FontFamilyList,
   9652 ) {
   9653    *out = FontFamilyList {
   9654        list: style_traits::arc_slice::ArcSlice::from_iter(names.iter().cloned()),
   9655    };
   9656 }
   9657 
   9658 #[no_mangle]
   9659 pub extern "C" fn Servo_FamilyName_Serialize(name: &FamilyName, result: &mut nsACString) {
   9660    name.to_css(&mut CssWriter::new(result)).unwrap()
   9661 }
   9662 
   9663 #[no_mangle]
   9664 pub extern "C" fn Servo_GenericFontFamily_Parse(input: &nsACString) -> GenericFontFamily {
   9665    let context = ParserContext::new(
   9666        Origin::Author,
   9667        unsafe { dummy_url_data() },
   9668        Some(CssRuleType::Style),
   9669        ParsingMode::DEFAULT,
   9670        QuirksMode::NoQuirks,
   9671        /* namespaces = */ Default::default(),
   9672        None,
   9673        None,
   9674    );
   9675    let value = input.to_utf8();
   9676    let mut input = ParserInput::new(&value);
   9677    let mut input = Parser::new(&mut input);
   9678    GenericFontFamily::parse(&context, &mut input).unwrap_or(GenericFontFamily::None)
   9679 }
   9680 
   9681 #[no_mangle]
   9682 pub extern "C" fn Servo_ColorScheme_Parse(input: &nsACString, out: &mut u8) -> bool {
   9683    use style::values::specified::ColorScheme;
   9684 
   9685    let context = ParserContext::new(
   9686        Origin::Author,
   9687        unsafe { dummy_url_data() },
   9688        Some(CssRuleType::Style),
   9689        ParsingMode::DEFAULT,
   9690        QuirksMode::NoQuirks,
   9691        /* namespaces = */ Default::default(),
   9692        None,
   9693        None,
   9694    );
   9695    let input = unsafe { input.as_str_unchecked() };
   9696    let mut input = ParserInput::new(&input);
   9697    let mut input = Parser::new(&mut input);
   9698    let scheme = match input.parse_entirely(|i| ColorScheme::parse(&context, i)) {
   9699        Ok(scheme) => scheme,
   9700        Err(..) => return false,
   9701    };
   9702    *out = scheme.raw_bits();
   9703    true
   9704 }
   9705 
   9706 #[no_mangle]
   9707 pub extern "C" fn Servo_LayerBlockRule_GetName(rule: &LayerBlockRule, result: &mut nsACString) {
   9708    if let Some(ref name) = rule.name {
   9709        name.to_css(&mut CssWriter::new(result)).unwrap()
   9710    }
   9711 }
   9712 
   9713 #[no_mangle]
   9714 pub extern "C" fn Servo_ScopeRule_GetStart(rule: &ScopeRule, result: &mut nsACString) {
   9715    if let Some(v) = rule.bounds.start.as_ref() {
   9716        v.to_css(&mut CssWriter::new(result)).unwrap();
   9717    } else {
   9718        result.set_is_void(true);
   9719    }
   9720 }
   9721 
   9722 #[no_mangle]
   9723 pub extern "C" fn Servo_ScopeRule_GetEnd(rule: &ScopeRule, result: &mut nsACString) {
   9724    if let Some(v) = rule.bounds.end.as_ref() {
   9725        v.to_css(&mut CssWriter::new(result)).unwrap();
   9726    } else {
   9727        result.set_is_void(true);
   9728    }
   9729 }
   9730 
   9731 #[no_mangle]
   9732 pub extern "C" fn Servo_LayerStatementRule_GetNameCount(rule: &LayerStatementRule) -> usize {
   9733    rule.names.len()
   9734 }
   9735 
   9736 #[no_mangle]
   9737 pub extern "C" fn Servo_LayerStatementRule_GetNameAt(
   9738    rule: &LayerStatementRule,
   9739    index: usize,
   9740    result: &mut nsACString,
   9741 ) {
   9742    if let Some(ref name) = rule.names.get(index) {
   9743        name.to_css(&mut CssWriter::new(result)).unwrap()
   9744    }
   9745 }
   9746 
   9747 #[no_mangle]
   9748 pub unsafe extern "C" fn Servo_InvalidateForViewportUnits(
   9749    document_style: &PerDocumentStyleData,
   9750    root: &RawGeckoElement,
   9751    dynamic_only: bool,
   9752 ) {
   9753    let mut document_data = document_style.borrow_mut();
   9754    let ref mut stylist = document_data.stylist;
   9755    let device = stylist.device();
   9756 
   9757    if !device.used_viewport_size() {
   9758        return;
   9759    }
   9760 
   9761    if dynamic_only && !device.used_dynamic_viewport_size() {
   9762        return;
   9763    }
   9764 
   9765    // If the viewport changed, then initial values containing viewport units need to be recomputed.
   9766    if stylist
   9767        .get_custom_property_initial_values_flags()
   9768        .intersects(ComputedValueFlags::USES_VIEWPORT_UNITS)
   9769    {
   9770        stylist.rebuild_initial_values_for_custom_properties();
   9771    }
   9772 
   9773    if style::invalidation::viewport_units::invalidate(GeckoElement(root)) {
   9774        // The invalidation machinery propagates the bits up, but we still need
   9775        // to tell the Gecko restyle root machinery about it.
   9776        bindings::Gecko_NoteDirtySubtreeForInvalidation(root);
   9777    }
   9778 }
   9779 
   9780 #[no_mangle]
   9781 pub extern "C" fn Servo_InterpolateColor(
   9782    interpolation: ColorInterpolationMethod,
   9783    start_color: &AbsoluteColor,
   9784    end_color: &AbsoluteColor,
   9785    progress: f32,
   9786 ) -> AbsoluteColor {
   9787    style::color::mix::mix(
   9788        interpolation,
   9789        start_color,
   9790        1.0 - progress,
   9791        end_color,
   9792        progress,
   9793        ColorMixFlags::empty(),
   9794    )
   9795 }
   9796 
   9797 #[no_mangle]
   9798 pub extern "C" fn Servo_EasingFunctionAt(
   9799    easing_function: &ComputedTimingFunction,
   9800    progress: f64,
   9801    before_flag: BeforeFlag,
   9802 ) -> f64 {
   9803    easing_function.calculate_output(progress, before_flag, 1e-7)
   9804 }
   9805 
   9806 fn parse_no_context<'i, F, R>(string: &'i str, parse: F) -> Result<R, ()>
   9807 where
   9808    F: FnOnce(&ParserContext, &mut Parser<'i, '_>) -> Result<R, ParseError<'i>>,
   9809 {
   9810    let context = ParserContext::new(
   9811        Origin::Author,
   9812        unsafe { dummy_url_data() },
   9813        None,
   9814        ParsingMode::DEFAULT,
   9815        QuirksMode::NoQuirks,
   9816        /* namespaces = */ Default::default(),
   9817        None,
   9818        None,
   9819    );
   9820    let mut input = ParserInput::new(string);
   9821    Parser::new(&mut input)
   9822        .parse_entirely(|i| parse(&context, i))
   9823        .map_err(|_| ())
   9824 }
   9825 
   9826 #[no_mangle]
   9827 // Parse a length without style context (for canvas2d letterSpacing/wordSpacing attributes).
   9828 // This accepts absolute lengths, and if a font-metrics-getter function is passed, also
   9829 // font-relative ones, but not other units (such as percentages, viewport-relative, etc)
   9830 // that would require a full style context to resolve.
   9831 pub extern "C" fn Servo_ParseLengthWithoutStyleContext(
   9832    len: &nsACString,
   9833    out: &mut f32,
   9834    get_font_metrics: Option<unsafe extern "C" fn(*mut c_void) -> GeckoFontMetrics>,
   9835    getter_context: *mut c_void,
   9836 ) -> bool {
   9837    let metrics_getter = if let Some(getter) = get_font_metrics {
   9838        Some(move || -> GeckoFontMetrics { unsafe { getter(getter_context) } })
   9839    } else {
   9840        None
   9841    };
   9842    let value = parse_no_context(unsafe { len.as_str_unchecked() }, specified::Length::parse)
   9843        .and_then(|p| p.to_computed_pixel_length_with_font_metrics(metrics_getter));
   9844    match value {
   9845        Ok(v) => {
   9846            *out = v;
   9847            true
   9848        },
   9849        Err(..) => false,
   9850    }
   9851 }
   9852 
   9853 #[no_mangle]
   9854 pub extern "C" fn Servo_SlowRgbToColorName(r: u8, g: u8, b: u8, result: &mut nsACString) -> bool {
   9855    let mut candidates = SmallVec::<[&'static str; 5]>::new();
   9856    for (name, color) in cssparser::color::all_named_colors() {
   9857        if color == (r, g, b) {
   9858            candidates.push(name);
   9859        }
   9860    }
   9861    if candidates.is_empty() {
   9862        return false;
   9863    }
   9864    // DevTools expect the first alphabetically.
   9865    candidates.sort();
   9866    result.assign(candidates[0]);
   9867    true
   9868 }
   9869 
   9870 #[no_mangle]
   9871 pub extern "C" fn Servo_SlowRgbToNearestColorName(
   9872    r: f32,
   9873    g: f32,
   9874    b: f32,
   9875    color_space: ColorSpace,
   9876    result: &mut nsACString,
   9877 ) -> bool {
   9878    let absolute = |r, g, b, color_space| AbsoluteColor {
   9879        components: ColorComponents(r, g, b),
   9880        alpha: 1.0,
   9881        color_space,
   9882        flags: Default::default(),
   9883    };
   9884 
   9885    let input = absolute(r, g, b, color_space).to_color_space(ColorSpace::Oklab);
   9886 
   9887    let mut nearest = (SquaredDistance::from_sqrt(f64::INFINITY), "black");
   9888    for (name, color) in cssparser::color::all_named_colors() {
   9889        let color = absolute(
   9890            color.0 as f32 / 255.0,
   9891            color.1 as f32 / 255.0,
   9892            color.2 as f32 / 255.0,
   9893            ColorSpace::Srgb,
   9894        )
   9895        .to_color_space(ColorSpace::Oklab);
   9896 
   9897        let distance = input.compute_squared_distance(&color).unwrap();
   9898        if distance < nearest.0 {
   9899            // DevTools expect the first alphabetically.
   9900            nearest = (distance, name);
   9901        }
   9902    }
   9903    result.assign(nearest.1);
   9904    return nearest.0 == SquaredDistance::from_sqrt(0.0);
   9905 }
   9906 
   9907 #[no_mangle]
   9908 pub extern "C" fn Servo_ColorNameToRgb(name: &nsACString, out: &mut structs::nscolor) -> bool {
   9909    match cssparser::color::parse_named_color(unsafe { name.as_str_unchecked() }) {
   9910        Ok((r, g, b)) => {
   9911            *out = style::gecko::values::convert_absolute_color_to_nscolor(&AbsoluteColor::new(
   9912                ColorSpace::Srgb,
   9913                r,
   9914                g,
   9915                b,
   9916                1.0,
   9917            ));
   9918            true
   9919        },
   9920        _ => false,
   9921    }
   9922 }
   9923 
   9924 #[repr(u8)]
   9925 pub enum RegisterCustomPropertyResult {
   9926    SuccessfullyRegistered,
   9927    InvalidName,
   9928    AlreadyRegistered,
   9929    InvalidSyntax,
   9930    NoInitialValue,
   9931    InvalidInitialValue,
   9932    InitialValueNotComputationallyIndependent,
   9933 }
   9934 
   9935 /// https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function
   9936 #[no_mangle]
   9937 pub extern "C" fn Servo_RegisterCustomProperty(
   9938    per_doc_data: &PerDocumentStyleData,
   9939    extra_data: *mut URLExtraData,
   9940    name: &nsACString,
   9941    syntax: &nsACString,
   9942    inherits: bool,
   9943    initial_value: Option<&nsACString>,
   9944 ) -> RegisterCustomPropertyResult {
   9945    use self::RegisterCustomPropertyResult::*;
   9946    use style::custom_properties::SpecifiedValue;
   9947    use style::properties_and_values::rule::{PropertyRegistrationError, PropertyRuleName};
   9948    use style::properties_and_values::syntax::Descriptor;
   9949 
   9950    let mut per_doc_data = per_doc_data.borrow_mut();
   9951    let url_data = unsafe { UrlExtraData::from_ptr_ref(&extra_data) };
   9952    let name = unsafe { name.as_str_unchecked() };
   9953    let syntax = unsafe { syntax.as_str_unchecked() };
   9954    let initial_value = initial_value.map(|v| unsafe { v.as_str_unchecked() });
   9955 
   9956    // If name is not a custom property name string, throw a SyntaxError and exit this algorithm.
   9957    let name = match style::custom_properties::parse_name(name) {
   9958        Ok(n) => Atom::from(n),
   9959        Err(()) => return InvalidName,
   9960    };
   9961 
   9962    // If property set already contains an entry with name as its property name (compared
   9963    // codepoint-wise), throw an InvalidModificationError and exit this algorithm.
   9964    if per_doc_data
   9965        .stylist
   9966        .custom_property_script_registry()
   9967        .get(&name)
   9968        .is_some()
   9969    {
   9970        return AlreadyRegistered;
   9971    }
   9972    // Attempt to consume a syntax definition from syntax. If it returns failure, throw a
   9973    // SyntaxError. Otherwise, let syntax definition be the returned syntax definition.
   9974    let Ok(syntax) = Descriptor::from_str(syntax, /* preserve_specified = */ false) else {
   9975        return InvalidSyntax;
   9976    };
   9977 
   9978    let initial_value = match initial_value {
   9979        Some(v) => {
   9980            let mut input = ParserInput::new(v);
   9981            let parsed = Parser::new(&mut input)
   9982                .parse_entirely(|input| {
   9983                    input.skip_whitespace();
   9984                    SpecifiedValue::parse(input, url_data).map(Arc::new)
   9985                })
   9986                .ok();
   9987            if parsed.is_none() {
   9988                return InvalidInitialValue;
   9989            }
   9990            parsed
   9991        },
   9992        None => None,
   9993    };
   9994 
   9995    if let Err(error) =
   9996        PropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())
   9997    {
   9998        return match error {
   9999            PropertyRegistrationError::InitialValueNotComputationallyIndependent => {
  10000                InitialValueNotComputationallyIndependent
  10001            },
  10002            PropertyRegistrationError::InvalidInitialValue => InvalidInitialValue,
  10003            PropertyRegistrationError::NoInitialValue => NoInitialValue,
  10004        };
  10005    }
  10006 
  10007    per_doc_data
  10008        .stylist
  10009        .custom_property_script_registry_mut()
  10010        .register(PropertyRegistration {
  10011            name: PropertyRuleName(name),
  10012            data: PropertyRegistrationData {
  10013                syntax,
  10014                inherits: if inherits {
  10015                    PropertyInherits::True
  10016                } else {
  10017                    PropertyInherits::False
  10018                },
  10019                initial_value,
  10020            },
  10021            url_data: url_data.clone(),
  10022            source_location: SourceLocation { line: 0, column: 0 },
  10023        });
  10024 
  10025    per_doc_data
  10026        .stylist
  10027        .rebuild_initial_values_for_custom_properties();
  10028 
  10029    SuccessfullyRegistered
  10030 }
  10031 
  10032 #[repr(C)]
  10033 pub struct PropDef {
  10034    // The name of the property.
  10035    pub name: Atom,
  10036    // The syntax of the property.
  10037    pub syntax: nsCString,
  10038    // Whether the property inherits.
  10039    pub inherits: bool,
  10040    pub has_initial_value: bool,
  10041    pub initial_value: nsCString,
  10042    // True if the property was set with CSS.registerProperty
  10043    pub from_js: bool,
  10044 }
  10045 
  10046 impl PropDef {
  10047    /// Creates a PropDef from a name and a PropertyRegistration.
  10048    pub fn new(name: Atom, property_registration: &PropertyRegistration, from_js: bool) -> Self {
  10049        let mut syntax = nsCString::new();
  10050        if let Some(spec) = property_registration.data.syntax.specified_string() {
  10051            syntax.assign(spec);
  10052        } else {
  10053            // FIXME: Descriptor::to_css should behave consistently (probably this shouldn't use
  10054            // the ToCss trait).
  10055            property_registration
  10056                .data
  10057                .syntax
  10058                .to_css(&mut CssWriter::new(&mut syntax))
  10059                .unwrap();
  10060        };
  10061        let initial_value = property_registration.data.initial_value.to_css_cssstring();
  10062        PropDef {
  10063            name,
  10064            syntax,
  10065            inherits: property_registration.data.inherits(),
  10066            has_initial_value: property_registration.data.initial_value.is_some(),
  10067            initial_value,
  10068            from_js,
  10069        }
  10070    }
  10071 }
  10072 
  10073 #[no_mangle]
  10074 pub extern "C" fn Servo_GetRegisteredCustomProperties(
  10075    per_doc_data: &PerDocumentStyleData,
  10076    custom_properties: &mut nsTArray<PropDef>,
  10077 ) {
  10078    let stylist = &per_doc_data.borrow().stylist;
  10079 
  10080    custom_properties.extend(
  10081        stylist
  10082            .custom_property_script_registry()
  10083            .get_all()
  10084            .iter()
  10085            .map(|(name, property_registration)| {
  10086                PropDef::new(name.clone(), property_registration, /* from_js */ true)
  10087            }),
  10088    );
  10089 
  10090    for (cascade_data, _origin) in stylist.iter_origins() {
  10091        custom_properties.extend(cascade_data.custom_property_registrations().iter().map(
  10092            |(name, value)| {
  10093                let property_registration = &value.last().unwrap().0;
  10094                PropDef::new(
  10095                    name.clone(),
  10096                    property_registration,
  10097                    /* from_js */
  10098                    false,
  10099                )
  10100            },
  10101        ))
  10102    }
  10103 }
  10104 
  10105 #[no_mangle]
  10106 pub unsafe extern "C" fn Servo_GetRegisteredCustomProperty(
  10107    per_doc_data: &PerDocumentStyleData,
  10108    name: &nsACString,
  10109    custom_property: &mut PropDef,
  10110 ) -> bool {
  10111    let name = name.as_str_unchecked();
  10112    if !name.starts_with("--") {
  10113        return false;
  10114    }
  10115    // We store registered property names without the leading "--".
  10116    let name = Atom::from(&name[2..]);
  10117 
  10118    let stylist = &per_doc_data.borrow().stylist;
  10119    if let Some(property_registration) = stylist.custom_property_script_registry().get(&name) {
  10120        *custom_property = PropDef::new(name, property_registration, /* from_js */ true);
  10121        return true;
  10122    }
  10123 
  10124    for (cascade_data, _) in stylist.iter_origins() {
  10125        if let Some(property_registration) = cascade_data.custom_property_registrations().get(&name)
  10126        {
  10127            *custom_property = PropDef::new(name, property_registration, /* from_js */ false);
  10128            return true;
  10129        }
  10130    }
  10131 
  10132    false
  10133 }
  10134 
  10135 #[no_mangle]
  10136 pub unsafe extern "C" fn Servo_Value_Matches_Syntax(
  10137    value: &nsACString,
  10138    syntax: &nsACString,
  10139    extra_data: *mut URLExtraData,
  10140 ) -> bool {
  10141    use crate::style::properties::CSSWideKeyword;
  10142    use style::properties_and_values::syntax::Descriptor;
  10143    use style::properties_and_values::value::{AllowComputationallyDependent, SpecifiedValue};
  10144 
  10145    // Attempt to consume a syntax definition from syntax.
  10146    let syntax = unsafe { syntax.as_str_unchecked() };
  10147    let Ok(syntax) = Descriptor::from_str(syntax, /* preserve_specified = */ false) else {
  10148        return false;
  10149    };
  10150 
  10151    let css_text = unsafe { value.as_str_unchecked() };
  10152    let mut input = ParserInput::new(css_text);
  10153    let mut input = Parser::new(&mut input);
  10154    input.skip_whitespace();
  10155 
  10156    // Consider CSS-wide keywords to match any syntax.
  10157    if input.try_parse(CSSWideKeyword::parse).is_ok() {
  10158        return true;
  10159    }
  10160 
  10161    let url_data = unsafe { UrlExtraData::from_ptr_ref(&extra_data) };
  10162 
  10163    SpecifiedValue::parse(
  10164        &mut input,
  10165        &syntax,
  10166        url_data,
  10167        AllowComputationallyDependent::Yes,
  10168    )
  10169    .is_ok()
  10170 }
  10171 
  10172 #[repr(C)]
  10173 pub struct SelectorWarningData {
  10174    /// Index to the selector generating the warning.
  10175    pub index: usize,
  10176    /// Kind of the warning.
  10177    pub kind: SelectorWarningKind,
  10178 }
  10179 
  10180 #[no_mangle]
  10181 pub extern "C" fn Servo_GetSelectorWarnings(
  10182    rule: &LockedStyleRule,
  10183    warnings: &mut nsTArray<SelectorWarningData>,
  10184 ) {
  10185    read_locked_arc(rule, |r| {
  10186        for (i, selector) in r.selectors.slice().iter().enumerate() {
  10187            for k in SelectorWarningKind::from_selector(selector) {
  10188                warnings.push(SelectorWarningData { index: i, kind: k });
  10189            }
  10190        }
  10191    });
  10192 }
  10193 
  10194 #[no_mangle]
  10195 pub extern "C" fn Servo_GetRuleBodyText(initial_text: &nsACString, ret_val: &mut nsACString) {
  10196    let css_text = unsafe { initial_text.as_str_unchecked() };
  10197    let mut input = ParserInput::new(&css_text);
  10198    let mut input = Parser::new(&mut input);
  10199 
  10200    let mut found_start = false;
  10201 
  10202    // Search forward for the opening brace.
  10203    while let Ok(token) = input.next() {
  10204        match *token {
  10205            Token::CurlyBracketBlock => {
  10206                found_start = true;
  10207                break;
  10208            },
  10209            _ => {},
  10210        }
  10211 
  10212        if token.is_parse_error() {
  10213            break;
  10214        }
  10215    }
  10216 
  10217    if !found_start {
  10218        ret_val.set_is_void(true);
  10219        return;
  10220    }
  10221 
  10222    let token_start = input.position();
  10223    // Parse the nested block to move the parser to the end of the block
  10224    let _ =
  10225        input.parse_nested_block(|_i| -> Result<(), CssParseError<'_, BasicParseError>> { Ok(()) });
  10226 
  10227    // We're not guaranteed to have a closing bracket, but when we do, we need to move
  10228    // the end offset before it.
  10229    let mut token_slice = input.slice_from(token_start);
  10230    if token_slice.ends_with("}") {
  10231        token_slice = token_slice.strip_suffix("}").unwrap();
  10232    }
  10233    ret_val.assign(token_slice);
  10234 }
  10235 
  10236 #[no_mangle]
  10237 pub extern "C" fn Servo_ReplaceBlockRuleBodyTextInStylesheetText(
  10238    stylesheet_text: &nsACString,
  10239    line: u32,
  10240    column: u32,
  10241    new_body_text: &nsACString,
  10242    ret_val: &mut nsACString,
  10243 ) {
  10244    let css_text = unsafe { stylesheet_text.as_str_unchecked() };
  10245 
  10246    let Some(rule_start_index) = get_byte_index_from_line_and_column(css_text, line, column) else {
  10247        ret_val.set_is_void(true);
  10248        return;
  10249    };
  10250 
  10251    let mut input = ParserInput::new(&css_text[rule_start_index..]);
  10252    let mut input = Parser::new(&mut input);
  10253    let mut found_start = false;
  10254 
  10255    // Search forward for the opening brace.
  10256    while let Ok(token) = input.next() {
  10257        if matches!(*token, Token::CurlyBracketBlock) {
  10258            found_start = true;
  10259            break;
  10260        }
  10261 
  10262        if token.is_parse_error() {
  10263            break;
  10264        }
  10265    }
  10266 
  10267    if !found_start {
  10268        ret_val.set_is_void(true);
  10269        return;
  10270    }
  10271 
  10272    let token_start = input.position();
  10273    let rule_body_start = rule_start_index + token_start.byte_index();
  10274    // Parse the nested block to move the parser to the end of the block
  10275    let _ =
  10276        input.parse_nested_block(|_i| -> Result<(), CssParseError<'_, BasicParseError>> { Ok(()) });
  10277    let mut rule_body_end = rule_start_index + input.position().byte_index();
  10278 
  10279    // We're not guaranteed to have a closing bracket, but when we do, we need to move
  10280    // the end offset before it.
  10281    let token_slice = input.slice_from(token_start);
  10282    if token_slice.ends_with("}") {
  10283        rule_body_end -= 1;
  10284    }
  10285 
  10286    ret_val.append(&css_text[..rule_body_start]);
  10287    ret_val.append(new_body_text);
  10288    ret_val.append(&css_text[rule_body_end..]);
  10289 }
  10290 
  10291 /// Find css_text byte position corresponding to the passed line and column
  10292 fn get_byte_index_from_line_and_column(css_text: &str, line: u32, column: u32) -> Option<usize> {
  10293    // Find the byte index of the start of the passed line within css_text
  10294    let mut line_byte_index = Some(0);
  10295    if line != 1 {
  10296        let mut current_line = 1;
  10297        let mut last_byte = None;
  10298        let mut bytes_iter = css_text.bytes();
  10299        line_byte_index = bytes_iter.position(|byte| {
  10300            // We want to get the position _after_ the EOF sequence
  10301            let on_expected_line = current_line == line;
  10302            let is_previous_byte_carriage_return = last_byte == Some(b'\r');
  10303            last_byte = Some(byte);
  10304 
  10305            if byte == b'\r' {
  10306                current_line += 1;
  10307            } else if byte == b'\n' {
  10308                if !is_previous_byte_carriage_return {
  10309                    current_line += 1;
  10310                } else {
  10311                    return false;
  10312                }
  10313            }
  10314            on_expected_line
  10315        });
  10316    }
  10317 
  10318    if line_byte_index.is_none() {
  10319        return None;
  10320    }
  10321 
  10322    if column == 1 {
  10323        return line_byte_index;
  10324    }
  10325 
  10326    let line_byte_index = line_byte_index.unwrap();
  10327    let mut current_column = 1;
  10328    for (byte_index, _char) in css_text[line_byte_index..].char_indices() {
  10329        if current_column == column {
  10330            return Some(line_byte_index + byte_index);
  10331        }
  10332        current_column += 1;
  10333    }
  10334 
  10335    None
  10336 }
  10337 
  10338 #[repr(C)]
  10339 pub struct CSSToken {
  10340    pub text: nsCString,
  10341    pub token_type: nsCString,
  10342    pub has_unit: bool,
  10343    pub unit: nsCString,
  10344    pub has_number: bool,
  10345    pub number: f32,
  10346    pub has_value: bool,
  10347    pub value: nsCString,
  10348    // line and column at which the token starts
  10349    pub line: u32,
  10350    pub column: u32,
  10351 }
  10352 
  10353 #[no_mangle]
  10354 pub unsafe extern "C" fn Servo_CSSParser_create(text: &nsACString) -> *mut ParserState {
  10355    let css_text = unsafe { text.as_str_unchecked() };
  10356    let mut parser_input = ParserInput::new(&css_text);
  10357    let input = Parser::new(&mut parser_input);
  10358    Box::into_raw(Box::new(input.state()))
  10359 }
  10360 
  10361 #[no_mangle]
  10362 pub unsafe extern "C" fn Servo_CSSParser_destroy(state: *mut ParserState) {
  10363    drop(Box::from_raw(state));
  10364 }
  10365 
  10366 #[no_mangle]
  10367 pub unsafe extern "C" fn Servo_CSSParser_GetCurrentLine(state: &ParserState) -> u32 {
  10368    return state.source_location().line;
  10369 }
  10370 
  10371 #[no_mangle]
  10372 pub unsafe extern "C" fn Servo_CSSParser_GetCurrentColumn(state: &ParserState) -> u32 {
  10373    return state.source_location().column;
  10374 }
  10375 
  10376 #[no_mangle]
  10377 pub unsafe extern "C" fn Servo_CSSParser_NextToken(
  10378    text: &nsACString,
  10379    state: &mut ParserState,
  10380    css_token: &mut CSSToken,
  10381 ) -> bool {
  10382    let css_text = unsafe { text.as_str_unchecked() };
  10383    let mut parser_input = ParserInput::new(&css_text);
  10384    let mut input = Parser::new(&mut parser_input);
  10385    input.reset(state);
  10386 
  10387    let token_start = input.position();
  10388    let location_start = state.source_location();
  10389    let Ok(token) = &input.next_including_whitespace_and_comments() else {
  10390        return false;
  10391    };
  10392 
  10393    let token_type = match *token {
  10394        Token::Ident(_) => "Ident",
  10395        Token::AtKeyword(_) => "AtKeyword",
  10396        Token::Hash(_) => "Hash",
  10397        Token::IDHash(_) => "IDHash",
  10398        Token::QuotedString(_) => "QuotedString",
  10399        Token::UnquotedUrl(_) => "UnquotedUrl",
  10400        Token::Delim(_) => "Delim",
  10401        Token::Number { .. } => "Number",
  10402        Token::Percentage { .. } => "Percentage",
  10403        Token::Dimension { .. } => "Dimension",
  10404        Token::WhiteSpace(_) => "WhiteSpace",
  10405        Token::Comment(_) => "Comment",
  10406        Token::Colon => "Colon",
  10407        Token::Semicolon => "Semicolon",
  10408        Token::Comma => "Comma",
  10409        Token::IncludeMatch => "IncludeMatch",
  10410        Token::DashMatch => "DashMatch",
  10411        Token::PrefixMatch => "PrefixMatch",
  10412        Token::SuffixMatch => "SuffixMatch",
  10413        Token::SubstringMatch => "SubstringMatch",
  10414        Token::CDO => "CDO",
  10415        Token::CDC => "CDC",
  10416        Token::Function(_) => "Function",
  10417        Token::ParenthesisBlock => "ParenthesisBlock",
  10418        Token::SquareBracketBlock => "SquareBracketBlock",
  10419        Token::CurlyBracketBlock => "CurlyBracketBlock",
  10420        Token::BadUrl(_) => "BadUrl",
  10421        Token::BadString(_) => "BadString",
  10422        Token::CloseParenthesis => "CloseParenthesis",
  10423        Token::CloseSquareBracket => "CloseSquareBracket",
  10424        Token::CloseCurlyBracket => "CloseCurlyBracket",
  10425    };
  10426 
  10427    let token_value = match *token {
  10428        Token::Ident(value) |
  10429        Token::AtKeyword(value) |
  10430        Token::Hash(value) |
  10431        Token::IDHash(value) |
  10432        Token::QuotedString(value) |
  10433        Token::UnquotedUrl(value) |
  10434        Token::Function(value) |
  10435        Token::BadUrl(value) |
  10436        Token::BadString(value) => {
  10437            let mut text = nsCString::new();
  10438            text.assign(value.as_bytes());
  10439            Some(text)
  10440        },
  10441        // value is a str here, we need a different branch to handle it
  10442        Token::Comment(value) => {
  10443            let mut text = nsCString::new();
  10444            text.assign(value.as_bytes());
  10445            Some(text)
  10446        },
  10447        // Delim and WhiteSpace also have value, but they will be similar to text, so don't
  10448        // include them
  10449        Token::Delim(_) |
  10450        Token::WhiteSpace(_) |
  10451        // Number, Percentage and Dimension expose numeric values that will be exposed in `number`
  10452        Token::Number{..}  |
  10453        Token::Percentage{..}  |
  10454        Token::Dimension{..}  |
  10455        // The rest of the tokens don't expose a string value
  10456        Token::Colon |
  10457        Token::Semicolon |
  10458        Token::Comma |
  10459        Token::IncludeMatch |
  10460        Token::DashMatch |
  10461        Token::PrefixMatch |
  10462        Token::SuffixMatch |
  10463        Token::SubstringMatch |
  10464        Token::CDO |
  10465        Token::CDC |
  10466        Token::ParenthesisBlock |
  10467        Token::SquareBracketBlock |
  10468        Token::CurlyBracketBlock |
  10469        Token::CloseParenthesis |
  10470        Token::CloseSquareBracket |
  10471        Token::CloseCurlyBracket => None
  10472    };
  10473 
  10474    let token_unit = match *token {
  10475        Token::Dimension { ref unit, .. } => {
  10476            let mut unit_text = nsCString::new();
  10477            unit_text.assign(unit.as_bytes());
  10478            Some(unit_text)
  10479        },
  10480        _ => None,
  10481    };
  10482 
  10483    let token_number = match *token {
  10484        Token::Dimension { ref value, .. } => Some(value),
  10485        Token::Number { ref value, .. } => Some(value),
  10486        Token::Percentage { ref unit_value, .. } => Some(unit_value),
  10487        _ => None,
  10488    };
  10489    css_token.has_number = token_number.is_some();
  10490    if css_token.has_number {
  10491        css_token.number = *token_number.unwrap();
  10492    }
  10493 
  10494    let need_to_parse_nested_block = match *token {
  10495        Token::Function(_)
  10496        | Token::ParenthesisBlock
  10497        | Token::CurlyBracketBlock
  10498        | Token::SquareBracketBlock => true,
  10499        _ => false,
  10500    };
  10501 
  10502    let mut text = nsCString::new();
  10503    text.assign(&input.slice_from(token_start));
  10504 
  10505    css_token.text = text;
  10506    css_token.token_type = token_type.into();
  10507    css_token.has_value = token_value.is_some();
  10508    if css_token.has_value {
  10509        css_token.value = token_value.unwrap();
  10510    }
  10511    css_token.has_unit = token_unit.is_some();
  10512    if css_token.has_unit {
  10513        css_token.unit = token_unit.unwrap();
  10514    }
  10515 
  10516    css_token.line = location_start.line;
  10517    css_token.column = location_start.column;
  10518 
  10519    if need_to_parse_nested_block {
  10520        let _ = input.parse_nested_block(|i| -> Result<(), CssParseError<'_, BasicParseError>> {
  10521            *state = i.state();
  10522            Ok(())
  10523        });
  10524    } else {
  10525        *state = input.state();
  10526    }
  10527 
  10528    return true;
  10529 }
  10530 
  10531 /// Result of resolving an anchor positioning function.
  10532 #[repr(u8)]
  10533 pub enum AnchorPositioningFunctionResolution {
  10534    /// Anchor function invalid.
  10535    Invalid,
  10536    /// Anchor function resolved to a reference to fallback.
  10537    ResolvedReference(*const computed::LengthPercentage),
  10538    /// Anchor function resolved to a value.
  10539    Resolved(computed::LengthPercentage),
  10540 }
  10541 
  10542 fn resolve_anchor_function(
  10543    func: &AnchorFunction,
  10544    params: &AnchorPosOffsetResolutionParams,
  10545    prop_side: PhysicalSide,
  10546 ) -> AnchorPositioningFunctionResolution {
  10547    if !func.valid_for(prop_side, params.mBaseParams.mPosition) {
  10548        return resolve_inset_fallback(func.fallback.as_ref(), params, prop_side);
  10549    }
  10550    let result = AnchorFunction::resolve(&func.target_element, &func.side, prop_side, params);
  10551    match result {
  10552        Ok(l) => {
  10553            AnchorPositioningFunctionResolution::Resolved(computed::LengthPercentage::new_length(l))
  10554        },
  10555        Err(()) => resolve_inset_fallback(func.fallback.as_ref(), params, prop_side),
  10556    }
  10557 }
  10558 
  10559 fn resolve_inset_fallback(
  10560    fallback: Option<&computed::Inset>,
  10561    params: &AnchorPosOffsetResolutionParams,
  10562    prop_side: PhysicalSide,
  10563 ) -> AnchorPositioningFunctionResolution {
  10564    use style::values::computed::length_percentage::Unpacked;
  10565    let Some(fallback) = fallback else {
  10566        return AnchorPositioningFunctionResolution::Invalid;
  10567    };
  10568    match fallback {
  10569        computed::Inset::Auto => AnchorPositioningFunctionResolution::Invalid,
  10570        computed::Inset::LengthPercentage(lp) => {
  10571            AnchorPositioningFunctionResolution::ResolvedReference(lp as *const _)
  10572        },
  10573        computed::Inset::AnchorSizeFunction(f) => do_resolve_anchor_size(
  10574            f,
  10575            params,
  10576            AllowAnchorPosResolutionInCalcPercentage::Both(prop_side),
  10577        ),
  10578        computed::Inset::AnchorFunction(f) => resolve_anchor_function(f, params, prop_side),
  10579        computed::Inset::AnchorContainingCalcFunction(clp) => {
  10580            let Unpacked::Calc(clp) = clp.unpack() else {
  10581                unreachable!();
  10582            };
  10583            match clp.resolve_anchor(
  10584                AllowAnchorPosResolutionInCalcPercentage::Both(prop_side),
  10585                params,
  10586            ) {
  10587                Ok((node, clamping_mode)) => AnchorPositioningFunctionResolution::Resolved(
  10588                    computed::LengthPercentage::new_calc(node, clamping_mode),
  10589                ),
  10590                Err(_) => AnchorPositioningFunctionResolution::Invalid,
  10591            }
  10592        },
  10593    }
  10594 }
  10595 
  10596 #[no_mangle]
  10597 pub extern "C" fn Servo_ResolveAnchorFunction(
  10598    func: &AnchorFunction,
  10599    params: &AnchorPosOffsetResolutionParams,
  10600    prop_side: PhysicalSide,
  10601    out: &mut AnchorPositioningFunctionResolution,
  10602 ) {
  10603    *out = resolve_anchor_function(func, params, prop_side);
  10604 }
  10605 
  10606 trait AnchorSizeFallbackResolver {
  10607    fn resolve_fallback(
  10608        fallback: Option<&Self>,
  10609        params: &AnchorPosOffsetResolutionParams,
  10610        allowed: AllowAnchorPosResolutionInCalcPercentage,
  10611    ) -> AnchorPositioningFunctionResolution;
  10612 }
  10613 
  10614 impl AnchorSizeFallbackResolver for computed::Inset {
  10615    fn resolve_fallback(
  10616        fallback: Option<&Self>,
  10617        params: &AnchorPosOffsetResolutionParams,
  10618        allowed: AllowAnchorPosResolutionInCalcPercentage,
  10619    ) -> AnchorPositioningFunctionResolution {
  10620        let Some(fallback) = fallback else {
  10621            return AnchorPositioningFunctionResolution::Invalid;
  10622        };
  10623        match fallback {
  10624            computed::Inset::Auto => AnchorPositioningFunctionResolution::Invalid,
  10625            computed::Inset::LengthPercentage(lp) => {
  10626                AnchorPositioningFunctionResolution::ResolvedReference(lp as *const _)
  10627            },
  10628            computed::Inset::AnchorFunction(f) => {
  10629                let side = match allowed {
  10630                    AllowAnchorPosResolutionInCalcPercentage::Both(s) => s,
  10631                    AllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(_) => {
  10632                        return AnchorPositioningFunctionResolution::Invalid
  10633                    },
  10634                };
  10635                resolve_anchor_function(f, params, side)
  10636            },
  10637            computed::Inset::AnchorSizeFunction(f) => do_resolve_anchor_size(f, params, allowed),
  10638            computed::Inset::AnchorContainingCalcFunction(clp) => {
  10639                let Unpacked::Calc(clp) = clp.unpack() else {
  10640                    unreachable!();
  10641                };
  10642                match clp.resolve_anchor(allowed, &params) {
  10643                    Ok((node, clamping_mode)) => AnchorPositioningFunctionResolution::Resolved(
  10644                        computed::LengthPercentage::new_calc(node, clamping_mode),
  10645                    ),
  10646                    Err(_) => AnchorPositioningFunctionResolution::Invalid,
  10647                }
  10648            },
  10649        }
  10650    }
  10651 }
  10652 
  10653 impl AnchorSizeFallbackResolver for computed::Margin {
  10654    fn resolve_fallback(
  10655        fallback: Option<&Self>,
  10656        params: &AnchorPosOffsetResolutionParams,
  10657        allowed: AllowAnchorPosResolutionInCalcPercentage,
  10658    ) -> AnchorPositioningFunctionResolution {
  10659        let Some(fallback) = fallback else {
  10660            return AnchorPositioningFunctionResolution::Invalid;
  10661        };
  10662        match fallback {
  10663            computed::Margin::Auto => AnchorPositioningFunctionResolution::Invalid,
  10664            computed::Margin::LengthPercentage(lp) => {
  10665                AnchorPositioningFunctionResolution::ResolvedReference(lp as *const _)
  10666            },
  10667            computed::Margin::AnchorSizeFunction(f) => do_resolve_anchor_size(f, params, allowed),
  10668            computed::Margin::AnchorContainingCalcFunction(clp) => {
  10669                let Unpacked::Calc(clp) = clp.unpack() else {
  10670                    unreachable!();
  10671                };
  10672                match clp.resolve_anchor(allowed, &params) {
  10673                    Ok((node, clamping_mode)) => AnchorPositioningFunctionResolution::Resolved(
  10674                        computed::LengthPercentage::new_calc(node, clamping_mode),
  10675                    ),
  10676                    Err(_) => AnchorPositioningFunctionResolution::Invalid,
  10677                }
  10678            },
  10679        }
  10680    }
  10681 }
  10682 
  10683 impl AnchorSizeFallbackResolver for computed::Size {
  10684    fn resolve_fallback(
  10685        fallback: Option<&Self>,
  10686        params: &AnchorPosOffsetResolutionParams,
  10687        allowed: AllowAnchorPosResolutionInCalcPercentage,
  10688    ) -> AnchorPositioningFunctionResolution {
  10689        let Some(fallback) = fallback else {
  10690            return AnchorPositioningFunctionResolution::Invalid;
  10691        };
  10692        match fallback {
  10693            computed::Size::Auto
  10694            | computed::Size::MaxContent
  10695            | computed::Size::MinContent
  10696            | computed::Size::FitContent
  10697            | computed::Size::MozAvailable
  10698            | computed::Size::WebkitFillAvailable
  10699            | computed::Size::Stretch
  10700            | computed::Size::FitContentFunction(_) => AnchorPositioningFunctionResolution::Invalid,
  10701            computed::Size::LengthPercentage(lp) => {
  10702                AnchorPositioningFunctionResolution::ResolvedReference(&lp.0 as *const _)
  10703            },
  10704            computed::Size::AnchorSizeFunction(f) => do_resolve_anchor_size(f, params, allowed),
  10705            computed::Size::AnchorContainingCalcFunction(clp) => {
  10706                let Unpacked::Calc(clp) = clp.0.unpack() else {
  10707                    unreachable!();
  10708                };
  10709                match clp.resolve_anchor(allowed, &params) {
  10710                    Ok((node, clamping_mode)) => AnchorPositioningFunctionResolution::Resolved(
  10711                        computed::LengthPercentage::new_calc(node, clamping_mode),
  10712                    ),
  10713                    Err(_) => AnchorPositioningFunctionResolution::Invalid,
  10714                }
  10715            },
  10716        }
  10717    }
  10718 }
  10719 
  10720 impl AnchorSizeFallbackResolver for computed::MaxSize {
  10721    fn resolve_fallback(
  10722        fallback: Option<&Self>,
  10723        params: &AnchorPosOffsetResolutionParams,
  10724        allowed: AllowAnchorPosResolutionInCalcPercentage,
  10725    ) -> AnchorPositioningFunctionResolution {
  10726        let Some(fallback) = fallback else {
  10727            return AnchorPositioningFunctionResolution::Invalid;
  10728        };
  10729        match fallback {
  10730            computed::MaxSize::None
  10731            | computed::MaxSize::MaxContent
  10732            | computed::MaxSize::MinContent
  10733            | computed::MaxSize::FitContent
  10734            | computed::MaxSize::MozAvailable
  10735            | computed::MaxSize::WebkitFillAvailable
  10736            | computed::MaxSize::Stretch
  10737            | computed::MaxSize::FitContentFunction(_) => {
  10738                AnchorPositioningFunctionResolution::Invalid
  10739            },
  10740            computed::MaxSize::LengthPercentage(lp) => {
  10741                AnchorPositioningFunctionResolution::ResolvedReference(&lp.0 as *const _)
  10742            },
  10743            computed::MaxSize::AnchorSizeFunction(f) => do_resolve_anchor_size(f, params, allowed),
  10744            computed::MaxSize::AnchorContainingCalcFunction(clp) => {
  10745                let Unpacked::Calc(clp) = clp.0.unpack() else {
  10746                    unreachable!();
  10747                };
  10748                match clp.resolve_anchor(allowed, &params) {
  10749                    Ok((node, clamping_mode)) => AnchorPositioningFunctionResolution::Resolved(
  10750                        computed::LengthPercentage::new_calc(node, clamping_mode),
  10751                    ),
  10752                    Err(_) => AnchorPositioningFunctionResolution::Invalid,
  10753                }
  10754            },
  10755        }
  10756    }
  10757 }
  10758 
  10759 fn do_resolve_anchor_size<Value: AnchorSizeFallbackResolver>(
  10760    func: &GenericAnchorSizeFunction<Value>,
  10761    params: &AnchorPosOffsetResolutionParams,
  10762    allowed: AllowAnchorPosResolutionInCalcPercentage,
  10763 ) -> AnchorPositioningFunctionResolution {
  10764    use style::values::computed::length::resolve_anchor_size;
  10765    if !func.valid_for(params.mBaseParams.mPosition) {
  10766        return Value::resolve_fallback(func.fallback.as_ref(), params, allowed);
  10767    }
  10768    let result = resolve_anchor_size(
  10769        &func.target_element,
  10770        allowed.to_axis(),
  10771        func.size,
  10772        &params.mBaseParams,
  10773    );
  10774    match result {
  10775        Ok(l) => {
  10776            AnchorPositioningFunctionResolution::Resolved(computed::LengthPercentage::new_length(l))
  10777        },
  10778        Err(()) => return Value::resolve_fallback(func.fallback.as_ref(), params, allowed),
  10779    }
  10780 }
  10781 
  10782 #[no_mangle]
  10783 pub extern "C" fn Servo_ResolveAnchorSizeFunctionForInset(
  10784    func: &GenericAnchorSizeFunction<computed::Inset>,
  10785    params: &AnchorPosOffsetResolutionParams,
  10786    prop_axis: PhysicalAxis,
  10787    out: &mut AnchorPositioningFunctionResolution,
  10788 ) {
  10789    *out = do_resolve_anchor_size(
  10790        func,
  10791        params,
  10792        AllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(prop_axis),
  10793    );
  10794 }
  10795 
  10796 fn offset_params_from_base_params(
  10797    params: &AnchorPosResolutionParams,
  10798 ) -> AnchorPosOffsetResolutionParams {
  10799    AnchorPosOffsetResolutionParams {
  10800        mCBSize: ptr::null(),
  10801        mBaseParams: AnchorPosResolutionParams {
  10802            mFrame: params.mFrame,
  10803            mPosition: params.mPosition,
  10804            mCache: params.mCache,
  10805            mAutoResolutionOverrideParams: params.mAutoResolutionOverrideParams,
  10806        },
  10807    }
  10808 }
  10809 
  10810 #[no_mangle]
  10811 pub extern "C" fn Servo_ResolveAnchorSizeFunctionForMargin(
  10812    func: &GenericAnchorSizeFunction<computed::Margin>,
  10813    params: &AnchorPosResolutionParams,
  10814    prop_axis: PhysicalAxis,
  10815    out: &mut AnchorPositioningFunctionResolution,
  10816 ) {
  10817    *out = do_resolve_anchor_size(
  10818        func,
  10819        &offset_params_from_base_params(params),
  10820        AllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(prop_axis),
  10821    );
  10822 }
  10823 
  10824 #[no_mangle]
  10825 pub extern "C" fn Servo_ResolveAnchorSizeFunctionForSize(
  10826    func: &GenericAnchorSizeFunction<computed::Size>,
  10827    params: &AnchorPosResolutionParams,
  10828    prop_axis: PhysicalAxis,
  10829    out: &mut AnchorPositioningFunctionResolution,
  10830 ) {
  10831    *out = do_resolve_anchor_size(
  10832        func,
  10833        &offset_params_from_base_params(params),
  10834        AllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(prop_axis),
  10835    );
  10836 }
  10837 
  10838 #[no_mangle]
  10839 pub extern "C" fn Servo_ResolveAnchorSizeFunctionForMaxSize(
  10840    func: &GenericAnchorSizeFunction<computed::MaxSize>,
  10841    params: &AnchorPosResolutionParams,
  10842    prop_axis: PhysicalAxis,
  10843    out: &mut AnchorPositioningFunctionResolution,
  10844 ) {
  10845    *out = do_resolve_anchor_size(
  10846        func,
  10847        &offset_params_from_base_params(params),
  10848        AllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(prop_axis),
  10849    );
  10850 }
  10851 
  10852 #[no_mangle]
  10853 pub extern "C" fn Servo_PhysicalizePositionArea(
  10854    area: &mut PositionArea,
  10855    cb_wm: &WritingMode,
  10856    self_wm: &WritingMode,
  10857 ) {
  10858    *area = area.to_physical(*cb_wm, *self_wm);
  10859 }
  10860 
  10861 /// https://drafts.csswg.org/css-anchor-position-1/#position-area-alignment
  10862 #[no_mangle]
  10863 pub extern "C" fn Servo_ResolvePositionAreaSelfAlignment(
  10864    area: &PositionArea,
  10865    axis: LogicalAxis,
  10866    cb_wm: &WritingMode,
  10867    self_wm: &WritingMode,
  10868    out: &mut AlignFlags,
  10869 ) {
  10870    // As well as converting `area` and `axis` to the same form for comparison
  10871    // this also makes sure `area`'s second keyword is explicit (not none).
  10872    let physical_area = area.to_physical(*cb_wm, *self_wm);
  10873    let physical_axis = axis.to_physical(*cb_wm);
  10874    let area_keyword = match physical_axis {
  10875        PhysicalAxis::Horizontal => physical_area.first,
  10876        PhysicalAxis::Vertical => physical_area.second,
  10877    };
  10878    // Note that area_keyword is a physical value (left/right/top/bottom).
  10879    let Some(align) = area_keyword.to_self_alignment(axis, cb_wm) else {
  10880        debug_assert!(
  10881            false,
  10882            "ResolvePositionAreaSelfAlignment called on {:?}",
  10883            area
  10884        );
  10885        return;
  10886    };
  10887    *out = align;
  10888 }