Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  glue.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

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

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

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

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

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

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

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

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

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

    DUMMY_URL_DATA = dummy_url_data;
    DUMMY_CHROME_URL_DATA = dummy_chrome_url_data;
}

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

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

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

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

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

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

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

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

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

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

    if !token.should_traverse() {
        return;
    }

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

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

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

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

    let element = GeckoElement(root);

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

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

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

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

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

    let was_initial_style = !element.has_data();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    let animated = convert_nscolor_to_absolute_color(color);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    let element = GeckoElement(element);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    *out_length = len as u32;
    ptr
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    contents.into()
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    let mut document_data = document_set.borrow_mut();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge