Bug 1833466 - Implement CSSMarginRule and the corresponding DOM API. r=webidl,firefox...
[gecko.git] / servo / ports / geckolib / glue.rs
blob69a2357aa455036fbf9fe7741745207607dc5011
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/. */
5 use super::error_reporter::ErrorReporter;
6 use super::stylesheet_loader::{AsyncStylesheetParser, StylesheetLoader};
7 use bincode::{deserialize, serialize};
8 use cssparser::ToCss as ParserToCss;
9 use cssparser::{BasicParseError, ParseError as CssParseError, Parser, ParserInput, ParserState, SourceLocation, UnicodeRange, Token};
10 use dom::{DocumentState, ElementState};
11 use malloc_size_of::MallocSizeOfOps;
12 use nsstring::{nsCString, nsString};
13 use selectors::matching::{ElementSelectorFlags, MatchingForInvalidation, SelectorCaches};
14 use selectors::{Element, OpaqueElement};
15 use servo_arc::{Arc, ArcBorrow};
16 use smallvec::SmallVec;
17 use std::collections::BTreeSet;
18 use std::fmt::Write;
19 use std::iter;
20 use std::os::raw::c_void;
21 use std::ptr;
22 use style::color::mix::ColorInterpolationMethod;
23 use style::color::{AbsoluteColor, ColorSpace};
24 use style::computed_value_flags::ComputedValueFlags;
25 use style::context::ThreadLocalStyleContext;
26 use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
27 use style::counter_style;
28 use style::custom_properties::DeferFontRelativeCustomPropertyResolution;
29 use style::data::{self, ElementStyles};
30 use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
31 use style::driver;
32 use style::error_reporting::{ParseErrorReporter, SelectorWarningKind};
33 use style::font_face::{self, FontFaceSourceFormat, FontFaceSourceListComponent, Source};
34 use style::gecko::arc_types::{
35     LockedCounterStyleRule, LockedCssRules, LockedDeclarationBlock, LockedFontFaceRule,
36     LockedImportRule, LockedKeyframe, LockedKeyframesRule, LockedMediaList, LockedPageRule,
37     LockedStyleRule,
39 use style::gecko::data::{
40     AuthorStyles, GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl,
42 use style::gecko::restyle_damage::GeckoRestyleDamage;
43 use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
44 use style::gecko::snapshot_helpers::classes_changed;
45 use style::gecko::traversal::RecalcStyleOnly;
46 use style::gecko::url;
47 use style::gecko::wrapper::{
48     slow_selector_flags_from_node_selector_flags, GeckoElement, GeckoNode,
50 use style::gecko_bindings::bindings;
51 use style::gecko_bindings::bindings::nsACString;
52 use style::gecko_bindings::bindings::nsAString;
53 use style::gecko_bindings::bindings::Gecko_AddPropertyToSet;
54 use style::gecko_bindings::bindings::Gecko_AppendPropertyValuePair;
55 use style::gecko_bindings::bindings::Gecko_ConstructFontFeatureValueSet;
56 use style::gecko_bindings::bindings::Gecko_ConstructFontPaletteValueSet;
57 use style::gecko_bindings::bindings::Gecko_GetOrCreateFinalKeyframe;
58 use style::gecko_bindings::bindings::Gecko_GetOrCreateInitialKeyframe;
59 use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
60 use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
61 use style::gecko_bindings::structs;
62 use style::gecko_bindings::structs::gfx::FontPaletteValueSet;
63 use style::gecko_bindings::structs::gfxFontFeatureValueSet;
64 use style::gecko_bindings::structs::ipc::ByteBuf;
65 use style::gecko_bindings::structs::nsAtom;
66 use style::gecko_bindings::structs::nsCSSCounterDesc;
67 use style::gecko_bindings::structs::nsCSSFontDesc;
68 use style::gecko_bindings::structs::nsCSSPropertyID;
69 use style::gecko_bindings::structs::nsChangeHint;
70 use style::gecko_bindings::structs::nsCompatibility;
71 use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
72 use style::gecko_bindings::structs::nsTArray;
73 use style::gecko_bindings::structs::nsresult;
74 use style::gecko_bindings::structs::CallerType;
75 use style::gecko_bindings::structs::CompositeOperation;
76 use style::gecko_bindings::structs::DeclarationBlockMutationClosure;
77 use style::gecko_bindings::structs::GeckoFontMetrics;
78 use style::gecko_bindings::structs::IterationCompositeOperation;
79 use style::gecko_bindings::structs::Loader;
80 use style::gecko_bindings::structs::LoaderReusableStyleSheets;
81 use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
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::sugar::ownership::Strong;
97 use style::gecko_bindings::sugar::refptr::RefPtr;
98 use style::global_style_data::{
99     GlobalStyleData, PlatformThreadHandle, StyleThreadPool, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL,
101 use style::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
102 use style::invalidation::element::invalidation_map::{
103     RelativeSelectorInvalidationMap, TSStateForInvalidation,
105 use style::invalidation::element::invalidator::{InvalidationResult, SiblingTraversalMap};
106 use style::invalidation::element::relative_selector::{
107     DomMutationOperation, RelativeSelectorDependencyCollector, RelativeSelectorInvalidator,
109 use style::invalidation::element::restyle_hints::RestyleHint;
110 use style::invalidation::stylesheets::RuleChangeKind;
111 use style::media_queries::MediaList;
112 use style::parser::{Parse, ParserContext};
113 #[cfg(feature = "gecko_debug")]
114 use style::properties::LonghandIdSet;
115 use style::properties::{
116     animated_properties::{AnimationValue, AnimationValueMap},
117     parse_one_declaration_into, parse_style_attribute, ComputedValues, CountedUnknownProperty,
118     Importance, LonghandId, NonCustomPropertyId, OwnedPropertyDeclarationId,
119     PropertyDeclarationBlock, PropertyDeclarationId, PropertyDeclarationIdSet, PropertyId,
120     ShorthandId, SourcePropertyDeclaration, StyleBuilder,
122 use style::properties_and_values::registry::{PropertyRegistration, PropertyRegistrationData};
123 use style::properties_and_values::rule::Inherits as PropertyInherits;
124 use style::rule_cache::RuleCacheConditions;
125 use style::rule_tree::StrongRuleNode;
126 use style::selector_parser::PseudoElementCascadeType;
127 use style::shared_lock::{
128     Locked, SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard,
130 use style::string_cache::{Atom, WeakAtom};
131 use style::style_adjuster::StyleAdjuster;
132 use style::stylesheets::container_rule::ContainerSizeQuery;
133 use style::stylesheets::import_rule::{ImportLayer, ImportSheet};
134 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
135 use style::stylesheets::supports_rule::parse_condition_or_declaration;
136 use style::stylesheets::{
137     AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleType, CssRuleTypes,
138     CssRules, CssRulesHelpers, DocumentRule, FontFaceRule, FontFeatureValuesRule,
139     FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
140     MarginRule, MediaRule, NamespaceRule, Origin, OriginSet, PagePseudoClassFlags, PageRule,
141     PropertyRule, SanitizationData, SanitizationKind, StartingStyleRule, StyleRule,
142     StylesheetContents, StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData,
143     ScopeRule,
145 use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
146 use style::thread_state;
147 use style::traversal::resolve_style;
148 use style::traversal::DomTraversal;
149 use style::traversal_flags::{self, TraversalFlags};
150 use style::use_counters::UseCounters;
151 use style::values::animated::{Animate, Procedure, ToAnimatedZero};
152 use style::values::computed::easing::ComputedTimingFunction;
153 use style::values::computed::effects::Filter;
154 use style::values::computed::font::{
155     FamilyName, FontFamily, FontFamilyList, FontStretch, FontStyle, FontWeight, GenericFontFamily,
157 use style::values::computed::{self, Context, ToComputedValue};
158 use style::values::distance::ComputeSquaredDistance;
159 use style::values::generics::color::ColorMixFlags;
160 use style::values::generics::easing::BeforeFlag;
161 use style::values::specified::gecko::IntersectionObserverRootMargin;
162 use style::values::specified::source_size_list::SourceSizeList;
163 use style::values::specified::{AbsoluteLength, NoCalcLength};
164 use style::values::{specified, AtomIdent, CustomIdent, KeyframesName};
165 use style_traits::{CssWriter, ParseError, ParsingMode, ToCss};
166 use thin_vec::ThinVec;
167 use to_shmem::SharedMemoryBuilder;
169 trait ClosureHelper {
170     fn invoke(&self, property_id: Option<NonCustomPropertyId>);
173 impl ClosureHelper for DeclarationBlockMutationClosure {
174     #[inline]
175     fn invoke(&self, property_id: Option<NonCustomPropertyId>) {
176         if let Some(function) = self.function.as_ref() {
177             let gecko_prop_id = match property_id {
178                 Some(p) => p.to_nscsspropertyid(),
179                 None => nsCSSPropertyID::eCSSPropertyExtra_variable,
180             };
181             unsafe { function(self.data, gecko_prop_id) }
182         }
183     }
187  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
188  * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
189  * those signatures as well, giving us a second declaration of all the Servo_* functions in this
190  * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
191  * depend on but good enough for our purposes.
192  */
194 // A dummy url data for where we don't pass url data in.
195 static mut DUMMY_URL_DATA: *mut URLExtraData = 0 as *mut _;
196 static mut DUMMY_CHROME_URL_DATA: *mut URLExtraData = 0 as *mut _;
198 #[no_mangle]
199 pub unsafe extern "C" fn Servo_Initialize(
200     dummy_url_data: *mut URLExtraData,
201     dummy_chrome_url_data: *mut URLExtraData,
202 ) {
203     use style::gecko_bindings::sugar::origin_flags;
205     // Pretend that we're a Servo Layout thread, to make some assertions happy.
206     thread_state::initialize(thread_state::ThreadState::LAYOUT);
208     debug_assert!(is_main_thread());
209     lazy_static::initialize(&STYLE_THREAD_POOL);
211     // Perform some debug-only runtime assertions.
212     origin_flags::assert_flags_match();
213     traversal_flags::assert_traversal_flags_match();
214     specified::font::assert_variant_east_asian_matches();
215     specified::font::assert_variant_ligatures_matches();
217     DUMMY_URL_DATA = dummy_url_data;
218     DUMMY_CHROME_URL_DATA = dummy_chrome_url_data;
221 #[no_mangle]
222 pub unsafe extern "C" fn Servo_Shutdown() {
223     DUMMY_URL_DATA = ptr::null_mut();
224     DUMMY_CHROME_URL_DATA = ptr::null_mut();
225     Stylist::shutdown();
226     url::shutdown();
229 #[inline(always)]
230 unsafe fn dummy_url_data() -> &'static UrlExtraData {
231     UrlExtraData::from_ptr_ref(std::ptr::addr_of!(DUMMY_URL_DATA).as_ref().unwrap())
234 #[inline(always)]
235 unsafe fn dummy_chrome_url_data() -> &'static UrlExtraData {
236     UrlExtraData::from_ptr_ref(std::ptr::addr_of!(DUMMY_CHROME_URL_DATA).as_ref().unwrap())
239 #[allow(dead_code)]
240 fn is_main_thread() -> bool {
241     unsafe { bindings::Gecko_IsMainThread() }
244 #[allow(dead_code)]
245 fn is_dom_worker_thread() -> bool {
246     unsafe { bindings::Gecko_IsDOMWorkerThread() }
249 thread_local! {
250     /// Thread-local style data for DOM workers
251     static DOM_WORKER_RWLOCK: SharedRwLock = SharedRwLock::new();
254 #[allow(dead_code)]
255 fn is_in_servo_traversal() -> bool {
256     unsafe { bindings::Gecko_IsInServoTraversal() }
259 fn create_shared_context<'a>(
260     global_style_data: &GlobalStyleData,
261     guard: &'a SharedRwLockReadGuard,
262     stylist: &'a Stylist,
263     traversal_flags: TraversalFlags,
264     snapshot_map: &'a ServoElementSnapshotTable,
265 ) -> SharedStyleContext<'a> {
266     SharedStyleContext {
267         stylist: &stylist,
268         visited_styles_enabled: stylist.device().visited_styles_enabled(),
269         options: global_style_data.options.clone(),
270         guards: StylesheetGuards::same(guard),
271         current_time_for_animations: 0.0, // Unused for Gecko, at least for now.
272         traversal_flags,
273         snapshot_map,
274     }
277 fn traverse_subtree(
278     element: GeckoElement,
279     global_style_data: &GlobalStyleData,
280     per_doc_data: &PerDocumentStyleDataImpl,
281     guard: &SharedRwLockReadGuard,
282     traversal_flags: TraversalFlags,
283     snapshots: &ServoElementSnapshotTable,
284 ) {
285     let shared_style_context = create_shared_context(
286         &global_style_data,
287         &guard,
288         &per_doc_data.stylist,
289         traversal_flags,
290         snapshots,
291     );
293     let token = RecalcStyleOnly::pre_traverse(element, &shared_style_context);
295     if !token.should_traverse() {
296         return;
297     }
299     debug!("Traversing subtree from {:?}", element);
301     let thread_pool_holder = &*STYLE_THREAD_POOL;
302     let pool;
303     let thread_pool = if traversal_flags.contains(TraversalFlags::ParallelTraversal) {
304         pool = thread_pool_holder.pool();
305         pool.as_ref()
306     } else {
307         None
308     };
310     let traversal = RecalcStyleOnly::new(shared_style_context);
311     driver::traverse_dom(&traversal, token, thread_pool);
314 /// Traverses the subtree rooted at `root` for restyling.
316 /// Returns whether the root was restyled. Whether anything else was restyled or
317 /// not can be inferred from the dirty bits in the rest of the tree.
318 #[no_mangle]
319 pub extern "C" fn Servo_TraverseSubtree(
320     root: &RawGeckoElement,
321     raw_data: &PerDocumentStyleData,
322     snapshots: *const ServoElementSnapshotTable,
323     raw_flags: ServoTraversalFlags,
324 ) -> bool {
325     let traversal_flags = TraversalFlags::from_bits_retain(raw_flags);
326     debug_assert!(!snapshots.is_null());
328     let element = GeckoElement(root);
330     debug!("Servo_TraverseSubtree (flags={:?})", traversal_flags);
331     debug!("{:?}", ShowSubtreeData(element.as_node()));
333     if cfg!(debug_assertions) {
334         if let Some(parent) = element.traversal_parent() {
335             let data = parent
336                 .borrow_data()
337                 .expect("Styling element with unstyled parent");
338             assert!(
339                 !data.styles.is_display_none(),
340                 "Styling element with display: none parent"
341             );
342         }
343     }
345     let needs_animation_only_restyle =
346         element.has_animation_only_dirty_descendants() || element.has_animation_restyle_hints();
348     let per_doc_data = raw_data.borrow();
349     debug_assert!(!per_doc_data.stylist.stylesheets_have_changed());
351     let global_style_data = &*GLOBAL_STYLE_DATA;
352     let guard = global_style_data.shared_lock.read();
354     let was_initial_style = !element.has_data();
356     if needs_animation_only_restyle {
357         debug!(
358             "Servo_TraverseSubtree doing animation-only restyle (aodd={})",
359             element.has_animation_only_dirty_descendants()
360         );
361         traverse_subtree(
362             element,
363             &global_style_data,
364             &per_doc_data,
365             &guard,
366             traversal_flags | TraversalFlags::AnimationOnly,
367             unsafe { &*snapshots },
368         );
369     }
371     traverse_subtree(
372         element,
373         &global_style_data,
374         &per_doc_data,
375         &guard,
376         traversal_flags,
377         unsafe { &*snapshots },
378     );
380     debug!(
381         "Servo_TraverseSubtree complete (dd={}, aodd={}, lfcd={}, lfc={}, data={:?})",
382         element.has_dirty_descendants(),
383         element.has_animation_only_dirty_descendants(),
384         element.descendants_need_frames(),
385         element.needs_frame(),
386         element.borrow_data().unwrap()
387     );
389     if was_initial_style {
390         debug_assert!(!element.borrow_data().unwrap().contains_restyle_data());
391         false
392     } else {
393         let element_was_restyled = element.borrow_data().unwrap().contains_restyle_data();
394         element_was_restyled
395     }
398 /// Checks whether the rule tree has crossed its threshold for unused nodes, and
399 /// if so, frees them.
400 #[no_mangle]
401 pub extern "C" fn Servo_MaybeGCRuleTree(raw_data: &PerDocumentStyleData) {
402     let per_doc_data = raw_data.borrow_mut();
403     per_doc_data.stylist.rule_tree().maybe_gc();
406 #[no_mangle]
407 pub extern "C" fn Servo_AnimationValues_Interpolate(
408     from: &AnimationValue,
409     to: &AnimationValue,
410     progress: f64,
411 ) -> Strong<AnimationValue> {
412     if let Ok(value) = from.animate(to, Procedure::Interpolate { progress }) {
413         Arc::new(value).into()
414     } else {
415         Strong::null()
416     }
419 #[no_mangle]
420 pub extern "C" fn Servo_AnimationValues_IsInterpolable(
421     from: &AnimationValue,
422     to: &AnimationValue,
423 ) -> bool {
424     from.interpolable_with(to)
427 #[no_mangle]
428 pub extern "C" fn Servo_AnimationValues_Add(
429     a: &AnimationValue,
430     b: &AnimationValue,
431 ) -> Strong<AnimationValue> {
432     if let Ok(value) = a.animate(b, Procedure::Add) {
433         Arc::new(value).into()
434     } else {
435         Strong::null()
436     }
439 #[no_mangle]
440 pub extern "C" fn Servo_AnimationValues_Accumulate(
441     a: &AnimationValue,
442     b: &AnimationValue,
443     count: u64,
444 ) -> Strong<AnimationValue> {
445     if let Ok(value) = a.animate(b, Procedure::Accumulate { count }) {
446         Arc::new(value).into()
447     } else {
448         Strong::null()
449     }
452 #[no_mangle]
453 pub extern "C" fn Servo_AnimationValues_GetZeroValue(
454     value_to_match: &AnimationValue,
455 ) -> Strong<AnimationValue> {
456     if let Ok(zero_value) = value_to_match.to_animated_zero() {
457         Arc::new(zero_value).into()
458     } else {
459         Strong::null()
460     }
463 #[no_mangle]
464 pub extern "C" fn Servo_AnimationValues_ComputeDistance(
465     from: &AnimationValue,
466     to: &AnimationValue,
467 ) -> f64 {
468     // If compute_squared_distance() failed, this function will return negative value
469     // in order to check whether we support the specified paced animation values.
470     from.compute_squared_distance(to).map_or(-1.0, |d| d.sqrt())
473 /// Compute one of the endpoints for the interpolation interval, compositing it with the
474 /// underlying value if needed.
475 /// An None returned value means, "Just use endpoint_value as-is."
476 /// It is the responsibility of the caller to ensure that |underlying_value| is provided
477 /// when it will be used.
478 fn composite_endpoint(
479     endpoint_value: Option<&AnimationValue>,
480     composite: CompositeOperation,
481     underlying_value: Option<&AnimationValue>,
482 ) -> Option<AnimationValue> {
483     match endpoint_value {
484         Some(endpoint_value) => match composite {
485             CompositeOperation::Add => underlying_value
486                 .expect("We should have an underlying_value")
487                 .animate(endpoint_value, Procedure::Add)
488                 .ok(),
489             CompositeOperation::Accumulate => underlying_value
490                 .expect("We should have an underlying value")
491                 .animate(endpoint_value, Procedure::Accumulate { count: 1 })
492                 .ok(),
493             _ => None,
494         },
495         None => underlying_value.map(|v| v.clone()),
496     }
499 /// Accumulate one of the endpoints of the animation interval.
500 /// A returned value of None means, "Just use endpoint_value as-is."
501 fn accumulate_endpoint(
502     endpoint_value: Option<&AnimationValue>,
503     composited_value: Option<AnimationValue>,
504     last_value: &AnimationValue,
505     current_iteration: u64,
506 ) -> Option<AnimationValue> {
507     debug_assert!(
508         endpoint_value.is_some() || composited_value.is_some(),
509         "Should have a suitable value to use"
510     );
512     let count = current_iteration;
513     match composited_value {
514         Some(endpoint) => last_value
515             .animate(&endpoint, Procedure::Accumulate { count })
516             .ok()
517             .or(Some(endpoint)),
518         None => last_value
519             .animate(endpoint_value.unwrap(), Procedure::Accumulate { count })
520             .ok(),
521     }
524 /// Compose the animation segment. We composite it with the underlying_value and last_value if
525 /// needed.
526 /// The caller is responsible for providing an underlying value and last value
527 /// in all situations where there are needed.
528 fn compose_animation_segment(
529     segment: &structs::AnimationPropertySegment,
530     underlying_value: Option<&AnimationValue>,
531     last_value: Option<&AnimationValue>,
532     iteration_composite: IterationCompositeOperation,
533     current_iteration: u64,
534     total_progress: f64,
535     segment_progress: f64,
536 ) -> AnimationValue {
537     // Extract keyframe values.
538     let keyframe_from_value = unsafe { segment.mFromValue.mServo.mRawPtr.as_ref() };
539     let keyframe_to_value = unsafe { segment.mToValue.mServo.mRawPtr.as_ref() };
540     let mut composited_from_value = composite_endpoint(
541         keyframe_from_value,
542         segment.mFromComposite,
543         underlying_value,
544     );
545     let mut composited_to_value =
546         composite_endpoint(keyframe_to_value, segment.mToComposite, underlying_value);
548     debug_assert!(
549         keyframe_from_value.is_some() || composited_from_value.is_some(),
550         "Should have a suitable from value to use"
551     );
552     debug_assert!(
553         keyframe_to_value.is_some() || composited_to_value.is_some(),
554         "Should have a suitable to value to use"
555     );
557     // Apply iteration composite behavior.
558     if iteration_composite == IterationCompositeOperation::Accumulate && current_iteration > 0 {
559         let last_value = last_value
560             .unwrap_or_else(|| underlying_value.expect("Should have a valid underlying value"));
562         composited_from_value = accumulate_endpoint(
563             keyframe_from_value,
564             composited_from_value,
565             last_value,
566             current_iteration,
567         );
568         composited_to_value = accumulate_endpoint(
569             keyframe_to_value,
570             composited_to_value,
571             last_value,
572             current_iteration,
573         );
574     }
576     // Use the composited value if there is one, otherwise, use the original keyframe value.
577     let from = composited_from_value
578         .as_ref()
579         .unwrap_or_else(|| keyframe_from_value.unwrap());
580     let to = composited_to_value
581         .as_ref()
582         .unwrap_or_else(|| keyframe_to_value.unwrap());
584     if segment.mToKey == segment.mFromKey {
585         return if total_progress < 0. {
586             from.clone()
587         } else {
588             to.clone()
589         };
590     }
592     match from.animate(
593         to,
594         Procedure::Interpolate {
595             progress: segment_progress,
596         },
597     ) {
598         Ok(value) => value,
599         _ => {
600             if segment_progress < 0.5 {
601                 from.clone()
602             } else {
603                 to.clone()
604             }
605         },
606     }
609 #[no_mangle]
610 pub extern "C" fn Servo_ComposeAnimationSegment(
611     segment: &structs::AnimationPropertySegment,
612     underlying_value: Option<&AnimationValue>,
613     last_value: Option<&AnimationValue>,
614     iteration_composite: IterationCompositeOperation,
615     progress: f64,
616     current_iteration: u64,
617 ) -> Strong<AnimationValue> {
618     let result = compose_animation_segment(
619         segment,
620         underlying_value,
621         last_value,
622         iteration_composite,
623         current_iteration,
624         progress,
625         progress,
626     );
627     Arc::new(result).into()
630 #[no_mangle]
631 pub extern "C" fn Servo_AnimationCompose(
632     value_map: &mut AnimationValueMap,
633     base_values: &structs::RawServoAnimationValueTable,
634     css_property: &structs::AnimatedPropertyID,
635     segment: &structs::AnimationPropertySegment,
636     last_segment: &structs::AnimationPropertySegment,
637     computed_timing: &structs::ComputedTiming,
638     iteration_composite: IterationCompositeOperation,
639 ) {
640     use style::gecko_bindings::bindings::Gecko_AnimationGetBaseStyle;
641     use style::gecko_bindings::bindings::Gecko_GetPositionInSegment;
642     use style::gecko_bindings::bindings::Gecko_GetProgressFromComputedTiming;
644     let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(css_property) {
645         Some(property) if property.as_borrowed().is_animatable() => property,
646         _ => return,
647     };
649     // We will need an underlying value if either of the endpoints is null...
650     let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
651                                 segment.mToValue.mServo.mRawPtr.is_null() ||
652                                 // ... or if they have a non-replace composite mode ...
653                                 segment.mFromComposite != CompositeOperation::Replace ||
654                                 segment.mToComposite != CompositeOperation::Replace ||
655                                 // ... or if we accumulate onto the last value and it is null.
656                                 (iteration_composite == IterationCompositeOperation::Accumulate &&
657                                  computed_timing.mCurrentIteration > 0 &&
658                                  last_segment.mToValue.mServo.mRawPtr.is_null());
660     // If either of the segment endpoints are null, get the underlying value to
661     // use from the current value in the values map (set by a lower-priority
662     // effect), or, if there is no current value, look up the cached base value
663     // for this property.
664     let underlying_value = if need_underlying_value {
665         let previous_composed_value = value_map.get(&property).map(|v| &*v);
666         previous_composed_value
667             .or_else(|| unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property).as_ref() })
668     } else {
669         None
670     };
672     if need_underlying_value && underlying_value.is_none() {
673         warn!("Underlying value should be valid when we expect to use it");
674         return;
675     }
677     let last_value = unsafe { last_segment.mToValue.mServo.mRawPtr.as_ref() };
678     let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
679     let position = if segment.mToKey == segment.mFromKey {
680         // Note: compose_animation_segment doesn't use this value
681         // if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine.
682         progress
683     } else {
684         unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }
685     };
687     let result = compose_animation_segment(
688         segment,
689         underlying_value,
690         last_value,
691         iteration_composite,
692         computed_timing.mCurrentIteration,
693         progress,
694         position,
695     );
696     value_map.insert(property, result);
699 macro_rules! get_property_id_from_nscsspropertyid {
700     ($property_id: ident, $ret: expr) => {{
701         match PropertyId::from_nscsspropertyid($property_id) {
702             Some(property_id) => property_id,
703             None => {
704                 return $ret;
705             },
706         }
707     }};
710 macro_rules! get_property_id_from_animatedpropertyid {
711     ($property_id: ident, $ret: expr) => {{
712         match PropertyId::from_gecko_animated_property_id($property_id) {
713             Some(property_id) => property_id,
714             None => {
715                 return $ret;
716             },
717         }
718     }};
721 #[no_mangle]
722 pub extern "C" fn Servo_AnimationValue_Serialize(
723     value: &AnimationValue,
724     property: &structs::AnimatedPropertyID,
725     raw_data: &PerDocumentStyleData,
726     buffer: &mut nsACString,
727 ) {
728     let uncomputed_value = value.uncompute();
729     let data = raw_data.borrow();
730     let rv = PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal)
731         .single_value_to_css(
732             &get_property_id_from_animatedpropertyid!(property, ()),
733             buffer,
734             None,
735             &data.stylist,
736         );
737     debug_assert!(rv.is_ok());
740 /// Debug: MOZ_DBG for AnimationValue.
741 #[no_mangle]
742 pub extern "C" fn Servo_AnimationValue_Dump(value: &AnimationValue, result: &mut nsACString) {
743     write!(result, "{:?}", value).unwrap();
746 #[no_mangle]
747 pub extern "C" fn Servo_AnimationValue_GetColor(
748     value: &AnimationValue,
749     foreground_color: structs::nscolor,
750 ) -> structs::nscolor {
751     use style::gecko::values::{
752         convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color,
753     };
754     use style::values::computed::color::Color as ComputedColor;
755     match *value {
756         AnimationValue::BackgroundColor(ref color) => {
757             let computed: ComputedColor = color.clone();
758             let foreground_color = convert_nscolor_to_absolute_color(foreground_color);
759             convert_absolute_color_to_nscolor(&computed.resolve_to_absolute(&foreground_color))
760         },
761         _ => panic!("Other color properties are not supported yet"),
762     }
765 #[no_mangle]
766 pub extern "C" fn Servo_AnimationValue_IsCurrentColor(value: &AnimationValue) -> bool {
767     match *value {
768         AnimationValue::BackgroundColor(ref color) => color.is_currentcolor(),
769         _ => {
770             debug_assert!(false, "Other color properties are not supported yet");
771             false
772         },
773     }
776 #[no_mangle]
777 pub extern "C" fn Servo_AnimationValue_GetOpacity(value: &AnimationValue) -> f32 {
778     if let AnimationValue::Opacity(opacity) = *value {
779         opacity
780     } else {
781         panic!("The AnimationValue should be Opacity");
782     }
785 #[no_mangle]
786 pub extern "C" fn Servo_AnimationValue_Opacity(opacity: f32) -> Strong<AnimationValue> {
787     Arc::new(AnimationValue::Opacity(opacity)).into()
790 #[no_mangle]
791 pub extern "C" fn Servo_AnimationValue_Color(
792     color_property: nsCSSPropertyID,
793     color: structs::nscolor,
794 ) -> Strong<AnimationValue> {
795     use style::gecko::values::convert_nscolor_to_absolute_color;
796     use style::values::animated::color::Color;
798     let property = LonghandId::from_nscsspropertyid(color_property)
799         .expect("We don't have shorthand property animation value");
801     let animated = convert_nscolor_to_absolute_color(color);
803     match property {
804         LonghandId::BackgroundColor => {
805             Arc::new(AnimationValue::BackgroundColor(Color::Absolute(animated))).into()
806         },
807         _ => panic!("Should be background-color property"),
808     }
811 #[no_mangle]
812 pub unsafe extern "C" fn Servo_AnimationValue_GetScale(
813     value: &AnimationValue,
814 ) -> *const computed::Scale {
815     match *value {
816         AnimationValue::Scale(ref value) => value,
817         _ => unreachable!("Expected scale"),
818     }
821 #[no_mangle]
822 pub unsafe extern "C" fn Servo_AnimationValue_GetTranslate(
823     value: &AnimationValue,
824 ) -> *const computed::Translate {
825     match *value {
826         AnimationValue::Translate(ref value) => value,
827         _ => unreachable!("Expected translate"),
828     }
831 #[no_mangle]
832 pub unsafe extern "C" fn Servo_AnimationValue_GetRotate(
833     value: &AnimationValue,
834 ) -> *const computed::Rotate {
835     match *value {
836         AnimationValue::Rotate(ref value) => value,
837         _ => unreachable!("Expected rotate"),
838     }
841 #[no_mangle]
842 pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
843     value: &AnimationValue,
844 ) -> *const computed::Transform {
845     match *value {
846         AnimationValue::Transform(ref value) => value,
847         _ => unreachable!("Unsupported transform animation value"),
848     }
851 #[no_mangle]
852 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPath(
853     value: &AnimationValue,
854     output: &mut computed::motion::OffsetPath,
855 ) {
856     use style::values::animated::ToAnimatedValue;
857     match *value {
858         AnimationValue::OffsetPath(ref value) => {
859             *output = ToAnimatedValue::from_animated_value(value.clone())
860         },
861         _ => unreachable!("Expected offset-path"),
862     }
865 #[no_mangle]
866 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetDistance(
867     value: &AnimationValue,
868 ) -> *const computed::LengthPercentage {
869     match *value {
870         AnimationValue::OffsetDistance(ref value) => value,
871         _ => unreachable!("Expected offset-distance"),
872     }
875 #[no_mangle]
876 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetRotate(
877     value: &AnimationValue,
878 ) -> *const computed::motion::OffsetRotate {
879     match *value {
880         AnimationValue::OffsetRotate(ref value) => value,
881         _ => unreachable!("Expected offset-rotate"),
882     }
885 #[no_mangle]
886 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetAnchor(
887     value: &AnimationValue,
888 ) -> *const computed::position::PositionOrAuto {
889     match *value {
890         AnimationValue::OffsetAnchor(ref value) => value,
891         _ => unreachable!("Expected offset-anchor"),
892     }
895 #[no_mangle]
896 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPosition(
897     value: &AnimationValue,
898 ) -> *const computed::motion::OffsetPosition {
899     match *value {
900         AnimationValue::OffsetPosition(ref value) => value,
901         _ => unreachable!("Expected offset-position"),
902     }
905 #[no_mangle]
906 pub unsafe extern "C" fn Servo_AnimationValue_IsOffsetPathUrl(value: &AnimationValue) -> bool {
907     use style::values::generics::motion::{GenericOffsetPath, GenericOffsetPathFunction};
908     if let AnimationValue::OffsetPath(ref op) = value {
909         if let GenericOffsetPath::OffsetPath { path, coord_box: _ } = op {
910             return matches!(**path, GenericOffsetPathFunction::Url(_));
911         }
912     }
913     false
916 #[no_mangle]
917 pub unsafe extern "C" fn Servo_AnimationValue_Rotate(
918     r: &computed::Rotate,
919 ) -> Strong<AnimationValue> {
920     Arc::new(AnimationValue::Rotate(r.clone())).into()
923 #[no_mangle]
924 pub unsafe extern "C" fn Servo_AnimationValue_Translate(
925     t: &computed::Translate,
926 ) -> Strong<AnimationValue> {
927     Arc::new(AnimationValue::Translate(t.clone())).into()
930 #[no_mangle]
931 pub unsafe extern "C" fn Servo_AnimationValue_Scale(s: &computed::Scale) -> Strong<AnimationValue> {
932     Arc::new(AnimationValue::Scale(s.clone())).into()
935 #[no_mangle]
936 pub unsafe extern "C" fn Servo_AnimationValue_Transform(
937     transform: &computed::Transform,
938 ) -> Strong<AnimationValue> {
939     Arc::new(AnimationValue::Transform(transform.clone())).into()
942 #[no_mangle]
943 pub unsafe extern "C" fn Servo_AnimationValue_OffsetPath(
944     p: &computed::motion::OffsetPath,
945 ) -> Strong<AnimationValue> {
946     use style::values::animated::ToAnimatedValue;
947     Arc::new(AnimationValue::OffsetPath(p.clone().to_animated_value())).into()
950 #[no_mangle]
951 pub unsafe extern "C" fn Servo_AnimationValue_OffsetDistance(
952     d: &computed::LengthPercentage,
953 ) -> Strong<AnimationValue> {
954     Arc::new(AnimationValue::OffsetDistance(d.clone())).into()
957 #[no_mangle]
958 pub unsafe extern "C" fn Servo_AnimationValue_OffsetRotate(
959     r: &computed::motion::OffsetRotate,
960 ) -> Strong<AnimationValue> {
961     Arc::new(AnimationValue::OffsetRotate(*r)).into()
964 #[no_mangle]
965 pub unsafe extern "C" fn Servo_AnimationValue_OffsetAnchor(
966     p: &computed::position::PositionOrAuto,
967 ) -> Strong<AnimationValue> {
968     Arc::new(AnimationValue::OffsetAnchor(p.clone())).into()
971 #[no_mangle]
972 pub unsafe extern "C" fn Servo_AnimationValue_OffsetPosition(
973     p: &computed::motion::OffsetPosition,
974 ) -> Strong<AnimationValue> {
975     Arc::new(AnimationValue::OffsetPosition(p.clone())).into()
978 #[no_mangle]
979 pub extern "C" fn Servo_AnimationValue_DeepEqual(
980     this: &AnimationValue,
981     other: &AnimationValue,
982 ) -> bool {
983     this == other
986 #[no_mangle]
987 pub extern "C" fn Servo_AnimationValue_Uncompute(
988     value: &AnimationValue,
989 ) -> Strong<LockedDeclarationBlock> {
990     let global_style_data = &*GLOBAL_STYLE_DATA;
991     Arc::new(
992         global_style_data
993             .shared_lock
994             .wrap(PropertyDeclarationBlock::with_one(
995                 value.uncompute(),
996                 Importance::Normal,
997             )),
998     )
999     .into()
1002 #[inline]
1003 fn create_byte_buf_from_vec(mut v: Vec<u8>) -> ByteBuf {
1004     let w = ByteBuf {
1005         mData: v.as_mut_ptr(),
1006         mLen: v.len(),
1007         mCapacity: v.capacity(),
1008     };
1009     std::mem::forget(v);
1010     w
1013 #[inline]
1014 fn view_byte_buf(b: &ByteBuf) -> &[u8] {
1015     if b.mData.is_null() {
1016         debug_assert_eq!(b.mCapacity, 0);
1017         return &[];
1018     }
1019     unsafe { std::slice::from_raw_parts(b.mData, b.mLen) }
1022 macro_rules! impl_basic_serde_funcs {
1023     ($ser_name:ident, $de_name:ident, $computed_type:ty) => {
1024         #[no_mangle]
1025         pub extern "C" fn $ser_name(v: &$computed_type, output: &mut ByteBuf) -> bool {
1026             let buf = match serialize(v) {
1027                 Ok(buf) => buf,
1028                 Err(..) => return false,
1029             };
1031             *output = create_byte_buf_from_vec(buf);
1032             true
1033         }
1035         #[no_mangle]
1036         pub unsafe extern "C" fn $de_name(input: &ByteBuf, v: *mut $computed_type) -> bool {
1037             let buf = match deserialize(view_byte_buf(input)) {
1038                 Ok(buf) => buf,
1039                 Err(..) => return false,
1040             };
1042             std::ptr::write(v, buf);
1043             true
1044         }
1045     };
1048 impl_basic_serde_funcs!(
1049     Servo_LengthPercentage_Serialize,
1050     Servo_LengthPercentage_Deserialize,
1051     computed::LengthPercentage
1054 impl_basic_serde_funcs!(
1055     Servo_StyleRotate_Serialize,
1056     Servo_StyleRotate_Deserialize,
1057     computed::transform::Rotate
1060 impl_basic_serde_funcs!(
1061     Servo_StyleScale_Serialize,
1062     Servo_StyleScale_Deserialize,
1063     computed::transform::Scale
1066 impl_basic_serde_funcs!(
1067     Servo_StyleTranslate_Serialize,
1068     Servo_StyleTranslate_Deserialize,
1069     computed::transform::Translate
1072 impl_basic_serde_funcs!(
1073     Servo_StyleTransform_Serialize,
1074     Servo_StyleTransform_Deserialize,
1075     computed::transform::Transform
1078 impl_basic_serde_funcs!(
1079     Servo_StyleOffsetPath_Serialize,
1080     Servo_StyleOffsetPath_Deserialize,
1081     computed::motion::OffsetPath
1084 impl_basic_serde_funcs!(
1085     Servo_StyleOffsetRotate_Serialize,
1086     Servo_StyleOffsetRotate_Deserialize,
1087     computed::motion::OffsetRotate
1090 impl_basic_serde_funcs!(
1091     Servo_StylePositionOrAuto_Serialize,
1092     Servo_StylePositionOrAuto_Deserialize,
1093     computed::position::PositionOrAuto
1096 impl_basic_serde_funcs!(
1097     Servo_StyleOffsetPosition_Serialize,
1098     Servo_StyleOffsetPosition_Deserialize,
1099     computed::motion::OffsetPosition
1102 impl_basic_serde_funcs!(
1103     Servo_StyleComputedTimingFunction_Serialize,
1104     Servo_StyleComputedTimingFunction_Deserialize,
1105     ComputedTimingFunction
1108 // Return the ComputedValues by a base ComputedValues and the rules.
1109 fn resolve_rules_for_element_with_context<'a>(
1110     element: GeckoElement<'a>,
1111     mut context: StyleContext<GeckoElement<'a>>,
1112     rules: StrongRuleNode,
1113     original_computed_values: &ComputedValues,
1114 ) -> Arc<ComputedValues> {
1115     use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};
1117     // This currently ignores visited styles, which seems acceptable, as
1118     // existing browsers don't appear to animate visited styles.
1119     let inputs = CascadeInputs {
1120         rules: Some(rules),
1121         visited_rules: None,
1122         flags: original_computed_values.flags.for_cascade_inputs(),
1123     };
1125     // Actually `PseudoElementResolution` doesn't matter.
1126     let mut resolver = StyleResolverForElement::new(
1127         element,
1128         &mut context,
1129         RuleInclusion::All,
1130         PseudoElementResolution::IfApplicable,
1131     );
1132     resolver
1133         .cascade_style_and_visited_with_default_parents(inputs)
1134         .0
1137 #[no_mangle]
1138 pub extern "C" fn Servo_AnimationValueMap_Create() -> *mut AnimationValueMap {
1139     Box::into_raw(Box::default())
1142 #[no_mangle]
1143 pub unsafe extern "C" fn Servo_AnimationValueMap_Drop(value_map: *mut AnimationValueMap) {
1144     let _ = Box::from_raw(value_map);
1147 #[no_mangle]
1148 pub extern "C" fn Servo_AnimationValueMap_GetValue(
1149     value_map: &AnimationValueMap,
1150     property_id: &structs::AnimatedPropertyID,
1151 ) -> Strong<AnimationValue> {
1152     let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
1153         Some(property) => property,
1154         None => return Strong::null(),
1155     };
1156     value_map
1157         .get(&property)
1158         .map_or(Strong::null(), |value| Arc::new(value.clone()).into())
1161 #[no_mangle]
1162 pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
1163     raw_style_set: &PerDocumentStyleData,
1164     element: &RawGeckoElement,
1165     computed_values: &ComputedValues,
1166     snapshots: *const ServoElementSnapshotTable,
1167 ) -> Strong<ComputedValues> {
1168     debug_assert!(!snapshots.is_null());
1169     let computed_values = unsafe { ArcBorrow::from_ref(computed_values) };
1171     let rules = match computed_values.rules {
1172         None => return computed_values.clone_arc().into(),
1173         Some(ref rules) => rules,
1174     };
1176     let doc_data = raw_style_set.borrow();
1177     let without_animations_rules = doc_data.stylist.rule_tree().remove_animation_rules(rules);
1178     if without_animations_rules == *rules {
1179         return computed_values.clone_arc().into();
1180     }
1182     let element = GeckoElement(element);
1184     let global_style_data = &*GLOBAL_STYLE_DATA;
1185     let guard = global_style_data.shared_lock.read();
1186     let shared = create_shared_context(
1187         &global_style_data,
1188         &guard,
1189         &doc_data.stylist,
1190         TraversalFlags::empty(),
1191         unsafe { &*snapshots },
1192     );
1193     let mut tlc = ThreadLocalStyleContext::new();
1194     let context = StyleContext {
1195         shared: &shared,
1196         thread_local: &mut tlc,
1197     };
1199     resolve_rules_for_element_with_context(
1200         element,
1201         context,
1202         without_animations_rules,
1203         &computed_values,
1204     )
1205     .into()
1208 #[repr(C)]
1209 #[derive(Default)]
1210 pub struct ShouldTransitionResult {
1211     should_animate: bool,
1212     old_transition_value_matches: bool,
1215 #[inline]
1216 fn is_transitionable(prop: PropertyDeclarationId, behavior: computed::TransitionBehavior) -> bool {
1217     if !prop.is_animatable() {
1218         return false;
1219     }
1220     // TODO(bug 1885995): Return `false` in is_discrete_animatable for interpolatable custom
1221     // property types.
1222     if matches!(prop, PropertyDeclarationId::Custom(..)) {
1223         return true;
1224     }
1226     match behavior {
1227         computed::TransitionBehavior::Normal => !prop.is_discrete_animatable(),
1228         // If transition-behavior is allow-discrete, transitionable is the same as animatable.
1229         computed::TransitionBehavior::AllowDiscrete => true,
1230     }
1233 #[no_mangle]
1234 pub extern "C" fn Servo_ComputedValues_ShouldTransition(
1235     old: &ComputedValues,
1236     new: &ComputedValues,
1237     prop: &structs::AnimatedPropertyID,
1238     behavior: computed::TransitionBehavior,
1239     old_transition_value: Option<&AnimationValue>,
1240     start: &mut structs::RefPtr<AnimationValue>,
1241     end: &mut structs::RefPtr<AnimationValue>,
1242 ) -> ShouldTransitionResult {
1243     let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
1244         return Default::default();
1245     };
1246     let prop = prop.as_borrowed();
1247     if !is_transitionable(prop, behavior) {
1248         return Default::default();
1249     }
1251     let Some(new_value) = AnimationValue::from_computed_values(prop, new) else {
1252         return Default::default();
1253     };
1255     if let Some(old_transition_value) = old_transition_value {
1256         if *old_transition_value == new_value {
1257             return ShouldTransitionResult {
1258                 should_animate: false,
1259                 old_transition_value_matches: true,
1260             };
1261         }
1262     }
1264     let Some(old_value) = AnimationValue::from_computed_values(prop, old) else {
1265         return Default::default();
1266     };
1267     if old_value == new_value
1268         || (matches!(behavior, computed::TransitionBehavior::Normal)
1269             && !old_value.interpolable_with(&new_value))
1270     {
1271         return Default::default();
1272     }
1274     start.set_arc(Arc::new(old_value));
1275     end.set_arc(Arc::new(new_value));
1277     ShouldTransitionResult {
1278         should_animate: true,
1279         old_transition_value_matches: false,
1280     }
1283 #[no_mangle]
1284 pub extern "C" fn Servo_ComputedValues_TransitionValueMatches(
1285     style: &ComputedValues,
1286     prop: &structs::AnimatedPropertyID,
1287     transition_value: &AnimationValue,
1288 ) -> bool {
1289     let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
1290         return false;
1291     };
1292     // Note: the running transitions should be transitionable, so it is always allow-discrete.
1293     let prop = prop.as_borrowed();
1294     if !is_transitionable(prop, computed::TransitionBehavior::AllowDiscrete) {
1295         return false;
1296     }
1297     let Some(value) = AnimationValue::from_computed_values(prop, style) else {
1298         return false;
1299     };
1300     value == *transition_value
1303 #[no_mangle]
1304 pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(
1305     computed_values: &ComputedValues,
1306     property_id: &structs::AnimatedPropertyID,
1307 ) -> Strong<AnimationValue> {
1308     let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
1309         Some(property) => property,
1310         None => return Strong::null(),
1311     };
1312     match AnimationValue::from_computed_values(property.as_borrowed(), &computed_values) {
1313         Some(v) => Arc::new(v).into(),
1314         None => Strong::null(),
1315     }
1318 #[no_mangle]
1319 pub extern "C" fn Servo_ResolveLogicalProperty(
1320     property_id: nsCSSPropertyID,
1321     style: &ComputedValues,
1322 ) -> nsCSSPropertyID {
1323     let longhand = LonghandId::from_nscsspropertyid(property_id)
1324         .expect("We shouldn't need to care about shorthands");
1326     longhand
1327         .to_physical(style.writing_mode)
1328         .to_nscsspropertyid()
1331 #[no_mangle]
1332 pub unsafe extern "C" fn Servo_Property_LookupEnabledForAllContent(
1333     prop: &nsACString,
1334 ) -> nsCSSPropertyID {
1335     match PropertyId::parse_enabled_for_all_content(prop.as_str_unchecked()) {
1336         Ok(p) => p.to_nscsspropertyid_resolving_aliases(),
1337         Err(..) => nsCSSPropertyID::eCSSProperty_UNKNOWN,
1338     }
1341 #[no_mangle]
1342 pub unsafe extern "C" fn Servo_Property_GetName(
1343     prop: nsCSSPropertyID,
1344     out_length: *mut u32,
1345 ) -> *const u8 {
1346     let (ptr, len) = match NonCustomPropertyId::from_nscsspropertyid(prop) {
1347         Some(p) => {
1348             let name = p.name();
1349             (name.as_bytes().as_ptr(), name.len())
1350         },
1351         None => (ptr::null(), 0),
1352     };
1354     *out_length = len as u32;
1355     ptr
1358 macro_rules! parse_enabled_property_name {
1359     ($prop_name:ident, $found:ident, $default:expr) => {{
1360         let prop_name = $prop_name.as_str_unchecked();
1361         match PropertyId::parse_enabled_for_all_content(prop_name) {
1362             Ok(p) => {
1363                 *$found = true;
1364                 p
1365             },
1366             Err(..) => {
1367                 *$found = false;
1368                 return $default;
1369             },
1370         }
1371     }};
1374 #[no_mangle]
1375 pub unsafe extern "C" fn Servo_Property_IsShorthand(
1376     prop_name: &nsACString,
1377     found: *mut bool,
1378 ) -> bool {
1379     let prop_id = parse_enabled_property_name!(prop_name, found, false);
1380     prop_id.is_shorthand()
1383 #[no_mangle]
1384 pub unsafe extern "C" fn Servo_Property_IsInherited(
1385     per_doc_data: &PerDocumentStyleData,
1386     prop_name: &nsACString,
1387 ) -> bool {
1388     let prop_name = prop_name.as_str_unchecked();
1389     let prop_id = match PropertyId::parse_enabled_for_all_content(prop_name) {
1390         Ok(id) => id,
1391         Err(_) => return false,
1392     };
1393     let longhand_id = match prop_id {
1394         PropertyId::Custom(property_name) => {
1395             let stylist = &per_doc_data.borrow().stylist;
1396             return stylist
1397                 .get_custom_property_registration(&property_name)
1398                 .inherits();
1399         },
1400         PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
1401             Ok(lh) => lh,
1402             Err(sh) => sh.longhands().next().unwrap(),
1403         },
1404     };
1405     longhand_id.inherited()
1408 #[no_mangle]
1409 pub unsafe extern "C" fn Servo_Property_SupportsType(
1410     prop_name: &nsACString,
1411     ty: u8,
1412     found: *mut bool,
1413 ) -> bool {
1414     let prop_id = parse_enabled_property_name!(prop_name, found, false);
1415     prop_id.supports_type(ty)
1418 // TODO(emilio): We could use ThinVec instead of nsTArray.
1419 #[no_mangle]
1420 pub unsafe extern "C" fn Servo_Property_GetCSSValuesForProperty(
1421     prop_name: &nsACString,
1422     found: *mut bool,
1423     result: &mut nsTArray<nsString>,
1424 ) {
1425     let prop_id = parse_enabled_property_name!(prop_name, found, ());
1426     // Use B-tree set for unique and sorted result.
1427     let mut values = BTreeSet::<&'static str>::new();
1428     prop_id.collect_property_completion_keywords(&mut |list| values.extend(list.iter()));
1430     let mut extras = vec![];
1431     if values.contains("transparent") {
1432         // This is a special value devtools use to avoid inserting the
1433         // long list of color keywords. We need to prepend it to values.
1434         extras.push("COLOR");
1435     }
1437     let len = extras.len() + values.len();
1438     bindings::Gecko_ResizeTArrayForStrings(result, len as u32);
1440     for (src, dest) in extras.iter().chain(values.iter()).zip(result.iter_mut()) {
1441         dest.write_str(src).unwrap();
1442     }
1445 #[no_mangle]
1446 pub extern "C" fn Servo_Property_IsAnimatable(prop: &structs::AnimatedPropertyID) -> bool {
1447     PropertyId::from_gecko_animated_property_id(prop).map_or(false, |p| p.is_animatable())
1450 #[no_mangle]
1451 pub extern "C" fn Servo_Property_IsDiscreteAnimatable(property: nsCSSPropertyID) -> bool {
1452     match LonghandId::from_nscsspropertyid(property) {
1453         Some(longhand) => longhand.is_discrete_animatable(),
1454         None => return false,
1455     }
1458 #[no_mangle]
1459 pub extern "C" fn Servo_Element_ClearData(element: &RawGeckoElement) {
1460     unsafe { GeckoElement(element).clear_data() };
1463 #[no_mangle]
1464 pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(
1465     malloc_size_of: GeckoMallocSizeOf,
1466     malloc_enclosing_size_of: GeckoMallocSizeOf,
1467     seen_ptrs: *mut SeenPtrs,
1468     element: &RawGeckoElement,
1469 ) -> usize {
1470     let element = GeckoElement(element);
1471     let borrow = element.borrow_data();
1472     if let Some(data) = borrow {
1473         let have_seen_ptr = move |ptr| unsafe { Gecko_HaveSeenPtr(seen_ptrs, ptr) };
1474         let mut ops = MallocSizeOfOps::new(
1475             malloc_size_of.unwrap(),
1476             Some(malloc_enclosing_size_of.unwrap()),
1477             Some(Box::new(have_seen_ptr)),
1478         );
1479         (*data).size_of_excluding_cvs(&mut ops)
1480     } else {
1481         0
1482     }
1485 #[no_mangle]
1486 pub extern "C" fn Servo_Element_GetMaybeOutOfDateStyle(
1487     element: &RawGeckoElement,
1488 ) -> *const ComputedValues {
1489     let element = GeckoElement(element);
1490     let data = match element.borrow_data() {
1491         Some(d) => d,
1492         None => return ptr::null(),
1493     };
1494     &**data.styles.primary() as *const _
1497 #[no_mangle]
1498 pub extern "C" fn Servo_Element_GetMaybeOutOfDatePseudoStyle(
1499     element: &RawGeckoElement,
1500     index: usize,
1501 ) -> *const ComputedValues {
1502     let element = GeckoElement(element);
1503     let data = match element.borrow_data() {
1504         Some(d) => d,
1505         None => return ptr::null(),
1506     };
1507     match data.styles.pseudos.as_array()[index].as_ref() {
1508         Some(style) => &**style as *const _,
1509         None => ptr::null(),
1510     }
1513 #[no_mangle]
1514 pub extern "C" fn Servo_Element_IsDisplayNone(element: &RawGeckoElement) -> bool {
1515     let element = GeckoElement(element);
1516     let data = element
1517         .get_data()
1518         .expect("Invoking Servo_Element_IsDisplayNone on unstyled element");
1520     // This function is hot, so we bypass the AtomicRefCell.
1521     //
1522     // It would be nice to also assert that we're not in the servo traversal,
1523     // but this function is called at various intermediate checkpoints when
1524     // managing the traversal on the Gecko side.
1525     debug_assert!(is_main_thread());
1526     unsafe { &*data.as_ptr() }.styles.is_display_none()
1529 #[no_mangle]
1530 pub extern "C" fn Servo_Element_IsDisplayContents(element: &RawGeckoElement) -> bool {
1531     let element = GeckoElement(element);
1532     let data = element
1533         .get_data()
1534         .expect("Invoking Servo_Element_IsDisplayContents on unstyled element");
1536     debug_assert!(is_main_thread());
1537     unsafe { &*data.as_ptr() }
1538         .styles
1539         .primary()
1540         .get_box()
1541         .clone_display()
1542         .is_contents()
1545 #[no_mangle]
1546 pub extern "C" fn Servo_Element_IsPrimaryStyleReusedViaRuleNode(element: &RawGeckoElement) -> bool {
1547     let element = GeckoElement(element);
1548     let data = element
1549         .borrow_data()
1550         .expect("Invoking Servo_Element_IsPrimaryStyleReusedViaRuleNode on unstyled element");
1551     data.flags
1552         .contains(data::ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE)
1555 fn mode_to_origin(mode: SheetParsingMode) -> Origin {
1556     match mode {
1557         SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
1558         SheetParsingMode::eUserSheetFeatures => Origin::User,
1559         SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
1560     }
1563 #[no_mangle]
1564 pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> Strong<StylesheetContents> {
1565     let global_style_data = &*GLOBAL_STYLE_DATA;
1566     let origin = mode_to_origin(mode);
1567     let shared_lock = &global_style_data.shared_lock;
1568     StylesheetContents::from_str(
1569         "",
1570         unsafe { dummy_url_data() }.clone(),
1571         origin,
1572         shared_lock,
1573         /* loader = */ None,
1574         None,
1575         QuirksMode::NoQuirks,
1576         /* use_counters = */ None,
1577         AllowImportRules::Yes,
1578         /* sanitization_data = */ None,
1579     )
1580     .into()
1583 /// Note: The load_data corresponds to this sheet, and is passed as the parent
1584 /// load data for child sheet loads. It may be null for certain cases where we
1585 /// know we won't have child loads.
1586 #[no_mangle]
1587 pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8Bytes(
1588     loader: *mut Loader,
1589     stylesheet: *mut DomStyleSheet,
1590     load_data: *mut SheetLoadData,
1591     bytes: &nsACString,
1592     mode: SheetParsingMode,
1593     extra_data: *mut URLExtraData,
1594     quirks_mode: nsCompatibility,
1595     reusable_sheets: *mut LoaderReusableStyleSheets,
1596     use_counters: Option<&UseCounters>,
1597     allow_import_rules: AllowImportRules,
1598     sanitization_kind: SanitizationKind,
1599     sanitized_output: Option<&mut nsAString>,
1600 ) -> Strong<StylesheetContents> {
1601     let global_style_data = &*GLOBAL_STYLE_DATA;
1602     let input = bytes.as_str_unchecked();
1604     let reporter = ErrorReporter::new(stylesheet, loader, extra_data);
1605     let url_data = UrlExtraData::from_ptr_ref(&extra_data);
1606     let loader = if loader.is_null() {
1607         None
1608     } else {
1609         debug_assert!(
1610             sanitized_output.is_none(),
1611             "Shouldn't trigger @import loads for sanitization",
1612         );
1613         Some(StylesheetLoader::new(
1614             loader,
1615             stylesheet,
1616             load_data,
1617             reusable_sheets,
1618         ))
1619     };
1621     // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
1622     let loader: Option<&dyn StyleStylesheetLoader> = match loader {
1623         None => None,
1624         Some(ref s) => Some(s),
1625     };
1627     let mut sanitization_data = SanitizationData::new(sanitization_kind);
1629     let contents = StylesheetContents::from_str(
1630         input,
1631         url_data.clone(),
1632         mode_to_origin(mode),
1633         &global_style_data.shared_lock,
1634         loader,
1635         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
1636         quirks_mode.into(),
1637         use_counters,
1638         allow_import_rules,
1639         sanitization_data.as_mut(),
1640     );
1642     if let Some(data) = sanitization_data {
1643         sanitized_output
1644             .unwrap()
1645             .assign_utf8(data.take().as_bytes());
1646     }
1648     contents.into()
1651 #[no_mangle]
1652 pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
1653     load_data: *mut SheetLoadDataHolder,
1654     extra_data: *mut URLExtraData,
1655     bytes: &nsACString,
1656     mode: SheetParsingMode,
1657     quirks_mode: nsCompatibility,
1658     should_record_use_counters: bool,
1659     allow_import_rules: AllowImportRules,
1660 ) {
1661     let load_data = RefPtr::new(load_data);
1662     let extra_data = UrlExtraData::new(extra_data);
1664     let mut sheet_bytes = nsCString::new();
1665     sheet_bytes.assign(bytes);
1667     let async_parser = AsyncStylesheetParser::new(
1668         load_data,
1669         extra_data,
1670         sheet_bytes,
1671         mode_to_origin(mode),
1672         quirks_mode.into(),
1673         should_record_use_counters,
1674         allow_import_rules,
1675     );
1677     if let Some(thread_pool) = STYLE_THREAD_POOL.pool().as_ref() {
1678         thread_pool.spawn(|| {
1679             gecko_profiler_label!(Layout, CSSParsing);
1680             async_parser.parse();
1681         });
1682     } else {
1683         async_parser.parse();
1684     }
1687 #[no_mangle]
1688 pub unsafe extern "C" fn Servo_ShutdownThreadPool() {
1689     debug_assert!(is_main_thread() && !is_in_servo_traversal());
1690     StyleThreadPool::shutdown();
1693 #[no_mangle]
1694 pub unsafe extern "C" fn Servo_ThreadPool_GetThreadHandles(
1695     handles: &mut ThinVec<PlatformThreadHandle>,
1696 ) {
1697     StyleThreadPool::get_thread_handles(handles);
1700 #[no_mangle]
1701 pub unsafe extern "C" fn Servo_StyleSheet_FromSharedData(
1702     extra_data: *mut URLExtraData,
1703     shared_rules: &LockedCssRules,
1704 ) -> Strong<StylesheetContents> {
1705     StylesheetContents::from_shared_data(
1706         Arc::from_raw_addrefed(shared_rules),
1707         Origin::UserAgent,
1708         UrlExtraData::new(extra_data),
1709         QuirksMode::NoQuirks,
1710     )
1711     .into()
1714 #[no_mangle]
1715 pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
1716     raw_data: &PerDocumentStyleData,
1717     sheet: *const DomStyleSheet,
1718 ) {
1719     let global_style_data = &*GLOBAL_STYLE_DATA;
1720     let mut data = raw_data.borrow_mut();
1721     let data = &mut *data;
1722     let guard = global_style_data.shared_lock.read();
1723     let sheet = unsafe { GeckoStyleSheet::new(sheet) };
1724     data.stylist.append_stylesheet(sheet, &guard);
1727 #[no_mangle]
1728 pub extern "C" fn Servo_AuthorStyles_Create() -> *mut AuthorStyles {
1729     Box::into_raw(Box::new(AuthorStyles::new()))
1732 #[no_mangle]
1733 pub unsafe extern "C" fn Servo_AuthorStyles_Drop(styles: *mut AuthorStyles) {
1734     let _ = Box::from_raw(styles);
1737 #[no_mangle]
1738 pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet(
1739     styles: &mut AuthorStyles,
1740     sheet: *const DomStyleSheet,
1741 ) {
1742     let global_style_data = &*GLOBAL_STYLE_DATA;
1743     let guard = global_style_data.shared_lock.read();
1744     let sheet = GeckoStyleSheet::new(sheet);
1745     styles.stylesheets.append_stylesheet(None, sheet, &guard);
1748 #[no_mangle]
1749 pub unsafe extern "C" fn Servo_AuthorStyles_InsertStyleSheetBefore(
1750     styles: &mut AuthorStyles,
1751     sheet: *const DomStyleSheet,
1752     before_sheet: *const DomStyleSheet,
1753 ) {
1754     let global_style_data = &*GLOBAL_STYLE_DATA;
1755     let guard = global_style_data.shared_lock.read();
1756     styles.stylesheets.insert_stylesheet_before(
1757         None,
1758         GeckoStyleSheet::new(sheet),
1759         GeckoStyleSheet::new(before_sheet),
1760         &guard,
1761     );
1764 #[no_mangle]
1765 pub unsafe extern "C" fn Servo_AuthorStyles_RemoveStyleSheet(
1766     styles: &mut AuthorStyles,
1767     sheet: *const DomStyleSheet,
1768 ) {
1769     let global_style_data = &*GLOBAL_STYLE_DATA;
1770     let guard = global_style_data.shared_lock.read();
1771     styles
1772         .stylesheets
1773         .remove_stylesheet(None, GeckoStyleSheet::new(sheet), &guard);
1776 #[no_mangle]
1777 pub extern "C" fn Servo_AuthorStyles_ForceDirty(styles: &mut AuthorStyles) {
1778     styles.stylesheets.force_dirty();
1781 #[no_mangle]
1782 pub extern "C" fn Servo_AuthorStyles_IsDirty(styles: &AuthorStyles) -> bool {
1783     styles.stylesheets.dirty()
1786 #[no_mangle]
1787 pub extern "C" fn Servo_AuthorStyles_Flush(
1788     styles: &mut AuthorStyles,
1789     document_set: &PerDocumentStyleData,
1790 ) {
1791     // Try to avoid the atomic borrow below if possible.
1792     if !styles.stylesheets.dirty() {
1793         return;
1794     }
1796     let global_style_data = &*GLOBAL_STYLE_DATA;
1797     let guard = global_style_data.shared_lock.read();
1799     let mut document_data = document_set.borrow_mut();
1801     // TODO(emilio): This is going to need an element or something to do proper
1802     // invalidation in Shadow roots.
1803     styles.flush::<GeckoElement>(&mut document_data.stylist, &guard);
1806 #[no_mangle]
1807 pub extern "C" fn Servo_StyleSet_RemoveUniqueEntriesFromAuthorStylesCache(
1808     document_set: &PerDocumentStyleData,
1809 ) {
1810     let mut document_data = document_set.borrow_mut();
1811     document_data
1812         .stylist
1813         .remove_unique_author_data_cache_entries();
1816 #[no_mangle]
1817 pub unsafe extern "C" fn Servo_DeclarationBlock_SizeOfIncludingThis(
1818     malloc_size_of: GeckoMallocSizeOf,
1819     malloc_enclosing_size_of: GeckoMallocSizeOf,
1820     declarations: &LockedDeclarationBlock,
1821 ) -> usize {
1822     use malloc_size_of::MallocSizeOf;
1823     use malloc_size_of::MallocUnconditionalShallowSizeOf;
1825     let global_style_data = &*GLOBAL_STYLE_DATA;
1826     let guard = global_style_data.shared_lock.read();
1828     let mut ops = MallocSizeOfOps::new(
1829         malloc_size_of.unwrap(),
1830         Some(malloc_enclosing_size_of.unwrap()),
1831         None,
1832     );
1834     ArcBorrow::from_ref(declarations).with_arc(|declarations| {
1835         let mut n = 0;
1836         n += declarations.unconditional_shallow_size_of(&mut ops);
1837         n += declarations.read_with(&guard).size_of(&mut ops);
1838         n
1839     })
1842 #[no_mangle]
1843 pub unsafe extern "C" fn Servo_AuthorStyles_SizeOfIncludingThis(
1844     malloc_size_of: GeckoMallocSizeOf,
1845     malloc_enclosing_size_of: GeckoMallocSizeOf,
1846     styles: &AuthorStyles,
1847 ) -> usize {
1848     // We cannot `use` MallocSizeOf at the top level, otherwise the compiler
1849     // would complain in `Servo_StyleSheet_SizeOfIncludingThis` for `size_of`
1850     // there.
1851     use malloc_size_of::MallocSizeOf;
1852     let malloc_size_of = malloc_size_of.unwrap();
1853     let malloc_size_of_this = malloc_size_of(styles as *const AuthorStyles as *const c_void);
1855     let mut ops = MallocSizeOfOps::new(
1856         malloc_size_of,
1857         Some(malloc_enclosing_size_of.unwrap()),
1858         None,
1859     );
1860     malloc_size_of_this + styles.size_of(&mut ops)
1863 #[no_mangle]
1864 pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
1865     document_set: &PerDocumentStyleData,
1866     non_document_styles: &mut nsTArray<&mut AuthorStyles>,
1867     may_affect_default_style: bool,
1868 ) -> structs::MediumFeaturesChangedResult {
1869     let global_style_data = &*GLOBAL_STYLE_DATA;
1870     let guard = global_style_data.shared_lock.read();
1872     // NOTE(emilio): We don't actually need to flush the stylist here and ensure
1873     // it's up to date.
1874     //
1875     // In case it isn't we would trigger a rebuild + restyle as needed too.
1876     //
1877     // We need to ensure the default computed values are up to date though,
1878     // because those can influence the result of media query evaluation.
1879     let mut document_data = document_set.borrow_mut();
1881     if may_affect_default_style {
1882         document_data.stylist.device_mut().reset_computed_values();
1883     }
1884     let guards = StylesheetGuards::same(&guard);
1886     let origins_in_which_rules_changed = document_data
1887         .stylist
1888         .media_features_change_changed_style(&guards, document_data.stylist.device());
1890     let affects_document_rules = !origins_in_which_rules_changed.is_empty();
1891     if affects_document_rules {
1892         document_data
1893             .stylist
1894             .force_stylesheet_origins_dirty(origins_in_which_rules_changed);
1895     }
1897     let mut affects_non_document_rules = false;
1898     for author_styles in &mut **non_document_styles {
1899         let affected_style = author_styles.stylesheets.iter().any(|sheet| {
1900             !author_styles.data.media_feature_affected_matches(
1901                 sheet,
1902                 &guards.author,
1903                 document_data.stylist.device(),
1904                 document_data.stylist.quirks_mode(),
1905             )
1906         });
1907         if affected_style {
1908             affects_non_document_rules = true;
1909             author_styles.stylesheets.force_dirty();
1910         }
1911     }
1913     structs::MediumFeaturesChangedResult {
1914         mAffectsDocumentRules: affects_document_rules,
1915         mAffectsNonDocumentRules: affects_non_document_rules,
1916     }
1919 #[no_mangle]
1920 pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
1921     raw_data: &PerDocumentStyleData,
1922     sheet: *const DomStyleSheet,
1923     before_sheet: *const DomStyleSheet,
1924 ) {
1925     let global_style_data = &*GLOBAL_STYLE_DATA;
1926     let mut data = raw_data.borrow_mut();
1927     let data = &mut *data;
1928     let guard = global_style_data.shared_lock.read();
1929     let sheet = unsafe { GeckoStyleSheet::new(sheet) };
1930     data.stylist.insert_stylesheet_before(
1931         sheet,
1932         unsafe { GeckoStyleSheet::new(before_sheet) },
1933         &guard,
1934     );
1937 #[no_mangle]
1938 pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(
1939     raw_data: &PerDocumentStyleData,
1940     sheet: *const DomStyleSheet,
1941 ) {
1942     let global_style_data = &*GLOBAL_STYLE_DATA;
1943     let mut data = raw_data.borrow_mut();
1944     let data = &mut *data;
1945     let guard = global_style_data.shared_lock.read();
1946     let sheet = unsafe { GeckoStyleSheet::new(sheet) };
1947     data.stylist.remove_stylesheet(sheet, &guard);
1950 #[no_mangle]
1951 pub unsafe extern "C" fn Servo_StyleSet_GetSheetAt(
1952     raw_data: &PerDocumentStyleData,
1953     origin: Origin,
1954     index: usize,
1955 ) -> *const DomStyleSheet {
1956     let data = raw_data.borrow();
1957     data.stylist
1958         .sheet_at(origin, index)
1959         .map_or(ptr::null(), |s| s.raw())
1962 #[no_mangle]
1963 pub unsafe extern "C" fn Servo_StyleSet_GetSheetCount(
1964     raw_data: &PerDocumentStyleData,
1965     origin: Origin,
1966 ) -> usize {
1967     let data = raw_data.borrow();
1968     data.stylist.sheet_count(origin)
1971 #[no_mangle]
1972 pub unsafe extern "C" fn Servo_StyleSet_FlushStyleSheets(
1973     raw_data: &PerDocumentStyleData,
1974     doc_element: Option<&RawGeckoElement>,
1975     snapshots: *const ServoElementSnapshotTable,
1976 ) {
1977     let global_style_data = &*GLOBAL_STYLE_DATA;
1978     let guard = global_style_data.shared_lock.read();
1979     let mut data = raw_data.borrow_mut();
1980     let doc_element = doc_element.map(GeckoElement);
1982     let have_invalidations = data.flush_stylesheets(&guard, doc_element, snapshots.as_ref());
1984     if have_invalidations && doc_element.is_some() {
1985         // The invalidation machinery propagates the bits up, but we still need
1986         // to tell the Gecko restyle root machinery about it.
1987         bindings::Gecko_NoteDirtySubtreeForInvalidation(doc_element.unwrap().0);
1988     }
1991 #[no_mangle]
1992 pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
1993     raw_data: &PerDocumentStyleData,
1994     changed_origins: OriginFlags,
1995 ) {
1996     let mut data = raw_data.borrow_mut();
1997     data.stylist
1998         .force_stylesheet_origins_dirty(OriginSet::from(changed_origins));
2001 #[no_mangle]
2002 pub extern "C" fn Servo_StyleSet_SetAuthorStyleDisabled(
2003     raw_data: &PerDocumentStyleData,
2004     author_style_disabled: bool,
2005 ) {
2006     let mut data = raw_data.borrow_mut();
2007     let enabled = if author_style_disabled {
2008         AuthorStylesEnabled::No
2009     } else {
2010         AuthorStylesEnabled::Yes
2011     };
2012     data.stylist.set_author_styles_enabled(enabled);
2015 #[no_mangle]
2016 pub extern "C" fn Servo_StyleSet_UsesFontMetrics(raw_data: &PerDocumentStyleData) -> bool {
2017     let doc_data = raw_data;
2018     doc_data.borrow().stylist.device().used_font_metrics()
2021 #[no_mangle]
2022 pub extern "C" fn Servo_StyleSheet_HasRules(raw_contents: &StylesheetContents) -> bool {
2023     let global_style_data = &*GLOBAL_STYLE_DATA;
2024     let guard = global_style_data.shared_lock.read();
2025     !raw_contents.rules.read_with(&guard).0.is_empty()
2028 #[no_mangle]
2029 pub extern "C" fn Servo_StyleSheet_GetRules(sheet: &StylesheetContents) -> Strong<LockedCssRules> {
2030     sheet.rules.clone().into()
2033 #[no_mangle]
2034 pub extern "C" fn Servo_StyleSheet_Clone(
2035     contents: &StylesheetContents,
2036     reference_sheet: *const DomStyleSheet,
2037 ) -> Strong<StylesheetContents> {
2038     use style::shared_lock::{DeepCloneParams, DeepCloneWithLock};
2039     let global_style_data = &*GLOBAL_STYLE_DATA;
2040     let guard = global_style_data.shared_lock.read();
2041     let params = DeepCloneParams { reference_sheet };
2043     Arc::new(contents.deep_clone_with_lock(&global_style_data.shared_lock, &guard, &params)).into()
2046 #[no_mangle]
2047 pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
2048     malloc_size_of: GeckoMallocSizeOf,
2049     malloc_enclosing_size_of: GeckoMallocSizeOf,
2050     sheet: &StylesheetContents,
2051 ) -> usize {
2052     let global_style_data = &*GLOBAL_STYLE_DATA;
2053     let guard = global_style_data.shared_lock.read();
2054     let mut ops = MallocSizeOfOps::new(
2055         malloc_size_of.unwrap(),
2056         Some(malloc_enclosing_size_of.unwrap()),
2057         None,
2058     );
2059     // TODO(emilio): We're not measuring the size of the Arc<StylesheetContents>
2060     // allocation itself here.
2061     sheet.size_of(&guard, &mut ops)
2064 #[no_mangle]
2065 pub extern "C" fn Servo_StyleSheet_GetOrigin(sheet: &StylesheetContents) -> Origin {
2066     sheet.origin
2069 #[no_mangle]
2070 pub extern "C" fn Servo_StyleSheet_GetSourceMapURL(
2071     contents: &StylesheetContents,
2072     result: &mut nsACString,
2073 ) {
2074     let url_opt = contents.source_map_url.read();
2075     if let Some(ref url) = *url_opt {
2076         result.assign(url);
2077     }
2080 #[no_mangle]
2081 pub extern "C" fn Servo_StyleSheet_GetSourceURL(
2082     contents: &StylesheetContents,
2083     result: &mut nsACString,
2084 ) {
2085     let url_opt = contents.source_url.read();
2086     if let Some(ref url) = *url_opt {
2087         result.assign(url);
2088     }
2091 fn with_maybe_worker_shared_lock<R>(func: impl FnOnce(&SharedRwLock) -> R) -> R {
2092     if is_dom_worker_thread() {
2093         DOM_WORKER_RWLOCK.with(func)
2094     } else {
2095         func(&GLOBAL_STYLE_DATA.shared_lock)
2096     }
2099 fn read_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
2100 where
2101     F: FnOnce(&T) -> R,
2103     debug_assert!(!is_dom_worker_thread());
2104     let global_style_data = &*GLOBAL_STYLE_DATA;
2105     let guard = global_style_data.shared_lock.read();
2106     func(raw.read_with(&guard))
2109 fn read_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
2110 where
2111     F: FnOnce(&T) -> R,
2113     with_maybe_worker_shared_lock(|lock| {
2114         let guard = lock.read();
2115         func(raw.read_with(&guard))
2116     })
2119 #[cfg(debug_assertions)]
2120 unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
2121 where
2122     F: FnOnce(&T) -> R,
2124     debug_assert!(is_main_thread() && !is_in_servo_traversal());
2125     read_locked_arc(raw, func)
2128 #[cfg(not(debug_assertions))]
2129 unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
2130 where
2131     F: FnOnce(&T) -> R,
2133     debug_assert!(!is_dom_worker_thread());
2134     func(raw.read_unchecked())
2137 fn write_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
2138 where
2139     F: FnOnce(&mut T) -> R,
2141     debug_assert!(!is_dom_worker_thread());
2142     let global_style_data = &*GLOBAL_STYLE_DATA;
2143     let mut guard = global_style_data.shared_lock.write();
2144     func(raw.write_with(&mut guard))
2147 fn write_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
2148 where
2149     F: FnOnce(&mut T) -> R,
2151     with_maybe_worker_shared_lock(|lock| {
2152         let mut guard = lock.write();
2153         func(raw.write_with(&mut guard))
2154     })
2157 #[no_mangle]
2158 pub extern "C" fn Servo_CssRules_ListTypes(rules: &LockedCssRules, result: &mut nsTArray<usize>) {
2159     read_locked_arc(rules, |rules: &CssRules| {
2160         result.assign_from_iter_pod(rules.0.iter().map(|rule| rule.rule_type() as usize));
2161     })
2164 #[no_mangle]
2165 pub extern "C" fn Servo_CssRules_InsertRule(
2166     rules: &LockedCssRules,
2167     contents: &StylesheetContents,
2168     rule: &nsACString,
2169     index: u32,
2170     containing_rule_types: u32,
2171     parse_relative_rule_type: Option<&CssRuleType>,
2172     loader: *mut Loader,
2173     allow_import_rules: AllowImportRules,
2174     gecko_stylesheet: *mut DomStyleSheet,
2175     rule_type: &mut CssRuleType,
2176 ) -> nsresult {
2177     let loader = if loader.is_null() {
2178         None
2179     } else {
2180         Some(StylesheetLoader::new(
2181             loader,
2182             gecko_stylesheet,
2183             ptr::null_mut(),
2184             ptr::null_mut(),
2185         ))
2186     };
2187     let loader = loader
2188         .as_ref()
2189         .map(|loader| loader as &dyn StyleStylesheetLoader);
2190     let rule = unsafe { rule.as_str_unchecked() };
2192     let global_style_data = &*GLOBAL_STYLE_DATA;
2193     let result = rules.insert_rule(
2194         &global_style_data.shared_lock,
2195         rule,
2196         contents,
2197         index as usize,
2198         CssRuleTypes::from_bits(containing_rule_types),
2199         parse_relative_rule_type.cloned(),
2200         loader,
2201         allow_import_rules,
2202     );
2204     match result {
2205         Ok(new_rule) => {
2206             *rule_type = new_rule.rule_type();
2207             nsresult::NS_OK
2208         },
2209         Err(err) => err.into(),
2210     }
2213 #[no_mangle]
2214 pub extern "C" fn Servo_CssRules_DeleteRule(rules: &LockedCssRules, index: u32) -> nsresult {
2215     write_locked_arc(rules, |rules: &mut CssRules| {
2216         match rules.remove_rule(index as usize) {
2217             Ok(_) => nsresult::NS_OK,
2218             Err(err) => err.into(),
2219         }
2220     })
2223 trait MaybeLocked<Target> {
2224     fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a Target;
2227 impl<T> MaybeLocked<T> for T {
2228     fn maybe_locked_read<'a>(&'a self, _: &'a SharedRwLockReadGuard) -> &'a T {
2229         self
2230     }
2233 impl<T> MaybeLocked<T> for Locked<T> {
2234     fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {
2235         self.read_with(guard)
2236     }
2239 macro_rules! impl_basic_rule_funcs_without_getter {
2240     {
2241         ($rule_type:ty, $maybe_locked_rule_type:ty),
2242         debug: $debug:ident,
2243         to_css: $to_css:ident,
2244     } => {
2245         #[cfg(debug_assertions)]
2246         #[no_mangle]
2247         pub extern "C" fn $debug(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
2248             let global_style_data = &*GLOBAL_STYLE_DATA;
2249             let guard = global_style_data.shared_lock.read();
2250             let rule: &$rule_type = rule.maybe_locked_read(&guard);
2251             write!(result, "{:?}", *rule).unwrap();
2252         }
2254         #[cfg(not(debug_assertions))]
2255         #[no_mangle]
2256         pub extern "C" fn $debug(_: &$maybe_locked_rule_type, _: &mut nsACString) {
2257             unreachable!()
2258         }
2260         #[no_mangle]
2261         pub extern "C" fn $to_css(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
2262             let global_style_data = &*GLOBAL_STYLE_DATA;
2263             let guard = global_style_data.shared_lock.read();
2264             let rule: &$rule_type = rule.maybe_locked_read(&guard);
2265             rule.to_css(&guard, result).unwrap();
2266         }
2267     }
2270 macro_rules! impl_basic_rule_funcs {
2271     { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
2272         getter: $getter:ident,
2273         debug: $debug:ident,
2274         to_css: $to_css:ident,
2275         changed: $changed:ident,
2276     } => {
2277         #[no_mangle]
2278         pub extern "C" fn $getter(
2279             rules: &LockedCssRules,
2280             index: u32,
2281             line: &mut u32,
2282             column: &mut u32,
2283         ) -> Strong<$maybe_locked_rule_type> {
2284             let global_style_data = &*GLOBAL_STYLE_DATA;
2285             let guard = global_style_data.shared_lock.read();
2286             let rules = rules.read_with(&guard);
2287             let index = index as usize;
2289             if index >= rules.0.len() {
2290                 return Strong::null();
2291             }
2293             match rules.0[index] {
2294                 CssRule::$name(ref arc) => {
2295                     let rule: &$rule_type = (&**arc).maybe_locked_read(&guard);
2296                     let location = rule.source_location;
2297                     *line = location.line as u32;
2298                     *column = location.column as u32;
2299                     arc.clone().into()
2300                 },
2301                 _ => {
2302                     Strong::null()
2303                 }
2304             }
2305         }
2307         #[no_mangle]
2308         pub extern "C" fn $changed(
2309             styleset: &PerDocumentStyleData,
2310             rule: &$maybe_locked_rule_type,
2311             sheet: &DomStyleSheet,
2312             change_kind: RuleChangeKind,
2313         ) {
2314             let mut data = styleset.borrow_mut();
2315             let data = &mut *data;
2316             let global_style_data = &*GLOBAL_STYLE_DATA;
2317             let guard = global_style_data.shared_lock.read();
2318             // TODO(emilio): Would be nice not to deal with refcount bumps here,
2319             // but it's probably not a huge deal.
2320             let rule = unsafe { CssRule::$name(Arc::from_raw_addrefed(rule)) };
2321             let sheet = unsafe { GeckoStyleSheet::new(sheet) };
2322             data.stylist.rule_changed(&sheet, &rule, &guard, change_kind);
2323         }
2325         impl_basic_rule_funcs_without_getter! {
2326             ($rule_type, $maybe_locked_rule_type),
2327             debug: $debug,
2328             to_css: $to_css,
2329         }
2330     }
2333 macro_rules! impl_group_rule_funcs {
2334     { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
2335       get_rules: $get_rules:ident,
2336       $($basic:tt)+
2337     } => {
2338         impl_basic_rule_funcs! { ($name, $rule_type, $maybe_locked_rule_type), $($basic)+ }
2340         #[no_mangle]
2341         pub extern "C" fn $get_rules(rule: &$maybe_locked_rule_type) -> Strong<LockedCssRules> {
2342             let global_style_data = &*GLOBAL_STYLE_DATA;
2343             let guard = global_style_data.shared_lock.read();
2344             let rule: &$rule_type = rule.maybe_locked_read(&guard);
2345             rule.rules.clone().into()
2346         }
2347     }
2350 impl_basic_rule_funcs! { (Style, StyleRule, Locked<StyleRule>),
2351     getter: Servo_CssRules_GetStyleRuleAt,
2352     debug: Servo_StyleRule_Debug,
2353     to_css: Servo_StyleRule_GetCssText,
2354     changed: Servo_StyleSet_StyleRuleChanged,
2357 #[no_mangle]
2358 pub extern "C" fn Servo_StyleRule_EnsureRules(
2359     rule: &LockedStyleRule,
2360     read_only: bool,
2361 ) -> Strong<LockedCssRules> {
2362     let global_style_data = &*GLOBAL_STYLE_DATA;
2363     let lock = &global_style_data.shared_lock;
2364     if read_only {
2365         let guard = lock.read();
2366         if let Some(ref rules) = rule.read_with(&guard).rules {
2367             return rules.clone().into();
2368         }
2369         return CssRules::new(vec![], lock).into();
2370     }
2371     let mut guard = lock.write();
2372     rule.write_with(&mut guard)
2373         .rules
2374         .get_or_insert_with(|| CssRules::new(vec![], lock))
2375         .clone()
2376         .into()
2379 impl_basic_rule_funcs! { (Import, ImportRule, Locked<ImportRule>),
2380     getter: Servo_CssRules_GetImportRuleAt,
2381     debug: Servo_ImportRule_Debug,
2382     to_css: Servo_ImportRule_GetCssText,
2383     changed: Servo_StyleSet_ImportRuleChanged,
2386 impl_basic_rule_funcs_without_getter! { (Keyframe, Locked<Keyframe>),
2387     debug: Servo_Keyframe_Debug,
2388     to_css: Servo_Keyframe_GetCssText,
2391 impl_basic_rule_funcs! { (Keyframes, KeyframesRule, Locked<KeyframesRule>),
2392     getter: Servo_CssRules_GetKeyframesRuleAt,
2393     debug: Servo_KeyframesRule_Debug,
2394     to_css: Servo_KeyframesRule_GetCssText,
2395     changed: Servo_StyleSet_KeyframesRuleChanged,
2398 impl_group_rule_funcs! { (Media, MediaRule, MediaRule),
2399     get_rules: Servo_MediaRule_GetRules,
2400     getter: Servo_CssRules_GetMediaRuleAt,
2401     debug: Servo_MediaRule_Debug,
2402     to_css: Servo_MediaRule_GetCssText,
2403     changed: Servo_StyleSet_MediaRuleChanged,
2406 impl_basic_rule_funcs! { (Margin, MarginRule, MarginRule),
2407     getter: Servo_CssRules_GetMarginRuleAt,
2408     debug: Servo_MarginRule_Debug,
2409     to_css: Servo_MarginRule_GetCssText,
2410     changed: Servo_StyleSet_MarginRuleChanged,
2413 impl_basic_rule_funcs! { (Namespace, NamespaceRule, NamespaceRule),
2414     getter: Servo_CssRules_GetNamespaceRuleAt,
2415     debug: Servo_NamespaceRule_Debug,
2416     to_css: Servo_NamespaceRule_GetCssText,
2417     changed: Servo_StyleSet_NamespaceRuleChanged,
2420 impl_group_rule_funcs! { (Page, PageRule, Locked<PageRule>),
2421     get_rules: Servo_PageRule_GetRules,
2422     getter: Servo_CssRules_GetPageRuleAt,
2423     debug: Servo_PageRule_Debug,
2424     to_css: Servo_PageRule_GetCssText,
2425     changed: Servo_StyleSet_PageRuleChanged,
2428 impl_basic_rule_funcs! { (Property, PropertyRule, PropertyRule),
2429     getter: Servo_CssRules_GetPropertyRuleAt,
2430     debug: Servo_PropertyRule_Debug,
2431     to_css: Servo_PropertyRule_GetCssText,
2432     changed: Servo_StyleSet_PropertyRuleChanged,
2435 impl_group_rule_funcs! { (Supports, SupportsRule, SupportsRule),
2436     get_rules: Servo_SupportsRule_GetRules,
2437     getter: Servo_CssRules_GetSupportsRuleAt,
2438     debug: Servo_SupportsRule_Debug,
2439     to_css: Servo_SupportsRule_GetCssText,
2440     changed: Servo_StyleSet_SupportsRuleChanged,
2443 impl_group_rule_funcs! { (Container, ContainerRule, ContainerRule),
2444     get_rules: Servo_ContainerRule_GetRules,
2445     getter: Servo_CssRules_GetContainerRuleAt,
2446     debug: Servo_ContainerRule_Debug,
2447     to_css: Servo_ContainerRule_GetCssText,
2448     changed: Servo_StyleSet_ContainerRuleChanged,
2451 impl_group_rule_funcs! { (LayerBlock, LayerBlockRule, LayerBlockRule),
2452     get_rules: Servo_LayerBlockRule_GetRules,
2453     getter: Servo_CssRules_GetLayerBlockRuleAt,
2454     debug: Servo_LayerBlockRule_Debug,
2455     to_css: Servo_LayerBlockRule_GetCssText,
2456     changed: Servo_StyleSet_LayerBlockRuleChanged,
2459 impl_basic_rule_funcs! { (LayerStatement, LayerStatementRule, LayerStatementRule),
2460     getter: Servo_CssRules_GetLayerStatementRuleAt,
2461     debug: Servo_LayerStatementRule_Debug,
2462     to_css: Servo_LayerStatementRule_GetCssText,
2463     changed: Servo_StyleSet_LayerStatementRuleChanged,
2466 impl_group_rule_funcs! { (Document, DocumentRule, DocumentRule),
2467     get_rules: Servo_DocumentRule_GetRules,
2468     getter: Servo_CssRules_GetDocumentRuleAt,
2469     debug: Servo_DocumentRule_Debug,
2470     to_css: Servo_DocumentRule_GetCssText,
2471     changed: Servo_StyleSet_DocumentRuleChanged,
2474 impl_basic_rule_funcs! { (FontFeatureValues, FontFeatureValuesRule, FontFeatureValuesRule),
2475     getter: Servo_CssRules_GetFontFeatureValuesRuleAt,
2476     debug: Servo_FontFeatureValuesRule_Debug,
2477     to_css: Servo_FontFeatureValuesRule_GetCssText,
2478     changed: Servo_StyleSet_FontFeatureValuesRuleChanged,
2481 impl_basic_rule_funcs! { (FontPaletteValues, FontPaletteValuesRule, FontPaletteValuesRule),
2482     getter: Servo_CssRules_GetFontPaletteValuesRuleAt,
2483     debug: Servo_FontPaletteValuesRule_Debug,
2484     to_css: Servo_FontPaletteValuesRule_GetCssText,
2485     changed: Servo_StyleSet_FontPaletteValuesRuleChanged,
2488 impl_basic_rule_funcs! { (FontFace, FontFaceRule, Locked<FontFaceRule>),
2489     getter: Servo_CssRules_GetFontFaceRuleAt,
2490     debug: Servo_FontFaceRule_Debug,
2491     to_css: Servo_FontFaceRule_GetCssText,
2492     changed: Servo_StyleSet_FontFaceRuleChanged,
2495 impl_basic_rule_funcs! { (CounterStyle, CounterStyleRule, Locked<CounterStyleRule>),
2496     getter: Servo_CssRules_GetCounterStyleRuleAt,
2497     debug: Servo_CounterStyleRule_Debug,
2498     to_css: Servo_CounterStyleRule_GetCssText,
2499     changed: Servo_StyleSet_CounterStyleRuleChanged,
2502 impl_group_rule_funcs! { (Scope, ScopeRule, ScopeRule),
2503     get_rules: Servo_ScopeRule_GetRules,
2504     getter: Servo_CssRules_GetScopeRuleAt,
2505     debug: Servo_ScopeRule_Debug,
2506     to_css: Servo_ScopeRule_GetCssText,
2507     changed: Servo_StyleSet_ScopeRuleChanged,
2510 impl_group_rule_funcs! { (StartingStyle, StartingStyleRule, StartingStyleRule),
2511     get_rules: Servo_StartingStyleRule_GetRules,
2512     getter: Servo_CssRules_GetStartingStyleRuleAt,
2513     debug: Servo_StartingStyleRule_Debug,
2514     to_css: Servo_StartingStyleRule_GetCssText,
2515     changed: Servo_StyleSet_StartingStyleRuleChanged,
2518 #[no_mangle]
2519 pub extern "C" fn Servo_StyleRule_GetStyle(
2520     rule: &LockedStyleRule,
2521 ) -> Strong<LockedDeclarationBlock> {
2522     read_locked_arc(rule, |rule: &StyleRule| rule.block.clone().into())
2525 #[no_mangle]
2526 pub extern "C" fn Servo_StyleRule_SetStyle(
2527     rule: &LockedStyleRule,
2528     declarations: &LockedDeclarationBlock,
2529 ) {
2530     write_locked_arc(rule, |rule: &mut StyleRule| {
2531         rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
2532     })
2535 #[no_mangle]
2536 pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: &LockedStyleRule, result: &mut nsACString) {
2537     read_locked_arc(rule, |rule| rule.selectors.to_css(result).unwrap());
2540 fn desugared_selector_list(rules: &ThinVec<&LockedStyleRule>) -> SelectorList {
2541     let mut selectors: Option<SelectorList> = None;
2542     for rule in rules.iter().rev() {
2543         selectors = Some(read_locked_arc(rule, |rule| match selectors {
2544             Some(ref s) => rule.selectors.replace_parent_selector(s),
2545             None => rule.selectors.clone(),
2546         }));
2547     }
2548     selectors.expect("Empty rule chain?")
2551 #[no_mangle]
2552 pub extern "C" fn Servo_StyleRule_GetSelectorDataAtIndex(
2553     rules: &ThinVec<&LockedStyleRule>,
2554     index: u32,
2555     text: Option<&mut nsACString>,
2556     specificity: Option<&mut u64>,
2557 ) {
2558     let selectors = desugared_selector_list(rules);
2559     let Some(selector) = selectors.slice().get(index as usize) else {
2560         return;
2561     };
2562     if let Some(text) = text {
2563         selector.to_css(text).unwrap();
2564     }
2565     if let Some(specificity) = specificity {
2566         *specificity = selector.specificity() as u64;
2567     }
2570 #[no_mangle]
2571 pub extern "C" fn Servo_StyleRule_GetSelectorCount(rule: &LockedStyleRule) -> u32 {
2572     read_locked_arc(rule, |rule| rule.selectors.len() as u32)
2575 #[no_mangle]
2576 pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(
2577     rules: &ThinVec<&LockedStyleRule>,
2578     element: &RawGeckoElement,
2579     index: u32,
2580     host: Option<&RawGeckoElement>,
2581     pseudo_type: PseudoStyleType,
2582     relevant_link_visited: bool,
2583 ) -> bool {
2584     use selectors::matching::{
2585         matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,
2586     };
2587     let selectors = desugared_selector_list(rules);
2588     let Some(selector) = selectors.slice().get(index as usize) else {
2589         return false;
2590     };
2591     let mut matching_mode = MatchingMode::Normal;
2592     match PseudoElement::from_pseudo_type(pseudo_type, None) {
2593         Some(pseudo) => {
2594             // We need to make sure that the requested pseudo element type
2595             // matches the selector pseudo element type before proceeding.
2596             match selector.pseudo_element() {
2597                 Some(selector_pseudo) if *selector_pseudo == pseudo => {
2598                     matching_mode = MatchingMode::ForStatelessPseudoElement
2599                 },
2600                 _ => return false,
2601             };
2602         },
2603         None => {
2604             // Do not attempt to match if a pseudo element is requested and
2605             // this is not a pseudo element selector, or vice versa.
2606             if selector.has_pseudo_element() {
2607                 return false;
2608             }
2609         },
2610     };
2612     let element = GeckoElement(element);
2613     let host = host.map(GeckoElement);
2614     let quirks_mode = element.as_node().owner_doc().quirks_mode();
2615     let mut selector_caches = SelectorCaches::default();
2616     let visited_mode = if relevant_link_visited {
2617         VisitedHandlingMode::RelevantLinkVisited
2618     } else {
2619         VisitedHandlingMode::AllLinksUnvisited
2620     };
2621     let mut ctx = MatchingContext::new_for_visited(
2622         matching_mode,
2623         /* bloom_filter = */ None,
2624         &mut selector_caches,
2625         visited_mode,
2626         quirks_mode,
2627         NeedsSelectorFlags::No,
2628         MatchingForInvalidation::No,
2629     );
2630     ctx.with_shadow_host(host, |ctx| {
2631         matches_selector(selector, 0, None, &element, ctx)
2632     })
2635 pub type SelectorList = selectors::SelectorList<style::gecko::selector_parser::SelectorImpl>;
2637 #[no_mangle]
2638 pub extern "C" fn Servo_StyleRule_SetSelectorText(
2639     contents: &StylesheetContents,
2640     rule: &LockedStyleRule,
2641     text: &nsACString,
2642 ) -> bool {
2643     let value_str = unsafe { text.as_str_unchecked() };
2645     write_locked_arc(rule, |rule: &mut StyleRule| {
2646         use selectors::parser::ParseRelative;
2647         use style::selector_parser::SelectorParser;
2649         let namespaces = contents.namespaces.read();
2650         let url_data = contents.url_data.read();
2651         let parser = SelectorParser {
2652             stylesheet_origin: contents.origin,
2653             namespaces: &namespaces,
2654             url_data: &url_data,
2655             for_supports_rule: false,
2656         };
2658         // TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style
2659         // rule?
2660         let mut parser_input = ParserInput::new(&value_str);
2661         match SelectorList::parse(
2662             &parser,
2663             &mut Parser::new(&mut parser_input),
2664             ParseRelative::No,
2665         ) {
2666             Ok(selectors) => {
2667                 rule.selectors = selectors;
2668                 true
2669             },
2670             Err(_) => false,
2671         }
2672     })
2675 #[no_mangle]
2676 pub unsafe extern "C" fn Servo_SelectorList_Closest(
2677     element: &RawGeckoElement,
2678     selectors: &SelectorList,
2679 ) -> *const RawGeckoElement {
2680     use style::dom_apis;
2682     let element = GeckoElement(element);
2683     let quirks_mode = element.as_node().owner_doc().quirks_mode();
2684     dom_apis::element_closest(element, &selectors, quirks_mode).map_or(ptr::null(), |e| e.0)
2687 #[no_mangle]
2688 pub unsafe extern "C" fn Servo_SelectorList_Matches(
2689     element: &RawGeckoElement,
2690     selectors: &SelectorList,
2691 ) -> bool {
2692     use style::dom_apis;
2694     let element = GeckoElement(element);
2695     let quirks_mode = element.as_node().owner_doc().quirks_mode();
2696     dom_apis::element_matches(&element, &selectors, quirks_mode)
2699 #[no_mangle]
2700 pub unsafe extern "C" fn Servo_SelectorList_QueryFirst(
2701     node: &RawGeckoNode,
2702     selectors: &SelectorList,
2703     may_use_invalidation: bool,
2704 ) -> *const RawGeckoElement {
2705     use style::dom_apis::{self, MayUseInvalidation, QueryFirst};
2707     let node = GeckoNode(node);
2708     let mut result = None;
2710     let may_use_invalidation = if may_use_invalidation {
2711         MayUseInvalidation::Yes
2712     } else {
2713         MayUseInvalidation::No
2714     };
2716     dom_apis::query_selector::<GeckoElement, QueryFirst>(
2717         node,
2718         &selectors,
2719         &mut result,
2720         may_use_invalidation,
2721     );
2723     result.map_or(ptr::null(), |e| e.0)
2726 #[no_mangle]
2727 pub unsafe extern "C" fn Servo_SelectorList_QueryAll(
2728     node: &RawGeckoNode,
2729     selectors: &SelectorList,
2730     content_list: *mut structs::nsSimpleContentList,
2731     may_use_invalidation: bool,
2732 ) {
2733     use style::dom_apis::{self, MayUseInvalidation, QueryAll};
2735     let node = GeckoNode(node);
2736     let mut result = SmallVec::new();
2738     let may_use_invalidation = if may_use_invalidation {
2739         MayUseInvalidation::Yes
2740     } else {
2741         MayUseInvalidation::No
2742     };
2744     dom_apis::query_selector::<GeckoElement, QueryAll>(
2745         node,
2746         &selectors,
2747         &mut result,
2748         may_use_invalidation,
2749     );
2751     if !result.is_empty() {
2752         // NOTE(emilio): This relies on a slice of GeckoElement having the same
2753         // memory representation than a slice of element pointers.
2754         bindings::Gecko_ContentList_AppendAll(
2755             content_list,
2756             result.as_ptr() as *mut *const _,
2757             result.len(),
2758         )
2759     }
2762 #[no_mangle]
2763 pub extern "C" fn Servo_ImportRule_GetHref(rule: &LockedImportRule, result: &mut nsAString) {
2764     read_locked_arc(rule, |rule: &ImportRule| {
2765         write!(result, "{}", rule.url.as_str()).unwrap();
2766     })
2769 #[no_mangle]
2770 pub extern "C" fn Servo_ImportRule_GetLayerName(rule: &LockedImportRule, result: &mut nsACString) {
2771     // https://w3c.github.io/csswg-drafts/cssom/#dom-cssimportrule-layername
2772     read_locked_arc(rule, |rule: &ImportRule| match rule.layer {
2773         ImportLayer::Named(ref name) => name.to_css(&mut CssWriter::new(result)).unwrap(), // "return the layer name declared in the at-rule itself"
2774         ImportLayer::Anonymous => {}, // "or an empty string if the layer is anonymous"
2775         ImportLayer::None => result.set_is_void(true), // "or null if the at-rule does not declare a layer"
2776     })
2779 #[no_mangle]
2780 pub extern "C" fn Servo_ImportRule_GetSupportsText(
2781     rule: &LockedImportRule,
2782     result: &mut nsACString,
2783 ) {
2784     read_locked_arc(rule, |rule: &ImportRule| match rule.supports {
2785         Some(ref supports) => supports
2786             .condition
2787             .to_css(&mut CssWriter::new(result))
2788             .unwrap(),
2789         None => result.set_is_void(true),
2790     })
2793 #[no_mangle]
2794 pub extern "C" fn Servo_ImportRule_GetSheet(rule: &LockedImportRule) -> *const DomStyleSheet {
2795     read_locked_arc(rule, |rule: &ImportRule| {
2796         rule.stylesheet
2797             .as_sheet()
2798             .map_or(ptr::null(), |s| s.raw() as *const DomStyleSheet)
2799     })
2802 #[no_mangle]
2803 pub unsafe extern "C" fn Servo_ImportRule_SetSheet(
2804     rule: &LockedImportRule,
2805     sheet: *mut DomStyleSheet,
2806 ) {
2807     write_locked_arc(rule, |rule: &mut ImportRule| {
2808         rule.stylesheet = ImportSheet::new(GeckoStyleSheet::new(sheet));
2809     })
2812 #[no_mangle]
2813 pub extern "C" fn Servo_Keyframe_GetKeyText(keyframe: &LockedKeyframe, result: &mut nsACString) {
2814     read_locked_arc(keyframe, |keyframe: &Keyframe| {
2815         keyframe
2816             .selector
2817             .to_css(&mut CssWriter::new(result))
2818             .unwrap()
2819     })
2822 #[no_mangle]
2823 pub extern "C" fn Servo_Keyframe_SetKeyText(keyframe: &LockedKeyframe, text: &nsACString) -> bool {
2824     let text = unsafe { text.as_str_unchecked() };
2825     let mut input = ParserInput::new(&text);
2826     if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) {
2827         write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
2828             keyframe.selector = selector;
2829         });
2830         true
2831     } else {
2832         false
2833     }
2836 #[no_mangle]
2837 pub extern "C" fn Servo_Keyframe_GetStyle(
2838     keyframe: &LockedKeyframe,
2839 ) -> Strong<LockedDeclarationBlock> {
2840     read_locked_arc(keyframe, |keyframe: &Keyframe| {
2841         keyframe.block.clone().into()
2842     })
2845 #[no_mangle]
2846 pub extern "C" fn Servo_Keyframe_SetStyle(
2847     keyframe: &LockedKeyframe,
2848     declarations: &LockedDeclarationBlock,
2849 ) {
2850     write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
2851         keyframe.block = unsafe { Arc::from_raw_addrefed(declarations) };
2852     })
2855 #[no_mangle]
2856 pub extern "C" fn Servo_KeyframesRule_GetName(rule: &LockedKeyframesRule) -> *mut nsAtom {
2857     read_locked_arc(rule, |rule: &KeyframesRule| rule.name.as_atom().as_ptr())
2860 #[no_mangle]
2861 pub unsafe extern "C" fn Servo_KeyframesRule_SetName(
2862     rule: &LockedKeyframesRule,
2863     name: *mut nsAtom,
2864 ) {
2865     write_locked_arc(rule, |rule: &mut KeyframesRule| {
2866         rule.name = KeyframesName::from_atom(Atom::from_addrefed(name));
2867     })
2870 #[no_mangle]
2871 pub extern "C" fn Servo_KeyframesRule_GetCount(rule: &LockedKeyframesRule) -> u32 {
2872     read_locked_arc(rule, |rule: &KeyframesRule| rule.keyframes.len() as u32)
2875 #[no_mangle]
2876 pub extern "C" fn Servo_KeyframesRule_GetKeyframeAt(
2877     rule: &LockedKeyframesRule,
2878     index: u32,
2879     line: &mut u32,
2880     column: &mut u32,
2881 ) -> Strong<LockedKeyframe> {
2882     let global_style_data = &*GLOBAL_STYLE_DATA;
2883     let guard = global_style_data.shared_lock.read();
2884     let key = rule.read_with(&guard).keyframes[index as usize].clone();
2885     let location = key.read_with(&guard).source_location;
2886     *line = location.line as u32;
2887     *column = location.column as u32;
2888     key.into()
2891 #[no_mangle]
2892 pub extern "C" fn Servo_KeyframesRule_FindRule(
2893     rule: &LockedKeyframesRule,
2894     key: &nsACString,
2895 ) -> u32 {
2896     let key = unsafe { key.as_str_unchecked() };
2897     let global_style_data = &*GLOBAL_STYLE_DATA;
2898     let guard = global_style_data.shared_lock.read();
2899     rule.read_with(&guard)
2900         .find_rule(&guard, key)
2901         .map(|index| index as u32)
2902         .unwrap_or(u32::max_value())
2905 #[no_mangle]
2906 pub extern "C" fn Servo_KeyframesRule_AppendRule(
2907     rule: &LockedKeyframesRule,
2908     contents: &StylesheetContents,
2909     css: &nsACString,
2910 ) -> bool {
2911     let css = unsafe { css.as_str_unchecked() };
2912     let global_style_data = &*GLOBAL_STYLE_DATA;
2914     match Keyframe::parse(css, &contents, &global_style_data.shared_lock) {
2915         Ok(keyframe) => {
2916             write_locked_arc(rule, |rule: &mut KeyframesRule| {
2917                 rule.keyframes.push(keyframe);
2918             });
2919             true
2920         },
2921         Err(..) => false,
2922     }
2925 #[no_mangle]
2926 pub extern "C" fn Servo_KeyframesRule_DeleteRule(rule: &LockedKeyframesRule, index: u32) {
2927     write_locked_arc(rule, |rule: &mut KeyframesRule| {
2928         rule.keyframes.remove(index as usize);
2929     })
2932 #[no_mangle]
2933 pub extern "C" fn Servo_MediaRule_GetMedia(rule: &MediaRule) -> Strong<LockedMediaList> {
2934     rule.media_queries.clone().into()
2937 #[no_mangle]
2938 pub extern "C" fn Servo_NamespaceRule_GetPrefix(rule: &NamespaceRule) -> *mut nsAtom {
2939     rule.prefix
2940         .as_ref()
2941         .map_or(atom!("").as_ptr(), |a| a.as_ptr())
2944 #[no_mangle]
2945 pub extern "C" fn Servo_NamespaceRule_GetURI(rule: &NamespaceRule) -> *mut nsAtom {
2946     rule.url.0.as_ptr()
2949 #[no_mangle]
2950 pub extern "C" fn Servo_MarginRule_GetStyle(rule: &MarginRule) -> Strong<LockedDeclarationBlock> {
2951     rule.block.clone().into()
2954 #[no_mangle]
2955 pub extern "C" fn Servo_MarginRule_GetName(rule: &MarginRule, out: &mut nsACString) {
2956     out.assign(rule.name());
2959 #[no_mangle]
2960 pub extern "C" fn Servo_PageRule_GetStyle(rule: &LockedPageRule) -> Strong<LockedDeclarationBlock> {
2961     read_locked_arc(rule, |rule: &PageRule| rule.block.clone().into())
2964 #[no_mangle]
2965 pub extern "C" fn Servo_PageRule_SetStyle(
2966     rule: &LockedPageRule,
2967     declarations: &LockedDeclarationBlock,
2968 ) {
2969     write_locked_arc(rule, |rule: &mut PageRule| {
2970         rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
2971     })
2974 #[no_mangle]
2975 pub extern "C" fn Servo_PageRule_GetSelectorText(rule: &LockedPageRule, result: &mut nsACString) {
2976     read_locked_arc(rule, |rule: &PageRule| {
2977         rule.selectors.to_css(&mut CssWriter::new(result)).unwrap();
2978     })
2981 #[no_mangle]
2982 pub extern "C" fn Servo_PageRule_SetSelectorText(
2983     contents: &StylesheetContents,
2984     rule: &LockedPageRule,
2985     text: &nsACString,
2986 ) -> bool {
2987     let value_str = unsafe { text.as_str_unchecked() };
2989     write_locked_arc(rule, |rule: &mut PageRule| {
2990         use style::stylesheets::PageSelectors;
2992         let mut parser_input = ParserInput::new(&value_str);
2993         let mut parser = Parser::new(&mut parser_input);
2995         // Ensure that a blank input results in empty page selectors
2996         if parser.is_exhausted() {
2997             rule.selectors = PageSelectors::default();
2998             return true;
2999         }
3001         let url_data = contents.url_data.read();
3002         let context = ParserContext::new(
3003             Origin::Author,
3004             &url_data,
3005             None,
3006             ParsingMode::DEFAULT,
3007             QuirksMode::NoQuirks,
3008             /* namespaces = */ Default::default(),
3009             None,
3010             None,
3011         );
3013         match parser.parse_entirely(|i| PageSelectors::parse(&context, i)) {
3014             Ok(selectors) => {
3015                 rule.selectors = selectors;
3016                 true
3017             },
3018             Err(_) => false,
3019         }
3020     })
3023 #[no_mangle]
3024 pub extern "C" fn Servo_PropertyRule_GetName(rule: &PropertyRule, result: &mut nsACString) {
3025     write!(result, "--{}", rule.name.0).unwrap();
3028 #[no_mangle]
3029 pub extern "C" fn Servo_PropertyRule_GetSyntax(rule: &PropertyRule, result: &mut nsACString) {
3030     if let Some(syntax) = rule.data.syntax.specified_string() {
3031         result.assign(syntax);
3032     } else {
3033         debug_assert!(false, "Rule without specified syntax?");
3034     }
3037 #[no_mangle]
3038 pub extern "C" fn Servo_PropertyRule_GetInherits(rule: &PropertyRule) -> bool {
3039     rule.inherits()
3042 #[no_mangle]
3043 pub extern "C" fn Servo_PropertyRule_GetInitialValue(
3044     rule: &PropertyRule,
3045     result: &mut nsACString,
3046 ) -> bool {
3047     rule.data
3048         .initial_value
3049         .to_css(&mut CssWriter::new(result))
3050         .unwrap();
3051     rule.data.initial_value.is_some()
3054 #[no_mangle]
3055 pub extern "C" fn Servo_SupportsRule_GetConditionText(
3056     rule: &SupportsRule,
3057     result: &mut nsACString,
3058 ) {
3059     rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
3062 #[no_mangle]
3063 pub extern "C" fn Servo_ContainerRule_GetConditionText(
3064     rule: &ContainerRule,
3065     result: &mut nsACString,
3066 ) {
3067     rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
3070 #[no_mangle]
3071 pub extern "C" fn Servo_ContainerRule_GetContainerQuery(
3072     rule: &ContainerRule,
3073     result: &mut nsACString,
3074 ) {
3075     rule.query_condition()
3076         .to_css(&mut CssWriter::new(result))
3077         .unwrap();
3080 #[no_mangle]
3081 pub extern "C" fn Servo_ContainerRule_QueryContainerFor(
3082     rule: &ContainerRule,
3083     element: &RawGeckoElement,
3084 ) -> *const RawGeckoElement {
3085     rule.condition
3086         .find_container(GeckoElement(element), None)
3087         .map_or(ptr::null(), |result| result.element.0)
3090 #[no_mangle]
3091 pub extern "C" fn Servo_ContainerRule_GetContainerName(
3092     rule: &ContainerRule,
3093     result: &mut nsACString,
3094 ) {
3095     let name = rule.container_name();
3096     if !name.is_none() {
3097         name.to_css(&mut CssWriter::new(result)).unwrap();
3098     }
3101 #[no_mangle]
3102 pub extern "C" fn Servo_DocumentRule_GetConditionText(
3103     rule: &DocumentRule,
3104     result: &mut nsACString,
3105 ) {
3106     rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
3109 #[no_mangle]
3110 pub extern "C" fn Servo_FontFeatureValuesRule_GetFontFamily(
3111     rule: &FontFeatureValuesRule,
3112     result: &mut nsACString,
3113 ) {
3114     rule.family_names
3115         .to_css(&mut CssWriter::new(result))
3116         .unwrap();
3119 #[no_mangle]
3120 pub extern "C" fn Servo_FontFeatureValuesRule_GetValueText(
3121     rule: &FontFeatureValuesRule,
3122     result: &mut nsACString,
3123 ) {
3124     rule.value_to_css(&mut CssWriter::new(result)).unwrap();
3127 #[no_mangle]
3128 pub extern "C" fn Servo_FontPaletteValuesRule_GetName(
3129     rule: &FontPaletteValuesRule,
3130     result: &mut nsACString,
3131 ) {
3132     rule.name.to_css(&mut CssWriter::new(result)).unwrap()
3135 #[no_mangle]
3136 pub extern "C" fn Servo_FontPaletteValuesRule_GetFontFamily(
3137     rule: &FontPaletteValuesRule,
3138     result: &mut nsACString,
3139 ) {
3140     if !rule.family_names.is_empty() {
3141         rule.family_names
3142             .to_css(&mut CssWriter::new(result))
3143             .unwrap()
3144     }
3147 #[no_mangle]
3148 pub extern "C" fn Servo_FontPaletteValuesRule_GetBasePalette(
3149     rule: &FontPaletteValuesRule,
3150     result: &mut nsACString,
3151 ) {
3152     rule.base_palette
3153         .to_css(&mut CssWriter::new(result))
3154         .unwrap()
3157 #[no_mangle]
3158 pub extern "C" fn Servo_FontPaletteValuesRule_GetOverrideColors(
3159     rule: &FontPaletteValuesRule,
3160     result: &mut nsACString,
3161 ) {
3162     if !rule.override_colors.is_empty() {
3163         rule.override_colors
3164             .to_css(&mut CssWriter::new(result))
3165             .unwrap()
3166     }
3169 #[no_mangle]
3170 pub extern "C" fn Servo_FontFaceRule_CreateEmpty() -> Strong<LockedFontFaceRule> {
3171     // XXX This is not great. We should split FontFace descriptor data
3172     // from the rule, so that we don't need to create the rule like this
3173     // and the descriptor data itself can be hold in UniquePtr from the
3174     // Gecko side. See bug 1450904.
3175     with_maybe_worker_shared_lock(|lock| {
3176         Arc::new(lock.wrap(FontFaceRule::empty(SourceLocation { line: 0, column: 0 }))).into()
3177     })
3180 #[no_mangle]
3181 pub unsafe extern "C" fn Servo_FontFaceRule_Clone(
3182     rule: &LockedFontFaceRule,
3183 ) -> Strong<LockedFontFaceRule> {
3184     let clone = read_locked_arc_worker(rule, |rule: &FontFaceRule| rule.clone());
3185     with_maybe_worker_shared_lock(|lock| Arc::new(lock.wrap(clone)).into())
3188 #[no_mangle]
3189 pub unsafe extern "C" fn Servo_FontFaceRule_GetSourceLocation(
3190     rule: &LockedFontFaceRule,
3191     line: *mut u32,
3192     column: *mut u32,
3193 ) {
3194     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3195         let location = rule.source_location;
3196         *line.as_mut().unwrap() = location.line as u32;
3197         *column.as_mut().unwrap() = location.column as u32;
3198     });
3201 macro_rules! apply_font_desc_list {
3202     ($apply_macro:ident) => {
3203         $apply_macro! {
3204             valid: [
3205                 eCSSFontDesc_Family => family,
3206                 eCSSFontDesc_Style => style,
3207                 eCSSFontDesc_Weight => weight,
3208                 eCSSFontDesc_Stretch => stretch,
3209                 eCSSFontDesc_Src => sources,
3210                 eCSSFontDesc_UnicodeRange => unicode_range,
3211                 eCSSFontDesc_FontFeatureSettings => feature_settings,
3212                 eCSSFontDesc_FontVariationSettings => variation_settings,
3213                 eCSSFontDesc_FontLanguageOverride => language_override,
3214                 eCSSFontDesc_Display => display,
3215                 eCSSFontDesc_AscentOverride => ascent_override,
3216                 eCSSFontDesc_DescentOverride => descent_override,
3217                 eCSSFontDesc_LineGapOverride => line_gap_override,
3218                 eCSSFontDesc_SizeAdjust => size_adjust,
3219             ]
3220             invalid: [
3221                 eCSSFontDesc_UNKNOWN,
3222                 eCSSFontDesc_COUNT,
3223             ]
3224         }
3225     };
3228 #[no_mangle]
3229 pub unsafe extern "C" fn Servo_FontFaceRule_Length(rule: &LockedFontFaceRule) -> u32 {
3230     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3231         let mut result = 0;
3232         macro_rules! count_values {
3233             (
3234                 valid: [$($v_enum_name:ident => $field:ident,)*]
3235                 invalid: [$($i_enum_name:ident,)*]
3236             ) => {
3237                 $(if rule.$field.is_some() {
3238                     result += 1;
3239                 })*
3240             }
3241         }
3242         apply_font_desc_list!(count_values);
3243         result
3244     })
3247 #[no_mangle]
3248 pub unsafe extern "C" fn Servo_FontFaceRule_IndexGetter(
3249     rule: &LockedFontFaceRule,
3250     index: u32,
3251 ) -> nsCSSFontDesc {
3252     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3253         let mut count = 0;
3254         macro_rules! lookup_index {
3255             (
3256                 valid: [$($v_enum_name:ident => $field:ident,)*]
3257                 invalid: [$($i_enum_name:ident,)*]
3258             ) => {
3259                 $(if rule.$field.is_some() {
3260                     count += 1;
3261                     if count - 1 == index {
3262                         return nsCSSFontDesc::$v_enum_name;
3263                     }
3264                 })*
3265             }
3266         }
3267         apply_font_desc_list!(lookup_index);
3268         return nsCSSFontDesc::eCSSFontDesc_UNKNOWN;
3269     })
3272 #[no_mangle]
3273 pub unsafe extern "C" fn Servo_FontFaceRule_GetDeclCssText(
3274     rule: &LockedFontFaceRule,
3275     result: &mut nsACString,
3276 ) {
3277     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3278         rule.decl_to_css(result).unwrap();
3279     })
3282 macro_rules! simple_font_descriptor_getter_impl {
3283     ($rule:ident, $out:ident, $field:ident, $compute:ident) => {
3284         read_locked_arc_worker($rule, |rule: &FontFaceRule| {
3285             match rule.$field {
3286                 None => return false,
3287                 Some(ref f) => *$out = f.$compute(),
3288             }
3289             true
3290         })
3291     };
3294 #[no_mangle]
3295 pub extern "C" fn Servo_FontFaceRule_GetFontWeight(
3296     rule: &LockedFontFaceRule,
3297     out: &mut font_face::ComputedFontWeightRange,
3298 ) -> bool {
3299     simple_font_descriptor_getter_impl!(rule, out, weight, compute)
3302 #[no_mangle]
3303 pub extern "C" fn Servo_FontFaceRule_GetFontStretch(
3304     rule: &LockedFontFaceRule,
3305     out: &mut font_face::ComputedFontStretchRange,
3306 ) -> bool {
3307     simple_font_descriptor_getter_impl!(rule, out, stretch, compute)
3310 #[no_mangle]
3311 pub extern "C" fn Servo_FontFaceRule_GetFontStyle(
3312     rule: &LockedFontFaceRule,
3313     out: &mut font_face::ComputedFontStyleDescriptor,
3314 ) -> bool {
3315     simple_font_descriptor_getter_impl!(rule, out, style, compute)
3318 #[no_mangle]
3319 pub extern "C" fn Servo_FontFaceRule_GetFontDisplay(
3320     rule: &LockedFontFaceRule,
3321     out: &mut font_face::FontDisplay,
3322 ) -> bool {
3323     simple_font_descriptor_getter_impl!(rule, out, display, clone)
3326 #[no_mangle]
3327 pub extern "C" fn Servo_FontFaceRule_GetFontLanguageOverride(
3328     rule: &LockedFontFaceRule,
3329     out: &mut computed::FontLanguageOverride,
3330 ) -> bool {
3331     simple_font_descriptor_getter_impl!(rule, out, language_override, clone)
3334 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
3335 // rather than an actual percentage value.
3336 #[no_mangle]
3337 pub extern "C" fn Servo_FontFaceRule_GetAscentOverride(
3338     rule: &LockedFontFaceRule,
3339     out: &mut computed::Percentage,
3340 ) -> bool {
3341     simple_font_descriptor_getter_impl!(rule, out, ascent_override, compute)
3344 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
3345 // rather than an actual percentage value.
3346 #[no_mangle]
3347 pub extern "C" fn Servo_FontFaceRule_GetDescentOverride(
3348     rule: &LockedFontFaceRule,
3349     out: &mut computed::Percentage,
3350 ) -> bool {
3351     simple_font_descriptor_getter_impl!(rule, out, descent_override, compute)
3354 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
3355 // rather than an actual percentage value.
3356 #[no_mangle]
3357 pub extern "C" fn Servo_FontFaceRule_GetLineGapOverride(
3358     rule: &LockedFontFaceRule,
3359     out: &mut computed::Percentage,
3360 ) -> bool {
3361     simple_font_descriptor_getter_impl!(rule, out, line_gap_override, compute)
3364 #[no_mangle]
3365 pub extern "C" fn Servo_FontFaceRule_GetSizeAdjust(
3366     rule: &LockedFontFaceRule,
3367     out: &mut computed::Percentage,
3368 ) -> bool {
3369     simple_font_descriptor_getter_impl!(rule, out, size_adjust, compute)
3372 #[no_mangle]
3373 pub unsafe extern "C" fn Servo_FontFaceRule_GetFamilyName(
3374     rule: &LockedFontFaceRule,
3375 ) -> *mut nsAtom {
3376     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3377         // TODO(emilio): font-family is a mandatory descriptor, can't we unwrap
3378         // here, and remove the null-checks in Gecko?
3379         rule.family
3380             .as_ref()
3381             .map_or(ptr::null_mut(), |f| f.name.as_ptr())
3382     })
3385 #[no_mangle]
3386 pub unsafe extern "C" fn Servo_FontFaceRule_GetUnicodeRanges(
3387     rule: &LockedFontFaceRule,
3388     out_len: *mut usize,
3389 ) -> *const UnicodeRange {
3390     *out_len = 0;
3391     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3392         let ranges = match rule.unicode_range {
3393             Some(ref ranges) => ranges,
3394             None => return ptr::null(),
3395         };
3396         *out_len = ranges.len();
3397         ranges.as_ptr() as *const _
3398     })
3401 #[no_mangle]
3402 pub unsafe extern "C" fn Servo_FontFaceRule_GetSources(
3403     rule: &LockedFontFaceRule,
3404     out: *mut nsTArray<FontFaceSourceListComponent>,
3405 ) {
3406     let out = &mut *out;
3407     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3408         let sources = match rule.sources {
3409             Some(ref s) => s,
3410             None => return,
3411         };
3412         let len = sources.0.iter().fold(0, |acc, src| {
3413             acc + match *src {
3414                 Source::Url(ref url) => {
3415                     (if url.format_hint.is_some() { 2 } else { 1 }) +
3416                         (if url.tech_flags.is_empty() { 0 } else { 1 })
3417                 },
3418                 Source::Local(_) => 1,
3419             }
3420         });
3422         out.set_len(len as u32);
3424         let mut iter = out.iter_mut();
3426         {
3427             let mut set_next = |component: FontFaceSourceListComponent| {
3428                 *iter.next().expect("miscalculated length") = component;
3429             };
3431             for source in sources.0.iter() {
3432                 match *source {
3433                     Source::Url(ref url) => {
3434                         set_next(FontFaceSourceListComponent::Url(&url.url));
3435                         if let Some(hint) = &url.format_hint {
3436                             match hint {
3437                                 FontFaceSourceFormat::Keyword(kw) => {
3438                                     set_next(FontFaceSourceListComponent::FormatHintKeyword(*kw))
3439                                 },
3440                                 FontFaceSourceFormat::String(s) => {
3441                                     set_next(FontFaceSourceListComponent::FormatHintString {
3442                                         length: s.len(),
3443                                         utf8_bytes: s.as_ptr(),
3444                                     })
3445                                 },
3446                             }
3447                         }
3448                         if !url.tech_flags.is_empty() {
3449                             set_next(FontFaceSourceListComponent::TechFlags(url.tech_flags));
3450                         }
3451                     },
3452                     Source::Local(ref name) => {
3453                         set_next(FontFaceSourceListComponent::Local(name.name.as_ptr()));
3454                     },
3455                 }
3456             }
3457         }
3459         assert!(iter.next().is_none(), "miscalculated");
3460     })
3463 #[no_mangle]
3464 pub unsafe extern "C" fn Servo_FontFaceRule_GetVariationSettings(
3465     rule: &LockedFontFaceRule,
3466     variations: *mut nsTArray<structs::gfxFontVariation>,
3467 ) {
3468     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3469         let source_variations = match rule.variation_settings {
3470             Some(ref v) => v,
3471             None => return,
3472         };
3474         (*variations).set_len(source_variations.0.len() as u32);
3475         for (target, source) in (*variations).iter_mut().zip(source_variations.0.iter()) {
3476             *target = structs::gfxFontVariation {
3477                 mTag: source.tag.0,
3478                 mValue: source.value.get(),
3479             };
3480         }
3481     });
3484 #[no_mangle]
3485 pub unsafe extern "C" fn Servo_FontFaceRule_GetFeatureSettings(
3486     rule: &LockedFontFaceRule,
3487     features: *mut nsTArray<structs::gfxFontFeature>,
3488 ) {
3489     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3490         let source_features = match rule.feature_settings {
3491             Some(ref v) => v,
3492             None => return,
3493         };
3495         (*features).set_len(source_features.0.len() as u32);
3496         for (target, source) in (*features).iter_mut().zip(source_features.0.iter()) {
3497             *target = structs::gfxFontFeature {
3498                 mTag: source.tag.0,
3499                 mValue: source.value.value() as u32,
3500             };
3501         }
3502     });
3505 #[no_mangle]
3506 pub extern "C" fn Servo_FontFaceRule_GetDescriptorCssText(
3507     rule: &LockedFontFaceRule,
3508     desc: nsCSSFontDesc,
3509     result: &mut nsACString,
3510 ) {
3511     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3512         let mut writer = CssWriter::new(result);
3513         macro_rules! to_css_text {
3514             (
3515                 valid: [$($v_enum_name:ident => $field:ident,)*]
3516                 invalid: [$($i_enum_name:ident,)*]
3517             ) => {
3518                 match desc {
3519                     $(
3520                         nsCSSFontDesc::$v_enum_name => {
3521                             if let Some(ref value) = rule.$field {
3522                                 value.to_css(&mut writer).unwrap();
3523                             }
3524                         }
3525                     )*
3526                     $(
3527                         nsCSSFontDesc::$i_enum_name => {
3528                             debug_assert!(false, "not a valid font descriptor");
3529                         }
3530                     )*
3531                 }
3532             }
3533         }
3534         apply_font_desc_list!(to_css_text)
3535     })
3538 #[no_mangle]
3539 pub unsafe extern "C" fn Servo_FontFaceRule_SetDescriptor(
3540     rule: &LockedFontFaceRule,
3541     desc: nsCSSFontDesc,
3542     value: &nsACString,
3543     data: *mut URLExtraData,
3544     out_changed: *mut bool,
3545 ) -> bool {
3546     let value = value.as_str_unchecked();
3547     let mut input = ParserInput::new(&value);
3548     let mut parser = Parser::new(&mut input);
3549     let url_data = UrlExtraData::from_ptr_ref(&data);
3550     let context = ParserContext::new(
3551         Origin::Author,
3552         url_data,
3553         Some(CssRuleType::FontFace),
3554         ParsingMode::DEFAULT,
3555         QuirksMode::NoQuirks,
3556         /* namespaces = */ Default::default(),
3557         None,
3558         None,
3559     );
3561     write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
3562         macro_rules! to_css_text {
3563             (
3564                 valid: [$($v_enum_name:ident => $field:ident,)*]
3565                 invalid: [$($i_enum_name:ident,)*]
3566             ) => {
3567                 match desc {
3568                     $(
3569                         nsCSSFontDesc::$v_enum_name => {
3570                             if let Ok(value) = parser.parse_entirely(|i| Parse::parse(&context, i)) {
3571                                 let result = Some(value);
3572                                 *out_changed = result != rule.$field;
3573                                 rule.$field = result;
3574                                 true
3575                             } else {
3576                                 false
3577                             }
3578                         }
3579                     )*
3580                     $(
3581                         nsCSSFontDesc::$i_enum_name => {
3582                             debug_assert!(false, "not a valid font descriptor");
3583                             false
3584                         }
3585                     )*
3586                 }
3587             }
3588         }
3589         apply_font_desc_list!(to_css_text)
3590     })
3593 #[no_mangle]
3594 pub unsafe extern "C" fn Servo_FontFaceRule_ResetDescriptor(
3595     rule: &LockedFontFaceRule,
3596     desc: nsCSSFontDesc,
3597 ) {
3598     write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
3599         macro_rules! reset_desc {
3600             (
3601                 valid: [$($v_enum_name:ident => $field:ident,)*]
3602                 invalid: [$($i_enum_name:ident,)*]
3603             ) => {
3604                 match desc {
3605                     $(nsCSSFontDesc::$v_enum_name => rule.$field = None,)*
3606                     $(nsCSSFontDesc::$i_enum_name => debug_assert!(false, "not a valid font descriptor"),)*
3607                 }
3608             }
3609         }
3610         apply_font_desc_list!(reset_desc)
3611     })
3614 #[no_mangle]
3615 pub unsafe extern "C" fn Servo_CounterStyleRule_GetName(
3616     rule: &LockedCounterStyleRule,
3617 ) -> *mut nsAtom {
3618     read_locked_arc(rule, |rule: &CounterStyleRule| rule.name().0.as_ptr())
3621 #[no_mangle]
3622 pub unsafe extern "C" fn Servo_CounterStyleRule_SetName(
3623     rule: &LockedCounterStyleRule,
3624     value: &nsACString,
3625 ) -> bool {
3626     let value = value.as_str_unchecked();
3627     let mut input = ParserInput::new(&value);
3628     let mut parser = Parser::new(&mut input);
3629     match parser.parse_entirely(counter_style::parse_counter_style_name_definition) {
3630         Ok(name) => {
3631             write_locked_arc(rule, |rule: &mut CounterStyleRule| rule.set_name(name));
3632             true
3633         },
3634         Err(_) => false,
3635     }
3638 #[no_mangle]
3639 pub unsafe extern "C" fn Servo_CounterStyleRule_GetGeneration(
3640     rule: &LockedCounterStyleRule,
3641 ) -> u32 {
3642     read_locked_arc(rule, |rule: &CounterStyleRule| rule.generation())
3645 fn symbol_to_string(s: &counter_style::Symbol) -> nsString {
3646     match *s {
3647         counter_style::Symbol::String(ref s) => nsString::from(&**s),
3648         counter_style::Symbol::Ident(ref i) => nsString::from(i.0.as_slice()),
3649     }
3652 // TODO(emilio): Cbindgen could be used to simplify a bunch of code here.
3653 #[no_mangle]
3654 pub unsafe extern "C" fn Servo_CounterStyleRule_GetPad(
3655     rule: &LockedCounterStyleRule,
3656     width: &mut i32,
3657     symbol: &mut nsString,
3658 ) -> bool {
3659     read_locked_arc(rule, |rule: &CounterStyleRule| {
3660         let pad = match rule.pad() {
3661             Some(pad) => pad,
3662             None => return false,
3663         };
3664         *width = pad.0.value();
3665         *symbol = symbol_to_string(&pad.1);
3666         true
3667     })
3670 fn get_symbol(s: Option<&counter_style::Symbol>, out: &mut nsString) -> bool {
3671     let s = match s {
3672         Some(s) => s,
3673         None => return false,
3674     };
3675     *out = symbol_to_string(s);
3676     true
3679 #[no_mangle]
3680 pub unsafe extern "C" fn Servo_CounterStyleRule_GetPrefix(
3681     rule: &LockedCounterStyleRule,
3682     out: &mut nsString,
3683 ) -> bool {
3684     read_locked_arc(rule, |rule: &CounterStyleRule| {
3685         get_symbol(rule.prefix(), out)
3686     })
3689 #[no_mangle]
3690 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSuffix(
3691     rule: &LockedCounterStyleRule,
3692     out: &mut nsString,
3693 ) -> bool {
3694     read_locked_arc(rule, |rule: &CounterStyleRule| {
3695         get_symbol(rule.suffix(), out)
3696     })
3699 #[no_mangle]
3700 pub unsafe extern "C" fn Servo_CounterStyleRule_GetNegative(
3701     rule: &LockedCounterStyleRule,
3702     prefix: &mut nsString,
3703     suffix: &mut nsString,
3704 ) -> bool {
3705     read_locked_arc(rule, |rule: &CounterStyleRule| {
3706         let negative = match rule.negative() {
3707             Some(n) => n,
3708             None => return false,
3709         };
3710         *prefix = symbol_to_string(&negative.0);
3711         *suffix = match negative.1 {
3712             Some(ref s) => symbol_to_string(s),
3713             None => nsString::new(),
3714         };
3715         true
3716     })
3719 #[repr(u8)]
3720 pub enum IsOrdinalInRange {
3721     Auto,
3722     InRange,
3723     NotInRange,
3724     NoOrdinalSpecified,
3727 #[no_mangle]
3728 pub unsafe extern "C" fn Servo_CounterStyleRule_IsInRange(
3729     rule: &LockedCounterStyleRule,
3730     ordinal: i32,
3731 ) -> IsOrdinalInRange {
3732     use style::counter_style::CounterBound;
3733     read_locked_arc(rule, |rule: &CounterStyleRule| {
3734         let range = match rule.range() {
3735             Some(r) => r,
3736             None => return IsOrdinalInRange::NoOrdinalSpecified,
3737         };
3739         if range.0.is_empty() {
3740             return IsOrdinalInRange::Auto;
3741         }
3743         let in_range = range.0.iter().any(|r| {
3744             if let CounterBound::Integer(start) = r.start {
3745                 if start.value() > ordinal {
3746                     return false;
3747                 }
3748             }
3750             if let CounterBound::Integer(end) = r.end {
3751                 if end.value() < ordinal {
3752                     return false;
3753                 }
3754             }
3756             true
3757         });
3759         if in_range {
3760             IsOrdinalInRange::InRange
3761         } else {
3762             IsOrdinalInRange::NotInRange
3763         }
3764     })
3767 #[no_mangle]
3768 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSymbols(
3769     rule: &LockedCounterStyleRule,
3770     symbols: &mut style::OwnedSlice<nsString>,
3771 ) {
3772     read_locked_arc(rule, |rule: &CounterStyleRule| {
3773         *symbols = match rule.symbols() {
3774             Some(s) => s.0.iter().map(symbol_to_string).collect(),
3775             None => style::OwnedSlice::default(),
3776         };
3777     })
3780 #[repr(C)]
3781 pub struct AdditiveSymbol {
3782     pub weight: i32,
3783     pub symbol: nsString,
3786 #[no_mangle]
3787 pub unsafe extern "C" fn Servo_CounterStyleRule_GetAdditiveSymbols(
3788     rule: &LockedCounterStyleRule,
3789     symbols: &mut style::OwnedSlice<AdditiveSymbol>,
3790 ) {
3791     read_locked_arc(rule, |rule: &CounterStyleRule| {
3792         *symbols = match rule.additive_symbols() {
3793             Some(s) => {
3794                 s.0.iter()
3795                     .map(|s| AdditiveSymbol {
3796                         weight: s.weight.value(),
3797                         symbol: symbol_to_string(&s.symbol),
3798                     })
3799                     .collect()
3800             },
3801             None => style::OwnedSlice::default(),
3802         };
3803     })
3806 #[repr(C, u8)]
3807 pub enum CounterSpeakAs {
3808     None,
3809     Auto,
3810     Bullets,
3811     Numbers,
3812     Words,
3813     Ident(*mut nsAtom),
3816 #[no_mangle]
3817 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSpeakAs(
3818     rule: &LockedCounterStyleRule,
3819     out: &mut CounterSpeakAs,
3820 ) {
3821     use style::counter_style::SpeakAs;
3822     *out = read_locked_arc(rule, |rule: &CounterStyleRule| {
3823         let speak_as = match rule.speak_as() {
3824             Some(s) => s,
3825             None => return CounterSpeakAs::None,
3826         };
3827         match *speak_as {
3828             SpeakAs::Auto => CounterSpeakAs::Auto,
3829             SpeakAs::Bullets => CounterSpeakAs::Bullets,
3830             SpeakAs::Numbers => CounterSpeakAs::Numbers,
3831             SpeakAs::Words => CounterSpeakAs::Words,
3832             SpeakAs::Other(ref other) => CounterSpeakAs::Ident(other.0.as_ptr()),
3833         }
3834     });
3837 #[repr(u8)]
3838 pub enum CounterSystem {
3839     Cyclic = 0,
3840     Numeric,
3841     Alphabetic,
3842     Symbolic,
3843     Additive,
3844     Fixed,
3845     Extends,
3848 #[no_mangle]
3849 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSystem(
3850     rule: &LockedCounterStyleRule,
3851 ) -> CounterSystem {
3852     use style::counter_style::System;
3853     read_locked_arc(rule, |rule: &CounterStyleRule| {
3854         match *rule.resolved_system() {
3855             System::Cyclic => CounterSystem::Cyclic,
3856             System::Numeric => CounterSystem::Numeric,
3857             System::Alphabetic => CounterSystem::Alphabetic,
3858             System::Symbolic => CounterSystem::Symbolic,
3859             System::Additive => CounterSystem::Additive,
3860             System::Fixed { .. } => CounterSystem::Fixed,
3861             System::Extends(_) => CounterSystem::Extends,
3862         }
3863     })
3866 #[no_mangle]
3867 pub unsafe extern "C" fn Servo_CounterStyleRule_GetExtended(
3868     rule: &LockedCounterStyleRule,
3869 ) -> *mut nsAtom {
3870     read_locked_arc(rule, |rule: &CounterStyleRule| {
3871         match *rule.resolved_system() {
3872             counter_style::System::Extends(ref name) => name.0.as_ptr(),
3873             _ => {
3874                 debug_assert!(false, "Not extends system");
3875                 ptr::null_mut()
3876             },
3877         }
3878     })
3881 #[no_mangle]
3882 pub unsafe extern "C" fn Servo_CounterStyleRule_GetFixedFirstValue(
3883     rule: &LockedCounterStyleRule,
3884 ) -> i32 {
3885     read_locked_arc(rule, |rule: &CounterStyleRule| {
3886         match *rule.resolved_system() {
3887             counter_style::System::Fixed { first_symbol_value } => {
3888                 first_symbol_value.map_or(1, |v| v.value())
3889             },
3890             _ => {
3891                 debug_assert!(false, "Not fixed system");
3892                 0
3893             },
3894         }
3895     })
3898 #[no_mangle]
3899 pub unsafe extern "C" fn Servo_CounterStyleRule_GetFallback(
3900     rule: &LockedCounterStyleRule,
3901 ) -> *mut nsAtom {
3902     read_locked_arc(rule, |rule: &CounterStyleRule| {
3903         rule.fallback().map_or(ptr::null_mut(), |i| i.0 .0.as_ptr())
3904     })
3907 macro_rules! counter_style_descriptors {
3908     {
3909         valid: [
3910             $($desc:ident => $getter:ident / $setter:ident,)+
3911         ]
3912         invalid: [
3913             $($i_desc:ident,)+
3914         ]
3915     } => {
3916         #[no_mangle]
3917         pub unsafe extern "C" fn Servo_CounterStyleRule_GetDescriptorCssText(
3918             rule: &LockedCounterStyleRule,
3919             desc: nsCSSCounterDesc,
3920             result: &mut nsACString,
3921         ) {
3922             let mut writer = CssWriter::new(result);
3923             read_locked_arc(rule, |rule: &CounterStyleRule| {
3924                 match desc {
3925                     $(nsCSSCounterDesc::$desc => {
3926                         if let Some(value) = rule.$getter() {
3927                             value.to_css(&mut writer).unwrap();
3928                         }
3929                     })+
3930                     $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
3931                 }
3932             });
3933         }
3935         #[no_mangle]
3936         pub unsafe extern "C" fn Servo_CounterStyleRule_SetDescriptor(
3937             rule: &LockedCounterStyleRule,
3938             desc: nsCSSCounterDesc,
3939             value: &nsACString,
3940         ) -> bool {
3941             let value = value.as_str_unchecked();
3942             let mut input = ParserInput::new(&value);
3943             let mut parser = Parser::new(&mut input);
3944             let url_data = dummy_url_data();
3945             let context = ParserContext::new(
3946                 Origin::Author,
3947                 url_data,
3948                 Some(CssRuleType::CounterStyle),
3949                 ParsingMode::DEFAULT,
3950                 QuirksMode::NoQuirks,
3951                 /* namespaces = */ Default::default(),
3952                 None,
3953                 None,
3954             );
3956             write_locked_arc(rule, |rule: &mut CounterStyleRule| {
3957                 match desc {
3958                     $(nsCSSCounterDesc::$desc => {
3959                         match parser.parse_entirely(|i| Parse::parse(&context, i)) {
3960                             Ok(value) => rule.$setter(value),
3961                             Err(_) => false,
3962                         }
3963                     })+
3964                     $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
3965                 }
3966             })
3967         }
3968     }
3971 counter_style_descriptors! {
3972     valid: [
3973         eCSSCounterDesc_System => system / set_system,
3974         eCSSCounterDesc_Symbols => symbols / set_symbols,
3975         eCSSCounterDesc_AdditiveSymbols => additive_symbols / set_additive_symbols,
3976         eCSSCounterDesc_Negative => negative / set_negative,
3977         eCSSCounterDesc_Prefix => prefix / set_prefix,
3978         eCSSCounterDesc_Suffix => suffix / set_suffix,
3979         eCSSCounterDesc_Range => range / set_range,
3980         eCSSCounterDesc_Pad => pad / set_pad,
3981         eCSSCounterDesc_Fallback => fallback / set_fallback,
3982         eCSSCounterDesc_SpeakAs => speak_as / set_speak_as,
3983     ]
3984     invalid: [
3985         eCSSCounterDesc_UNKNOWN,
3986         eCSSCounterDesc_COUNT,
3987     ]
3990 #[no_mangle]
3991 pub unsafe extern "C" fn Servo_ComputedValues_GetForPageContent(
3992     raw_data: &PerDocumentStyleData,
3993     page_name: *const nsAtom,
3994     pseudos: PagePseudoClassFlags,
3995 ) -> Strong<ComputedValues> {
3996     let global_style_data = &*GLOBAL_STYLE_DATA;
3997     let guard = global_style_data.shared_lock.read();
3998     let guards = StylesheetGuards::same(&guard);
3999     let data = raw_data.borrow_mut();
4000     let cascade_data = data.stylist.cascade_data();
4002     let mut extra_declarations = vec![];
4003     let iter = data.stylist.iter_extra_data_origins_rev();
4004     let name = if !page_name.is_null() {
4005         Some(Atom::from_raw(page_name as *mut nsAtom))
4006     } else {
4007         None
4008     };
4009     for (data, origin) in iter {
4010         data.pages.match_and_append_rules(
4011             &mut extra_declarations,
4012             origin,
4013             &guards,
4014             cascade_data,
4015             &name,
4016             pseudos,
4017         );
4018     }
4020     let rule_node = data.stylist.rule_node_for_precomputed_pseudo(
4021         &guards,
4022         &PseudoElement::PageContent,
4023         extra_declarations,
4024     );
4026     data.stylist
4027         .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
4028             &guards,
4029             &PseudoElement::PageContent,
4030             None,
4031             rule_node,
4032         )
4033         .into()
4036 #[no_mangle]
4037 pub unsafe extern "C" fn Servo_ComputedValues_GetForAnonymousBox(
4038     parent_style_or_null: Option<&ComputedValues>,
4039     pseudo: PseudoStyleType,
4040     raw_data: &PerDocumentStyleData,
4041 ) -> Strong<ComputedValues> {
4042     let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
4043     debug_assert!(pseudo.is_anon_box());
4044     debug_assert_ne!(pseudo, PseudoElement::PageContent);
4045     let global_style_data = &*GLOBAL_STYLE_DATA;
4046     let guard = global_style_data.shared_lock.read();
4047     let guards = StylesheetGuards::same(&guard);
4048     let data = raw_data.borrow_mut();
4049     let rule_node = data
4050         .stylist
4051         .rule_node_for_precomputed_pseudo(&guards, &pseudo, vec![]);
4053     data.stylist
4054         .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
4055             &guards,
4056             &pseudo,
4057             parent_style_or_null.map(|x| &*x),
4058             rule_node,
4059         )
4060         .into()
4063 fn get_functional_pseudo_parameter_atom(
4064     functional_pseudo_parameter: *mut nsAtom,
4065 ) -> Option<AtomIdent> {
4066     if functional_pseudo_parameter.is_null() {
4067         None
4068     } else {
4069         Some(AtomIdent::new(unsafe {
4070             Atom::from_raw(functional_pseudo_parameter)
4071         }))
4072     }
4075 #[no_mangle]
4076 pub extern "C" fn Servo_ResolvePseudoStyle(
4077     element: &RawGeckoElement,
4078     pseudo_type: PseudoStyleType,
4079     functional_pseudo_parameter: *mut nsAtom,
4080     is_probe: bool,
4081     inherited_style: Option<&ComputedValues>,
4082     raw_data: &PerDocumentStyleData,
4083 ) -> Strong<ComputedValues> {
4084     let element = GeckoElement(element);
4085     let doc_data = raw_data.borrow();
4087     debug!(
4088         "Servo_ResolvePseudoStyle: {:?} {:?}, is_probe: {}",
4089         element,
4090         PseudoElement::from_pseudo_type(
4091             pseudo_type,
4092             get_functional_pseudo_parameter_atom(functional_pseudo_parameter)
4093         ),
4094         is_probe
4095     );
4097     let data = element.borrow_data();
4099     let data = match data.as_ref() {
4100         Some(data) if data.has_styles() => data,
4101         _ => {
4102             // FIXME(bholley, emilio): Assert against this.
4103             //
4104             // Known offender is nsMathMLmoFrame::MarkIntrinsicISizesDirty,
4105             // which goes and does a bunch of work involving style resolution.
4106             //
4107             // Bug 1403865 tracks fixing it, and potentially adding an assert
4108             // here instead.
4109             warn!("Calling Servo_ResolvePseudoStyle on unstyled element");
4110             return if is_probe {
4111                 Strong::null()
4112             } else {
4113                 doc_data.default_computed_values().clone().into()
4114             };
4115         },
4116     };
4118     let pseudo_element = PseudoElement::from_pseudo_type(
4119         pseudo_type,
4120         get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
4121     )
4122     .expect("ResolvePseudoStyle with a non-pseudo?");
4124     let matching_fn = |pseudo: &PseudoElement| *pseudo == pseudo_element;
4126     let global_style_data = &*GLOBAL_STYLE_DATA;
4127     let guard = global_style_data.shared_lock.read();
4128     let style = get_pseudo_style(
4129         &guard,
4130         element,
4131         &pseudo_element,
4132         RuleInclusion::All,
4133         &data.styles,
4134         inherited_style,
4135         &doc_data.stylist,
4136         is_probe,
4137         /* matching_func = */
4138         if pseudo_element.is_highlight() {
4139             Some(&matching_fn)
4140         } else {
4141             None
4142         },
4143     );
4145     match style {
4146         Some(s) => s.into(),
4147         None => {
4148             debug_assert!(is_probe);
4149             Strong::null()
4150         },
4151     }
4154 fn debug_atom_array(atoms: &nsTArray<structs::RefPtr<nsAtom>>) -> String {
4155     let mut result = String::from("[");
4156     for atom in atoms.iter() {
4157         if atom.mRawPtr.is_null() {
4158             result += "(null), ";
4159         } else {
4160             let atom = unsafe { WeakAtom::new(atom.mRawPtr) };
4161             write!(result, "{}, ", atom).unwrap();
4162         }
4163     }
4164     result.push(']');
4165     result
4168 #[no_mangle]
4169 pub extern "C" fn Servo_ComputedValues_ResolveXULTreePseudoStyle(
4170     element: &RawGeckoElement,
4171     pseudo_tag: *mut nsAtom,
4172     inherited_style: &ComputedValues,
4173     input_word: &nsTArray<structs::RefPtr<nsAtom>>,
4174     raw_data: &PerDocumentStyleData,
4175 ) -> Strong<ComputedValues> {
4176     let element = GeckoElement(element);
4177     let data = element
4178         .borrow_data()
4179         .expect("Calling ResolveXULTreePseudoStyle on unstyled element?");
4181     let pseudo = unsafe {
4182         Atom::with(pseudo_tag, |atom| {
4183             PseudoElement::from_tree_pseudo_atom(atom, Box::new([]))
4184         })
4185         .expect("ResolveXULTreePseudoStyle with a non-tree pseudo?")
4186     };
4187     let doc_data = raw_data.borrow();
4189     debug!(
4190         "ResolveXULTreePseudoStyle: {:?} {:?} {}",
4191         element,
4192         pseudo,
4193         debug_atom_array(input_word)
4194     );
4196     let matching_fn = |pseudo: &PseudoElement| {
4197         let args = pseudo
4198             .tree_pseudo_args()
4199             .expect("Not a tree pseudo-element?");
4200         args.iter()
4201             .all(|atom| input_word.iter().any(|item| atom.as_ptr() == item.mRawPtr))
4202     };
4204     let global_style_data = &*GLOBAL_STYLE_DATA;
4205     let guard = global_style_data.shared_lock.read();
4206     get_pseudo_style(
4207         &guard,
4208         element,
4209         &pseudo,
4210         RuleInclusion::All,
4211         &data.styles,
4212         Some(inherited_style),
4213         &doc_data.stylist,
4214         /* is_probe = */ false,
4215         Some(&matching_fn),
4216     )
4217     .unwrap()
4218     .into()
4221 #[no_mangle]
4222 pub extern "C" fn Servo_SetExplicitStyle(element: &RawGeckoElement, style: &ComputedValues) {
4223     let element = GeckoElement(element);
4224     debug!("Servo_SetExplicitStyle: {:?}", element);
4225     // We only support this API for initial styling. There's no reason it couldn't
4226     // work for other things, we just haven't had a reason to do so.
4227     debug_assert!(!element.has_data());
4228     let mut data = unsafe { element.ensure_data() };
4229     data.styles.primary = Some(unsafe { Arc::from_raw_addrefed(style) });
4232 fn get_pseudo_style(
4233     guard: &SharedRwLockReadGuard,
4234     element: GeckoElement,
4235     pseudo: &PseudoElement,
4236     rule_inclusion: RuleInclusion,
4237     styles: &ElementStyles,
4238     inherited_styles: Option<&ComputedValues>,
4239     stylist: &Stylist,
4240     is_probe: bool,
4241     matching_func: Option<&dyn Fn(&PseudoElement) -> bool>,
4242 ) -> Option<Arc<ComputedValues>> {
4243     let style = match pseudo.cascade_type() {
4244         PseudoElementCascadeType::Eager => {
4245             match *pseudo {
4246                 PseudoElement::FirstLetter => {
4247                     styles.pseudos.get(&pseudo).map(|pseudo_styles| {
4248                         // inherited_styles can be None when doing lazy resolution
4249                         // (e.g. for computed style) or when probing.  In that case
4250                         // we just inherit from our element, which is what Gecko
4251                         // does in that situation.  What should actually happen in
4252                         // the computed style case is a bit unclear.
4253                         let inherited_styles = inherited_styles.unwrap_or(styles.primary());
4254                         let guards = StylesheetGuards::same(guard);
4255                         let inputs = CascadeInputs::new_from_style(pseudo_styles);
4256                         stylist.compute_pseudo_element_style_with_inputs(
4257                             inputs,
4258                             pseudo,
4259                             &guards,
4260                             Some(inherited_styles),
4261                             Some(element),
4262                         )
4263                     })
4264                 },
4265                 _ => {
4266                     // Unfortunately, we can't assert that inherited_styles, if
4267                     // present, is pointer-equal to styles.primary(), or even
4268                     // equal in any meaningful way.  The way it can fail is as
4269                     // follows.  Say we append an element with a ::before,
4270                     // ::after, or ::first-line to a parent with a ::first-line,
4271                     // such that the element ends up on the first line of the
4272                     // parent (e.g. it's an inline-block in the case it has a
4273                     // ::first-line, or any container in the ::before/::after
4274                     // cases).  Then gecko will update its frame's style to
4275                     // inherit from the parent's ::first-line.  The next time we
4276                     // try to get the ::before/::after/::first-line style for
4277                     // the kid, we'll likely pass in the frame's style as
4278                     // inherited_styles, but that's not pointer-identical to
4279                     // styles.primary(), because it got reparented.
4280                     //
4281                     // Now in practice this turns out to be OK, because all the
4282                     // cases in which there's a mismatch go ahead and reparent
4283                     // styles again as needed to make sure the ::first-line
4284                     // affects all the things it should affect.  But it makes it
4285                     // impossible to assert anything about the two styles
4286                     // matching here, unfortunately.
4287                     styles.pseudos.get(&pseudo).cloned()
4288                 },
4289             }
4290         },
4291         PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
4292         PseudoElementCascadeType::Lazy => {
4293             debug_assert!(
4294                 inherited_styles.is_none() ||
4295                     ptr::eq(inherited_styles.unwrap(), &**styles.primary())
4296             );
4297             let originating_element_style = styles.primary();
4298             let guards = StylesheetGuards::same(guard);
4299             stylist.lazily_compute_pseudo_element_style(
4300                 &guards,
4301                 element,
4302                 &pseudo,
4303                 rule_inclusion,
4304                 originating_element_style,
4305                 is_probe,
4306                 matching_func,
4307             )
4308         },
4309     };
4311     if is_probe {
4312         return style;
4313     }
4315     Some(style.unwrap_or_else(|| {
4316         StyleBuilder::for_inheritance(
4317             stylist.device(),
4318             Some(stylist),
4319             Some(styles.primary()),
4320             Some(pseudo),
4321         )
4322         .build()
4323     }))
4326 #[no_mangle]
4327 pub unsafe extern "C" fn Servo_ComputedValues_Inherit(
4328     raw_data: &PerDocumentStyleData,
4329     pseudo: PseudoStyleType,
4330     parent_style_context: Option<&ComputedValues>,
4331     target: structs::InheritTarget,
4332 ) -> Strong<ComputedValues> {
4333     let data = raw_data.borrow();
4335     let for_text = target == structs::InheritTarget::Text;
4336     let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
4337     debug_assert!(pseudo.is_anon_box());
4339     let mut style = StyleBuilder::for_inheritance(
4340         data.stylist.device(),
4341         Some(&data.stylist),
4342         parent_style_context,
4343         Some(&pseudo),
4344     );
4346     if for_text {
4347         StyleAdjuster::new(&mut style).adjust_for_text();
4348     }
4350     style.build().into()
4353 #[no_mangle]
4354 pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(
4355     values: &ComputedValues,
4356 ) -> bool {
4357     let ui = values.get_ui();
4358     ui.specifies_animations() || ui.specifies_transitions()
4361 #[no_mangle]
4362 pub extern "C" fn Servo_ComputedValues_GetStyleRuleList(
4363     values: &ComputedValues,
4364     rules: &mut ThinVec<*const LockedStyleRule>,
4365 ) {
4366     let rule_node = match values.rules {
4367         Some(ref r) => r,
4368         None => return,
4369     };
4371     for node in rule_node.self_and_ancestors() {
4372         let style_rule = match node.style_source().and_then(|x| x.as_rule()) {
4373             Some(rule) => rule,
4374             _ => continue,
4375         };
4377         // For the rules with any important declaration, we insert them into
4378         // rule tree twice, one for normal level and another for important
4379         // level. So, we skip the important one to keep the specificity order of
4380         // rules.
4381         if node.importance().important() {
4382             continue;
4383         }
4385         rules.push(&*style_rule);
4386     }
4389 /// println_stderr!() calls Gecko's printf_stderr(), which, unlike eprintln!(),
4390 /// will funnel output to Android logcat.
4391 #[cfg(feature = "gecko_debug")]
4392 macro_rules! println_stderr {
4393     ($($e:expr),+) => {
4394         {
4395             let mut s = nsCString::new();
4396             write!(s, $($e),+).unwrap();
4397             s.write_char('\n').unwrap();
4398             unsafe { bindings::Gecko_PrintfStderr(&s); }
4399         }
4400     }
4403 #[cfg(feature = "gecko_debug")]
4404 fn dump_properties_and_rules(cv: &ComputedValues, properties: &LonghandIdSet) {
4405     println_stderr!("  Properties:");
4406     for p in properties.iter() {
4407         let mut v = nsCString::new();
4408         cv.computed_or_resolved_value(p, None, &mut v).unwrap();
4409         println_stderr!("    {:?}: {}", p, v);
4410     }
4411     dump_rules(cv);
4414 #[cfg(feature = "gecko_debug")]
4415 fn dump_rules(cv: &ComputedValues) {
4416     println_stderr!("  Rules({:?}):", cv.pseudo());
4417     let global_style_data = &*GLOBAL_STYLE_DATA;
4418     let guard = global_style_data.shared_lock.read();
4419     if let Some(rules) = cv.rules.as_ref() {
4420         for rn in rules.self_and_ancestors() {
4421             if rn.importance().important() {
4422                 continue;
4423             }
4424             if let Some(d) = rn.style_source().and_then(|s| s.as_declarations()) {
4425                 println_stderr!("    [DeclarationBlock: {:?}]", d);
4426             }
4427             if let Some(r) = rn.style_source().and_then(|s| s.as_rule()) {
4428                 let mut s = nsCString::new();
4429                 r.read_with(&guard).to_css(&guard, &mut s).unwrap();
4430                 println_stderr!("    {}", s);
4431             }
4432         }
4433     }
4436 #[cfg(feature = "gecko_debug")]
4437 #[no_mangle]
4438 pub extern "C" fn Servo_ComputedValues_EqualForCachedAnonymousContentStyle(
4439     a: &ComputedValues,
4440     b: &ComputedValues,
4441 ) -> bool {
4442     let mut differing_properties = a.differing_properties(b);
4444     // Ignore any difference in -x-lang, which we can't override in the rules in scrollbars.css,
4445     // but which makes no difference for the anonymous content subtrees we cache style for.
4446     differing_properties.remove(LonghandId::XLang);
4447     // Similarly, -x-lang can influence the font-family fallback we have for the initial
4448     // font-family so remove it as well.
4449     differing_properties.remove(LonghandId::FontFamily);
4450     // We reset font-size to an explicit pixel value, and thus it can get affected by our inherited
4451     // effective zoom. But we don't care about it for the same reason as above.
4452     differing_properties.remove(LonghandId::FontSize);
4454     // Ignore any difference in pref-controlled, inherited properties.  These properties may or may
4455     // not be set by the 'all' declaration in scrollbars.css, depending on whether the pref was
4456     // enabled at the time the UA sheets were parsed.
4457     //
4458     // If you add a new pref-controlled, inherited property, it must be defined with
4459     // `has_effect_on_gecko_scrollbars=False` to declare that different values of this property on
4460     // a <scrollbar> element or its descendant scrollbar part elements should have no effect on
4461     // their rendering and behavior.
4462     //
4463     // If you do need a pref-controlled, inherited property to have an effect on these elements,
4464     // then you will need to add some checks to the
4465     // nsIAnonymousContentCreator::CreateAnonymousContent implementations of nsHTMLScrollFrame and
4466     // nsScrollbarFrame to clear the AnonymousContentKey if a non-initial value is used.
4467     differing_properties.remove_all(&LonghandIdSet::has_no_effect_on_gecko_scrollbars());
4469     if !differing_properties.is_empty() {
4470         println_stderr!("Actual style:");
4471         dump_properties_and_rules(a, &differing_properties);
4472         println_stderr!("Expected style:");
4473         dump_properties_and_rules(b, &differing_properties);
4474     }
4476     differing_properties.is_empty()
4479 #[cfg(feature = "gecko_debug")]
4480 #[no_mangle]
4481 pub extern "C" fn Servo_ComputedValues_DumpMatchedRules(s: &ComputedValues) {
4482     dump_rules(s);
4485 #[no_mangle]
4486 pub extern "C" fn Servo_ComputedValues_BlockifiedDisplay(
4487     style: &ComputedValues,
4488     is_root_element: bool,
4489 ) -> u16 {
4490     let display = style.get_box().clone_display();
4491     let blockified_display = display.equivalent_block_display(is_root_element);
4492     blockified_display.to_u16()
4495 #[no_mangle]
4496 pub extern "C" fn Servo_StyleSet_Init(doc: &structs::Document) -> *mut PerDocumentStyleData {
4497     let data = Box::new(PerDocumentStyleData::new(doc));
4499     // Do this here rather than in Servo_Initialize since we need a document to
4500     // get the default computed values from.
4501     style::properties::generated::gecko::assert_initial_values_match(&data);
4503     Box::into_raw(data) as *mut PerDocumentStyleData
4506 #[no_mangle]
4507 pub unsafe extern "C" fn Servo_StyleSet_Drop(data: *mut PerDocumentStyleData) {
4508     let _ = Box::from_raw(data);
4511 #[no_mangle]
4512 pub extern "C" fn Servo_StyleSet_RebuildCachedData(raw_data: &PerDocumentStyleData) {
4513     let mut data = raw_data.borrow_mut();
4514     data.stylist.device_mut().rebuild_cached_data();
4515     data.undisplayed_style_cache.clear();
4518 #[no_mangle]
4519 pub unsafe extern "C" fn Servo_StyleSet_CompatModeChanged(raw_data: &PerDocumentStyleData) {
4520     let mut data = raw_data.borrow_mut();
4521     let quirks_mode = data.stylist.device().document().mCompatMode;
4522     data.stylist.set_quirks_mode(quirks_mode.into());
4525 fn parse_property_into(
4526     declarations: &mut SourcePropertyDeclaration,
4527     property_id: PropertyId,
4528     value: &nsACString,
4529     origin: Origin,
4530     url_data: &UrlExtraData,
4531     parsing_mode: ParsingMode,
4532     quirks_mode: QuirksMode,
4533     rule_type: CssRuleType,
4534     reporter: Option<&dyn ParseErrorReporter>,
4535 ) -> Result<(), ()> {
4536     let value = unsafe { value.as_str_unchecked() };
4538     if let Some(non_custom) = property_id.non_custom_id() {
4539         if !non_custom.allowed_in_rule(rule_type.into()) {
4540             return Err(());
4541         }
4542     }
4544     parse_one_declaration_into(
4545         declarations,
4546         property_id,
4547         value,
4548         origin,
4549         url_data,
4550         reporter,
4551         parsing_mode,
4552         quirks_mode,
4553         rule_type,
4554     )
4557 #[no_mangle]
4558 pub unsafe extern "C" fn Servo_ParseProperty(
4559     property: &structs::AnimatedPropertyID,
4560     value: &nsACString,
4561     data: *mut URLExtraData,
4562     parsing_mode: ParsingMode,
4563     quirks_mode: nsCompatibility,
4564     loader: *mut Loader,
4565     rule_type: CssRuleType,
4566 ) -> Strong<LockedDeclarationBlock> {
4567     let id = get_property_id_from_animatedpropertyid!(property, Strong::null());
4568     let mut declarations = SourcePropertyDeclaration::default();
4569     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
4570     let data = UrlExtraData::from_ptr_ref(&data);
4571     let result = parse_property_into(
4572         &mut declarations,
4573         id,
4574         value,
4575         Origin::Author,
4576         data,
4577         parsing_mode,
4578         quirks_mode.into(),
4579         rule_type,
4580         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
4581     );
4583     match result {
4584         Ok(()) => {
4585             let global_style_data = &*GLOBAL_STYLE_DATA;
4586             let mut block = PropertyDeclarationBlock::new();
4587             block.extend(declarations.drain(), Importance::Normal);
4588             Arc::new(global_style_data.shared_lock.wrap(block)).into()
4589         },
4590         Err(_) => Strong::null(),
4591     }
4594 #[no_mangle]
4595 pub extern "C" fn Servo_ParseEasing(
4596     easing: &nsACString,
4597     output: &mut ComputedTimingFunction,
4598 ) -> bool {
4599     use style::properties::longhands::transition_timing_function;
4601     let context = ParserContext::new(
4602         Origin::Author,
4603         unsafe { dummy_url_data() },
4604         Some(CssRuleType::Style),
4605         ParsingMode::DEFAULT,
4606         QuirksMode::NoQuirks,
4607         /* namespaces = */ Default::default(),
4608         None,
4609         None,
4610     );
4611     let easing = easing.to_string();
4612     let mut input = ParserInput::new(&easing);
4613     let mut parser = Parser::new(&mut input);
4614     let result =
4615         parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
4616     match result {
4617         Ok(parsed_easing) => {
4618             *output = parsed_easing.to_computed_value_without_context();
4619             true
4620         },
4621         Err(_) => false,
4622     }
4625 #[no_mangle]
4626 pub extern "C" fn Servo_SerializeEasing(easing: &ComputedTimingFunction, output: &mut nsACString) {
4627     easing.to_css(&mut CssWriter::new(output)).unwrap();
4630 #[no_mangle]
4631 pub extern "C" fn Servo_GetProperties_Overriding_Animation(
4632     element: &RawGeckoElement,
4633     list: &nsTArray<nsCSSPropertyID>,
4634     set: &mut structs::nsCSSPropertyIDSet,
4635 ) {
4636     let element = GeckoElement(element);
4637     let element_data = match element.borrow_data() {
4638         Some(data) => data,
4639         None => return,
4640     };
4641     let global_style_data = &*GLOBAL_STYLE_DATA;
4642     let guard = global_style_data.shared_lock.read();
4643     let guards = StylesheetGuards::same(&guard);
4644     let (overridden, custom) = element_data
4645         .styles
4646         .primary()
4647         .rules()
4648         .get_properties_overriding_animations(&guards);
4649     for p in list.iter() {
4650         match NonCustomPropertyId::from_nscsspropertyid(*p) {
4651             Some(property) => {
4652                 if let Some(id) = property.as_longhand() {
4653                     if overridden.contains(id) {
4654                         unsafe { Gecko_AddPropertyToSet(set, *p) };
4655                     }
4656                 }
4657             },
4658             None => {
4659                 if *p == nsCSSPropertyID::eCSSPropertyExtra_variable && custom {
4660                     unsafe { Gecko_AddPropertyToSet(set, *p) };
4661                 }
4662             },
4663         }
4664     }
4667 #[no_mangle]
4668 pub extern "C" fn Servo_MatrixTransform_Operate(
4669     matrix_operator: MatrixTransformOperator,
4670     from: *const structs::Matrix4x4Components,
4671     to: *const structs::Matrix4x4Components,
4672     progress: f64,
4673     output: *mut structs::Matrix4x4Components,
4674 ) {
4675     use self::MatrixTransformOperator::{Accumulate, Interpolate};
4676     use style::values::computed::transform::Matrix3D;
4678     let from = Matrix3D::from(unsafe { from.as_ref() }.expect("not a valid 'from' matrix"));
4679     let to = Matrix3D::from(unsafe { to.as_ref() }.expect("not a valid 'to' matrix"));
4680     let result = match matrix_operator {
4681         Interpolate => from.animate(&to, Procedure::Interpolate { progress }),
4682         Accumulate => from.animate(
4683             &to,
4684             Procedure::Accumulate {
4685                 count: progress as u64,
4686             },
4687         ),
4688     };
4690     let output = unsafe { output.as_mut() }.expect("not a valid 'output' matrix");
4691     if let Ok(result) = result {
4692         *output = result.into();
4693     } else if progress < 0.5 {
4694         *output = from.clone().into();
4695     } else {
4696         *output = to.clone().into();
4697     }
4700 #[no_mangle]
4701 pub unsafe extern "C" fn Servo_ParseStyleAttribute(
4702     data: &nsACString,
4703     raw_extra_data: *mut URLExtraData,
4704     quirks_mode: nsCompatibility,
4705     loader: *mut Loader,
4706     rule_type: CssRuleType,
4707 ) -> Strong<LockedDeclarationBlock> {
4708     let global_style_data = &*GLOBAL_STYLE_DATA;
4709     let value = data.as_str_unchecked();
4710     let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
4711     let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
4712     Arc::new(global_style_data.shared_lock.wrap(parse_style_attribute(
4713         value,
4714         url_data,
4715         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
4716         quirks_mode.into(),
4717         rule_type,
4718     )))
4719     .into()
4722 #[no_mangle]
4723 pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> Strong<LockedDeclarationBlock> {
4724     let global_style_data = &*GLOBAL_STYLE_DATA;
4725     Arc::new(
4726         global_style_data
4727             .shared_lock
4728             .wrap(PropertyDeclarationBlock::new()),
4729     )
4730     .into()
4733 #[no_mangle]
4734 pub extern "C" fn Servo_DeclarationBlock_Clear(declarations: &LockedDeclarationBlock) {
4735     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
4736         decls.clear();
4737     });
4740 #[no_mangle]
4741 pub extern "C" fn Servo_DeclarationBlock_Clone(
4742     declarations: &LockedDeclarationBlock,
4743 ) -> Strong<LockedDeclarationBlock> {
4744     let global_style_data = &*GLOBAL_STYLE_DATA;
4745     let guard = global_style_data.shared_lock.read();
4746     Arc::new(
4747         global_style_data
4748             .shared_lock
4749             .wrap(declarations.read_with(&guard).clone()),
4750     )
4751     .into()
4754 #[no_mangle]
4755 pub extern "C" fn Servo_DeclarationBlock_Equals(
4756     a: &LockedDeclarationBlock,
4757     b: &LockedDeclarationBlock,
4758 ) -> bool {
4759     let global_style_data = &*GLOBAL_STYLE_DATA;
4760     let guard = global_style_data.shared_lock.read();
4761     a.read_with(&guard).declarations() == b.read_with(&guard).declarations()
4764 #[no_mangle]
4765 pub extern "C" fn Servo_DeclarationBlock_GetCssText(
4766     declarations: &LockedDeclarationBlock,
4767     result: &mut nsACString,
4768 ) {
4769     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4770         decls.to_css(result).unwrap()
4771     })
4774 #[no_mangle]
4775 pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue(
4776     decls: &LockedDeclarationBlock,
4777     property_id: &structs::AnimatedPropertyID,
4778     buffer: &mut nsACString,
4779     computed_values: Option<&ComputedValues>,
4780     data: &PerDocumentStyleData,
4781 ) {
4782     let property_id = get_property_id_from_animatedpropertyid!(property_id, ());
4784     let global_style_data = &*GLOBAL_STYLE_DATA;
4785     let guard = global_style_data.shared_lock.read();
4786     let data = data.borrow();
4787     let rv = decls.read_with(&guard).single_value_to_css(
4788         &property_id,
4789         buffer,
4790         computed_values,
4791         &data.stylist,
4792     );
4793     debug_assert!(rv.is_ok());
4796 #[no_mangle]
4797 pub unsafe extern "C" fn Servo_SerializeFontValueForCanvas(
4798     declarations: &LockedDeclarationBlock,
4799     buffer: &mut nsACString,
4800 ) {
4801     use style::properties::shorthands::font;
4802     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4803         let longhands = match font::LonghandsToSerialize::from_iter(decls.declarations().iter()) {
4804             Ok(l) => l,
4805             Err(()) => {
4806                 warn!("Unexpected property!");
4807                 return;
4808             },
4809         };
4811         let rv = longhands.to_css(&mut CssWriter::new(buffer));
4812         debug_assert!(rv.is_ok());
4813     })
4816 #[no_mangle]
4817 pub extern "C" fn Servo_DeclarationBlock_Count(declarations: &LockedDeclarationBlock) -> u32 {
4818     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4819         decls.declarations().len() as u32
4820     })
4823 #[no_mangle]
4824 pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(
4825     declarations: &LockedDeclarationBlock,
4826     index: u32,
4827     result: &mut nsACString,
4828 ) -> bool {
4829     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4830         if let Some(decl) = decls.declarations().get(index as usize) {
4831             result.assign(&decl.id().name());
4832             true
4833         } else {
4834             false
4835         }
4836     })
4839 macro_rules! get_property_id_from_property {
4840     ($property: ident, $ret: expr) => {{
4841         let property = $property.as_str_unchecked();
4842         match PropertyId::parse_enabled_for_all_content(property) {
4843             Ok(property_id) => property_id,
4844             Err(_) => return $ret,
4845         }
4846     }};
4849 unsafe fn get_property_value(
4850     declarations: &LockedDeclarationBlock,
4851     property_id: PropertyId,
4852     value: &mut nsACString,
4853 ) {
4854     // This callsite is hot enough that the lock acquisition shows up in profiles.
4855     // Using an unchecked read here improves our performance by ~10% on the
4856     // microbenchmark in bug 1355599.
4857     read_locked_arc_unchecked(declarations, |decls: &PropertyDeclarationBlock| {
4858         decls.property_value_to_css(&property_id, value).unwrap();
4859     })
4862 #[no_mangle]
4863 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValue(
4864     declarations: &LockedDeclarationBlock,
4865     property: &nsACString,
4866     value: &mut nsACString,
4867 ) {
4868     get_property_value(
4869         declarations,
4870         get_property_id_from_property!(property, ()),
4871         value,
4872     )
4875 #[no_mangle]
4876 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValueById(
4877     declarations: &LockedDeclarationBlock,
4878     property: nsCSSPropertyID,
4879     value: &mut nsACString,
4880 ) {
4881     get_property_value(
4882         declarations,
4883         get_property_id_from_nscsspropertyid!(property, ()),
4884         value,
4885     )
4888 #[no_mangle]
4889 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(
4890     declarations: &LockedDeclarationBlock,
4891     property: &nsACString,
4892 ) -> bool {
4893     let property_id = get_property_id_from_property!(property, false);
4894     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4895         decls.property_priority(&property_id).important()
4896     })
4899 #[inline(always)]
4900 fn set_property_to_declarations(
4901     non_custom_property_id: Option<NonCustomPropertyId>,
4902     block: &LockedDeclarationBlock,
4903     parsed_declarations: &mut SourcePropertyDeclaration,
4904     before_change_closure: DeclarationBlockMutationClosure,
4905     importance: Importance,
4906 ) -> bool {
4907     let mut updates = Default::default();
4908     let will_change = read_locked_arc(block, |decls: &PropertyDeclarationBlock| {
4909         decls.prepare_for_update(&parsed_declarations, importance, &mut updates)
4910     });
4911     if !will_change {
4912         return false;
4913     }
4915     before_change_closure.invoke(non_custom_property_id);
4916     write_locked_arc(block, |decls: &mut PropertyDeclarationBlock| {
4917         decls.update(parsed_declarations.drain(), importance, &mut updates)
4918     });
4919     true
4922 fn set_property(
4923     declarations: &LockedDeclarationBlock,
4924     property_id: PropertyId,
4925     value: &nsACString,
4926     is_important: bool,
4927     data: &UrlExtraData,
4928     parsing_mode: ParsingMode,
4929     quirks_mode: QuirksMode,
4930     loader: *mut Loader,
4931     rule_type: CssRuleType,
4932     before_change_closure: DeclarationBlockMutationClosure,
4933 ) -> bool {
4934     let mut source_declarations = SourcePropertyDeclaration::default();
4935     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data.ptr());
4936     let non_custom_property_id = property_id.non_custom_id();
4937     let result = parse_property_into(
4938         &mut source_declarations,
4939         property_id,
4940         value,
4941         Origin::Author,
4942         data,
4943         parsing_mode,
4944         quirks_mode,
4945         rule_type,
4946         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
4947     );
4949     if result.is_err() {
4950         return false;
4951     }
4953     let importance = if is_important {
4954         Importance::Important
4955     } else {
4956         Importance::Normal
4957     };
4959     set_property_to_declarations(
4960         non_custom_property_id,
4961         declarations,
4962         &mut source_declarations,
4963         before_change_closure,
4964         importance,
4965     )
4968 #[no_mangle]
4969 pub unsafe extern "C" fn Servo_DeclarationBlock_SetProperty(
4970     declarations: &LockedDeclarationBlock,
4971     property: &nsACString,
4972     value: &nsACString,
4973     is_important: bool,
4974     data: *mut URLExtraData,
4975     parsing_mode: ParsingMode,
4976     quirks_mode: nsCompatibility,
4977     loader: *mut Loader,
4978     rule_type: CssRuleType,
4979     before_change_closure: DeclarationBlockMutationClosure,
4980 ) -> bool {
4981     set_property(
4982         declarations,
4983         get_property_id_from_property!(property, false),
4984         value,
4985         is_important,
4986         UrlExtraData::from_ptr_ref(&data),
4987         parsing_mode,
4988         quirks_mode.into(),
4989         loader,
4990         rule_type,
4991         before_change_closure,
4992     )
4995 #[no_mangle]
4996 pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue(
4997     declarations: &LockedDeclarationBlock,
4998     animation_value: &AnimationValue,
4999     before_change_closure: DeclarationBlockMutationClosure,
5000 ) -> bool {
5001     let non_custom_property_id = match animation_value.id() {
5002         PropertyDeclarationId::Longhand(id) => Some(id.into()),
5003         PropertyDeclarationId::Custom(_) => None,
5004     };
5005     let mut source_declarations = SourcePropertyDeclaration::with_one(animation_value.uncompute());
5007     set_property_to_declarations(
5008         non_custom_property_id,
5009         declarations,
5010         &mut source_declarations,
5011         before_change_closure,
5012         Importance::Normal,
5013     )
5016 #[no_mangle]
5017 pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyById(
5018     declarations: &LockedDeclarationBlock,
5019     property: nsCSSPropertyID,
5020     value: &nsACString,
5021     is_important: bool,
5022     data: *mut URLExtraData,
5023     parsing_mode: ParsingMode,
5024     quirks_mode: nsCompatibility,
5025     loader: *mut Loader,
5026     rule_type: CssRuleType,
5027     before_change_closure: DeclarationBlockMutationClosure,
5028 ) -> bool {
5029     set_property(
5030         declarations,
5031         get_property_id_from_nscsspropertyid!(property, false),
5032         value,
5033         is_important,
5034         UrlExtraData::from_ptr_ref(&data),
5035         parsing_mode,
5036         quirks_mode.into(),
5037         loader,
5038         rule_type,
5039         before_change_closure,
5040     )
5043 fn remove_property(
5044     declarations: &LockedDeclarationBlock,
5045     property_id: PropertyId,
5046     before_change_closure: DeclarationBlockMutationClosure,
5047 ) -> bool {
5048     let first_declaration = read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
5049         decls.first_declaration_to_remove(&property_id)
5050     });
5052     let first_declaration = match first_declaration {
5053         Some(i) => i,
5054         None => return false,
5055     };
5057     before_change_closure.invoke(property_id.non_custom_id());
5058     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5059         decls.remove_property(&property_id, first_declaration)
5060     });
5062     true
5065 #[no_mangle]
5066 pub unsafe extern "C" fn Servo_DeclarationBlock_RemoveProperty(
5067     declarations: &LockedDeclarationBlock,
5068     property: &nsACString,
5069     before_change_closure: DeclarationBlockMutationClosure,
5070 ) -> bool {
5071     remove_property(
5072         declarations,
5073         get_property_id_from_property!(property, false),
5074         before_change_closure,
5075     )
5078 #[no_mangle]
5079 pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(
5080     declarations: &LockedDeclarationBlock,
5081     property: nsCSSPropertyID,
5082     before_change_closure: DeclarationBlockMutationClosure,
5083 ) -> bool {
5084     remove_property(
5085         declarations,
5086         get_property_id_from_nscsspropertyid!(property, false),
5087         before_change_closure,
5088     )
5091 #[no_mangle]
5092 pub extern "C" fn Servo_MediaList_Create() -> Strong<LockedMediaList> {
5093     let global_style_data = &*GLOBAL_STYLE_DATA;
5094     Arc::new(global_style_data.shared_lock.wrap(MediaList::empty())).into()
5097 #[no_mangle]
5098 pub extern "C" fn Servo_MediaList_DeepClone(list: &LockedMediaList) -> Strong<LockedMediaList> {
5099     let global_style_data = &*GLOBAL_STYLE_DATA;
5100     read_locked_arc(list, |list: &MediaList| {
5101         Arc::new(global_style_data.shared_lock.wrap(list.clone())).into()
5102     })
5105 #[no_mangle]
5106 pub extern "C" fn Servo_MediaList_Matches(
5107     list: &LockedMediaList,
5108     raw_data: &PerDocumentStyleData,
5109 ) -> bool {
5110     let per_doc_data = raw_data.borrow();
5111     read_locked_arc(list, |list: &MediaList| {
5112         list.evaluate(
5113             per_doc_data.stylist.device(),
5114             per_doc_data.stylist.quirks_mode(),
5115         )
5116     })
5119 #[no_mangle]
5120 pub extern "C" fn Servo_DeclarationBlock_HasCSSWideKeyword(
5121     declarations: &LockedDeclarationBlock,
5122     property: nsCSSPropertyID,
5123 ) -> bool {
5124     let property_id = get_property_id_from_nscsspropertyid!(property, false);
5125     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
5126         decls.has_css_wide_keyword(&property_id)
5127     })
5130 #[no_mangle]
5131 pub extern "C" fn Servo_MediaList_GetText(list: &LockedMediaList, result: &mut nsACString) {
5132     read_locked_arc(list, |list: &MediaList| {
5133         list.to_css(&mut CssWriter::new(result)).unwrap();
5134     })
5137 #[no_mangle]
5138 pub unsafe extern "C" fn Servo_MediaList_SetText(
5139     list: &LockedMediaList,
5140     text: &nsACString,
5141     caller_type: CallerType,
5142 ) {
5143     let text = text.as_str_unchecked();
5145     let mut input = ParserInput::new(&text);
5146     let mut parser = Parser::new(&mut input);
5147     let url_data = dummy_url_data();
5149     // TODO(emilio): If the need for `CallerType` appears in more places,
5150     // consider adding an explicit member in `ParserContext` instead of doing
5151     // this (or adding a dummy "chrome://" url data).
5152     //
5153     // For media query parsing it's effectively the same, so for now...
5154     let origin = match caller_type {
5155         CallerType::System => Origin::UserAgent,
5156         CallerType::NonSystem => Origin::Author,
5157     };
5159     let context = ParserContext::new(
5160         origin,
5161         url_data,
5162         Some(CssRuleType::Media),
5163         ParsingMode::DEFAULT,
5164         QuirksMode::NoQuirks,
5165         /* namespaces = */ Default::default(),
5166         // TODO(emilio): Looks like error reporting could be useful here?
5167         None,
5168         None,
5169     );
5171     write_locked_arc(list, |list: &mut MediaList| {
5172         *list = MediaList::parse(&context, &mut parser);
5173     })
5176 #[no_mangle]
5177 pub extern "C" fn Servo_MediaList_IsViewportDependent(list: &LockedMediaList) -> bool {
5178     read_locked_arc(list, |list: &MediaList| list.is_viewport_dependent())
5181 #[no_mangle]
5182 pub extern "C" fn Servo_MediaList_GetLength(list: &LockedMediaList) -> u32 {
5183     read_locked_arc(list, |list: &MediaList| list.media_queries.len() as u32)
5186 #[no_mangle]
5187 pub extern "C" fn Servo_MediaList_GetMediumAt(
5188     list: &LockedMediaList,
5189     index: u32,
5190     result: &mut nsACString,
5191 ) -> bool {
5192     read_locked_arc(list, |list: &MediaList| {
5193         let media_query = match list.media_queries.get(index as usize) {
5194             Some(mq) => mq,
5195             None => return false,
5196         };
5197         media_query.to_css(&mut CssWriter::new(result)).unwrap();
5198         true
5199     })
5202 #[no_mangle]
5203 pub extern "C" fn Servo_MediaList_AppendMedium(list: &LockedMediaList, new_medium: &nsACString) {
5204     let new_medium = unsafe { new_medium.as_str_unchecked() };
5205     let url_data = unsafe { dummy_url_data() };
5206     let context = ParserContext::new(
5207         Origin::Author,
5208         url_data,
5209         Some(CssRuleType::Media),
5210         ParsingMode::DEFAULT,
5211         QuirksMode::NoQuirks,
5212         /* namespaces = */ Default::default(),
5213         None,
5214         None,
5215     );
5216     write_locked_arc(list, |list: &mut MediaList| {
5217         list.append_medium(&context, new_medium);
5218     })
5221 #[no_mangle]
5222 pub extern "C" fn Servo_MediaList_DeleteMedium(
5223     list: &LockedMediaList,
5224     old_medium: &nsACString,
5225 ) -> bool {
5226     let old_medium = unsafe { old_medium.as_str_unchecked() };
5227     let url_data = unsafe { dummy_url_data() };
5228     let context = ParserContext::new(
5229         Origin::Author,
5230         url_data,
5231         Some(CssRuleType::Media),
5232         ParsingMode::DEFAULT,
5233         QuirksMode::NoQuirks,
5234         /* namespaces = */ Default::default(),
5235         None,
5236         None,
5237     );
5238     write_locked_arc(list, |list: &mut MediaList| {
5239         list.delete_medium(&context, old_medium)
5240     })
5243 #[no_mangle]
5244 pub extern "C" fn Servo_MediaList_SizeOfIncludingThis(
5245     malloc_size_of: GeckoMallocSizeOf,
5246     malloc_enclosing_size_of: GeckoMallocSizeOf,
5247     list: &LockedMediaList,
5248 ) -> usize {
5249     use malloc_size_of::MallocSizeOf;
5250     use malloc_size_of::MallocUnconditionalShallowSizeOf;
5252     let global_style_data = &*GLOBAL_STYLE_DATA;
5253     let guard = global_style_data.shared_lock.read();
5255     let mut ops = MallocSizeOfOps::new(
5256         malloc_size_of.unwrap(),
5257         Some(malloc_enclosing_size_of.unwrap()),
5258         None,
5259     );
5261     unsafe { ArcBorrow::from_ref(list) }.with_arc(|list| {
5262         let mut n = 0;
5263         n += list.unconditional_shallow_size_of(&mut ops);
5264         n += list.read_with(&guard).size_of(&mut ops);
5265         n
5266     })
5269 macro_rules! get_longhand_from_id {
5270     ($id:expr) => {
5271         match LonghandId::from_nscsspropertyid($id) {
5272             Some(lh) => lh,
5273             _ => panic!("stylo: unknown presentation property with id"),
5274         }
5275     };
5278 macro_rules! match_wrap_declared {
5279     ($longhand:ident, $($property:ident => $inner:expr,)*) => (
5280         match $longhand {
5281             $(
5282                 LonghandId::$property => PropertyDeclaration::$property($inner),
5283             )*
5284             _ => {
5285                 panic!("stylo: Don't know how to handle presentation property");
5286             }
5287         }
5288     )
5291 #[no_mangle]
5292 pub extern "C" fn Servo_DeclarationBlock_PropertyIsSet(
5293     declarations: &LockedDeclarationBlock,
5294     property: nsCSSPropertyID,
5295 ) -> bool {
5296     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
5297         decls.contains(PropertyDeclarationId::Longhand(get_longhand_from_id!(
5298             property
5299         )))
5300     })
5303 #[no_mangle]
5304 pub unsafe extern "C" fn Servo_DeclarationBlock_SetIdentStringValue(
5305     declarations: &LockedDeclarationBlock,
5306     property: nsCSSPropertyID,
5307     value: *mut nsAtom,
5308 ) {
5309     use style::properties::longhands::_x_lang::computed_value::T as Lang;
5310     use style::properties::PropertyDeclaration;
5312     let long = get_longhand_from_id!(property);
5313     let prop = match_wrap_declared! { long,
5314         XLang => Lang(Atom::from_raw(value)),
5315     };
5316     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5317         decls.push(prop, Importance::Normal);
5318     })
5321 #[no_mangle]
5322 #[allow(unreachable_code)]
5323 pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(
5324     declarations: &LockedDeclarationBlock,
5325     property: nsCSSPropertyID,
5326     value: i32,
5327 ) {
5328     use num_traits::FromPrimitive;
5329     use style::properties::longhands;
5330     use style::properties::PropertyDeclaration;
5331     use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword};
5332     use style::values::generics::font::FontStyle;
5333     use style::values::specified::{
5334         table::CaptionSide, BorderStyle, Clear, Display, Float, TextAlign, TextEmphasisPosition,
5335         TextTransform,
5336     };
5338     fn get_from_computed<T>(value: u32) -> T
5339     where
5340         T: ToComputedValue,
5341         T::ComputedValue: FromPrimitive,
5342     {
5343         T::from_computed_value(&T::ComputedValue::from_u32(value).unwrap())
5344     }
5346     let long = get_longhand_from_id!(property);
5347     let value = value as u32;
5349     let prop = match_wrap_declared! { long,
5350         MozUserModify => longhands::_moz_user_modify::SpecifiedValue::from_gecko_keyword(value),
5351         Direction => get_from_computed::<longhands::direction::SpecifiedValue>(value),
5352         Display => get_from_computed::<Display>(value),
5353         Float => get_from_computed::<Float>(value),
5354         Clear => get_from_computed::<Clear>(value),
5355         VerticalAlign => VerticalAlign::Keyword(VerticalAlignKeyword::from_u32(value).unwrap()),
5356         TextAlign => get_from_computed::<TextAlign>(value),
5357         TextEmphasisPosition => TextEmphasisPosition::from_bits_retain(value as u8),
5358         FontSize => {
5359             // We rely on Gecko passing in font-size values (0...7) here.
5360             longhands::font_size::SpecifiedValue::from_html_size(value as u8)
5361         },
5362         FontStyle => {
5363             style::values::specified::FontStyle::Specified(if value == structs::NS_FONT_STYLE_ITALIC {
5364                 FontStyle::Italic
5365             } else {
5366                 debug_assert_eq!(value, structs::NS_FONT_STYLE_NORMAL);
5367                 FontStyle::Normal
5368             })
5369         },
5370         FontWeight => longhands::font_weight::SpecifiedValue::from_gecko_keyword(value),
5371         ListStyleType => Box::new(longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value)),
5372         MathStyle => longhands::math_style::SpecifiedValue::from_gecko_keyword(value),
5373         MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value),
5374         WhiteSpaceCollapse => get_from_computed::<longhands::white_space_collapse::SpecifiedValue>(value),
5375         TextWrapMode => get_from_computed::<longhands::text_wrap_mode::SpecifiedValue>(value),
5376         CaptionSide => get_from_computed::<CaptionSide>(value),
5377         BorderTopStyle => get_from_computed::<BorderStyle>(value),
5378         BorderRightStyle => get_from_computed::<BorderStyle>(value),
5379         BorderBottomStyle => get_from_computed::<BorderStyle>(value),
5380         BorderLeftStyle => get_from_computed::<BorderStyle>(value),
5381         TextTransform => {
5382             debug_assert_eq!(value, structs::StyleTextTransformCase_None as u32);
5383             TextTransform::none()
5384         },
5385     };
5386     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5387         decls.push(prop, Importance::Normal);
5388     })
5391 #[no_mangle]
5392 pub extern "C" fn Servo_DeclarationBlock_SetIntValue(
5393     declarations: &LockedDeclarationBlock,
5394     property: nsCSSPropertyID,
5395     value: i32,
5396 ) {
5397     use style::properties::PropertyDeclaration;
5398     use style::values::specified::Integer;
5400     let long = get_longhand_from_id!(property);
5401     let prop = match_wrap_declared! { long,
5402         XSpan => Integer::new(value),
5403     };
5404     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5405         decls.push(prop, Importance::Normal);
5406     })
5409 #[no_mangle]
5410 pub extern "C" fn Servo_DeclarationBlock_SetMathDepthValue(
5411     declarations: &LockedDeclarationBlock,
5412     value: i32,
5413     is_relative: bool,
5414 ) {
5415     use style::properties::longhands::math_depth::SpecifiedValue as MathDepth;
5416     use style::properties::PropertyDeclaration;
5418     let integer_value = style::values::specified::Integer::new(value);
5419     let prop = PropertyDeclaration::MathDepth(if is_relative {
5420         MathDepth::Add(integer_value)
5421     } else {
5422         MathDepth::Absolute(integer_value)
5423     });
5424     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5425         decls.push(prop, Importance::Normal);
5426     })
5429 #[no_mangle]
5430 pub extern "C" fn Servo_DeclarationBlock_SetCounterResetListItem(
5431     declarations: &LockedDeclarationBlock,
5432     counter_value: i32,
5433     is_reversed: bool,
5434 ) {
5435     use style::properties::PropertyDeclaration;
5436     use style::values::generics::counters::{CounterPair, CounterReset};
5438     let prop = PropertyDeclaration::CounterReset(CounterReset::new(vec![CounterPair {
5439         name: CustomIdent(atom!("list-item")),
5440         value: style::values::specified::Integer::new(counter_value),
5441         is_reversed,
5442     }]));
5443     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5444         decls.push(prop, Importance::Normal);
5445     })
5448 #[no_mangle]
5449 pub extern "C" fn Servo_DeclarationBlock_SetCounterSetListItem(
5450     declarations: &LockedDeclarationBlock,
5451     counter_value: i32,
5452 ) {
5453     use style::properties::PropertyDeclaration;
5454     use style::values::generics::counters::{CounterPair, CounterSet};
5456     let prop = PropertyDeclaration::CounterSet(CounterSet::new(vec![CounterPair {
5457         name: CustomIdent(atom!("list-item")),
5458         value: style::values::specified::Integer::new(counter_value),
5459         is_reversed: false,
5460     }]));
5461     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5462         decls.push(prop, Importance::Normal);
5463     })
5466 #[no_mangle]
5467 pub extern "C" fn Servo_DeclarationBlock_SetPixelValue(
5468     declarations: &LockedDeclarationBlock,
5469     property: nsCSSPropertyID,
5470     value: f32,
5471 ) {
5472     use style::properties::longhands::border_spacing::SpecifiedValue as BorderSpacing;
5473     use style::properties::PropertyDeclaration;
5474     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5475     use style::values::generics::NonNegative;
5476     use style::values::specified::length::{
5477         LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
5478     };
5479     use style::values::specified::{BorderCornerRadius, BorderSideWidth};
5481     let long = get_longhand_from_id!(property);
5482     let nocalc = NoCalcLength::from_px(value);
5483     let lp = LengthPercentage::Length(nocalc);
5484     let lp_or_auto = LengthPercentageOrAuto::LengthPercentage(lp.clone());
5485     let prop = match_wrap_declared! { long,
5486         Height => Size::LengthPercentage(NonNegative(lp)),
5487         Width => Size::LengthPercentage(NonNegative(lp)),
5488         BorderTopWidth => BorderSideWidth::from_px(value),
5489         BorderRightWidth => BorderSideWidth::from_px(value),
5490         BorderBottomWidth => BorderSideWidth::from_px(value),
5491         BorderLeftWidth => BorderSideWidth::from_px(value),
5492         MarginTop => lp_or_auto,
5493         MarginRight => lp_or_auto,
5494         MarginBottom => lp_or_auto,
5495         MarginLeft => lp_or_auto,
5496         PaddingTop => NonNegative(lp),
5497         PaddingRight => NonNegative(lp),
5498         PaddingBottom => NonNegative(lp),
5499         PaddingLeft => NonNegative(lp),
5500         BorderSpacing => {
5501             let v = NonNegativeLength::from(nocalc);
5502             Box::new(BorderSpacing::new(v.clone(), v))
5503         },
5504         BorderTopLeftRadius => {
5505             let length = NonNegativeLengthPercentage::from(nocalc);
5506             Box::new(BorderCornerRadius::new(length.clone(), length))
5507         },
5508         BorderTopRightRadius => {
5509             let length = NonNegativeLengthPercentage::from(nocalc);
5510             Box::new(BorderCornerRadius::new(length.clone(), length))
5511         },
5512         BorderBottomLeftRadius => {
5513             let length = NonNegativeLengthPercentage::from(nocalc);
5514             Box::new(BorderCornerRadius::new(length.clone(), length))
5515         },
5516         BorderBottomRightRadius => {
5517             let length = NonNegativeLengthPercentage::from(nocalc);
5518             Box::new(BorderCornerRadius::new(length.clone(), length))
5519         },
5520     };
5521     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5522         decls.push(prop, Importance::Normal);
5523     })
5526 #[no_mangle]
5527 pub extern "C" fn Servo_DeclarationBlock_SetLengthValue(
5528     declarations: &LockedDeclarationBlock,
5529     property: nsCSSPropertyID,
5530     value: f32,
5531     unit: structs::nsCSSUnit,
5532 ) {
5533     use style::properties::PropertyDeclaration;
5534     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5535     use style::values::generics::NonNegative;
5536     use style::values::specified::length::{
5537         FontRelativeLength, LengthPercentage, ViewportPercentageLength,
5538     };
5539     use style::values::specified::FontSize;
5541     let long = get_longhand_from_id!(property);
5542     let nocalc = match unit {
5543         structs::nsCSSUnit::eCSSUnit_EM => {
5544             NoCalcLength::FontRelative(FontRelativeLength::Em(value))
5545         },
5546         structs::nsCSSUnit::eCSSUnit_XHeight => {
5547             NoCalcLength::FontRelative(FontRelativeLength::Ex(value))
5548         },
5549         structs::nsCSSUnit::eCSSUnit_RootEM => {
5550             NoCalcLength::FontRelative(FontRelativeLength::Rem(value))
5551         },
5552         structs::nsCSSUnit::eCSSUnit_Char => {
5553             NoCalcLength::FontRelative(FontRelativeLength::Ch(value))
5554         },
5555         structs::nsCSSUnit::eCSSUnit_Ideographic => {
5556             NoCalcLength::FontRelative(FontRelativeLength::Ic(value))
5557         },
5558         structs::nsCSSUnit::eCSSUnit_CapHeight => {
5559             NoCalcLength::FontRelative(FontRelativeLength::Cap(value))
5560         },
5561         structs::nsCSSUnit::eCSSUnit_Pixel => NoCalcLength::Absolute(AbsoluteLength::Px(value)),
5562         structs::nsCSSUnit::eCSSUnit_Inch => NoCalcLength::Absolute(AbsoluteLength::In(value)),
5563         structs::nsCSSUnit::eCSSUnit_Centimeter => {
5564             NoCalcLength::Absolute(AbsoluteLength::Cm(value))
5565         },
5566         structs::nsCSSUnit::eCSSUnit_Millimeter => {
5567             NoCalcLength::Absolute(AbsoluteLength::Mm(value))
5568         },
5569         structs::nsCSSUnit::eCSSUnit_Point => NoCalcLength::Absolute(AbsoluteLength::Pt(value)),
5570         structs::nsCSSUnit::eCSSUnit_Pica => NoCalcLength::Absolute(AbsoluteLength::Pc(value)),
5571         structs::nsCSSUnit::eCSSUnit_Quarter => NoCalcLength::Absolute(AbsoluteLength::Q(value)),
5572         structs::nsCSSUnit::eCSSUnit_VW => {
5573             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value))
5574         },
5575         structs::nsCSSUnit::eCSSUnit_VH => {
5576             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value))
5577         },
5578         structs::nsCSSUnit::eCSSUnit_VMin => {
5579             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value))
5580         },
5581         structs::nsCSSUnit::eCSSUnit_VMax => {
5582             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value))
5583         },
5584         _ => unreachable!("Unknown unit passed to SetLengthValue"),
5585     };
5587     let prop = match_wrap_declared! { long,
5588         Width => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5589         Height => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5590         X =>  LengthPercentage::Length(nocalc),
5591         Y =>  LengthPercentage::Length(nocalc),
5592         Cx => LengthPercentage::Length(nocalc),
5593         Cy => LengthPercentage::Length(nocalc),
5594         R =>  NonNegative(LengthPercentage::Length(nocalc)),
5595         Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5596         Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5597         FontSize => FontSize::Length(LengthPercentage::Length(nocalc)),
5598     };
5599     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5600         decls.push(prop, Importance::Normal);
5601     })
5604 #[no_mangle]
5605 pub extern "C" fn Servo_DeclarationBlock_SetPathValue(
5606     declarations: &LockedDeclarationBlock,
5607     property: nsCSSPropertyID,
5608     path: &nsTArray<f32>,
5609 ) {
5610     use style::properties::PropertyDeclaration;
5611     use style::values::specified::DProperty;
5613     // 1. Decode the path data from SVG.
5614     let path = match specified::SVGPathData::decode_from_f32_array(path) {
5615         Ok(p) => p,
5616         Err(()) => return,
5617     };
5619     // 2. Set decoded path into style.
5620     let long = get_longhand_from_id!(property);
5621     let prop = match_wrap_declared! { long,
5622         D => if path.0.is_empty() { DProperty::None } else { DProperty::Path(path) },
5623     };
5624     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5625         decls.push(prop, Importance::Normal);
5626     })
5629 #[no_mangle]
5630 pub extern "C" fn Servo_DeclarationBlock_SetPercentValue(
5631     declarations: &LockedDeclarationBlock,
5632     property: nsCSSPropertyID,
5633     value: f32,
5634 ) {
5635     use style::properties::PropertyDeclaration;
5636     use style::values::computed::Percentage;
5637     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5638     use style::values::generics::NonNegative;
5639     use style::values::specified::length::LengthPercentage;
5640     use style::values::specified::FontSize;
5642     let long = get_longhand_from_id!(property);
5643     let pc = Percentage(value);
5644     let lp = LengthPercentage::Percentage(pc);
5645     let lp_or_auto = LengthPercentageOrAuto::LengthPercentage(lp.clone());
5647     let prop = match_wrap_declared! { long,
5648         Height => Size::LengthPercentage(NonNegative(lp)),
5649         Width => Size::LengthPercentage(NonNegative(lp)),
5650         X =>  lp,
5651         Y =>  lp,
5652         Cx => lp,
5653         Cy => lp,
5654         R =>  NonNegative(lp),
5655         Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
5656         Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
5657         MarginTop => lp_or_auto,
5658         MarginRight => lp_or_auto,
5659         MarginBottom => lp_or_auto,
5660         MarginLeft => lp_or_auto,
5661         FontSize => FontSize::Length(LengthPercentage::Percentage(pc)),
5662     };
5663     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5664         decls.push(prop, Importance::Normal);
5665     })
5668 #[no_mangle]
5669 pub extern "C" fn Servo_DeclarationBlock_SetAutoValue(
5670     declarations: &LockedDeclarationBlock,
5671     property: nsCSSPropertyID,
5672 ) {
5673     use style::properties::PropertyDeclaration;
5674     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5676     let long = get_longhand_from_id!(property);
5677     let auto = LengthPercentageOrAuto::Auto;
5679     let prop = match_wrap_declared! { long,
5680         Height => Size::auto(),
5681         Width => Size::auto(),
5682         MarginTop => auto,
5683         MarginRight => auto,
5684         MarginBottom => auto,
5685         MarginLeft => auto,
5686         AspectRatio => specified::AspectRatio::auto(),
5687     };
5688     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5689         decls.push(prop, Importance::Normal);
5690     })
5693 #[no_mangle]
5694 pub extern "C" fn Servo_DeclarationBlock_SetCurrentColor(
5695     declarations: &LockedDeclarationBlock,
5696     property: nsCSSPropertyID,
5697 ) {
5698     use style::properties::PropertyDeclaration;
5699     use style::values::specified::Color;
5701     let long = get_longhand_from_id!(property);
5702     let cc = Color::currentcolor();
5704     let prop = match_wrap_declared! { long,
5705         BorderTopColor => cc,
5706         BorderRightColor => cc,
5707         BorderBottomColor => cc,
5708         BorderLeftColor => cc,
5709     };
5710     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5711         decls.push(prop, Importance::Normal);
5712     })
5715 #[no_mangle]
5716 pub extern "C" fn Servo_DeclarationBlock_SetColorValue(
5717     declarations: &LockedDeclarationBlock,
5718     property: nsCSSPropertyID,
5719     value: structs::nscolor,
5720 ) {
5721     use style::gecko::values::convert_nscolor_to_absolute_color;
5722     use style::properties::longhands;
5723     use style::properties::PropertyDeclaration;
5724     use style::values::specified::Color;
5726     let long = get_longhand_from_id!(property);
5727     let rgba = convert_nscolor_to_absolute_color(value);
5728     let color = Color::from_absolute_color(rgba);
5730     let prop = match_wrap_declared! { long,
5731         BorderTopColor => color,
5732         BorderRightColor => color,
5733         BorderBottomColor => color,
5734         BorderLeftColor => color,
5735         Color => longhands::color::SpecifiedValue(color),
5736         BackgroundColor => color,
5737     };
5738     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5739         decls.push(prop, Importance::Normal);
5740     })
5743 #[no_mangle]
5744 pub unsafe extern "C" fn Servo_DeclarationBlock_SetFontFamily(
5745     declarations: &LockedDeclarationBlock,
5746     value: &nsACString,
5747 ) {
5748     use style::properties::longhands::font_family::SpecifiedValue as FontFamily;
5749     use style::properties::PropertyDeclaration;
5751     let string = value.as_str_unchecked();
5752     let mut input = ParserInput::new(&string);
5753     let mut parser = Parser::new(&mut input);
5754     let context = ParserContext::new(
5755         Origin::Author,
5756         dummy_url_data(),
5757         Some(CssRuleType::Style),
5758         ParsingMode::DEFAULT,
5759         QuirksMode::NoQuirks,
5760         /* namespaces = */ Default::default(),
5761         None,
5762         None,
5763     );
5764     let result = FontFamily::parse(&context, &mut parser);
5765     if let Ok(family) = result {
5766         if parser.is_exhausted() {
5767             let decl = PropertyDeclaration::FontFamily(family);
5768             write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5769                 decls.push(decl, Importance::Normal);
5770             })
5771         }
5772     }
5775 #[no_mangle]
5776 pub unsafe extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(
5777     declarations: &LockedDeclarationBlock,
5778     value: &nsACString,
5779     raw_extra_data: *mut URLExtraData,
5780 ) {
5781     use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage;
5782     use style::properties::PropertyDeclaration;
5783     use style::stylesheets::CorsMode;
5784     use style::values::generics::image::Image;
5785     use style::values::specified::url::SpecifiedImageUrl;
5787     let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
5788     let string = value.as_str_unchecked();
5789     let context = ParserContext::new(
5790         Origin::Author,
5791         url_data,
5792         Some(CssRuleType::Style),
5793         ParsingMode::DEFAULT,
5794         QuirksMode::NoQuirks,
5795         /* namespaces = */ Default::default(),
5796         None,
5797         None,
5798     );
5799     let url = SpecifiedImageUrl::parse_from_string(string.into(), &context, CorsMode::None);
5800     let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(vec![Image::Url(url)].into()));
5801     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5802         decls.push(decl, Importance::Normal);
5803     });
5806 #[no_mangle]
5807 pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(
5808     declarations: &LockedDeclarationBlock,
5809 ) {
5810     use style::properties::PropertyDeclaration;
5811     use style::values::specified::text::TextDecorationLine;
5813     let decoration = TextDecorationLine::COLOR_OVERRIDE;
5814     let decl = PropertyDeclaration::TextDecorationLine(decoration);
5815     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5816         decls.push(decl, Importance::Normal);
5817     })
5820 #[no_mangle]
5821 pub extern "C" fn Servo_DeclarationBlock_SetAspectRatio(
5822     declarations: &LockedDeclarationBlock,
5823     width: f32,
5824     height: f32,
5825 ) {
5826     use style::properties::PropertyDeclaration;
5827     use style::values::generics::position::AspectRatio;
5829     let decl = PropertyDeclaration::AspectRatio(AspectRatio::from_mapped_ratio(width, height));
5830     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5831         decls.push(decl, Importance::Normal);
5832     })
5835 #[no_mangle]
5836 pub extern "C" fn Servo_CSSSupports2(property: &nsACString, value: &nsACString) -> bool {
5837     let id = unsafe { get_property_id_from_property!(property, false) };
5839     let mut declarations = SourcePropertyDeclaration::default();
5840     parse_property_into(
5841         &mut declarations,
5842         id,
5843         value,
5844         Origin::Author,
5845         unsafe { dummy_url_data() },
5846         ParsingMode::DEFAULT,
5847         QuirksMode::NoQuirks,
5848         CssRuleType::Style,
5849         None,
5850     )
5851     .is_ok()
5854 #[no_mangle]
5855 pub extern "C" fn Servo_CSSSupports(
5856     cond: &nsACString,
5857     ua_origin: bool,
5858     chrome_sheet: bool,
5859     quirks: bool,
5860 ) -> bool {
5861     let condition = unsafe { cond.as_str_unchecked() };
5862     let mut input = ParserInput::new(&condition);
5863     let mut input = Parser::new(&mut input);
5864     let cond = match input.parse_entirely(parse_condition_or_declaration) {
5865         Ok(c) => c,
5866         Err(..) => return false,
5867     };
5869     let origin = if ua_origin {
5870         Origin::UserAgent
5871     } else {
5872         Origin::Author
5873     };
5874     let url_data = unsafe {
5875         if chrome_sheet {
5876             dummy_chrome_url_data()
5877         } else {
5878             dummy_url_data()
5879         }
5880     };
5881     let quirks_mode = if quirks {
5882         QuirksMode::Quirks
5883     } else {
5884         QuirksMode::NoQuirks
5885     };
5887     // NOTE(emilio): The supports API is not associated to any stylesheet,
5888     // so the fact that there is no namespace map here is fine.
5889     let context = ParserContext::new(
5890         origin,
5891         url_data,
5892         Some(CssRuleType::Style),
5893         ParsingMode::DEFAULT,
5894         quirks_mode,
5895         /* namespaces = */ Default::default(),
5896         None,
5897         None,
5898     );
5900     cond.eval(&context)
5903 #[no_mangle]
5904 pub extern "C" fn Servo_CSSSupportsForImport(after_rule: &nsACString) -> bool {
5905     let condition = unsafe { after_rule.as_str_unchecked() };
5906     let mut input = ParserInput::new(&condition);
5907     let mut input = Parser::new(&mut input);
5909     // NOTE(emilio): The supports API is not associated to any stylesheet,
5910     // so the fact that there is no namespace map here is fine.
5911     let mut context = ParserContext::new(
5912         Origin::Author,
5913         unsafe { dummy_url_data() },
5914         Some(CssRuleType::Style),
5915         ParsingMode::DEFAULT,
5916         QuirksMode::NoQuirks,
5917         /* namespaces = */ Default::default(),
5918         None,
5919         None,
5920     );
5922     let (_layer, supports) = ImportRule::parse_layer_and_supports(&mut input, &mut context);
5924     supports.map_or(true, |s| s.enabled)
5927 #[no_mangle]
5928 pub unsafe extern "C" fn Servo_NoteExplicitHints(
5929     element: &RawGeckoElement,
5930     restyle_hint: RestyleHint,
5931     change_hint: nsChangeHint,
5932 ) {
5933     GeckoElement(element).note_explicit_hints(restyle_hint, change_hint);
5936 #[no_mangle]
5937 pub extern "C" fn Servo_TakeChangeHint(element: &RawGeckoElement, was_restyled: &mut bool) -> u32 {
5938     let element = GeckoElement(element);
5940     let damage = match element.mutate_data() {
5941         Some(mut data) => {
5942             *was_restyled = data.is_restyle();
5944             let damage = data.damage;
5945             data.clear_restyle_state();
5946             damage
5947         },
5948         None => {
5949             warn!("Trying to get change hint from unstyled element");
5950             *was_restyled = false;
5951             GeckoRestyleDamage::empty()
5952         },
5953     };
5955     debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage);
5956     // We'd like to return `nsChangeHint` here, but bindgen bitfield enums don't
5957     // work as return values with the Linux 32-bit ABI at the moment because
5958     // they wrap the value in a struct, so for now just unwrap it.
5959     damage.as_change_hint().0
5962 #[no_mangle]
5963 pub extern "C" fn Servo_ResolveStyle(element: &RawGeckoElement) -> Strong<ComputedValues> {
5964     let element = GeckoElement(element);
5965     debug!("Servo_ResolveStyle: {:?}", element);
5966     let data = element
5967         .borrow_data()
5968         .expect("Resolving style on unstyled element");
5970     debug_assert!(
5971         element.has_current_styles(&*data),
5972         "Resolving style on {:?} without current styles: {:?}",
5973         element,
5974         data
5975     );
5976     data.styles.primary().clone().into()
5979 #[no_mangle]
5980 pub extern "C" fn Servo_ResolveStyleLazily(
5981     element: &RawGeckoElement,
5982     pseudo_type: PseudoStyleType,
5983     functional_pseudo_parameter: *mut nsAtom,
5984     rule_inclusion: StyleRuleInclusion,
5985     snapshots: *const ServoElementSnapshotTable,
5986     cache_generation: u64,
5987     can_use_cache: bool,
5988     raw_data: &PerDocumentStyleData,
5989 ) -> Strong<ComputedValues> {
5990     debug_assert!(!snapshots.is_null());
5991     let global_style_data = &*GLOBAL_STYLE_DATA;
5992     let guard = global_style_data.shared_lock.read();
5993     let element = GeckoElement(element);
5994     let mut data = raw_data.borrow_mut();
5995     let data = &mut *data;
5996     let rule_inclusion = RuleInclusion::from(rule_inclusion);
5997     let pseudo_element = PseudoElement::from_pseudo_type(
5998         pseudo_type,
5999         get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
6000     );
6002     let matching_fn = |pseudo: &PseudoElement| match pseudo_element {
6003         Some(ref p) => *pseudo == *p,
6004         _ => false,
6005     };
6007     if cache_generation != data.undisplayed_style_cache_generation {
6008         data.undisplayed_style_cache.clear();
6009         data.undisplayed_style_cache_generation = cache_generation;
6010     }
6012     let stylist = &data.stylist;
6013     let finish = |styles: &ElementStyles, is_probe: bool| -> Option<Arc<ComputedValues>> {
6014         match pseudo_element {
6015             Some(ref pseudo) => {
6016                 get_pseudo_style(
6017                     &guard,
6018                     element,
6019                     pseudo,
6020                     rule_inclusion,
6021                     styles,
6022                     /* inherited_styles = */ None,
6023                     &stylist,
6024                     is_probe,
6025                     if pseudo.is_highlight() {
6026                         Some(&matching_fn)
6027                     } else {
6028                         None
6029                     },
6030                 )
6031             },
6032             None => Some(styles.primary().clone()),
6033         }
6034     };
6036     let is_before_or_after = pseudo_element
6037         .as_ref()
6038         .map_or(false, |p| p.is_before_or_after());
6040     // In the common case we already have the style. Check that before setting
6041     // up all the computation machinery.
6042     //
6043     // Also, only probe in the ::before or ::after case, since their styles may
6044     // not be in the `ElementData`, given they may exist but not be applicable
6045     // to generate an actual pseudo-element (like, having a `content: none`).
6046     if rule_inclusion == RuleInclusion::All {
6047         let styles = element.borrow_data().and_then(|d| {
6048             if d.has_styles() {
6049                 finish(&d.styles, is_before_or_after)
6050             } else {
6051                 None
6052             }
6053         });
6054         if let Some(result) = styles {
6055             return result.into();
6056         }
6057         if pseudo_element.is_none() && can_use_cache {
6058             if let Some(style) = data.undisplayed_style_cache.get(&element.opaque()) {
6059                 return style.clone().into();
6060             }
6061         }
6062     }
6064     // We don't have the style ready. Go ahead and compute it as necessary.
6065     let shared = create_shared_context(
6066         &global_style_data,
6067         &guard,
6068         &stylist,
6069         TraversalFlags::empty(),
6070         unsafe { &*snapshots },
6071     );
6072     let mut tlc = ThreadLocalStyleContext::new();
6073     let mut context = StyleContext {
6074         shared: &shared,
6075         thread_local: &mut tlc,
6076     };
6078     let styles = resolve_style(
6079         &mut context,
6080         element,
6081         rule_inclusion,
6082         pseudo_element.as_ref(),
6083         if can_use_cache {
6084             Some(&mut data.undisplayed_style_cache)
6085         } else {
6086             None
6087         },
6088     );
6090     finish(&styles, /* is_probe = */ false)
6091         .expect("We're not probing, so we should always get a style back")
6092         .into()
6095 #[no_mangle]
6096 pub extern "C" fn Servo_ReparentStyle(
6097     style_to_reparent: &ComputedValues,
6098     parent_style: &ComputedValues,
6099     layout_parent_style: &ComputedValues,
6100     element: Option<&RawGeckoElement>,
6101     raw_data: &PerDocumentStyleData,
6102 ) -> Strong<ComputedValues> {
6103     use style::properties::FirstLineReparenting;
6105     let global_style_data = &*GLOBAL_STYLE_DATA;
6106     let guard = global_style_data.shared_lock.read();
6107     let doc_data = raw_data.borrow();
6108     let inputs = CascadeInputs::new_from_style(style_to_reparent);
6109     let pseudo = style_to_reparent.pseudo();
6110     let element = element.map(GeckoElement);
6112     doc_data
6113         .stylist
6114         .cascade_style_and_visited(
6115             element,
6116             pseudo.as_ref(),
6117             inputs,
6118             &StylesheetGuards::same(&guard),
6119             Some(parent_style),
6120             Some(layout_parent_style),
6121             FirstLineReparenting::Yes { style_to_reparent },
6122             /* rule_cache = */ None,
6123             &mut RuleCacheConditions::default(),
6124         )
6125         .into()
6128 #[cfg(feature = "gecko_debug")]
6129 fn simulate_compute_values_failure(property: &PropertyValuePair) -> bool {
6130     let p = &property.mProperty;
6131     let id = get_property_id_from_animatedpropertyid!(p, false);
6132     id.as_shorthand().is_ok() && property.mSimulateComputeValuesFailure
6135 #[cfg(not(feature = "gecko_debug"))]
6136 fn simulate_compute_values_failure(_: &PropertyValuePair) -> bool {
6137     false
6140 fn create_context_for_animation<'a>(
6141     per_doc_data: &'a PerDocumentStyleDataImpl,
6142     style: &'a ComputedValues,
6143     parent_style: Option<&'a ComputedValues>,
6144     for_smil_animation: bool,
6145     rule_cache_conditions: &'a mut RuleCacheConditions,
6146     container_size_query: ContainerSizeQuery<'a>,
6147 ) -> Context<'a> {
6148     Context::new_for_animation(
6149         StyleBuilder::for_animation(
6150             per_doc_data.stylist.device(),
6151             Some(&per_doc_data.stylist),
6152             style,
6153             parent_style,
6154         ),
6155         for_smil_animation,
6156         per_doc_data.stylist.quirks_mode(),
6157         rule_cache_conditions,
6158         container_size_query,
6159     )
6162 struct PropertyAndIndex {
6163     property: PropertyId,
6164     index: usize,
6167 struct PrioritizedPropertyIter<'a> {
6168     properties: &'a [PropertyValuePair],
6169     sorted_property_indices: Vec<PropertyAndIndex>,
6170     curr: usize,
6173 impl<'a> PrioritizedPropertyIter<'a> {
6174     fn new(properties: &'a [PropertyValuePair]) -> PrioritizedPropertyIter {
6175         use style::values::animated::compare_property_priority;
6177         // If we fail to convert a nsCSSPropertyID into a PropertyId we
6178         // shouldn't fail outright but instead by treating that property as the
6179         // 'all' property we make it sort last.
6180         let mut sorted_property_indices: Vec<PropertyAndIndex> = properties
6181             .iter()
6182             .enumerate()
6183             .map(|(index, pair)| {
6184                 let property = PropertyId::from_gecko_animated_property_id(&pair.mProperty)
6185                     .unwrap_or(PropertyId::NonCustom(ShorthandId::All.into()));
6186                 PropertyAndIndex { property, index }
6187             })
6188             .collect();
6189         sorted_property_indices.sort_by(|a, b| compare_property_priority(&a.property, &b.property));
6191         PrioritizedPropertyIter {
6192             properties,
6193             sorted_property_indices,
6194             curr: 0,
6195         }
6196     }
6198     fn reset(&mut self) {
6199         self.curr = 0;
6200     }
6203 impl<'a> Iterator for PrioritizedPropertyIter<'a> {
6204     type Item = &'a PropertyValuePair;
6206     fn next(&mut self) -> Option<&'a PropertyValuePair> {
6207         if self.curr >= self.sorted_property_indices.len() {
6208             return None;
6209         }
6210         self.curr += 1;
6211         Some(&self.properties[self.sorted_property_indices[self.curr - 1].index])
6212     }
6215 #[no_mangle]
6216 pub extern "C" fn Servo_GetComputedKeyframeValues(
6217     keyframes: &nsTArray<structs::Keyframe>,
6218     element: &RawGeckoElement,
6219     pseudo_type: PseudoStyleType,
6220     style: &ComputedValues,
6221     raw_data: &PerDocumentStyleData,
6222     computed_keyframes: &mut nsTArray<structs::ComputedKeyframeValues>,
6223 ) {
6224     use style::applicable_declarations::CascadePriority;
6225     use style::custom_properties::CustomPropertiesBuilder;
6226     use style::properties::PropertyDeclaration;
6227     let data = raw_data.borrow();
6228     let element = GeckoElement(element);
6229     let pseudo = PseudoElement::from_pseudo_type(pseudo_type, None);
6230     let parent_element = if pseudo.is_none() {
6231         element.inheritance_parent()
6232     } else {
6233         Some(element)
6234     };
6235     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
6236     let parent_style = parent_data
6237         .as_ref()
6238         .map(|d| d.styles.primary())
6239         .map(|x| &**x);
6241     let container_size_query =
6242         ContainerSizeQuery::for_element(element, parent_style, pseudo.is_some());
6243     let mut conditions = Default::default();
6244     let mut context = create_context_for_animation(
6245         &data,
6246         &style,
6247         parent_style,
6248         /* for_smil_animation = */ false,
6249         &mut conditions,
6250         container_size_query,
6251     );
6253     let restriction = pseudo.and_then(|p| p.property_restriction());
6255     let global_style_data = &*GLOBAL_STYLE_DATA;
6256     let guard = global_style_data.shared_lock.read();
6257     let default_values = data.default_computed_values();
6259     for (index, keyframe) in keyframes.iter().enumerate() {
6260         let ref mut animation_values = computed_keyframes[index];
6262         let mut seen = PropertyDeclarationIdSet::default();
6263         let mut iter = PrioritizedPropertyIter::new(&keyframe.mPropertyValues);
6265         // FIXME (bug 1883255): This is pretty much a hack. Instead, the AnimatedValue should be
6266         // better integrated in the cascade.
6267         {
6268             let mut builder = CustomPropertiesBuilder::new_with_properties(
6269                 &data.stylist,
6270                 style.custom_properties().clone(),
6271                 &mut context,
6272             );
6273             let priority = CascadePriority::same_tree_author_normal_at_root_layer();
6274             for property in &mut iter {
6275                 let is_custom =
6276                     match PropertyId::from_gecko_animated_property_id(&property.mProperty) {
6277                         Some(PropertyId::Custom(..)) => true,
6278                         _ => false,
6279                     };
6280                 if !is_custom {
6281                     break; // Custom props are guaranteed to sort earlier.
6282                 }
6283                 if property.mServoDeclarationBlock.mRawPtr.is_null() {
6284                     continue;
6285                 }
6286                 let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
6287                 let guard = declarations.read_with(&guard);
6288                 for decl in guard.normal_declaration_iter() {
6289                     if let PropertyDeclaration::Custom(ref declaration) = *decl {
6290                         builder.cascade(declaration, priority);
6291                     }
6292                 }
6293             }
6294             iter.reset();
6295             let _deferred = builder.build(DeferFontRelativeCustomPropertyResolution::No);
6296             debug_assert!(
6297                 _deferred.is_none(),
6298                 "Custom property processing deferred despite specifying otherwise?"
6299             );
6300         };
6302         let mut property_index = 0;
6303         for property in iter {
6304             if simulate_compute_values_failure(property) {
6305                 continue;
6306             }
6308             let mut maybe_append_animation_value =
6309                 |property: PropertyDeclarationId, value: Option<AnimationValue>| {
6310                     debug_assert!(!property.is_logical());
6311                     debug_assert!(property.is_animatable());
6313                     // 'display' is only animatable from SMIL
6314                     if property == PropertyDeclarationId::Longhand(LonghandId::Display) {
6315                         return;
6316                     }
6318                     // Skip restricted properties
6319                     if restriction.map_or(false, |r| !property.flags().contains(r)) {
6320                         return;
6321                     }
6323                     if seen.contains(property) {
6324                         return;
6325                     }
6326                     seen.insert(property);
6328                     // This is safe since we immediately write to the uninitialized values.
6329                     unsafe {
6330                         animation_values.set_len((property_index + 1) as u32);
6331                         ptr::write(
6332                             &mut animation_values[property_index],
6333                             structs::PropertyStyleAnimationValuePair {
6334                                 mProperty: property
6335                                     .to_gecko_animated_property_id(/* owned = */ true),
6336                                 mValue: structs::AnimationValue {
6337                                     mServo: value.map_or(structs::RefPtr::null(), |v| {
6338                                         structs::RefPtr::from_arc(Arc::new(v))
6339                                     }),
6340                                 },
6341                             },
6342                         );
6343                     }
6344                     property_index += 1;
6345                 };
6347             if property.mServoDeclarationBlock.mRawPtr.is_null() {
6348                 if let Some(prop) =
6349                     OwnedPropertyDeclarationId::from_gecko_animated_property_id(&property.mProperty)
6350                 {
6351                     maybe_append_animation_value(prop.as_borrowed(), None);
6352                 }
6353                 continue;
6354             }
6356             let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
6357             let guard = declarations.read_with(&guard);
6358             let iter = guard.to_animation_value_iter(&mut context, &default_values);
6360             for value in iter {
6361                 maybe_append_animation_value(value.id(), Some(value.clone()));
6362             }
6363         }
6364     }
6367 #[no_mangle]
6368 pub extern "C" fn Servo_GetAnimationValues(
6369     declarations: &LockedDeclarationBlock,
6370     element: &RawGeckoElement,
6371     style: &ComputedValues,
6372     raw_data: &PerDocumentStyleData,
6373     animation_values: &mut ThinVec<structs::RefPtr<AnimationValue>>,
6374 ) {
6375     let data = raw_data.borrow();
6376     let element = GeckoElement(element);
6377     let parent_element = element.inheritance_parent();
6378     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
6379     let parent_style = parent_data
6380         .as_ref()
6381         .map(|d| d.styles.primary())
6382         .map(|x| &**x);
6384     let container_size_query =
6385         ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
6386     let mut conditions = Default::default();
6387     let mut context = create_context_for_animation(
6388         &data,
6389         &style,
6390         parent_style,
6391         /* for_smil_animation = */ true,
6392         &mut conditions,
6393         container_size_query,
6394     );
6396     let default_values = data.default_computed_values();
6397     let global_style_data = &*GLOBAL_STYLE_DATA;
6398     let guard = global_style_data.shared_lock.read();
6400     let guard = declarations.read_with(&guard);
6401     let iter = guard.to_animation_value_iter(&mut context, &default_values);
6402     animation_values.extend(iter.map(|v| structs::RefPtr::from_arc(Arc::new(v))));
6405 #[no_mangle]
6406 pub extern "C" fn Servo_AnimationValue_GetPropertyId(
6407     value: &AnimationValue,
6408     property_id: &mut structs::AnimatedPropertyID,
6409 ) {
6410     *property_id = value.id().to_gecko_animated_property_id(/* owned = */ true);
6413 #[no_mangle]
6414 pub extern "C" fn Servo_AnimationValue_Compute(
6415     element: &RawGeckoElement,
6416     declarations: &LockedDeclarationBlock,
6417     style: &ComputedValues,
6418     raw_data: &PerDocumentStyleData,
6419 ) -> Strong<AnimationValue> {
6420     let data = raw_data.borrow();
6422     let element = GeckoElement(element);
6423     let parent_element = element.inheritance_parent();
6424     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
6425     let parent_style = parent_data
6426         .as_ref()
6427         .map(|d| d.styles.primary())
6428         .map(|x| &**x);
6430     let container_size_query =
6431         ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
6432     let mut conditions = Default::default();
6433     let mut context = create_context_for_animation(
6434         &data,
6435         style,
6436         parent_style,
6437         /* for_smil_animation = */ false,
6438         &mut conditions,
6439         container_size_query,
6440     );
6442     let default_values = data.default_computed_values();
6443     let global_style_data = &*GLOBAL_STYLE_DATA;
6444     let guard = global_style_data.shared_lock.read();
6445     // We only compute the first element in declarations.
6446     match declarations
6447         .read_with(&guard)
6448         .declaration_importance_iter()
6449         .next()
6450     {
6451         Some((decl, imp)) if imp == Importance::Normal => {
6452             let animation = AnimationValue::from_declaration(decl, &mut context, default_values);
6453             animation.map_or(Strong::null(), |value| Arc::new(value).into())
6454         },
6455         _ => Strong::null(),
6456     }
6459 #[no_mangle]
6460 pub extern "C" fn Servo_AssertTreeIsClean(root: &RawGeckoElement) {
6461     if !cfg!(feature = "gecko_debug") {
6462         panic!("Calling Servo_AssertTreeIsClean in release build");
6463     }
6465     let root = GeckoElement(root);
6466     debug!("Servo_AssertTreeIsClean: ");
6467     debug!("{:?}", ShowSubtreeData(root.as_node()));
6469     fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) {
6470         debug_assert!(
6471             !el.has_dirty_descendants() && !el.has_animation_only_dirty_descendants(),
6472             "{:?} has still dirty bit {:?} or animation-only dirty bit {:?}",
6473             el,
6474             el.has_dirty_descendants(),
6475             el.has_animation_only_dirty_descendants()
6476         );
6477         for child in el.traversal_children() {
6478             if let Some(child) = child.as_element() {
6479                 assert_subtree_is_clean(child);
6480             }
6481         }
6482     }
6484     assert_subtree_is_clean(root);
6487 #[no_mangle]
6488 pub extern "C" fn Servo_IsWorkerThread() -> bool {
6489     thread_state::get().is_worker()
6492 enum Offset {
6493     Zero,
6494     One,
6497 fn fill_in_missing_keyframe_values(
6498     all_properties: &PropertyDeclarationIdSet,
6499     timing_function: &ComputedTimingFunction,
6500     properties_at_offset: &PropertyDeclarationIdSet,
6501     offset: Offset,
6502     keyframes: &mut nsTArray<structs::Keyframe>,
6503 ) {
6504     // Return early if all animated properties are already set.
6505     if properties_at_offset.contains_all(all_properties) {
6506         return;
6507     }
6509     // Use auto for missing keyframes.
6510     // FIXME: This may be a spec issue in css-animations-2 because the spec says the default
6511     // keyframe-specific composite is replace, but web-animations-1 uses auto. Use auto now so we
6512     // use the value of animation-composition of the element, for missing keyframes.
6513     // https://github.com/w3c/csswg-drafts/issues/7476
6514     let composition = structs::CompositeOperationOrAuto::Auto;
6515     let keyframe = match offset {
6516         Offset::Zero => unsafe {
6517             Gecko_GetOrCreateInitialKeyframe(keyframes, timing_function, composition)
6518         },
6519         Offset::One => unsafe {
6520             Gecko_GetOrCreateFinalKeyframe(keyframes, timing_function, composition)
6521         },
6522     };
6524     // Append properties that have not been set at this offset.
6525     for property in all_properties.iter() {
6526         if !properties_at_offset.contains(property) {
6527             unsafe {
6528                 Gecko_AppendPropertyValuePair(
6529                     &mut *(*keyframe).mPropertyValues,
6530                     &property.to_gecko_animated_property_id(/* owned = */ false),
6531                 );
6532             }
6533         }
6534     }
6537 #[no_mangle]
6538 pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName(
6539     raw_data: &PerDocumentStyleData,
6540     element: &RawGeckoElement,
6541     style: &ComputedValues,
6542     name: *mut nsAtom,
6543     inherited_timing_function: &ComputedTimingFunction,
6544     keyframes: &mut nsTArray<structs::Keyframe>,
6545 ) -> bool {
6546     use style::gecko_bindings::structs::CompositeOperationOrAuto;
6547     use style::values::computed::AnimationComposition;
6549     debug_assert!(keyframes.len() == 0, "keyframes should be initially empty");
6551     let element = GeckoElement(element);
6552     let data = raw_data.borrow();
6553     let name = Atom::from_raw(name);
6555     let animation = match data.stylist.get_animation(&name, element) {
6556         Some(animation) => animation,
6557         None => return false,
6558     };
6560     let global_style_data = &*GLOBAL_STYLE_DATA;
6561     let guard = global_style_data.shared_lock.read();
6563     let mut properties_set_at_current_offset = PropertyDeclarationIdSet::default();
6564     let mut properties_set_at_start = PropertyDeclarationIdSet::default();
6565     let mut properties_set_at_end = PropertyDeclarationIdSet::default();
6566     let mut has_complete_initial_keyframe = false;
6567     let mut has_complete_final_keyframe = false;
6568     let mut current_offset = -1.;
6570     let writing_mode = style.writing_mode;
6572     // Iterate over the keyframe rules backwards so we can drop overridden
6573     // properties (since declarations in later rules override those in earlier
6574     // ones).
6575     for step in animation.steps.iter().rev() {
6576         if step.start_percentage.0 != current_offset {
6577             properties_set_at_current_offset.clear();
6578             current_offset = step.start_percentage.0;
6579         }
6581         // Override timing_function if the keyframe has an animation-timing-function.
6582         let timing_function = match step.get_animation_timing_function(&guard) {
6583             Some(val) => val.to_computed_value_without_context(),
6584             None => (*inherited_timing_function).clone(),
6585         };
6587         // Override composite operation if the keyframe has an animation-composition.
6588         let composition =
6589             step.get_animation_composition(&guard)
6590                 .map_or(CompositeOperationOrAuto::Auto, |val| match val {
6591                     AnimationComposition::Replace => CompositeOperationOrAuto::Replace,
6592                     AnimationComposition::Add => CompositeOperationOrAuto::Add,
6593                     AnimationComposition::Accumulate => CompositeOperationOrAuto::Accumulate,
6594                 });
6596         // Look for an existing keyframe with the same offset, timing function, and compsition, or
6597         // else add a new keyframe at the beginning of the keyframe array.
6598         let keyframe = Gecko_GetOrCreateKeyframeAtStart(
6599             keyframes,
6600             step.start_percentage.0 as f32,
6601             &timing_function,
6602             composition,
6603         );
6605         match step.value {
6606             KeyframesStepValue::ComputedValues => {
6607                 // In KeyframesAnimation::from_keyframes if there is no 0% or
6608                 // 100% keyframe at all, we will create a 'ComputedValues' step
6609                 // to represent that all properties animated by the keyframes
6610                 // animation should be set to the underlying computed value for
6611                 // that keyframe.
6612                 let mut seen = PropertyDeclarationIdSet::default();
6613                 for property in animation.properties_changed.iter() {
6614                     let property = property.to_physical(writing_mode);
6615                     if seen.contains(property) {
6616                         continue;
6617                     }
6618                     seen.insert(property);
6620                     Gecko_AppendPropertyValuePair(
6621                         &mut *(*keyframe).mPropertyValues,
6622                         &property.to_gecko_animated_property_id(/* owned = */ false),
6623                     );
6624                 }
6625                 if current_offset == 0.0 {
6626                     has_complete_initial_keyframe = true;
6627                 } else if current_offset == 1.0 {
6628                     has_complete_final_keyframe = true;
6629                 }
6630             },
6631             KeyframesStepValue::Declarations { ref block } => {
6632                 let guard = block.read_with(&guard);
6634                 // Filter out non-animatable properties and properties with
6635                 // !important.
6636                 //
6637                 // Also, iterate in reverse to respect the source order in case
6638                 // there are logical and physical longhands in the same block.
6639                 for declaration in guard.normal_declaration_iter().rev() {
6640                     let id = declaration.id().to_physical(writing_mode);
6642                     // Skip non-animatable properties, including the 'display' property because
6643                     // although it is animatable from SMIL, it should not be animatable from CSS
6644                     // Animations.
6645                     if !id.is_animatable() ||
6646                         id == PropertyDeclarationId::Longhand(LonghandId::Display)
6647                     {
6648                         continue;
6649                     }
6651                     if properties_set_at_current_offset.contains(id) {
6652                         continue;
6653                     }
6655                     let pair = Gecko_AppendPropertyValuePair(
6656                         &mut *(*keyframe).mPropertyValues,
6657                         &id.to_gecko_animated_property_id(/* owned = */ false),
6658                     );
6660                     (*pair).mServoDeclarationBlock.set_arc(Arc::new(
6661                         global_style_data
6662                             .shared_lock
6663                             .wrap(PropertyDeclarationBlock::with_one(
6664                                 declaration.to_physical(writing_mode),
6665                                 Importance::Normal,
6666                             )),
6667                     ));
6669                     if current_offset == 0.0 {
6670                         properties_set_at_start.insert(id);
6671                     } else if current_offset == 1.0 {
6672                         properties_set_at_end.insert(id);
6673                     }
6674                     properties_set_at_current_offset.insert(id);
6675                 }
6676             },
6677         }
6678     }
6680     let mut properties_changed = PropertyDeclarationIdSet::default();
6681     for property in animation.properties_changed.iter() {
6682         properties_changed.insert(property.to_physical(writing_mode));
6683     }
6685     // Append property values that are missing in the initial or the final keyframes.
6686     if !has_complete_initial_keyframe {
6687         fill_in_missing_keyframe_values(
6688             &properties_changed,
6689             inherited_timing_function,
6690             &properties_set_at_start,
6691             Offset::Zero,
6692             keyframes,
6693         );
6694     }
6695     if !has_complete_final_keyframe {
6696         fill_in_missing_keyframe_values(
6697             &properties_changed,
6698             inherited_timing_function,
6699             &properties_set_at_end,
6700             Offset::One,
6701             keyframes,
6702         );
6703     }
6704     true
6707 #[no_mangle]
6708 pub extern "C" fn Servo_StyleSet_GetFontFaceRules(
6709     raw_data: &PerDocumentStyleData,
6710     rules: &mut ThinVec<structs::nsFontFaceRuleContainer>,
6711 ) {
6712     let data = raw_data.borrow();
6713     debug_assert_eq!(rules.len(), 0);
6715     // Reversed iterator because Gecko expects rules to appear sorted
6716     // UserAgent first, Author last.
6717     let font_face_iter = data
6718         .stylist
6719         .iter_extra_data_origins_rev()
6720         .flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
6722     rules.extend(font_face_iter.map(|(&(ref rule, _layer_id), origin)| {
6723         structs::nsFontFaceRuleContainer {
6724             mRule: structs::RefPtr::from_arc(rule.clone()),
6725             mOrigin: origin,
6726         }
6727     }))
6730 // XXX Ideally this should return a Option<&LockedCounterStyleRule>,
6731 // but we cannot, because the value from AtomicRefCell::borrow() can only
6732 // live in this function, and thus anything derived from it cannot get the
6733 // same lifetime as raw_data in parameter. See bug 1451543.
6734 #[no_mangle]
6735 pub unsafe extern "C" fn Servo_StyleSet_GetCounterStyleRule(
6736     raw_data: &PerDocumentStyleData,
6737     name: *mut nsAtom,
6738 ) -> *const LockedCounterStyleRule {
6739     let data = raw_data.borrow();
6740     Atom::with(name, |name| {
6741         data.stylist
6742             .iter_extra_data_origins()
6743             .find_map(|(d, _)| d.counter_styles.get(name))
6744             .map_or(ptr::null(), |rule| &**rule as *const _)
6745     })
6748 #[no_mangle]
6749 pub extern "C" fn Servo_StyleSet_BuildFontFeatureValueSet(
6750     raw_data: &PerDocumentStyleData,
6751 ) -> *mut gfxFontFeatureValueSet {
6752     let data = raw_data.borrow();
6754     let has_rule = data
6755         .stylist
6756         .iter_extra_data_origins()
6757         .any(|(d, _)| !d.font_feature_values.is_empty());
6759     if !has_rule {
6760         return ptr::null_mut();
6761     }
6763     let font_feature_values_iter = data
6764         .stylist
6765         .iter_extra_data_origins_rev()
6766         .flat_map(|(d, _)| d.font_feature_values.iter());
6768     let set = unsafe { Gecko_ConstructFontFeatureValueSet() };
6769     for &(ref rule, _) in font_feature_values_iter {
6770         rule.set_at_rules(set);
6771     }
6772     set
6775 #[no_mangle]
6776 pub extern "C" fn Servo_StyleSet_BuildFontPaletteValueSet(
6777     raw_data: &PerDocumentStyleData,
6778 ) -> *mut FontPaletteValueSet {
6779     let data = raw_data.borrow();
6781     let has_rule = data
6782         .stylist
6783         .iter_extra_data_origins()
6784         .any(|(d, _)| !d.font_palette_values.is_empty());
6786     if !has_rule {
6787         return ptr::null_mut();
6788     }
6790     let font_palette_values_iter = data
6791         .stylist
6792         .iter_extra_data_origins_rev()
6793         .flat_map(|(d, _)| d.font_palette_values.iter());
6795     let set = unsafe { Gecko_ConstructFontPaletteValueSet() };
6796     for &(ref rule, _) in font_palette_values_iter {
6797         rule.to_gecko_palette_value_set(set);
6798     }
6799     set
6802 #[no_mangle]
6803 pub extern "C" fn Servo_StyleSet_ResolveForDeclarations(
6804     raw_data: &PerDocumentStyleData,
6805     parent_style_context: Option<&ComputedValues>,
6806     declarations: &LockedDeclarationBlock,
6807 ) -> Strong<ComputedValues> {
6808     let doc_data = raw_data.borrow();
6809     let global_style_data = &*GLOBAL_STYLE_DATA;
6810     let guard = global_style_data.shared_lock.read();
6811     let guards = StylesheetGuards::same(&guard);
6813     let parent_style = match parent_style_context {
6814         Some(parent) => &*parent,
6815         None => doc_data.default_computed_values(),
6816     };
6818     doc_data
6819         .stylist
6820         .compute_for_declarations::<GeckoElement>(&guards, parent_style, unsafe {
6821             Arc::from_raw_addrefed(declarations)
6822         })
6823         .into()
6826 #[no_mangle]
6827 pub extern "C" fn Servo_StyleSet_AddSizeOfExcludingThis(
6828     malloc_size_of: GeckoMallocSizeOf,
6829     malloc_enclosing_size_of: GeckoMallocSizeOf,
6830     sizes: *mut ServoStyleSetSizes,
6831     raw_data: &PerDocumentStyleData,
6832 ) {
6833     let data = raw_data.borrow_mut();
6834     let mut ops = MallocSizeOfOps::new(
6835         malloc_size_of.unwrap(),
6836         Some(malloc_enclosing_size_of.unwrap()),
6837         None,
6838     );
6839     let sizes = unsafe { sizes.as_mut() }.unwrap();
6840     data.add_size_of(&mut ops, sizes);
6843 #[no_mangle]
6844 pub extern "C" fn Servo_UACache_AddSizeOf(
6845     malloc_size_of: GeckoMallocSizeOf,
6846     malloc_enclosing_size_of: GeckoMallocSizeOf,
6847     sizes: *mut ServoStyleSetSizes,
6848 ) {
6849     let mut ops = MallocSizeOfOps::new(
6850         malloc_size_of.unwrap(),
6851         Some(malloc_enclosing_size_of.unwrap()),
6852         None,
6853     );
6854     let sizes = unsafe { sizes.as_mut() }.unwrap();
6855     add_size_of_ua_cache(&mut ops, sizes);
6858 #[no_mangle]
6859 pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(
6860     raw_data: &PerDocumentStyleData,
6861     element: &RawGeckoElement,
6862     local_name: *mut nsAtom,
6863 ) -> bool {
6864     let data = raw_data.borrow();
6865     let element = GeckoElement(element);
6867     unsafe {
6868         AtomIdent::with(local_name, |atom| {
6869             data.stylist.any_applicable_rule_data(element, |data| {
6870                 data.might_have_attribute_dependency(atom)
6871             })
6872         })
6873     }
6876 #[no_mangle]
6877 pub extern "C" fn Servo_StyleSet_MightHaveNthOfIDDependency(
6878     raw_data: &PerDocumentStyleData,
6879     element: &RawGeckoElement,
6880     old_id: *mut nsAtom,
6881     new_id: *mut nsAtom,
6882 ) -> bool {
6883     let data = raw_data.borrow();
6884     let element = GeckoElement(element);
6886     data.stylist.any_applicable_rule_data(element, |data| {
6887         [old_id, new_id]
6888             .iter()
6889             .filter(|id| !id.is_null())
6890             .any(|id| unsafe {
6891                 AtomIdent::with(*id, |atom| data.might_have_nth_of_id_dependency(atom))
6892             }) ||
6893             data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("id")))
6894     })
6897 #[no_mangle]
6898 pub extern "C" fn Servo_StyleSet_MightHaveNthOfClassDependency(
6899     raw_data: &PerDocumentStyleData,
6900     element: &RawGeckoElement,
6901     snapshots: &ServoElementSnapshotTable,
6902 ) -> bool {
6903     let data = raw_data.borrow();
6904     let element = GeckoElement(element);
6906     data.stylist.any_applicable_rule_data(element, |data| {
6907         classes_changed(&element, snapshots)
6908             .iter()
6909             .any(|atom| data.might_have_nth_of_class_dependency(atom)) ||
6910             data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("class")))
6911     })
6914 #[no_mangle]
6915 pub extern "C" fn Servo_StyleSet_MightHaveNthOfAttributeDependency(
6916     raw_data: &PerDocumentStyleData,
6917     element: &RawGeckoElement,
6918     local_name: *mut nsAtom,
6919 ) -> bool {
6920     let data = raw_data.borrow();
6921     let element = GeckoElement(element);
6923     unsafe {
6924         AtomIdent::with(local_name, |atom| {
6925             data.stylist.any_applicable_rule_data(element, |data| {
6926                 data.might_have_nth_of_attribute_dependency(atom)
6927             })
6928         })
6929     }
6932 fn on_siblings_invalidated(element: GeckoElement) {
6933     let parent = element
6934         .traversal_parent()
6935         .expect("How could we invalidate siblings without a common parent?");
6936     unsafe {
6937         parent.set_dirty_descendants();
6938         bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
6939     }
6942 fn restyle_for_nth_of(element: GeckoElement, flags: ElementSelectorFlags) {
6943     debug_assert!(
6944         !flags.is_empty(),
6945         "Calling restyle for nth but no relevant flag is set."
6946     );
6947     fn invalidate_siblings_of(
6948         element: GeckoElement,
6949         get_sibling: fn(GeckoElement) -> Option<GeckoElement>,
6950     ) {
6951         let mut sibling = get_sibling(element);
6952         while let Some(sib) = sibling {
6953             if let Some(mut data) = sib.mutate_data() {
6954                 data.hint.insert(RestyleHint::restyle_subtree());
6955             }
6956             sibling = get_sibling(sib);
6957         }
6958     }
6960     if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
6961         invalidate_siblings_of(element, |e| e.prev_sibling_element());
6962     }
6963     if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
6964         invalidate_siblings_of(element, |e| e.next_sibling_element());
6965     }
6966     on_siblings_invalidated(element);
6969 fn relative_selector_invalidated_at(element: GeckoElement, result: &InvalidationResult) {
6970     if result.has_invalidated_siblings() {
6971         on_siblings_invalidated(element);
6972     } else if result.has_invalidated_descendants() {
6973         unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
6974     } else if result.has_invalidated_self() {
6975         unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
6976         let flags = element
6977             .parent_element()
6978             .map_or(ElementSelectorFlags::empty(), |e| e.slow_selector_flags());
6979         // We invalidated up to the anchor, and it has a flag for nth-of invalidation.
6980         if !flags.is_empty() {
6981             restyle_for_nth_of(element, flags);
6982         }
6983     }
6986 fn add_relative_selector_attribute_dependency<'a>(
6987     element: &GeckoElement<'a>,
6988     scope: &Option<OpaqueElement>,
6989     invalidation_map: &'a RelativeSelectorInvalidationMap,
6990     attribute: &AtomIdent,
6991     collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
6992 ) {
6993     match invalidation_map
6994         .map
6995         .other_attribute_affecting_selectors
6996         .get(attribute)
6997     {
6998         Some(v) => {
6999             for dependency in v {
7000                 collector.add_dependency(dependency, *element, *scope);
7001             }
7002         },
7003         None => (),
7004     };
7007 fn inherit_relative_selector_search_direction(
7008     parent: Option<GeckoElement>,
7009     prev_sibling: Option<GeckoElement>,
7010 ) -> ElementSelectorFlags {
7011     let mut inherited = ElementSelectorFlags::empty();
7012     if let Some(parent) = parent {
7013         inherited |= parent
7014             .relative_selector_search_direction()
7015             .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR);
7016     }
7017     if let Some(sibling) = prev_sibling {
7018         // Inherit both, for e.g. a sibling with `:has(~.sibling .descendant)`
7019         inherited |= sibling.relative_selector_search_direction().intersection(
7020             ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING,
7021         );
7022     }
7023     inherited
7026 #[no_mangle]
7027 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorIDDependency(
7028     raw_data: &PerDocumentStyleData,
7029     element: &RawGeckoElement,
7030     old_id: *mut nsAtom,
7031     new_id: *mut nsAtom,
7032     snapshots: &ServoElementSnapshotTable,
7033 ) {
7034     let data = raw_data.borrow();
7035     let element = GeckoElement(element);
7037     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7038     let invalidator = RelativeSelectorInvalidator {
7039         element,
7040         quirks_mode,
7041         snapshot_table: Some(snapshots),
7042         invalidated: relative_selector_invalidated_at,
7043         sibling_traversal_map: SiblingTraversalMap::default(),
7044         _marker: std::marker::PhantomData,
7045     };
7047     invalidator.invalidate_relative_selectors_for_this(
7048         &data.stylist,
7049         |element, scope, data, quirks_mode, collector| {
7050             let invalidation_map = data.relative_selector_invalidation_map();
7051             relative_selector_dependencies_for_id(
7052                 old_id,
7053                 new_id,
7054                 element,
7055                 scope,
7056                 quirks_mode,
7057                 &invalidation_map,
7058                 collector,
7059             );
7060             add_relative_selector_attribute_dependency(
7061                 element,
7062                 &scope,
7063                 invalidation_map,
7064                 &AtomIdent(atom!("id")),
7065                 collector,
7066             );
7067         },
7068     );
7071 #[no_mangle]
7072 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorClassDependency(
7073     raw_data: &PerDocumentStyleData,
7074     element: &RawGeckoElement,
7075     snapshots: &ServoElementSnapshotTable,
7076 ) {
7077     let data = raw_data.borrow();
7078     let element = GeckoElement(element);
7079     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7080     let invalidator = RelativeSelectorInvalidator {
7081         element,
7082         quirks_mode,
7083         snapshot_table: Some(snapshots),
7084         invalidated: relative_selector_invalidated_at,
7085         sibling_traversal_map: SiblingTraversalMap::default(),
7086         _marker: std::marker::PhantomData,
7087     };
7089     invalidator.invalidate_relative_selectors_for_this(
7090         &data.stylist,
7091         |element, scope, data, quirks_mode, mut collector| {
7092             let invalidation_map = data.relative_selector_invalidation_map();
7094             relative_selector_dependencies_for_class(
7095                 &classes_changed(element, snapshots),
7096                 &element,
7097                 scope,
7098                 quirks_mode,
7099                 invalidation_map,
7100                 collector,
7101             );
7102             add_relative_selector_attribute_dependency(
7103                 element,
7104                 &scope,
7105                 invalidation_map,
7106                 &AtomIdent(atom!("class")),
7107                 &mut collector,
7108             );
7109         },
7110     );
7113 #[no_mangle]
7114 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorAttributeDependency(
7115     raw_data: &PerDocumentStyleData,
7116     element: &RawGeckoElement,
7117     local_name: *mut nsAtom,
7118     snapshots: &ServoElementSnapshotTable,
7119 ) {
7120     let data = raw_data.borrow();
7121     let element = GeckoElement(element);
7123     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7124     unsafe {
7125         AtomIdent::with(local_name, |atom| {
7126             let invalidator = RelativeSelectorInvalidator {
7127                 element,
7128                 quirks_mode,
7129                 snapshot_table: Some(snapshots),
7130                 invalidated: relative_selector_invalidated_at,
7131                 sibling_traversal_map: SiblingTraversalMap::default(),
7132                 _marker: std::marker::PhantomData,
7133             };
7135             invalidator.invalidate_relative_selectors_for_this(
7136                 &data.stylist,
7137                 |element, scope, data, _quirks_mode, mut collector| {
7138                     add_relative_selector_attribute_dependency(
7139                         element,
7140                         &scope,
7141                         data.relative_selector_invalidation_map(),
7142                         atom,
7143                         &mut collector,
7144                     );
7145                 },
7146             );
7147         })
7148     }
7151 #[no_mangle]
7152 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorStateDependency(
7153     raw_data: &PerDocumentStyleData,
7154     element: &RawGeckoElement,
7155     state: u64,
7156     snapshots: &ServoElementSnapshotTable,
7157 ) {
7158     let element = GeckoElement(element);
7160     let state = match ElementState::from_bits(state) {
7161         Some(state) => state,
7162         None => return,
7163     };
7164     let data = raw_data.borrow();
7165     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7167     let invalidator = RelativeSelectorInvalidator {
7168         element,
7169         quirks_mode,
7170         snapshot_table: Some(snapshots),
7171         invalidated: relative_selector_invalidated_at,
7172         sibling_traversal_map: SiblingTraversalMap::default(),
7173         _marker: std::marker::PhantomData,
7174     };
7176     invalidator.invalidate_relative_selectors_for_this(
7177         &data.stylist,
7178         |element, scope, data, quirks_mode, collector| {
7179             let invalidation_map = data.relative_selector_invalidation_map();
7180             invalidation_map
7181                 .map
7182                 .state_affecting_selectors
7183                 .lookup_with_additional(*element, quirks_mode, None, &[], state, |dependency| {
7184                     if !dependency.state.intersects(state) {
7185                         return true;
7186                     }
7187                     collector.add_dependency(&dependency.dep, *element, scope);
7188                     true
7189                 });
7190         },
7191     );
7194 #[no_mangle]
7195 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorCustomStateDependency(
7196     raw_data: &PerDocumentStyleData,
7197     element: &RawGeckoElement,
7198     state: *mut nsAtom,
7199     snapshots: &ServoElementSnapshotTable,
7200 ) {
7201     let data = raw_data.borrow();
7202     let element = GeckoElement(element);
7204     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7205     let invalidator = RelativeSelectorInvalidator {
7206         element,
7207         quirks_mode,
7208         snapshot_table: Some(snapshots),
7209         invalidated: relative_selector_invalidated_at,
7210         sibling_traversal_map: SiblingTraversalMap::default(),
7211         _marker: std::marker::PhantomData,
7212     };
7214     invalidator.invalidate_relative_selectors_for_this(
7215         &data.stylist,
7216         |element, scope, data, _quirks_mode, collector| {
7217             let invalidation_map = data.relative_selector_invalidation_map();
7218             relative_selector_dependencies_for_custom_state(
7219                 state,
7220                 *element,
7221                 scope,
7222                 &invalidation_map,
7223                 collector,
7224             );
7225         },
7226     );
7229 fn invalidate_relative_selector_prev_sibling_side_effect(
7230     prev_sibling: GeckoElement,
7231     quirks_mode: QuirksMode,
7232     sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
7233     stylist: &Stylist,
7234 ) {
7235     let invalidator = RelativeSelectorInvalidator {
7236         element: prev_sibling,
7237         quirks_mode,
7238         snapshot_table: None,
7239         invalidated: relative_selector_invalidated_at,
7240         sibling_traversal_map,
7241         _marker: std::marker::PhantomData,
7242     };
7243     invalidator.invalidate_relative_selectors_for_dom_mutation(
7244         false,
7245         &stylist,
7246         ElementSelectorFlags::empty(),
7247         DomMutationOperation::SideEffectPrevSibling,
7248     );
7251 fn invalidate_relative_selector_next_sibling_side_effect(
7252     next_sibling: GeckoElement,
7253     quirks_mode: QuirksMode,
7254     sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
7255     stylist: &Stylist,
7256 ) {
7257     let invalidator = RelativeSelectorInvalidator {
7258         element: next_sibling,
7259         quirks_mode,
7260         snapshot_table: None,
7261         invalidated: relative_selector_invalidated_at,
7262         sibling_traversal_map,
7263         _marker: std::marker::PhantomData,
7264     };
7265     invalidator.invalidate_relative_selectors_for_dom_mutation(
7266         false,
7267         &stylist,
7268         ElementSelectorFlags::empty(),
7269         DomMutationOperation::SideEffectNextSibling,
7270     );
7273 fn invalidate_relative_selector_ts_dependency(
7274     raw_data: &PerDocumentStyleData,
7275     element: GeckoElement,
7276     state: TSStateForInvalidation,
7277 ) {
7278     let data = raw_data.borrow();
7279     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7281     let invalidator = RelativeSelectorInvalidator {
7282         element,
7283         quirks_mode,
7284         snapshot_table: None,
7285         invalidated: relative_selector_invalidated_at,
7286         sibling_traversal_map: SiblingTraversalMap::default(),
7287         _marker: std::marker::PhantomData,
7288     };
7290     invalidator.invalidate_relative_selectors_for_this(
7291         &data.stylist,
7292         |element, scope, data, quirks_mode, collector| {
7293             let invalidation_map = data.relative_selector_invalidation_map();
7294             invalidation_map
7295                 .ts_state_to_selector
7296                 .lookup_with_additional(
7297                     *element,
7298                     quirks_mode,
7299                     None,
7300                     &[],
7301                     ElementState::empty(),
7302                     |dependency| {
7303                         if !dependency.state.intersects(state) {
7304                             return true;
7305                         }
7306                         collector.add_dependency(&dependency.dep, *element, scope);
7307                         true
7308                     },
7309                 );
7310         },
7311     );
7314 #[no_mangle]
7315 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorEmptyDependency(
7316     raw_data: &PerDocumentStyleData,
7317     element: &RawGeckoElement,
7318 ) {
7319     invalidate_relative_selector_ts_dependency(
7320         raw_data,
7321         GeckoElement(element),
7322         TSStateForInvalidation::EMPTY,
7323     );
7326 #[no_mangle]
7327 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthEdgeDependency(
7328     raw_data: &PerDocumentStyleData,
7329     element: &RawGeckoElement,
7330 ) {
7331     invalidate_relative_selector_ts_dependency(
7332         raw_data,
7333         GeckoElement(element),
7334         TSStateForInvalidation::NTH_EDGE,
7335     );
7338 #[no_mangle]
7339 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthDependencyFromSibling(
7340     raw_data: &PerDocumentStyleData,
7341     element: &RawGeckoElement,
7342 ) {
7343     let mut element = Some(GeckoElement(element));
7345     // Short of doing the actual matching, any of the siblings can match the selector, so we
7346     // have to try invalidating against all of them.
7347     while let Some(sibling) = element {
7348         invalidate_relative_selector_ts_dependency(raw_data, sibling, TSStateForInvalidation::NTH);
7349         element = sibling.next_sibling_element();
7350     }
7353 #[no_mangle]
7354 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForInsertion(
7355     raw_data: &PerDocumentStyleData,
7356     element: &RawGeckoElement,
7357 ) {
7358     let element = GeckoElement(element);
7359     let data = raw_data.borrow();
7360     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7362     let inherited = inherit_relative_selector_search_direction(
7363         element.parent_element(),
7364         element.prev_sibling_element(),
7365     );
7366     // Technically, we're not handling breakouts, where the anchor is a (later-sibling) descendant.
7367     // For descendant case, we're ok since it's a descendant of an element yet to be styled.
7368     // For later-sibling descendant, `HAS_SLOW_SELECTOR_LATER_SIBLINGS` is set anyway.
7369     if inherited.is_empty() {
7370         return;
7371     }
7373     // Ok, we could've been inserted between two sibling elements that were connected
7374     // through next sibling. This can happen in two ways:
7375     // * `.a:has(+ .b)`
7376     // * `:has(.. .a + .b ..)`
7377     // Note that the previous sibling may be the anchor, and not part of the invalidation chain.
7378     // Either way, there must be siblings to both sides of the element being inserted
7379     // to consider it.
7380     match (
7381         element.prev_sibling_element(),
7382         element.next_sibling_element(),
7383     ) {
7384         (Some(prev_sibling), Some(next_sibling)) => 'sibling: {
7385             // If the prev sibling is not on the sibling search path, skip.
7386             if !prev_sibling
7387                 .relative_selector_search_direction()
7388                 .intersects(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING)
7389             {
7390                 break 'sibling;
7391             }
7392             element.apply_selector_flags(
7393                 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
7394             );
7395             invalidate_relative_selector_prev_sibling_side_effect(
7396                 prev_sibling,
7397                 quirks_mode,
7398                 SiblingTraversalMap::new(
7399                     prev_sibling,
7400                     prev_sibling.prev_sibling_element(),
7401                     element.next_sibling_element(),
7402                 ), // Pretend this inserted element isn't here.
7403                 &data.stylist,
7404             );
7405             invalidate_relative_selector_next_sibling_side_effect(
7406                 next_sibling,
7407                 quirks_mode,
7408                 SiblingTraversalMap::new(
7409                     next_sibling,
7410                     Some(prev_sibling),
7411                     next_sibling.next_sibling_element(),
7412                 ),
7413                 &data.stylist,
7414             );
7415         },
7416         _ => (),
7417     };
7419     let invalidator = RelativeSelectorInvalidator {
7420         element,
7421         quirks_mode,
7422         snapshot_table: None,
7423         invalidated: relative_selector_invalidated_at,
7424         sibling_traversal_map: SiblingTraversalMap::default(),
7425         _marker: std::marker::PhantomData,
7426     };
7428     invalidator.invalidate_relative_selectors_for_dom_mutation(
7429         true,
7430         &data.stylist,
7431         inherited,
7432         DomMutationOperation::Insert,
7433     );
7436 #[no_mangle]
7437 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForAppend(
7438     raw_data: &PerDocumentStyleData,
7439     first_node: &RawGeckoNode,
7440 ) {
7441     let first_node = GeckoNode(first_node);
7442     let inherited = inherit_relative_selector_search_direction(
7443         first_node.parent_element(),
7444         first_node.prev_sibling_element(),
7445     );
7446     if inherited.is_empty() {
7447         return;
7448     }
7449     let first_element = if let Some(e) = first_node.as_element() {
7450         e
7451     } else if let Some(e) = first_node.next_sibling_element() {
7452         e
7453     } else {
7454         return;
7455     };
7456     let data = raw_data.borrow();
7457     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7459     let mut element = Some(first_element);
7460     while let Some(e) = element {
7461         let invalidator = RelativeSelectorInvalidator {
7462             element: e,
7463             quirks_mode,
7464             snapshot_table: None,
7465             sibling_traversal_map: SiblingTraversalMap::default(),
7466             invalidated: relative_selector_invalidated_at,
7467             _marker: std::marker::PhantomData,
7468         };
7469         invalidator.invalidate_relative_selectors_for_dom_mutation(
7470             true,
7471             &data.stylist,
7472             inherited,
7473             DomMutationOperation::Append,
7474         );
7475         element = e.next_sibling_element();
7476     }
7479 fn get_siblings_of_element<'e>(
7480     element: GeckoElement<'e>,
7481     following_node: &'e Option<GeckoNode<'e>>,
7482 ) -> (Option<GeckoElement<'e>>, Option<GeckoElement<'e>>) {
7483     let node = match following_node {
7484         Some(n) => n,
7485         None => {
7486             return match element.as_node().parent_node() {
7487                 Some(p) => (p.last_child_element(), None),
7488                 None => (None, None),
7489             }
7490         },
7491     };
7493     (node.prev_sibling_element(), node.next_sibling_element())
7496 #[no_mangle]
7497 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForRemoval(
7498     raw_data: &PerDocumentStyleData,
7499     element: &RawGeckoElement,
7500     following_node: Option<&RawGeckoNode>,
7501 ) {
7502     let element = GeckoElement(element);
7504     // This element was in-tree, so we can safely say that if it was not on
7505     // the relative selector search path, its removal will not invalidate any
7506     // relative selector.
7507     if element.relative_selector_search_direction().is_empty() {
7508         return;
7509     }
7510     let following_node = following_node.map(GeckoNode);
7511     let (prev_sibling, next_sibling) = get_siblings_of_element(element, &following_node);
7512     let data = raw_data.borrow();
7513     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7515     let inherited =
7516         inherit_relative_selector_search_direction(element.parent_element(), prev_sibling);
7517     if inherited.is_empty() {
7518         return;
7519     }
7521     // Same comment as insertion applies.
7522     match (prev_sibling, next_sibling) {
7523         (Some(prev_sibling), Some(next_sibling)) => {
7524             invalidate_relative_selector_prev_sibling_side_effect(
7525                 prev_sibling,
7526                 quirks_mode,
7527                 SiblingTraversalMap::default(),
7528                 &data.stylist,
7529             );
7530             invalidate_relative_selector_next_sibling_side_effect(
7531                 next_sibling,
7532                 quirks_mode,
7533                 SiblingTraversalMap::default(),
7534                 &data.stylist,
7535             );
7536         },
7537         _ => (),
7538     };
7539     let invalidator = RelativeSelectorInvalidator {
7540         element,
7541         quirks_mode,
7542         snapshot_table: None,
7543         sibling_traversal_map: SiblingTraversalMap::new(element, prev_sibling, next_sibling),
7544         invalidated: relative_selector_invalidated_at,
7545         _marker: std::marker::PhantomData,
7546     };
7547     invalidator.invalidate_relative_selectors_for_dom_mutation(
7548         true,
7549         &data.stylist,
7550         inherited,
7551         DomMutationOperation::Remove,
7552     );
7555 #[no_mangle]
7556 pub extern "C" fn Servo_StyleSet_HasStateDependency(
7557     raw_data: &PerDocumentStyleData,
7558     element: &RawGeckoElement,
7559     state: u64,
7560 ) -> bool {
7561     let element = GeckoElement(element);
7563     let state = ElementState::from_bits_retain(state);
7564     let data = raw_data.borrow();
7566     data.stylist
7567         .any_applicable_rule_data(element, |data| data.has_state_dependency(state))
7570 #[no_mangle]
7571 pub extern "C" fn Servo_StyleSet_HasNthOfCustomStateDependency(
7572     raw_data: &PerDocumentStyleData,
7573     element: &RawGeckoElement,
7574     state: *mut nsAtom,
7575 ) -> bool {
7576     let element = GeckoElement(element);
7577     let data = raw_data.borrow();
7578     data.stylist
7579         .any_applicable_rule_data(element, |data| unsafe {
7580             AtomIdent::with(state, |atom| data.has_nth_of_custom_state_dependency(atom))
7581         })
7585 #[no_mangle]
7586 pub extern "C" fn Servo_StyleSet_HasNthOfStateDependency(
7587     raw_data: &PerDocumentStyleData,
7588     element: &RawGeckoElement,
7589     state: u64,
7590 ) -> bool {
7591     let element = GeckoElement(element);
7593     let state = ElementState::from_bits_retain(state);
7594     let data = raw_data.borrow();
7596     data.stylist
7597         .any_applicable_rule_data(element, |data| data.has_nth_of_state_dependency(state))
7600 #[no_mangle]
7601 pub extern "C" fn Servo_StyleSet_RestyleSiblingsForNthOf(element: &RawGeckoElement, flags: u32) {
7602     let flags = slow_selector_flags_from_node_selector_flags(flags);
7603     let element = GeckoElement(element);
7604     restyle_for_nth_of(element, flags);
7607 #[no_mangle]
7608 pub extern "C" fn Servo_StyleSet_HasDocumentStateDependency(
7609     raw_data: &PerDocumentStyleData,
7610     state: u64,
7611 ) -> bool {
7612     let state = DocumentState::from_bits_retain(state);
7613     let data = raw_data.borrow();
7615     data.stylist.has_document_state_dependency(state)
7618 fn computed_or_resolved_value(
7619     style: &ComputedValues,
7620     prop: nsCSSPropertyID,
7621     context: Option<&style::values::resolved::Context>,
7622     value: &mut nsACString,
7623 ) {
7624     if let Some(longhand) = LonghandId::from_nscsspropertyid(prop) {
7625         return style
7626             .computed_or_resolved_value(longhand, context, value)
7627             .unwrap();
7628     }
7630     let shorthand =
7631         ShorthandId::from_nscsspropertyid(prop).expect("Not a shorthand nor a longhand?");
7632     let mut block = PropertyDeclarationBlock::new();
7633     for longhand in shorthand.longhands() {
7634         block.push(
7635             style.computed_or_resolved_declaration(longhand, context),
7636             Importance::Normal,
7637         );
7638     }
7639     block.shorthand_to_css(shorthand, value).unwrap();
7642 #[no_mangle]
7643 pub unsafe extern "C" fn Servo_GetComputedValue(
7644     style: &ComputedValues,
7645     prop: nsCSSPropertyID,
7646     value: &mut nsACString,
7647 ) {
7648     computed_or_resolved_value(style, prop, None, value)
7651 #[no_mangle]
7652 pub unsafe extern "C" fn Servo_GetResolvedValue(
7653     style: &ComputedValues,
7654     prop: nsCSSPropertyID,
7655     raw_data: &PerDocumentStyleData,
7656     element: &RawGeckoElement,
7657     value: &mut nsACString,
7658 ) {
7659     use style::values::resolved;
7661     let data = raw_data.borrow();
7662     let device = data.stylist.device();
7663     let context = resolved::Context {
7664         style,
7665         device,
7666         element_info: resolved::ResolvedElementInfo {
7667             element: GeckoElement(element),
7668         },
7669     };
7671     computed_or_resolved_value(style, prop, Some(&context), value)
7674 #[no_mangle]
7675 pub unsafe extern "C" fn Servo_GetCustomPropertyValue(
7676     computed_values: &ComputedValues,
7677     raw_style_set: &PerDocumentStyleData,
7678     name: &nsACString,
7679     value: &mut nsACString,
7680 ) -> bool {
7681     let doc_data = raw_style_set.borrow();
7682     let name = Atom::from(name.as_str_unchecked());
7683     let custom_registration = doc_data.stylist.get_custom_property_registration(&name);
7684     let computed_value = if custom_registration.inherits() {
7685         computed_values.custom_properties.inherited.get(&name)
7686     } else {
7687         computed_values.custom_properties.non_inherited.get(&name)
7688     };
7690     if let Some(v) = computed_value {
7691         v.to_css(&mut CssWriter::new(value)).unwrap();
7692         true
7693     } else {
7694         false
7695     }
7698 #[no_mangle]
7699 pub extern "C" fn Servo_GetCustomPropertiesCount(computed_values: &ComputedValues) -> u32 {
7700     // Just expose the custom property items from custom_properties.inherited
7701     // and custom_properties.non_inherited.
7702     let properties = computed_values.custom_properties();
7703     properties.inherited.len() as u32 + properties.non_inherited.len() as u32
7706 #[no_mangle]
7707 pub extern "C" fn Servo_GetCustomPropertyNameAt(
7708     computed_values: &ComputedValues,
7709     index: u32,
7710 ) -> *mut nsAtom {
7711     match &computed_values
7712         .custom_properties
7713         .property_at(index as usize)
7714     {
7715         Some((name, _value)) => name.as_ptr(),
7716         None => ptr::null_mut(),
7717     }
7720 #[no_mangle]
7721 pub extern "C" fn Servo_CssUrl_IsLocalRef(url: &url::CssUrl) -> bool {
7722     url.is_fragment()
7725 fn relative_selector_dependencies_for_id<'a>(
7726     old_id: *const nsAtom,
7727     new_id: *const nsAtom,
7728     element: &GeckoElement<'a>,
7729     scope: Option<OpaqueElement>,
7730     quirks_mode: QuirksMode,
7731     invalidation_map: &'a RelativeSelectorInvalidationMap,
7732     collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
7733 ) {
7734     [old_id, new_id]
7735         .iter()
7736         .filter(|id| !id.is_null())
7737         .for_each(|id| unsafe {
7738             AtomIdent::with(*id, |atom| {
7739                 match invalidation_map.map.id_to_selector.get(atom, quirks_mode) {
7740                     Some(v) => {
7741                         for dependency in v {
7742                             collector.add_dependency(dependency, *element, scope);
7743                         }
7744                     },
7745                     None => (),
7746                 };
7747             })
7748         });
7751 fn relative_selector_dependencies_for_class<'a>(
7752     classes_changed: &SmallVec<[Atom; 8]>,
7753     element: &GeckoElement<'a>,
7754     scope: Option<OpaqueElement>,
7755     quirks_mode: QuirksMode,
7756     invalidation_map: &'a RelativeSelectorInvalidationMap,
7757     collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
7758 ) {
7759     classes_changed.iter().for_each(|atom| {
7760         match invalidation_map
7761             .map
7762             .class_to_selector
7763             .get(atom, quirks_mode)
7764         {
7765             Some(v) => {
7766                 for dependency in v {
7767                     collector.add_dependency(dependency, *element, scope);
7768                 }
7769             },
7770             None => (),
7771         };
7772     });
7775 fn relative_selector_dependencies_for_custom_state<'a>(
7776     state: *const nsAtom,
7777     element: GeckoElement<'a>,
7778     scope: Option<OpaqueElement>,
7779     invalidation_map: &'a RelativeSelectorInvalidationMap,
7780     collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
7781 ) {
7782     unsafe {
7783         AtomIdent::with(state, |atom| {
7784             match invalidation_map
7785                 .map
7786                 .custom_state_affecting_selectors
7787                 .get(atom)
7788             {
7789                 Some(v) => {
7790                     for dependency in v {
7791                         collector.add_dependency(dependency, element, scope);
7792                     }
7793                 },
7794                 None => (),
7795             };
7796         })
7797     }
7800 fn process_relative_selector_invalidations(
7801     element: &GeckoElement,
7802     snapshot_table: &ServoElementSnapshotTable,
7803     data: &PerDocumentStyleDataImpl,
7804 ) {
7805     let snapshot = match snapshot_table.get(element) {
7806         None => return,
7807         Some(s) => s,
7808     };
7809     let mut states = None;
7810     let mut classes = None;
7812     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7813     let invalidator = RelativeSelectorInvalidator {
7814         element: *element,
7815         quirks_mode,
7816         invalidated: relative_selector_invalidated_at,
7817         sibling_traversal_map: SiblingTraversalMap::default(),
7818         snapshot_table: Some(snapshot_table),
7819         _marker: std::marker::PhantomData,
7820     };
7822     invalidator.invalidate_relative_selectors_for_this(
7823         &data.stylist,
7824         |element, scope, data, quirks_mode, collector| {
7825             let invalidation_map = data.relative_selector_invalidation_map();
7826             let states = *states.get_or_insert_with(|| {
7827                 ElementWrapper::new(*element, snapshot_table).state_changes()
7828             });
7829             let classes = classes.get_or_insert_with(|| classes_changed(element, snapshot_table));
7830             if snapshot.id_changed() {
7831                 relative_selector_dependencies_for_id(
7832                     element
7833                         .id()
7834                         .map(|id| id.as_ptr().cast_const())
7835                         .unwrap_or(ptr::null()),
7836                     snapshot
7837                         .id_attr()
7838                         .map(|id| id.as_ptr().cast_const())
7839                         .unwrap_or(ptr::null()),
7840                     element,
7841                     scope,
7842                     quirks_mode,
7843                     invalidation_map,
7844                     collector,
7845                 );
7846             }
7847             relative_selector_dependencies_for_class(
7848                 &classes,
7849                 element,
7850                 scope,
7851                 quirks_mode,
7852                 invalidation_map,
7853                 collector,
7854             );
7855             snapshot.each_attr_changed(|attr| {
7856                 add_relative_selector_attribute_dependency(
7857                     element,
7858                     &scope,
7859                     invalidation_map,
7860                     attr,
7861                     collector,
7862                 )
7863             });
7864             invalidation_map
7865                 .map
7866                 .state_affecting_selectors
7867                 .lookup_with_additional(*element, quirks_mode, None, &[], states, |dependency| {
7868                     if !dependency.state.intersects(states) {
7869                         return true;
7870                     }
7871                     collector.add_dependency(&dependency.dep, *element, scope);
7872                     true
7873                 });
7874         },
7875     );
7878 #[no_mangle]
7879 pub extern "C" fn Servo_ProcessInvalidations(
7880     set: &PerDocumentStyleData,
7881     element: &RawGeckoElement,
7882     snapshots: *const ServoElementSnapshotTable,
7883 ) {
7884     debug_assert!(!snapshots.is_null());
7886     let element = GeckoElement(element);
7887     debug_assert!(element.has_snapshot());
7888     debug_assert!(!element.handled_snapshot());
7890     let snapshot_table = unsafe { &*snapshots };
7891     let per_doc_data = set.borrow();
7892     process_relative_selector_invalidations(&element, snapshot_table, &per_doc_data);
7894     let mut data = element.mutate_data();
7895     if data.is_none() {
7896         // Snapshot for unstyled element is really only meant for relative selector
7897         // invalidation, so this is fine.
7898         return;
7899     }
7901     let global_style_data = &*GLOBAL_STYLE_DATA;
7902     let guard = global_style_data.shared_lock.read();
7903     let per_doc_data = set.borrow();
7904     let shared_style_context = create_shared_context(
7905         &global_style_data,
7906         &guard,
7907         &per_doc_data.stylist,
7908         TraversalFlags::empty(),
7909         snapshot_table,
7910     );
7911     let mut data = data.as_mut().map(|d| &mut **d);
7913     let mut selector_caches = SelectorCaches::default();
7914     if let Some(ref mut data) = data {
7915         // FIXME(emilio): Ideally we could share the nth-index-cache across all
7916         // the elements?
7917         let result = data.invalidate_style_if_needed(
7918             element,
7919             &shared_style_context,
7920             None,
7921             &mut selector_caches,
7922         );
7924         if result.has_invalidated_siblings() {
7925             let parent = element
7926                 .traversal_parent()
7927                 .expect("How could we invalidate siblings without a common parent?");
7928             unsafe {
7929                 parent.set_dirty_descendants();
7930                 bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
7931             }
7932         } else if result.has_invalidated_descendants() {
7933             unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
7934         } else if result.has_invalidated_self() {
7935             unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
7936         }
7937     }
7940 #[no_mangle]
7941 pub extern "C" fn Servo_HasPendingRestyleAncestor(
7942     element: &RawGeckoElement,
7943     may_need_to_flush_layout: bool,
7944 ) -> bool {
7945     let mut has_yet_to_be_styled = false;
7946     let mut element = Some(GeckoElement(element));
7947     while let Some(e) = element {
7948         if e.has_any_animation() {
7949             return true;
7950         }
7952         // If the element needs a frame, it means that we haven't styled it yet
7953         // after it got inserted in the document, and thus we may need to do
7954         // that for transitions and animations to trigger.
7955         //
7956         // This is a fast path in the common case, but `has_yet_to_be_styled` is
7957         // the real check for this.
7958         if e.needs_frame() {
7959             return true;
7960         }
7962         let data = e.borrow_data();
7963         if let Some(ref data) = data {
7964             if !data.hint.is_empty() {
7965                 return true;
7966             }
7967             if has_yet_to_be_styled && !data.styles.is_display_none() {
7968                 return true;
7969             }
7970             // Ideally, DOM mutations wouldn't affect layout trees of siblings.
7971             //
7972             // In practice, this can happen because Gecko deals pretty badly
7973             // with some kinds of content insertion and removals.
7974             //
7975             // If we may need to flush layout, we need frames to accurately
7976             // determine whether we'll actually flush, so if we have to
7977             // reconstruct we need to flush style, which is what will take care
7978             // of ensuring that frames are constructed, even if the style itself
7979             // is up-to-date.
7980             if may_need_to_flush_layout && data.damage.contains(GeckoRestyleDamage::reconstruct()) {
7981                 return true;
7982             }
7983         }
7984         has_yet_to_be_styled = data.is_none();
7986         element = e.traversal_parent();
7987     }
7988     false
7991 #[no_mangle]
7992 pub unsafe extern "C" fn Servo_SelectorList_Parse(
7993     selector_list: &nsACString,
7994     is_chrome: bool,
7995 ) -> *mut SelectorList {
7996     use style::selector_parser::SelectorParser;
7998     let url_data = if is_chrome {
7999         dummy_chrome_url_data()
8000     } else {
8001         dummy_url_data()
8002     };
8004     let input = selector_list.as_str_unchecked();
8005     let selector_list = match SelectorParser::parse_author_origin_no_namespace(&input, url_data) {
8006         Ok(selector_list) => selector_list,
8007         Err(..) => return ptr::null_mut(),
8008     };
8010     Box::into_raw(Box::new(selector_list))
8013 #[no_mangle]
8014 pub unsafe extern "C" fn Servo_SelectorList_Drop(list: *mut SelectorList) {
8015     let _ = Box::from_raw(list);
8018 #[no_mangle]
8019 pub unsafe extern "C" fn Servo_IsValidCSSColor(value: &nsACString) -> bool {
8020     let mut input = ParserInput::new(value.as_str_unchecked());
8021     let mut input = Parser::new(&mut input);
8022     let context = ParserContext::new(
8023         Origin::Author,
8024         dummy_url_data(),
8025         Some(CssRuleType::Style),
8026         ParsingMode::DEFAULT,
8027         QuirksMode::NoQuirks,
8028         /* namespaces = */ Default::default(),
8029         None,
8030         None,
8031     );
8032     specified::Color::is_valid(&context, &mut input)
8035 #[no_mangle]
8036 pub unsafe extern "C" fn Servo_ComputeColor(
8037     raw_data: Option<&PerDocumentStyleData>,
8038     current_color: structs::nscolor,
8039     value: &nsACString,
8040     result_color: &mut structs::nscolor,
8041     was_current_color: *mut bool,
8042     loader: *mut Loader,
8043 ) -> bool {
8044     let mut input = ParserInput::new(value.as_str_unchecked());
8045     let mut input = Parser::new(&mut input);
8046     let reporter = loader.as_mut().and_then(|loader| {
8047         // Make an ErrorReporter that will report errors as being "from DOM".
8048         ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
8049     });
8051     let context = ParserContext::new(
8052         Origin::Author,
8053         dummy_url_data(),
8054         Some(CssRuleType::Style),
8055         ParsingMode::DEFAULT,
8056         QuirksMode::NoQuirks,
8057         /* namespaces = */ Default::default(),
8058         reporter.as_ref().map(|e| e as &dyn ParseErrorReporter),
8059         None,
8060     );
8062     let data;
8063     let device = match raw_data {
8064         Some(d) => {
8065             data = d.borrow();
8066             Some(data.stylist.device())
8067         },
8068         None => None,
8069     };
8071     let computed = match specified::Color::parse_and_compute(&context, &mut input, device) {
8072         Some(c) => c,
8073         None => return false,
8074     };
8076     let current_color = style::gecko::values::convert_nscolor_to_absolute_color(current_color);
8077     if !was_current_color.is_null() {
8078         *was_current_color = computed.is_currentcolor();
8079     }
8081     let rgba = computed.resolve_to_absolute(&current_color);
8082     *result_color = style::gecko::values::convert_absolute_color_to_nscolor(&rgba);
8083     true
8086 #[no_mangle]
8087 pub unsafe extern "C" fn Servo_ColorTo(
8088     from_color: &nsACString,
8089     to_color_space: &nsACString,
8090     result_color: &mut nsACString,
8091     result_components: &mut nsTArray<f32>,
8092     result_adjusted: &mut bool,
8093     loader: *mut Loader,
8094 ) -> bool {
8095     // Figure out the color space.
8096     let mut input = ParserInput::new(to_color_space.as_str_unchecked());
8097     let mut input = Parser::new(&mut input);
8098     let to_color_space = match ColorSpace::parse(&mut input) {
8099         Ok(color_space) => color_space,
8100         Err(_) => {
8101             // Can't parse the color space? Fail the conversion.
8102             return false;
8103         },
8104     };
8106     let mut input = ParserInput::new(from_color.as_str_unchecked());
8107     let mut input = Parser::new(&mut input);
8109     let reporter = loader.as_mut().and_then(|loader| {
8110         // Make an ErrorReporter that will report errors as being "from DOM".
8111         ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
8112     });
8114     let context = ParserContext::new(
8115         Origin::Author,
8116         dummy_url_data(),
8117         Some(CssRuleType::Style),
8118         ParsingMode::DEFAULT,
8119         QuirksMode::NoQuirks,
8120         /* namespaces = */ Default::default(),
8121         reporter.as_ref().map(|e| e as &dyn ParseErrorReporter),
8122         None,
8123     );
8125     let specified = match specified::Color::parse(&context, &mut input) {
8126         Ok(color) => color,
8127         Err(_) => return false,
8128     };
8130     let color = match specified {
8131         specified::Color::Absolute(ref absolute) => &absolute.color,
8132         _ => {
8133             // Can't do anything with a non-absolute color from here, so we
8134             // fail the conversion.
8135             return false;
8136         },
8137     };
8139     let color = color.to_color_space(to_color_space);
8140     let mut s = String::new();
8141     color
8142         .write_author_preferred_value(&mut CssWriter::new(&mut s))
8143         .unwrap();
8144     result_color.assign(&s);
8146     result_components.assign_from_iter_pod(color.raw_components().iter().copied());
8148     // For now we don't do gamut mapping, so always false.
8149     *result_adjusted = false;
8151     true
8154 #[no_mangle]
8155 pub extern "C" fn Servo_ResolveColor(
8156     color: &computed::Color,
8157     foreground: &style::color::AbsoluteColor,
8158 ) -> style::color::AbsoluteColor {
8159     color.resolve_to_absolute(foreground)
8162 #[no_mangle]
8163 pub extern "C" fn Servo_ResolveCalcLengthPercentage(
8164     calc: &computed::length_percentage::CalcLengthPercentage,
8165     basis: f32,
8166 ) -> f32 {
8167     calc.resolve(computed::Length::new(basis)).px()
8170 #[no_mangle]
8171 pub extern "C" fn Servo_ConvertColorSpace(
8172     color: &AbsoluteColor,
8173     color_space: ColorSpace,
8174 ) -> AbsoluteColor {
8175     color.to_color_space(color_space)
8178 #[no_mangle]
8179 pub unsafe extern "C" fn Servo_IntersectionObserverRootMargin_Parse(
8180     value: &nsACString,
8181     result: *mut IntersectionObserverRootMargin,
8182 ) -> bool {
8183     let value = value.as_str_unchecked();
8184     let result = result.as_mut().unwrap();
8186     let mut input = ParserInput::new(&value);
8187     let mut parser = Parser::new(&mut input);
8189     let url_data = dummy_url_data();
8190     let context = ParserContext::new(
8191         Origin::Author,
8192         url_data,
8193         Some(CssRuleType::Style),
8194         ParsingMode::DEFAULT,
8195         QuirksMode::NoQuirks,
8196         /* namespaces = */ Default::default(),
8197         None,
8198         None,
8199     );
8201     let margin = parser.parse_entirely(|p| IntersectionObserverRootMargin::parse(&context, p));
8202     match margin {
8203         Ok(margin) => {
8204             *result = margin;
8205             true
8206         },
8207         Err(..) => false,
8208     }
8211 #[no_mangle]
8212 pub extern "C" fn Servo_IntersectionObserverRootMargin_ToString(
8213     root_margin: &IntersectionObserverRootMargin,
8214     result: &mut nsACString,
8215 ) {
8216     let mut writer = CssWriter::new(result);
8217     root_margin.to_css(&mut writer).unwrap();
8220 #[no_mangle]
8221 pub extern "C" fn Servo_ParseTransformIntoMatrix(
8222     value: &nsACString,
8223     contain_3d: &mut bool,
8224     result: &mut structs::Matrix4x4Components,
8225 ) -> bool {
8226     use style::properties::longhands::transform;
8228     let string = unsafe { value.as_str_unchecked() };
8229     let mut input = ParserInput::new(&string);
8230     let mut parser = Parser::new(&mut input);
8231     let context = ParserContext::new(
8232         Origin::Author,
8233         unsafe { dummy_url_data() },
8234         Some(CssRuleType::Style),
8235         ParsingMode::DEFAULT,
8236         QuirksMode::NoQuirks,
8237         /* namespaces = */ Default::default(),
8238         None,
8239         None,
8240     );
8242     let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
8243         Ok(t) => t,
8244         Err(..) => return false,
8245     };
8247     let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
8248         Ok(result) => result,
8249         Err(..) => return false,
8250     };
8252     *result = m.to_array();
8253     *contain_3d = is_3d;
8254     true
8257 #[no_mangle]
8258 pub extern "C" fn Servo_ParseFilters(
8259     value: &nsACString,
8260     ignore_urls: bool,
8261     data: *mut URLExtraData,
8262     out: &mut style::OwnedSlice<Filter>,
8263 ) -> bool {
8264     use style::values::specified::effects::SpecifiedFilter;
8266     let string = unsafe { value.as_str_unchecked() };
8267     let mut input = ParserInput::new(&string);
8268     let mut parser = Parser::new(&mut input);
8269     let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
8270     let context = ParserContext::new(
8271         Origin::Author,
8272         url_data,
8273         None,
8274         ParsingMode::DEFAULT,
8275         QuirksMode::NoQuirks,
8276         /* namespaces = */ Default::default(),
8277         None,
8278         None,
8279     );
8281     let mut filters = vec![];
8283     if parser
8284         .try_parse(|i| i.expect_ident_matching("none"))
8285         .is_ok()
8286     {
8287         return parser.expect_exhausted().is_ok();
8288     }
8290     if parser.is_exhausted() {
8291         return false;
8292     }
8294     while !parser.is_exhausted() {
8295         let specified_filter = match SpecifiedFilter::parse(&context, &mut parser) {
8296             Ok(f) => f,
8297             Err(..) => return false,
8298         };
8300         let filter = match specified_filter.to_computed_value_without_context() {
8301             Ok(f) => f,
8302             Err(..) => return false,
8303         };
8305         if ignore_urls && matches!(filter, Filter::Url(_)) {
8306             continue;
8307         }
8309         filters.push(filter);
8310     }
8312     *out = style::OwnedSlice::from(filters);
8313     true
8316 #[no_mangle]
8317 pub unsafe extern "C" fn Servo_ParseFontShorthandForMatching(
8318     value: &nsACString,
8319     data: *mut URLExtraData,
8320     family: &mut FontFamilyList,
8321     style: &mut FontStyle,
8322     stretch: &mut FontStretch,
8323     weight: &mut FontWeight,
8324     size: Option<&mut f32>,
8325     small_caps: Option<&mut bool>,
8326 ) -> bool {
8327     use style::properties::shorthands::font;
8328     use style::values::generics::font::FontStyle as GenericFontStyle;
8329     use style::values::specified::font as specified;
8331     let string = value.as_str_unchecked();
8332     let mut input = ParserInput::new(&string);
8333     let mut parser = Parser::new(&mut input);
8334     let url_data = UrlExtraData::from_ptr_ref(&data);
8335     let context = ParserContext::new(
8336         Origin::Author,
8337         url_data,
8338         Some(CssRuleType::FontFace),
8339         ParsingMode::DEFAULT,
8340         QuirksMode::NoQuirks,
8341         /* namespaces = */ Default::default(),
8342         None,
8343         None,
8344     );
8346     let font = match parser.parse_entirely(|f| font::parse_value(&context, f)) {
8347         Ok(f) => f,
8348         Err(..) => return false,
8349     };
8351     // The system font is not acceptable, so we return false.
8352     match font.font_family {
8353         specified::FontFamily::Values(list) => *family = list,
8354         specified::FontFamily::System(_) => return false,
8355     }
8357     let specified_font_style = match font.font_style {
8358         specified::FontStyle::Specified(ref s) => s,
8359         specified::FontStyle::System(_) => return false,
8360     };
8362     *style = match *specified_font_style {
8363         GenericFontStyle::Normal => FontStyle::NORMAL,
8364         GenericFontStyle::Italic => FontStyle::ITALIC,
8365         GenericFontStyle::Oblique(ref angle) => FontStyle::oblique(angle.degrees()),
8366     };
8368     *stretch = match font.font_stretch {
8369         specified::FontStretch::Keyword(ref k) => k.compute(),
8370         specified::FontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()),
8371         specified::FontStretch::System(_) => return false,
8372     };
8374     *weight = match font.font_weight {
8375         specified::FontWeight::Absolute(w) => w.compute(),
8376         // Resolve relative font weights against the initial of font-weight
8377         // (normal, which is equivalent to 400).
8378         specified::FontWeight::Bolder => FontWeight::normal().bolder(),
8379         specified::FontWeight::Lighter => FontWeight::normal().lighter(),
8380         specified::FontWeight::System(_) => return false,
8381     };
8383     // XXX This is unfinished; see values::specified::FontSize::ToComputedValue
8384     // for a more complete implementation (but we can't use it as-is).
8385     if let Some(size) = size {
8386         *size = match font.font_size {
8387             specified::FontSize::Length(lp) => {
8388                 use style::values::generics::transform::ToAbsoluteLength;
8389                 match lp.to_pixel_length(None) {
8390                     Ok(len) => len,
8391                     Err(..) => return false,
8392                 }
8393             },
8394             specified::FontSize::Keyword(info) => {
8395                 let keyword = if info.kw != specified::FontSizeKeyword::Math {
8396                     info.kw
8397                 } else {
8398                     specified::FontSizeKeyword::Medium
8399                 };
8400                 // Map absolute-size keywords to sizes.
8401                 // TODO: Maybe get a meaningful quirks / base size from the caller?
8402                 let quirks_mode = QuirksMode::NoQuirks;
8403                 keyword
8404                     .to_length_without_context(
8405                         quirks_mode,
8406                         computed::Length::new(specified::FONT_MEDIUM_PX),
8407                     )
8408                     .0
8409                     .px()
8410             },
8411             // smaller, larger not currently supported
8412             specified::FontSize::Smaller |
8413             specified::FontSize::Larger |
8414             specified::FontSize::System(_) => {
8415                 return false;
8416             },
8417         };
8418     }
8420     if let Some(small_caps) = small_caps {
8421         use style::computed_values::font_variant_caps::T::SmallCaps;
8422         *small_caps = font.font_variant_caps == SmallCaps;
8423     }
8425     true
8428 #[no_mangle]
8429 pub unsafe extern "C" fn Servo_SourceSizeList_Parse(value: &nsACString) -> *mut SourceSizeList {
8430     let value = value.as_str_unchecked();
8431     let mut input = ParserInput::new(value);
8432     let mut parser = Parser::new(&mut input);
8434     let context = ParserContext::new(
8435         Origin::Author,
8436         dummy_url_data(),
8437         Some(CssRuleType::Style),
8438         ParsingMode::DEFAULT,
8439         QuirksMode::NoQuirks,
8440         /* namespaces = */ Default::default(),
8441         None,
8442         None,
8443     );
8445     // NB: Intentionally not calling parse_entirely.
8446     let list = SourceSizeList::parse(&context, &mut parser);
8447     Box::into_raw(Box::new(list))
8450 #[no_mangle]
8451 pub unsafe extern "C" fn Servo_SourceSizeList_Evaluate(
8452     raw_data: &PerDocumentStyleData,
8453     list: Option<&SourceSizeList>,
8454 ) -> i32 {
8455     let doc_data = raw_data.borrow();
8456     let device = doc_data.stylist.device();
8457     let quirks_mode = doc_data.stylist.quirks_mode();
8459     let result = match list {
8460         Some(list) => list.evaluate(device, quirks_mode),
8461         None => SourceSizeList::empty().evaluate(device, quirks_mode),
8462     };
8464     result.0
8467 #[no_mangle]
8468 pub unsafe extern "C" fn Servo_SourceSizeList_Drop(list: *mut SourceSizeList) {
8469     let _ = Box::from_raw(list);
8472 #[no_mangle]
8473 pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges(
8474     root: &RawGeckoElement,
8475     document_style: &PerDocumentStyleData,
8476     non_document_styles: &nsTArray<&AuthorStyles>,
8477     states_changed: u64,
8478 ) {
8479     use style::invalidation::element::document_state::DocumentStateInvalidationProcessor;
8480     use style::invalidation::element::invalidator::TreeStyleInvalidator;
8482     let document_data = document_style.borrow();
8484     let iter = document_data
8485         .stylist
8486         .iter_origins()
8487         .map(|(data, _origin)| data)
8488         .chain(
8489             non_document_styles
8490                 .iter()
8491                 .map(|author_styles| &*author_styles.data),
8492         );
8494     let mut selector_caches = SelectorCaches::default();
8495     let root = GeckoElement(root);
8496     let mut processor = DocumentStateInvalidationProcessor::new(
8497         iter,
8498         DocumentState::from_bits_retain(states_changed),
8499         &mut selector_caches,
8500         root.as_node().owner_doc().quirks_mode(),
8501     );
8503     let result =
8504         TreeStyleInvalidator::new(root, /* stack_limit_checker = */ None, &mut processor)
8505             .invalidate();
8507     debug_assert!(!result.has_invalidated_siblings(), "How in the world?");
8508     if result.has_invalidated_descendants() {
8509         bindings::Gecko_NoteDirtySubtreeForInvalidation(root.0);
8510     } else if result.has_invalidated_self() {
8511         bindings::Gecko_NoteDirtyElement(root.0);
8512     }
8515 #[no_mangle]
8516 pub unsafe extern "C" fn Servo_PseudoClass_GetStates(name: &nsACString) -> u64 {
8517     let name = name.as_str_unchecked();
8518     match NonTSPseudoClass::parse_non_functional(name) {
8519         None => 0,
8520         // Ignore :any-link since it contains both visited and unvisited state.
8521         Some(NonTSPseudoClass::AnyLink) => 0,
8522         Some(pseudo_class) => pseudo_class.state_flag().bits(),
8523     }
8526 #[no_mangle]
8527 pub unsafe extern "C" fn Servo_UseCounters_Create() -> *mut UseCounters {
8528     Box::into_raw(Box::<UseCounters>::default())
8531 #[no_mangle]
8532 pub unsafe extern "C" fn Servo_UseCounters_Drop(c: *mut UseCounters) {
8533     let _ = Box::from_raw(c);
8536 #[no_mangle]
8537 pub unsafe extern "C" fn Servo_UseCounters_Merge(
8538     doc_counters: &UseCounters,
8539     sheet_counters: &UseCounters,
8540 ) {
8541     doc_counters.merge(sheet_counters)
8544 #[no_mangle]
8545 pub unsafe extern "C" fn Servo_IsPropertyIdRecordedInUseCounter(
8546     use_counters: &UseCounters,
8547     id: nsCSSPropertyID,
8548 ) -> bool {
8549     let id = NonCustomPropertyId::from_nscsspropertyid(id).unwrap();
8550     use_counters.non_custom_properties.recorded(id)
8553 #[no_mangle]
8554 pub unsafe extern "C" fn Servo_IsUnknownPropertyRecordedInUseCounter(
8555     use_counters: &UseCounters,
8556     p: CountedUnknownProperty,
8557 ) -> bool {
8558     use_counters.counted_unknown_properties.recorded(p)
8561 #[no_mangle]
8562 pub unsafe extern "C" fn Servo_IsCssPropertyRecordedInUseCounter(
8563     use_counters: &UseCounters,
8564     property: &nsACString,
8565     known_prop: *mut bool,
8566 ) -> bool {
8567     *known_prop = false;
8569     let prop_name = property.as_str_unchecked();
8570     if let Ok(p) = PropertyId::parse_unchecked_for_testing(prop_name) {
8571         if let Some(id) = p.non_custom_id() {
8572             *known_prop = true;
8573             return use_counters.non_custom_properties.recorded(id);
8574         }
8575     }
8577     if let Some(p) = CountedUnknownProperty::parse_for_testing(prop_name) {
8578         *known_prop = true;
8579         return use_counters.counted_unknown_properties.recorded(p);
8580     }
8582     false
8585 #[no_mangle]
8586 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Create(
8587     buffer: *mut u8,
8588     len: usize,
8589 ) -> *mut SharedMemoryBuilder {
8590     Box::into_raw(Box::new(SharedMemoryBuilder::new(buffer, len)))
8593 #[no_mangle]
8594 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_AddStylesheet(
8595     builder: &mut SharedMemoryBuilder,
8596     contents: &StylesheetContents,
8597     error_message: &mut nsACString,
8598 ) -> *const LockedCssRules {
8599     // Assert some things we assume when we create a style sheet from shared
8600     // memory.
8601     debug_assert_eq!(contents.origin, Origin::UserAgent);
8602     debug_assert_eq!(contents.quirks_mode, QuirksMode::NoQuirks);
8603     debug_assert!(contents.source_map_url.read().is_none());
8604     debug_assert!(contents.source_url.read().is_none());
8606     match builder.write(&contents.rules) {
8607         Ok(rules_ptr) => &**rules_ptr,
8608         Err(message) => {
8609             error_message.assign(&message);
8610             ptr::null()
8611         },
8612     }
8615 #[no_mangle]
8616 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_GetLength(
8617     builder: &SharedMemoryBuilder,
8618 ) -> usize {
8619     builder.len()
8622 #[no_mangle]
8623 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Drop(builder: *mut SharedMemoryBuilder) {
8624     let _ = Box::from_raw(builder);
8627 #[no_mangle]
8628 pub unsafe extern "C" fn Servo_StyleArcSlice_EmptyPtr() -> *mut c_void {
8629     style_traits::arc_slice::ArcSlice::<u64>::leaked_empty_ptr()
8632 #[no_mangle]
8633 pub unsafe extern "C" fn Servo_LoadData_GetLazy(
8634     source: &url::LoadDataSource,
8635 ) -> *const url::LoadData {
8636     source.get()
8639 #[no_mangle]
8640 pub extern "C" fn Servo_LengthPercentage_ToCss(
8641     lp: &computed::LengthPercentage,
8642     result: &mut nsACString,
8643 ) {
8644     lp.to_css(&mut CssWriter::new(result)).unwrap();
8647 #[no_mangle]
8648 pub extern "C" fn Servo_FontStyle_ToCss(s: &FontStyle, result: &mut nsACString) {
8649     s.to_css(&mut CssWriter::new(result)).unwrap()
8652 #[no_mangle]
8653 pub extern "C" fn Servo_FontWeight_ToCss(w: &FontWeight, result: &mut nsACString) {
8654     w.to_css(&mut CssWriter::new(result)).unwrap()
8657 #[no_mangle]
8658 pub extern "C" fn Servo_FontStretch_ToCss(s: &FontStretch, result: &mut nsACString) {
8659     s.to_css(&mut CssWriter::new(result)).unwrap()
8662 #[no_mangle]
8663 pub extern "C" fn Servo_FontStretch_SerializeKeyword(
8664     s: &FontStretch,
8665     result: &mut nsACString,
8666 ) -> bool {
8667     let kw = match s.as_keyword() {
8668         Some(kw) => kw,
8669         None => return false,
8670     };
8671     kw.to_css(&mut CssWriter::new(result)).unwrap();
8672     true
8675 #[no_mangle]
8676 pub unsafe extern "C" fn Servo_CursorKind_Parse(
8677     cursor: &nsACString,
8678     result: &mut computed::ui::CursorKind,
8679 ) -> bool {
8680     match computed::ui::CursorKind::from_ident(cursor.as_str_unchecked()) {
8681         Ok(c) => {
8682             *result = c;
8683             true
8684         },
8685         Err(..) => false,
8686     }
8689 #[no_mangle]
8690 pub extern "C" fn Servo_FontFamily_Generic(generic: GenericFontFamily) -> &'static FontFamily {
8691     FontFamily::generic(generic)
8694 #[no_mangle]
8695 pub extern "C" fn Servo_FontFamily_ForSystemFont(name: &nsACString, out: &mut FontFamily) {
8696     *out = FontFamily::for_system_font(&name.to_utf8());
8699 #[no_mangle]
8700 pub extern "C" fn Servo_FontFamilyList_WithNames(
8701     names: &nsTArray<computed::font::SingleFontFamily>,
8702     out: &mut FontFamilyList,
8703 ) {
8704     *out = FontFamilyList {
8705         list: style_traits::arc_slice::ArcSlice::from_iter(names.iter().cloned()),
8706     };
8709 #[no_mangle]
8710 pub extern "C" fn Servo_FamilyName_Serialize(name: &FamilyName, result: &mut nsACString) {
8711     name.to_css(&mut CssWriter::new(result)).unwrap()
8714 #[no_mangle]
8715 pub extern "C" fn Servo_GenericFontFamily_Parse(input: &nsACString) -> GenericFontFamily {
8716     let context = ParserContext::new(
8717         Origin::Author,
8718         unsafe { dummy_url_data() },
8719         Some(CssRuleType::Style),
8720         ParsingMode::DEFAULT,
8721         QuirksMode::NoQuirks,
8722         /* namespaces = */ Default::default(),
8723         None,
8724         None,
8725     );
8726     let value = input.to_utf8();
8727     let mut input = ParserInput::new(&value);
8728     let mut input = Parser::new(&mut input);
8729     GenericFontFamily::parse(&context, &mut input).unwrap_or(GenericFontFamily::None)
8732 #[no_mangle]
8733 pub extern "C" fn Servo_ColorScheme_Parse(input: &nsACString, out: &mut u8) -> bool {
8734     use style::values::specified::ColorScheme;
8736     let context = ParserContext::new(
8737         Origin::Author,
8738         unsafe { dummy_url_data() },
8739         Some(CssRuleType::Style),
8740         ParsingMode::DEFAULT,
8741         QuirksMode::NoQuirks,
8742         /* namespaces = */ Default::default(),
8743         None,
8744         None,
8745     );
8746     let input = unsafe { input.as_str_unchecked() };
8747     let mut input = ParserInput::new(&input);
8748     let mut input = Parser::new(&mut input);
8749     let scheme = match input.parse_entirely(|i| ColorScheme::parse(&context, i)) {
8750         Ok(scheme) => scheme,
8751         Err(..) => return false,
8752     };
8753     *out = scheme.raw_bits();
8754     true
8757 #[no_mangle]
8758 pub extern "C" fn Servo_LayerBlockRule_GetName(rule: &LayerBlockRule, result: &mut nsACString) {
8759     if let Some(ref name) = rule.name {
8760         name.to_css(&mut CssWriter::new(result)).unwrap()
8761     }
8764 #[no_mangle]
8765 pub extern "C" fn Servo_ScopeRule_GetStart(rule: &ScopeRule, result: &mut nsACString) {
8766     if let Some(v) = rule.bounds.start.as_ref() {
8767         v.to_css(&mut CssWriter::new(result)).unwrap();
8768     } else {
8769         result.set_is_void(true);
8770     }
8773 #[no_mangle]
8774 pub extern "C" fn Servo_ScopeRule_GetEnd(rule: &ScopeRule, result: &mut nsACString) {
8775     if let Some(v) = rule.bounds.end.as_ref() {
8776         v.to_css(&mut CssWriter::new(result)).unwrap();
8777     } else {
8778         result.set_is_void(true);
8779     }
8782 #[no_mangle]
8783 pub extern "C" fn Servo_LayerStatementRule_GetNameCount(rule: &LayerStatementRule) -> usize {
8784     rule.names.len()
8787 #[no_mangle]
8788 pub extern "C" fn Servo_LayerStatementRule_GetNameAt(
8789     rule: &LayerStatementRule,
8790     index: usize,
8791     result: &mut nsACString,
8792 ) {
8793     if let Some(ref name) = rule.names.get(index) {
8794         name.to_css(&mut CssWriter::new(result)).unwrap()
8795     }
8798 #[no_mangle]
8799 pub unsafe extern "C" fn Servo_InvalidateForViewportUnits(
8800     document_style: &PerDocumentStyleData,
8801     root: &RawGeckoElement,
8802     dynamic_only: bool,
8803 ) {
8804     let mut document_data = document_style.borrow_mut();
8805     let ref mut stylist = document_data.stylist;
8806     let device = stylist.device();
8808     if !device.used_viewport_size() {
8809         return;
8810     }
8812     if dynamic_only && !device.used_dynamic_viewport_size() {
8813         return;
8814     }
8816     // If the viewport changed, then initial values containing viewport units need to be recomputed.
8817     if stylist
8818         .get_custom_property_initial_values_flags()
8819         .intersects(ComputedValueFlags::USES_VIEWPORT_UNITS)
8820     {
8821         stylist.rebuild_initial_values_for_custom_properties();
8822     }
8824     if style::invalidation::viewport_units::invalidate(GeckoElement(root)) {
8825         // The invalidation machinery propagates the bits up, but we still need
8826         // to tell the Gecko restyle root machinery about it.
8827         bindings::Gecko_NoteDirtySubtreeForInvalidation(root);
8828     }
8831 #[no_mangle]
8832 pub extern "C" fn Servo_InterpolateColor(
8833     interpolation: ColorInterpolationMethod,
8834     left: &AbsoluteColor,
8835     right: &AbsoluteColor,
8836     progress: f32,
8837 ) -> AbsoluteColor {
8838     style::color::mix::mix(
8839         interpolation,
8840         left,
8841         progress,
8842         right,
8843         1.0 - progress,
8844         ColorMixFlags::empty(),
8845     )
8848 #[no_mangle]
8849 pub extern "C" fn Servo_EasingFunctionAt(
8850     easing_function: &ComputedTimingFunction,
8851     progress: f64,
8852     before_flag: BeforeFlag,
8853 ) -> f64 {
8854     easing_function.calculate_output(progress, before_flag, 1e-7)
8857 fn parse_no_context<'i, F, R>(string: &'i str, parse: F) -> Result<R, ()>
8858 where
8859     F: FnOnce(&ParserContext, &mut Parser<'i, '_>) -> Result<R, ParseError<'i>>,
8861     let context = ParserContext::new(
8862         Origin::Author,
8863         unsafe { dummy_url_data() },
8864         None,
8865         ParsingMode::DEFAULT,
8866         QuirksMode::NoQuirks,
8867         /* namespaces = */ Default::default(),
8868         None,
8869         None,
8870     );
8871     let mut input = ParserInput::new(string);
8872     Parser::new(&mut input)
8873         .parse_entirely(|i| parse(&context, i))
8874         .map_err(|_| ())
8877 #[no_mangle]
8878 // Parse a length without style context (for canvas2d letterSpacing/wordSpacing attributes).
8879 // This accepts absolute lengths, and if a font-metrics-getter function is passed, also
8880 // font-relative ones, but not other units (such as percentages, viewport-relative, etc)
8881 // that would require a full style context to resolve.
8882 pub extern "C" fn Servo_ParseLengthWithoutStyleContext(
8883     len: &nsACString,
8884     out: &mut f32,
8885     get_font_metrics: Option<unsafe extern "C" fn(*mut c_void) -> GeckoFontMetrics>,
8886     getter_context: *mut c_void,
8887 ) -> bool {
8888     let metrics_getter = if let Some(getter) = get_font_metrics {
8889         Some(move || -> GeckoFontMetrics { unsafe { getter(getter_context) } })
8890     } else {
8891         None
8892     };
8893     let value = parse_no_context(unsafe { len.as_str_unchecked() }, specified::Length::parse)
8894         .and_then(|p| p.to_computed_pixel_length_with_font_metrics(metrics_getter));
8895     match value {
8896         Ok(v) => {
8897             *out = v;
8898             true
8899         },
8900         Err(..) => false,
8901     }
8904 #[no_mangle]
8905 pub extern "C" fn Servo_SlowRgbToColorName(r: u8, g: u8, b: u8, result: &mut nsACString) -> bool {
8906     let mut candidates = SmallVec::<[&'static str; 5]>::new();
8907     for (name, color) in cssparser::color::all_named_colors() {
8908         if color == (r, g, b) {
8909             candidates.push(name);
8910         }
8911     }
8912     if candidates.is_empty() {
8913         return false;
8914     }
8915     // DevTools expect the first alphabetically.
8916     candidates.sort();
8917     result.assign(candidates[0]);
8918     true
8921 #[no_mangle]
8922 pub extern "C" fn Servo_ColorNameToRgb(name: &nsACString, out: &mut structs::nscolor) -> bool {
8923     match cssparser::color::parse_named_color(unsafe { name.as_str_unchecked() }) {
8924         Ok((r, g, b)) => {
8925             *out = style::gecko::values::convert_absolute_color_to_nscolor(&AbsoluteColor::new(
8926                 ColorSpace::Srgb,
8927                 r,
8928                 g,
8929                 b,
8930                 1.0,
8931             ));
8932             true
8933         },
8934         _ => false,
8935     }
8938 #[repr(u8)]
8939 pub enum RegisterCustomPropertyResult {
8940     SuccessfullyRegistered,
8941     InvalidName,
8942     AlreadyRegistered,
8943     InvalidSyntax,
8944     NoInitialValue,
8945     InvalidInitialValue,
8946     InitialValueNotComputationallyIndependent,
8949 /// https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function
8950 #[no_mangle]
8951 pub extern "C" fn Servo_RegisterCustomProperty(
8952     per_doc_data: &PerDocumentStyleData,
8953     extra_data: *mut URLExtraData,
8954     name: &nsACString,
8955     syntax: &nsACString,
8956     inherits: bool,
8957     initial_value: Option<&nsACString>,
8958 ) -> RegisterCustomPropertyResult {
8959     use self::RegisterCustomPropertyResult::*;
8960     use style::custom_properties::SpecifiedValue;
8961     use style::properties_and_values::rule::{PropertyRegistrationError, PropertyRuleName};
8962     use style::properties_and_values::syntax::Descriptor;
8964     let mut per_doc_data = per_doc_data.borrow_mut();
8965     let url_data = unsafe { UrlExtraData::from_ptr_ref(&extra_data) };
8966     let name = unsafe { name.as_str_unchecked() };
8967     let syntax = unsafe { syntax.as_str_unchecked() };
8968     let initial_value = initial_value.map(|v| unsafe { v.as_str_unchecked() });
8970     // If name is not a custom property name string, throw a SyntaxError and exit this algorithm.
8971     let name = match style::custom_properties::parse_name(name) {
8972         Ok(n) => Atom::from(n),
8973         Err(()) => return InvalidName,
8974     };
8976     // If property set already contains an entry with name as its property name (compared
8977     // codepoint-wise), throw an InvalidModificationError and exit this algorithm.
8978     if per_doc_data
8979         .stylist
8980         .custom_property_script_registry()
8981         .get(&name)
8982         .is_some()
8983     {
8984         return AlreadyRegistered;
8985     }
8986     // Attempt to consume a syntax definition from syntax. If it returns failure, throw a
8987     // SyntaxError. Otherwise, let syntax definition be the returned syntax definition.
8988     let Ok(syntax) = Descriptor::from_str(syntax, /* preserve_specified = */ false) else {
8989         return InvalidSyntax;
8990     };
8992     let initial_value = match initial_value {
8993         Some(v) => {
8994             let mut input = ParserInput::new(v);
8995             let parsed = Parser::new(&mut input)
8996                 .parse_entirely(|input| {
8997                     input.skip_whitespace();
8998                     SpecifiedValue::parse(input, url_data).map(Arc::new)
8999                 })
9000                 .ok();
9001             if parsed.is_none() {
9002                 return InvalidInitialValue;
9003             }
9004             parsed
9005         },
9006         None => None,
9007     };
9009     if let Err(error) =
9010         PropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())
9011     {
9012         return match error {
9013             PropertyRegistrationError::InitialValueNotComputationallyIndependent => {
9014                 InitialValueNotComputationallyIndependent
9015             },
9016             PropertyRegistrationError::InvalidInitialValue => InvalidInitialValue,
9017             PropertyRegistrationError::NoInitialValue => NoInitialValue,
9018         };
9019     }
9021     per_doc_data
9022         .stylist
9023         .custom_property_script_registry_mut()
9024         .register(PropertyRegistration {
9025             name: PropertyRuleName(name),
9026             data: PropertyRegistrationData {
9027                 syntax,
9028                 inherits: if inherits {
9029                     PropertyInherits::True
9030                 } else {
9031                     PropertyInherits::False
9032                 },
9033                 initial_value,
9034             },
9035             url_data: url_data.clone(),
9036             source_location: SourceLocation { line: 0, column: 0 },
9037         });
9039     per_doc_data
9040         .stylist
9041         .rebuild_initial_values_for_custom_properties();
9043     SuccessfullyRegistered
9046 #[repr(C)]
9047 pub struct PropDef {
9048     // The name of the property.
9049     pub name: Atom,
9050     // The syntax of the property.
9051     pub syntax: nsCString,
9052     // Whether the property inherits.
9053     pub inherits: bool,
9054     pub has_initial_value: bool,
9055     pub initial_value: nsCString,
9056     // True if the property was set with CSS.registerProperty
9057     pub from_js: bool,
9060 impl PropDef {
9061     /// Creates a PropDef from a name and a PropertyRegistration.
9062     pub fn new(name: Atom, property_registration: &PropertyRegistration, from_js: bool) -> Self {
9063         let mut syntax = nsCString::new();
9064         if let Some(spec) = property_registration.data.syntax.specified_string() {
9065             syntax.assign(spec);
9066         } else {
9067             // FIXME: Descriptor::to_css should behave consistently (probably this shouldn't use
9068             // the ToCss trait).
9069             property_registration
9070                 .data
9071                 .syntax
9072                 .to_css(&mut CssWriter::new(&mut syntax))
9073                 .unwrap();
9074         };
9075         let initial_value = property_registration.data.initial_value.to_css_nscstring();
9076         PropDef {
9077             name,
9078             syntax,
9079             inherits: property_registration.data.inherits(),
9080             has_initial_value: property_registration.data.initial_value.is_some(),
9081             initial_value,
9082             from_js,
9083         }
9084     }
9087 #[no_mangle]
9088 pub extern "C" fn Servo_GetRegisteredCustomProperties(
9089     per_doc_data: &PerDocumentStyleData,
9090     custom_properties: &mut ThinVec<PropDef>,
9091 ) {
9092     let stylist = &per_doc_data.borrow().stylist;
9094     custom_properties.extend(
9095         stylist
9096             .custom_property_script_registry()
9097             .get_all()
9098             .iter()
9099             .map(|(name, property_registration)| {
9100                 PropDef::new(name.clone(), property_registration, /* from_js */ true)
9101             }),
9102     );
9104     for (cascade_data, _origin) in stylist.iter_origins() {
9105         custom_properties.extend(cascade_data.custom_property_registrations().iter().map(
9106             |(name, value)| {
9107                 let property_registration = &value.last().unwrap().0;
9108                 PropDef::new(
9109                     name.clone(),
9110                     property_registration,
9111                     /* from_js */
9112                     false,
9113                 )
9114             },
9115         ))
9116     }
9119 #[repr(C)]
9120 pub struct SelectorWarningData {
9121     /// Index to the selector generating the warning.
9122     pub index: usize,
9123     /// Kind of the warning.
9124     pub kind: SelectorWarningKind,
9127 #[no_mangle]
9128 pub extern "C" fn Servo_GetSelectorWarnings(
9129     rule: &LockedStyleRule,
9130     warnings: &mut ThinVec<SelectorWarningData>,
9131 ) {
9132     read_locked_arc(rule, |r| {
9133         for (i, selector) in r.selectors.slice().iter().enumerate() {
9134             for k in SelectorWarningKind::from_selector(selector) {
9135                 warnings.push(SelectorWarningData { index: i, kind: k });
9136             }
9137         }
9138     });
9141 #[no_mangle]
9142 pub extern "C" fn Servo_GetRuleBodyText(
9143     initial_text: &nsACString,
9144     ret_val: &mut nsACString,
9145 ) {
9146     let css_text = unsafe { initial_text.as_str_unchecked() };
9147     let mut input = ParserInput::new(&css_text);
9148     let mut input = Parser::new(&mut input);
9150     let mut found_start = false;
9152     // Search forward for the opening brace.
9153     while let Ok(token) = input.next() {
9154         match *token {
9155             Token::CurlyBracketBlock => {
9156                 found_start = true;
9157                 break;
9158             },
9159             _ => {}
9160         }
9162         if token.is_parse_error() {
9163             break;
9164         }
9165     }
9168     if !found_start {
9169         ret_val.set_is_void(true);
9170         return;
9171     }
9173     let token_start = input.position();
9174     // Parse the nested block to move the parser to the end of the block
9175     let _ = input.parse_nested_block(
9176         |_i| -> Result<(), CssParseError<'_, BasicParseError>> {
9177             Ok(())
9178         }
9179     );
9181     // We're not guaranteed to have a closing bracket, but when we do, we need to move
9182     // the end offset before it.
9183     let mut token_slice = input.slice_from(token_start);
9184     if token_slice.ends_with("}") {
9185         token_slice = token_slice.strip_suffix("}").unwrap();
9186     }
9187     ret_val.assign(token_slice);
9190 #[no_mangle]
9191 pub extern "C" fn Servo_ReplaceBlockRuleBodyTextInStylesheetText(
9192     stylesheet_text: &nsACString,
9193     line: u32,
9194     column: u32,
9195     new_body_text: &nsACString,
9196     ret_val: &mut nsACString,
9197 ) {
9198     let css_text = unsafe { stylesheet_text.as_str_unchecked() };
9200     let Some(rule_start_index) = get_byte_index_from_line_and_column(css_text, line, column) else {
9201         ret_val.set_is_void(true);
9202         return;
9203     };
9205     let mut input = ParserInput::new(&css_text[rule_start_index..]);
9206     let mut input = Parser::new(&mut input);
9207     let mut found_start = false;
9209     // Search forward for the opening brace.
9210     while let Ok(token) = input.next() {
9211         if matches!(*token, Token::CurlyBracketBlock) {
9212             found_start = true;
9213             break;
9214         }
9216         if token.is_parse_error() {
9217             break;
9218         }
9219     }
9221     if !found_start {
9222         ret_val.set_is_void(true);
9223         return;
9224     }
9226     let token_start = input.position();
9227     let rule_body_start = rule_start_index + token_start.byte_index();
9228     // Parse the nested block to move the parser to the end of the block
9229     let _ = input.parse_nested_block(
9230         |_i| -> Result<(), CssParseError<'_, BasicParseError>> {
9231             Ok(())
9232         }
9233     );
9234     let mut rule_body_end = rule_start_index + input.position().byte_index();
9236     // We're not guaranteed to have a closing bracket, but when we do, we need to move
9237     // the end offset before it.
9238     let token_slice = input.slice_from(token_start);
9239     if token_slice.ends_with("}") {
9240         rule_body_end -= 1;
9241     }
9243     ret_val.append(&css_text[..rule_body_start]);
9244     ret_val.append(new_body_text);
9245     ret_val.append(&css_text[rule_body_end..]);
9248 /// Find css_text byte position corresponding to the passed line and column
9249 fn get_byte_index_from_line_and_column(
9250     css_text: &str,
9251     line: u32,
9252     column: u32,
9253 ) -> Option<usize> {
9254     // Find the byte index of the start of the passed line within css_text
9255     let mut line_byte_index = Some(0);
9256     if line != 1 {
9257         let mut current_line = 1;
9258         let mut last_byte = None;
9259         let mut bytes_iter = css_text.bytes();
9260         line_byte_index = bytes_iter.position(|byte| {
9261             // We want to get the position _after_ the EOF sequence
9262             let on_expected_line = current_line == line;
9263             let is_previous_byte_carriage_return = last_byte == Some(b'\r');
9264             last_byte = Some(byte);
9266             if byte == b'\r' {
9267                 current_line += 1;
9268             } else if byte == b'\n' {
9269                 if !is_previous_byte_carriage_return {
9270                     current_line += 1;
9271                 } else {
9272                     return false;
9273                 }
9274             }
9275             on_expected_line
9276         });
9277     }
9279     if line_byte_index.is_none() {
9280         return None;
9281     }
9283     if column == 1 {
9284         return line_byte_index;
9285     }
9287     let line_byte_index = line_byte_index.unwrap();
9288     let mut current_column = 1;
9289     for (byte_index, _char) in css_text[line_byte_index..].char_indices() {
9290         if current_column == column {
9291             return Some(line_byte_index + byte_index);
9292         }
9293         current_column += 1;
9294     }
9296     None
9299 #[repr(C)]
9300 pub struct CSSToken {
9301     pub text: nsCString,
9302     pub token_type: nsCString,
9303     pub has_unit: bool,
9304     pub unit: nsCString,
9305     pub has_number: bool,
9306     pub number: f32,
9307     pub has_value: bool,
9308     pub value: nsCString,
9309     // line and column at which the token starts
9310     pub line: u32,
9311     pub column: u32,
9314 #[no_mangle]
9315 pub unsafe extern "C" fn Servo_CSSParser_create(
9316     text: &nsACString,
9317 ) -> *mut ParserState {
9318     let css_text = unsafe { text.as_str_unchecked() };
9319     let mut parser_input = ParserInput::new(&css_text);
9320     let input = Parser::new(&mut parser_input);
9321     Box::into_raw(Box::new(input.state()))
9324 #[no_mangle]
9325 pub unsafe extern "C" fn Servo_CSSParser_destroy(
9326     state: *mut ParserState,
9327 ) {
9328     drop(Box::from_raw(state));
9331 #[no_mangle]
9332 pub unsafe extern "C" fn Servo_CSSParser_GetCurrentLine(
9333     state: &ParserState,
9334 ) -> u32 {
9335     return state.source_location().line;
9338 #[no_mangle]
9339 pub unsafe extern "C" fn Servo_CSSParser_GetCurrentColumn(
9340     state: &ParserState,
9341 ) -> u32 {
9342     return state.source_location().column;
9345 #[no_mangle]
9346 pub unsafe extern "C" fn Servo_CSSParser_NextToken(
9347     text: &nsACString,
9348     state: &mut ParserState,
9349     css_token: &mut CSSToken,
9350 ) -> bool {
9351     let css_text = unsafe { text.as_str_unchecked() };
9352     let mut parser_input = ParserInput::new(&css_text);
9353     let mut input = Parser::new(&mut parser_input);
9354     input.reset(state);
9356     let token_start = input.position();
9357     let location_start = state.source_location();
9358     let Ok(token) = &input.next_including_whitespace_and_comments() else {
9359         return false;
9360     };
9362     let token_type = match *token {
9363         Token::Ident(_) => "Ident",
9364         Token::AtKeyword(_) => "AtKeyword",
9365         Token::Hash(_) => "Hash",
9366         Token::IDHash(_) => "IDHash",
9367         Token::QuotedString(_) => "QuotedString",
9368         Token::UnquotedUrl(_) => "UnquotedUrl",
9369         Token::Delim(_) => "Delim",
9370         Token::Number{..} => "Number",
9371         Token::Percentage{..} => "Percentage",
9372         Token::Dimension{..} => "Dimension",
9373         Token::WhiteSpace(_) => "WhiteSpace",
9374         Token::Comment(_) => "Comment",
9375         Token::Colon => "Colon",
9376         Token::Semicolon => "Semicolon",
9377         Token::Comma => "Comma",
9378         Token::IncludeMatch => "IncludeMatch",
9379         Token::DashMatch => "DashMatch",
9380         Token::PrefixMatch => "PrefixMatch",
9381         Token::SuffixMatch => "SuffixMatch",
9382         Token::SubstringMatch => "SubstringMatch",
9383         Token::CDO => "CDO",
9384         Token::CDC => "CDC",
9385         Token::Function(_) => "Function",
9386         Token::ParenthesisBlock => "ParenthesisBlock",
9387         Token::SquareBracketBlock => "SquareBracketBlock",
9388         Token::CurlyBracketBlock => "CurlyBracketBlock",
9389         Token::BadUrl(_) => "BadUrl",
9390         Token::BadString(_) => "BadString",
9391         Token::CloseParenthesis => "CloseParenthesis",
9392         Token::CloseSquareBracket => "CloseSquareBracket",
9393         Token::CloseCurlyBracket => "CloseCurlyBracket",
9394     };
9396     let token_value = match *token {
9397         Token::Ident(value) |
9398         Token::AtKeyword(value) |
9399         Token::Hash(value) |
9400         Token::IDHash(value) |
9401         Token::QuotedString(value) |
9402         Token::UnquotedUrl(value) |
9403         Token::Function(value) |
9404         Token::BadUrl(value) |
9405         Token::BadString(value) => {
9406             let mut text = nsCString::new();
9407             text.assign(value.as_bytes());
9408             Some(text)
9409         },
9410         // value is a str here, we need a different branch to handle it
9411         Token::Comment(value) => {
9412             let mut text = nsCString::new();
9413             text.assign(value.as_bytes());
9414             Some(text)
9415         },
9416         // Delim and WhiteSpace also have value, but they will be similar to text, so don't
9417         // include them
9418         Token::Delim(_) |
9419         Token::WhiteSpace(_) |
9420         // Number, Percentage and Dimension expose numeric values that will be exposed in `number`
9421         Token::Number{..}  |
9422         Token::Percentage{..}  |
9423         Token::Dimension{..}  |
9424         // The rest of the tokens don't expose a string value
9425         Token::Colon |
9426         Token::Semicolon |
9427         Token::Comma |
9428         Token::IncludeMatch |
9429         Token::DashMatch |
9430         Token::PrefixMatch |
9431         Token::SuffixMatch |
9432         Token::SubstringMatch |
9433         Token::CDO |
9434         Token::CDC |
9435         Token::ParenthesisBlock |
9436         Token::SquareBracketBlock |
9437         Token::CurlyBracketBlock |
9438         Token::CloseParenthesis |
9439         Token::CloseSquareBracket |
9440         Token::CloseCurlyBracket => None
9441     };
9443     let token_unit = match *token {
9444         Token::Dimension{
9445             ref unit, ..
9446         } => {
9447             let mut unit_text = nsCString::new();
9448             unit_text.assign(unit.as_bytes());
9449             Some(unit_text)
9450         },
9451         _ => None
9452     };
9454     let token_number = match *token {
9455         Token::Dimension {
9456             ref value, ..
9457         } => Some(value),
9458         Token::Number{
9459             ref value, ..
9460         } => Some(value),
9461         Token::Percentage{
9462             ref unit_value, ..
9463         } => Some(unit_value),
9464         _ => None
9465     };
9466     css_token.has_number = token_number.is_some();
9467     if css_token.has_number {
9468         css_token.number = *token_number.unwrap();
9469     }
9471     let need_to_parse_nested_block = match *token {
9472         Token::Function(_) |
9473         Token::ParenthesisBlock |
9474         Token::CurlyBracketBlock |
9475         Token::SquareBracketBlock => true,
9476         _ => false,
9477     };
9479     let mut text = nsCString::new();
9480     text.assign(&input.slice_from(token_start));
9482     css_token.text = text;
9483     css_token.token_type = token_type.into();
9484     css_token.has_value = token_value.is_some();
9485     if css_token.has_value {
9486         css_token.value = token_value.unwrap();
9487     }
9488     css_token.has_unit = token_unit.is_some();
9489     if css_token.has_unit {
9490         css_token.unit = token_unit.unwrap();
9491     }
9493     css_token.line = location_start.line;
9494     css_token.column = location_start.column;
9496     if need_to_parse_nested_block {
9497         let _ = input.parse_nested_block(
9498             |i| -> Result<(), CssParseError<'_, BasicParseError>> {
9499                 *state = i.state();
9500                 Ok(())
9501             },
9502         );
9503     } else {
9504         *state = input.state();
9505     }
9507     return true;