Backed out 2 changesets (bug 1882581) for causing bustage on InspectorUtils.cpp....
[gecko.git] / servo / ports / geckolib / glue.rs
blob83c55ad9e01f43377e3cfcbedd4dd530c97395d2
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, 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     MediaRule, NamespaceRule, Origin, OriginSet, PagePseudoClassFlags, PageRule, PropertyRule,
141     SanitizationData, SanitizationKind, StyleRule, StylesheetContents,
142     StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData,
144 use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
145 use style::thread_state;
146 use style::traversal::resolve_style;
147 use style::traversal::DomTraversal;
148 use style::traversal_flags::{self, TraversalFlags};
149 use style::use_counters::UseCounters;
150 use style::values::animated::{Animate, Procedure, ToAnimatedZero};
151 use style::values::computed::easing::ComputedTimingFunction;
152 use style::values::computed::effects::Filter;
153 use style::values::computed::font::{
154     FamilyName, FontFamily, FontFamilyList, FontStretch, FontStyle, FontWeight, GenericFontFamily,
156 use style::values::computed::{self, Context, ToComputedValue};
157 use style::values::distance::ComputeSquaredDistance;
158 use style::values::generics::color::ColorMixFlags;
159 use style::values::generics::easing::BeforeFlag;
160 use style::values::specified::gecko::IntersectionObserverRootMargin;
161 use style::values::specified::source_size_list::SourceSizeList;
162 use style::values::specified::{AbsoluteLength, NoCalcLength};
163 use style::values::{specified, AtomIdent, CustomIdent, KeyframesName};
164 use style_traits::{CssWriter, ParseError, ParsingMode, ToCss};
165 use thin_vec::ThinVec;
166 use to_shmem::SharedMemoryBuilder;
168 trait ClosureHelper {
169     fn invoke(&self, property_id: Option<NonCustomPropertyId>);
172 impl ClosureHelper for DeclarationBlockMutationClosure {
173     #[inline]
174     fn invoke(&self, property_id: Option<NonCustomPropertyId>) {
175         if let Some(function) = self.function.as_ref() {
176             let gecko_prop_id = match property_id {
177                 Some(p) => p.to_nscsspropertyid(),
178                 None => nsCSSPropertyID::eCSSPropertyExtra_variable,
179             };
180             unsafe { function(self.data, gecko_prop_id) }
181         }
182     }
186  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
187  * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
188  * those signatures as well, giving us a second declaration of all the Servo_* functions in this
189  * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
190  * depend on but good enough for our purposes.
191  */
193 // A dummy url data for where we don't pass url data in.
194 static mut DUMMY_URL_DATA: *mut URLExtraData = 0 as *mut _;
195 static mut DUMMY_CHROME_URL_DATA: *mut URLExtraData = 0 as *mut _;
197 #[no_mangle]
198 pub unsafe extern "C" fn Servo_Initialize(
199     dummy_url_data: *mut URLExtraData,
200     dummy_chrome_url_data: *mut URLExtraData,
201 ) {
202     use style::gecko_bindings::sugar::origin_flags;
204     // Pretend that we're a Servo Layout thread, to make some assertions happy.
205     thread_state::initialize(thread_state::ThreadState::LAYOUT);
207     debug_assert!(is_main_thread());
208     lazy_static::initialize(&STYLE_THREAD_POOL);
210     // Perform some debug-only runtime assertions.
211     origin_flags::assert_flags_match();
212     traversal_flags::assert_traversal_flags_match();
213     specified::font::assert_variant_east_asian_matches();
214     specified::font::assert_variant_ligatures_matches();
216     DUMMY_URL_DATA = dummy_url_data;
217     DUMMY_CHROME_URL_DATA = dummy_chrome_url_data;
220 #[no_mangle]
221 pub unsafe extern "C" fn Servo_Shutdown() {
222     DUMMY_URL_DATA = ptr::null_mut();
223     DUMMY_CHROME_URL_DATA = ptr::null_mut();
224     Stylist::shutdown();
225     url::shutdown();
228 #[inline(always)]
229 unsafe fn dummy_url_data() -> &'static UrlExtraData {
230     UrlExtraData::from_ptr_ref(&DUMMY_URL_DATA)
233 #[allow(dead_code)]
234 fn is_main_thread() -> bool {
235     unsafe { bindings::Gecko_IsMainThread() }
238 #[allow(dead_code)]
239 fn is_dom_worker_thread() -> bool {
240     unsafe { bindings::Gecko_IsDOMWorkerThread() }
243 thread_local! {
244     /// Thread-local style data for DOM workers
245     static DOM_WORKER_RWLOCK: SharedRwLock = SharedRwLock::new();
248 #[allow(dead_code)]
249 fn is_in_servo_traversal() -> bool {
250     unsafe { bindings::Gecko_IsInServoTraversal() }
253 fn create_shared_context<'a>(
254     global_style_data: &GlobalStyleData,
255     guard: &'a SharedRwLockReadGuard,
256     stylist: &'a Stylist,
257     traversal_flags: TraversalFlags,
258     snapshot_map: &'a ServoElementSnapshotTable,
259 ) -> SharedStyleContext<'a> {
260     SharedStyleContext {
261         stylist: &stylist,
262         visited_styles_enabled: stylist.device().visited_styles_enabled(),
263         options: global_style_data.options.clone(),
264         guards: StylesheetGuards::same(guard),
265         current_time_for_animations: 0.0, // Unused for Gecko, at least for now.
266         traversal_flags,
267         snapshot_map,
268     }
271 fn traverse_subtree(
272     element: GeckoElement,
273     global_style_data: &GlobalStyleData,
274     per_doc_data: &PerDocumentStyleDataImpl,
275     guard: &SharedRwLockReadGuard,
276     traversal_flags: TraversalFlags,
277     snapshots: &ServoElementSnapshotTable,
278 ) {
279     let shared_style_context = create_shared_context(
280         &global_style_data,
281         &guard,
282         &per_doc_data.stylist,
283         traversal_flags,
284         snapshots,
285     );
287     let token = RecalcStyleOnly::pre_traverse(element, &shared_style_context);
289     if !token.should_traverse() {
290         return;
291     }
293     debug!("Traversing subtree from {:?}", element);
295     let thread_pool_holder = &*STYLE_THREAD_POOL;
296     let pool;
297     let thread_pool = if traversal_flags.contains(TraversalFlags::ParallelTraversal) {
298         pool = thread_pool_holder.pool();
299         pool.as_ref()
300     } else {
301         None
302     };
304     let traversal = RecalcStyleOnly::new(shared_style_context);
305     driver::traverse_dom(&traversal, token, thread_pool);
308 /// Traverses the subtree rooted at `root` for restyling.
310 /// Returns whether the root was restyled. Whether anything else was restyled or
311 /// not can be inferred from the dirty bits in the rest of the tree.
312 #[no_mangle]
313 pub extern "C" fn Servo_TraverseSubtree(
314     root: &RawGeckoElement,
315     raw_data: &PerDocumentStyleData,
316     snapshots: *const ServoElementSnapshotTable,
317     raw_flags: ServoTraversalFlags,
318 ) -> bool {
319     let traversal_flags = TraversalFlags::from_bits_retain(raw_flags);
320     debug_assert!(!snapshots.is_null());
322     let element = GeckoElement(root);
324     debug!("Servo_TraverseSubtree (flags={:?})", traversal_flags);
325     debug!("{:?}", ShowSubtreeData(element.as_node()));
327     if cfg!(debug_assertions) {
328         if let Some(parent) = element.traversal_parent() {
329             let data = parent
330                 .borrow_data()
331                 .expect("Styling element with unstyled parent");
332             assert!(
333                 !data.styles.is_display_none(),
334                 "Styling element with display: none parent"
335             );
336         }
337     }
339     let needs_animation_only_restyle =
340         element.has_animation_only_dirty_descendants() || element.has_animation_restyle_hints();
342     let per_doc_data = raw_data.borrow();
343     debug_assert!(!per_doc_data.stylist.stylesheets_have_changed());
345     let global_style_data = &*GLOBAL_STYLE_DATA;
346     let guard = global_style_data.shared_lock.read();
348     let was_initial_style = !element.has_data();
350     if needs_animation_only_restyle {
351         debug!(
352             "Servo_TraverseSubtree doing animation-only restyle (aodd={})",
353             element.has_animation_only_dirty_descendants()
354         );
355         traverse_subtree(
356             element,
357             &global_style_data,
358             &per_doc_data,
359             &guard,
360             traversal_flags | TraversalFlags::AnimationOnly,
361             unsafe { &*snapshots },
362         );
363     }
365     traverse_subtree(
366         element,
367         &global_style_data,
368         &per_doc_data,
369         &guard,
370         traversal_flags,
371         unsafe { &*snapshots },
372     );
374     debug!(
375         "Servo_TraverseSubtree complete (dd={}, aodd={}, lfcd={}, lfc={}, data={:?})",
376         element.has_dirty_descendants(),
377         element.has_animation_only_dirty_descendants(),
378         element.descendants_need_frames(),
379         element.needs_frame(),
380         element.borrow_data().unwrap()
381     );
383     if was_initial_style {
384         debug_assert!(!element.borrow_data().unwrap().contains_restyle_data());
385         false
386     } else {
387         let element_was_restyled = element.borrow_data().unwrap().contains_restyle_data();
388         element_was_restyled
389     }
392 /// Checks whether the rule tree has crossed its threshold for unused nodes, and
393 /// if so, frees them.
394 #[no_mangle]
395 pub extern "C" fn Servo_MaybeGCRuleTree(raw_data: &PerDocumentStyleData) {
396     let per_doc_data = raw_data.borrow_mut();
397     per_doc_data.stylist.rule_tree().maybe_gc();
400 #[no_mangle]
401 pub extern "C" fn Servo_AnimationValues_Interpolate(
402     from: &AnimationValue,
403     to: &AnimationValue,
404     progress: f64,
405 ) -> Strong<AnimationValue> {
406     if let Ok(value) = from.animate(to, Procedure::Interpolate { progress }) {
407         Arc::new(value).into()
408     } else {
409         Strong::null()
410     }
413 #[no_mangle]
414 pub extern "C" fn Servo_AnimationValues_IsInterpolable(
415     from: &AnimationValue,
416     to: &AnimationValue,
417 ) -> bool {
418     from.interpolable_with(to)
421 #[no_mangle]
422 pub extern "C" fn Servo_AnimationValues_Add(
423     a: &AnimationValue,
424     b: &AnimationValue,
425 ) -> Strong<AnimationValue> {
426     if let Ok(value) = a.animate(b, Procedure::Add) {
427         Arc::new(value).into()
428     } else {
429         Strong::null()
430     }
433 #[no_mangle]
434 pub extern "C" fn Servo_AnimationValues_Accumulate(
435     a: &AnimationValue,
436     b: &AnimationValue,
437     count: u64,
438 ) -> Strong<AnimationValue> {
439     if let Ok(value) = a.animate(b, Procedure::Accumulate { count }) {
440         Arc::new(value).into()
441     } else {
442         Strong::null()
443     }
446 #[no_mangle]
447 pub extern "C" fn Servo_AnimationValues_GetZeroValue(
448     value_to_match: &AnimationValue,
449 ) -> Strong<AnimationValue> {
450     if let Ok(zero_value) = value_to_match.to_animated_zero() {
451         Arc::new(zero_value).into()
452     } else {
453         Strong::null()
454     }
457 #[no_mangle]
458 pub extern "C" fn Servo_AnimationValues_ComputeDistance(
459     from: &AnimationValue,
460     to: &AnimationValue,
461 ) -> f64 {
462     // If compute_squared_distance() failed, this function will return negative value
463     // in order to check whether we support the specified paced animation values.
464     from.compute_squared_distance(to).map_or(-1.0, |d| d.sqrt())
467 /// Compute one of the endpoints for the interpolation interval, compositing it with the
468 /// underlying value if needed.
469 /// An None returned value means, "Just use endpoint_value as-is."
470 /// It is the responsibility of the caller to ensure that |underlying_value| is provided
471 /// when it will be used.
472 fn composite_endpoint(
473     endpoint_value: Option<&AnimationValue>,
474     composite: CompositeOperation,
475     underlying_value: Option<&AnimationValue>,
476 ) -> Option<AnimationValue> {
477     match endpoint_value {
478         Some(endpoint_value) => match composite {
479             CompositeOperation::Add => underlying_value
480                 .expect("We should have an underlying_value")
481                 .animate(endpoint_value, Procedure::Add)
482                 .ok(),
483             CompositeOperation::Accumulate => underlying_value
484                 .expect("We should have an underlying value")
485                 .animate(endpoint_value, Procedure::Accumulate { count: 1 })
486                 .ok(),
487             _ => None,
488         },
489         None => underlying_value.map(|v| v.clone()),
490     }
493 /// Accumulate one of the endpoints of the animation interval.
494 /// A returned value of None means, "Just use endpoint_value as-is."
495 fn accumulate_endpoint(
496     endpoint_value: Option<&AnimationValue>,
497     composited_value: Option<AnimationValue>,
498     last_value: &AnimationValue,
499     current_iteration: u64,
500 ) -> Option<AnimationValue> {
501     debug_assert!(
502         endpoint_value.is_some() || composited_value.is_some(),
503         "Should have a suitable value to use"
504     );
506     let count = current_iteration;
507     match composited_value {
508         Some(endpoint) => last_value
509             .animate(&endpoint, Procedure::Accumulate { count })
510             .ok()
511             .or(Some(endpoint)),
512         None => last_value
513             .animate(endpoint_value.unwrap(), Procedure::Accumulate { count })
514             .ok(),
515     }
518 /// Compose the animation segment. We composite it with the underlying_value and last_value if
519 /// needed.
520 /// The caller is responsible for providing an underlying value and last value
521 /// in all situations where there are needed.
522 fn compose_animation_segment(
523     segment: &structs::AnimationPropertySegment,
524     underlying_value: Option<&AnimationValue>,
525     last_value: Option<&AnimationValue>,
526     iteration_composite: IterationCompositeOperation,
527     current_iteration: u64,
528     total_progress: f64,
529     segment_progress: f64,
530 ) -> AnimationValue {
531     // Extract keyframe values.
532     let keyframe_from_value = unsafe { segment.mFromValue.mServo.mRawPtr.as_ref() };
533     let keyframe_to_value = unsafe { segment.mToValue.mServo.mRawPtr.as_ref() };
534     let mut composited_from_value = composite_endpoint(
535         keyframe_from_value,
536         segment.mFromComposite,
537         underlying_value,
538     );
539     let mut composited_to_value =
540         composite_endpoint(keyframe_to_value, segment.mToComposite, underlying_value);
542     debug_assert!(
543         keyframe_from_value.is_some() || composited_from_value.is_some(),
544         "Should have a suitable from value to use"
545     );
546     debug_assert!(
547         keyframe_to_value.is_some() || composited_to_value.is_some(),
548         "Should have a suitable to value to use"
549     );
551     // Apply iteration composite behavior.
552     if iteration_composite == IterationCompositeOperation::Accumulate && current_iteration > 0 {
553         let last_value = last_value
554             .unwrap_or_else(|| underlying_value.expect("Should have a valid underlying value"));
556         composited_from_value = accumulate_endpoint(
557             keyframe_from_value,
558             composited_from_value,
559             last_value,
560             current_iteration,
561         );
562         composited_to_value = accumulate_endpoint(
563             keyframe_to_value,
564             composited_to_value,
565             last_value,
566             current_iteration,
567         );
568     }
570     // Use the composited value if there is one, otherwise, use the original keyframe value.
571     let from = composited_from_value
572         .as_ref()
573         .unwrap_or_else(|| keyframe_from_value.unwrap());
574     let to = composited_to_value
575         .as_ref()
576         .unwrap_or_else(|| keyframe_to_value.unwrap());
578     if segment.mToKey == segment.mFromKey {
579         return if total_progress < 0. {
580             from.clone()
581         } else {
582             to.clone()
583         };
584     }
586     match from.animate(
587         to,
588         Procedure::Interpolate {
589             progress: segment_progress,
590         },
591     ) {
592         Ok(value) => value,
593         _ => {
594             if segment_progress < 0.5 {
595                 from.clone()
596             } else {
597                 to.clone()
598             }
599         },
600     }
603 #[no_mangle]
604 pub extern "C" fn Servo_ComposeAnimationSegment(
605     segment: &structs::AnimationPropertySegment,
606     underlying_value: Option<&AnimationValue>,
607     last_value: Option<&AnimationValue>,
608     iteration_composite: IterationCompositeOperation,
609     progress: f64,
610     current_iteration: u64,
611 ) -> Strong<AnimationValue> {
612     let result = compose_animation_segment(
613         segment,
614         underlying_value,
615         last_value,
616         iteration_composite,
617         current_iteration,
618         progress,
619         progress,
620     );
621     Arc::new(result).into()
624 #[no_mangle]
625 pub extern "C" fn Servo_AnimationCompose(
626     value_map: &mut AnimationValueMap,
627     base_values: &structs::RawServoAnimationValueTable,
628     css_property: &structs::AnimatedPropertyID,
629     segment: &structs::AnimationPropertySegment,
630     last_segment: &structs::AnimationPropertySegment,
631     computed_timing: &structs::ComputedTiming,
632     iteration_composite: IterationCompositeOperation,
633 ) {
634     use style::gecko_bindings::bindings::Gecko_AnimationGetBaseStyle;
635     use style::gecko_bindings::bindings::Gecko_GetPositionInSegment;
636     use style::gecko_bindings::bindings::Gecko_GetProgressFromComputedTiming;
638     let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(css_property) {
639         Some(property) if property.as_borrowed().is_animatable() => property,
640         _ => return,
641     };
643     // We will need an underlying value if either of the endpoints is null...
644     let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
645                                 segment.mToValue.mServo.mRawPtr.is_null() ||
646                                 // ... or if they have a non-replace composite mode ...
647                                 segment.mFromComposite != CompositeOperation::Replace ||
648                                 segment.mToComposite != CompositeOperation::Replace ||
649                                 // ... or if we accumulate onto the last value and it is null.
650                                 (iteration_composite == IterationCompositeOperation::Accumulate &&
651                                  computed_timing.mCurrentIteration > 0 &&
652                                  last_segment.mToValue.mServo.mRawPtr.is_null());
654     // If either of the segment endpoints are null, get the underlying value to
655     // use from the current value in the values map (set by a lower-priority
656     // effect), or, if there is no current value, look up the cached base value
657     // for this property.
658     let underlying_value = if need_underlying_value {
659         let previous_composed_value = value_map.get(&property).map(|v| &*v);
660         previous_composed_value
661             .or_else(|| unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property).as_ref() })
662     } else {
663         None
664     };
666     if need_underlying_value && underlying_value.is_none() {
667         warn!("Underlying value should be valid when we expect to use it");
668         return;
669     }
671     let last_value = unsafe { last_segment.mToValue.mServo.mRawPtr.as_ref() };
672     let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
673     let position = if segment.mToKey == segment.mFromKey {
674         // Note: compose_animation_segment doesn't use this value
675         // if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine.
676         progress
677     } else {
678         unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }
679     };
681     let result = compose_animation_segment(
682         segment,
683         underlying_value,
684         last_value,
685         iteration_composite,
686         computed_timing.mCurrentIteration,
687         progress,
688         position,
689     );
690     value_map.insert(property, result);
693 macro_rules! get_property_id_from_nscsspropertyid {
694     ($property_id: ident, $ret: expr) => {{
695         match PropertyId::from_nscsspropertyid($property_id) {
696             Some(property_id) => property_id,
697             None => {
698                 return $ret;
699             },
700         }
701     }};
704 macro_rules! get_property_id_from_animatedpropertyid {
705     ($property_id: ident, $ret: expr) => {{
706         match PropertyId::from_gecko_animated_property_id($property_id) {
707             Some(property_id) => property_id,
708             None => {
709                 return $ret;
710             },
711         }
712     }};
715 #[no_mangle]
716 pub extern "C" fn Servo_AnimationValue_Serialize(
717     value: &AnimationValue,
718     property: &structs::AnimatedPropertyID,
719     raw_data: &PerDocumentStyleData,
720     buffer: &mut nsACString,
721 ) {
722     let uncomputed_value = value.uncompute();
723     let data = raw_data.borrow();
724     let rv = PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal)
725         .single_value_to_css(
726             &get_property_id_from_animatedpropertyid!(property, ()),
727             buffer,
728             None,
729             &data.stylist,
730         );
731     debug_assert!(rv.is_ok());
734 /// Debug: MOZ_DBG for AnimationValue.
735 #[no_mangle]
736 pub extern "C" fn Servo_AnimationValue_Dump(value: &AnimationValue, result: &mut nsACString) {
737     write!(result, "{:?}", value).unwrap();
740 #[no_mangle]
741 pub extern "C" fn Servo_AnimationValue_GetColor(
742     value: &AnimationValue,
743     foreground_color: structs::nscolor,
744 ) -> structs::nscolor {
745     use style::gecko::values::{
746         convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color,
747     };
748     use style::values::computed::color::Color as ComputedColor;
749     match *value {
750         AnimationValue::BackgroundColor(ref color) => {
751             let computed: ComputedColor = color.clone();
752             let foreground_color = convert_nscolor_to_absolute_color(foreground_color);
753             convert_absolute_color_to_nscolor(&computed.resolve_to_absolute(&foreground_color))
754         },
755         _ => panic!("Other color properties are not supported yet"),
756     }
759 #[no_mangle]
760 pub extern "C" fn Servo_AnimationValue_IsCurrentColor(value: &AnimationValue) -> bool {
761     match *value {
762         AnimationValue::BackgroundColor(ref color) => color.is_currentcolor(),
763         _ => {
764             debug_assert!(false, "Other color properties are not supported yet");
765             false
766         },
767     }
770 #[no_mangle]
771 pub extern "C" fn Servo_AnimationValue_GetOpacity(value: &AnimationValue) -> f32 {
772     if let AnimationValue::Opacity(opacity) = *value {
773         opacity
774     } else {
775         panic!("The AnimationValue should be Opacity");
776     }
779 #[no_mangle]
780 pub extern "C" fn Servo_AnimationValue_Opacity(opacity: f32) -> Strong<AnimationValue> {
781     Arc::new(AnimationValue::Opacity(opacity)).into()
784 #[no_mangle]
785 pub extern "C" fn Servo_AnimationValue_Color(
786     color_property: nsCSSPropertyID,
787     color: structs::nscolor,
788 ) -> Strong<AnimationValue> {
789     use style::gecko::values::convert_nscolor_to_absolute_color;
790     use style::values::animated::color::Color;
792     let property = LonghandId::from_nscsspropertyid(color_property)
793         .expect("We don't have shorthand property animation value");
795     let animated = convert_nscolor_to_absolute_color(color);
797     match property {
798         LonghandId::BackgroundColor => {
799             Arc::new(AnimationValue::BackgroundColor(Color::Absolute(animated))).into()
800         },
801         _ => panic!("Should be background-color property"),
802     }
805 #[no_mangle]
806 pub unsafe extern "C" fn Servo_AnimationValue_GetScale(
807     value: &AnimationValue,
808 ) -> *const computed::Scale {
809     match *value {
810         AnimationValue::Scale(ref value) => value,
811         _ => unreachable!("Expected scale"),
812     }
815 #[no_mangle]
816 pub unsafe extern "C" fn Servo_AnimationValue_GetTranslate(
817     value: &AnimationValue,
818 ) -> *const computed::Translate {
819     match *value {
820         AnimationValue::Translate(ref value) => value,
821         _ => unreachable!("Expected translate"),
822     }
825 #[no_mangle]
826 pub unsafe extern "C" fn Servo_AnimationValue_GetRotate(
827     value: &AnimationValue,
828 ) -> *const computed::Rotate {
829     match *value {
830         AnimationValue::Rotate(ref value) => value,
831         _ => unreachable!("Expected rotate"),
832     }
835 #[no_mangle]
836 pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
837     value: &AnimationValue,
838 ) -> *const computed::Transform {
839     match *value {
840         AnimationValue::Transform(ref value) => value,
841         _ => unreachable!("Unsupported transform animation value"),
842     }
845 #[no_mangle]
846 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPath(
847     value: &AnimationValue,
848     output: &mut computed::motion::OffsetPath,
849 ) {
850     use style::values::animated::ToAnimatedValue;
851     match *value {
852         AnimationValue::OffsetPath(ref value) => {
853             *output = ToAnimatedValue::from_animated_value(value.clone())
854         },
855         _ => unreachable!("Expected offset-path"),
856     }
859 #[no_mangle]
860 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetDistance(
861     value: &AnimationValue,
862 ) -> *const computed::LengthPercentage {
863     match *value {
864         AnimationValue::OffsetDistance(ref value) => value,
865         _ => unreachable!("Expected offset-distance"),
866     }
869 #[no_mangle]
870 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetRotate(
871     value: &AnimationValue,
872 ) -> *const computed::motion::OffsetRotate {
873     match *value {
874         AnimationValue::OffsetRotate(ref value) => value,
875         _ => unreachable!("Expected offset-rotate"),
876     }
879 #[no_mangle]
880 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetAnchor(
881     value: &AnimationValue,
882 ) -> *const computed::position::PositionOrAuto {
883     match *value {
884         AnimationValue::OffsetAnchor(ref value) => value,
885         _ => unreachable!("Expected offset-anchor"),
886     }
889 #[no_mangle]
890 pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPosition(
891     value: &AnimationValue,
892 ) -> *const computed::motion::OffsetPosition {
893     match *value {
894         AnimationValue::OffsetPosition(ref value) => value,
895         _ => unreachable!("Expected offset-position"),
896     }
899 #[no_mangle]
900 pub unsafe extern "C" fn Servo_AnimationValue_IsOffsetPathUrl(value: &AnimationValue) -> bool {
901     use style::values::generics::motion::{GenericOffsetPath, GenericOffsetPathFunction};
902     if let AnimationValue::OffsetPath(ref op) = value {
903         if let GenericOffsetPath::OffsetPath { path, coord_box: _ } = op {
904             return matches!(**path, GenericOffsetPathFunction::Url(_));
905         }
906     }
907     false
910 #[no_mangle]
911 pub unsafe extern "C" fn Servo_AnimationValue_Rotate(
912     r: &computed::Rotate,
913 ) -> Strong<AnimationValue> {
914     Arc::new(AnimationValue::Rotate(r.clone())).into()
917 #[no_mangle]
918 pub unsafe extern "C" fn Servo_AnimationValue_Translate(
919     t: &computed::Translate,
920 ) -> Strong<AnimationValue> {
921     Arc::new(AnimationValue::Translate(t.clone())).into()
924 #[no_mangle]
925 pub unsafe extern "C" fn Servo_AnimationValue_Scale(s: &computed::Scale) -> Strong<AnimationValue> {
926     Arc::new(AnimationValue::Scale(s.clone())).into()
929 #[no_mangle]
930 pub unsafe extern "C" fn Servo_AnimationValue_Transform(
931     transform: &computed::Transform,
932 ) -> Strong<AnimationValue> {
933     Arc::new(AnimationValue::Transform(transform.clone())).into()
936 #[no_mangle]
937 pub unsafe extern "C" fn Servo_AnimationValue_OffsetPath(
938     p: &computed::motion::OffsetPath,
939 ) -> Strong<AnimationValue> {
940     use style::values::animated::ToAnimatedValue;
941     Arc::new(AnimationValue::OffsetPath(p.clone().to_animated_value())).into()
944 #[no_mangle]
945 pub unsafe extern "C" fn Servo_AnimationValue_OffsetDistance(
946     d: &computed::LengthPercentage,
947 ) -> Strong<AnimationValue> {
948     Arc::new(AnimationValue::OffsetDistance(d.clone())).into()
951 #[no_mangle]
952 pub unsafe extern "C" fn Servo_AnimationValue_OffsetRotate(
953     r: &computed::motion::OffsetRotate,
954 ) -> Strong<AnimationValue> {
955     Arc::new(AnimationValue::OffsetRotate(*r)).into()
958 #[no_mangle]
959 pub unsafe extern "C" fn Servo_AnimationValue_OffsetAnchor(
960     p: &computed::position::PositionOrAuto,
961 ) -> Strong<AnimationValue> {
962     Arc::new(AnimationValue::OffsetAnchor(p.clone())).into()
965 #[no_mangle]
966 pub unsafe extern "C" fn Servo_AnimationValue_OffsetPosition(
967     p: &computed::motion::OffsetPosition,
968 ) -> Strong<AnimationValue> {
969     Arc::new(AnimationValue::OffsetPosition(p.clone())).into()
972 #[no_mangle]
973 pub extern "C" fn Servo_AnimationValue_DeepEqual(
974     this: &AnimationValue,
975     other: &AnimationValue,
976 ) -> bool {
977     this == other
980 #[no_mangle]
981 pub extern "C" fn Servo_AnimationValue_Uncompute(
982     value: &AnimationValue,
983 ) -> Strong<LockedDeclarationBlock> {
984     let global_style_data = &*GLOBAL_STYLE_DATA;
985     Arc::new(
986         global_style_data
987             .shared_lock
988             .wrap(PropertyDeclarationBlock::with_one(
989                 value.uncompute(),
990                 Importance::Normal,
991             )),
992     )
993     .into()
996 #[inline]
997 fn create_byte_buf_from_vec(mut v: Vec<u8>) -> ByteBuf {
998     let w = ByteBuf {
999         mData: v.as_mut_ptr(),
1000         mLen: v.len(),
1001         mCapacity: v.capacity(),
1002     };
1003     std::mem::forget(v);
1004     w
1007 #[inline]
1008 fn view_byte_buf(b: &ByteBuf) -> &[u8] {
1009     if b.mData.is_null() {
1010         debug_assert_eq!(b.mCapacity, 0);
1011         return &[];
1012     }
1013     unsafe { std::slice::from_raw_parts(b.mData, b.mLen) }
1016 macro_rules! impl_basic_serde_funcs {
1017     ($ser_name:ident, $de_name:ident, $computed_type:ty) => {
1018         #[no_mangle]
1019         pub extern "C" fn $ser_name(v: &$computed_type, output: &mut ByteBuf) -> bool {
1020             let buf = match serialize(v) {
1021                 Ok(buf) => buf,
1022                 Err(..) => return false,
1023             };
1025             *output = create_byte_buf_from_vec(buf);
1026             true
1027         }
1029         #[no_mangle]
1030         pub unsafe extern "C" fn $de_name(input: &ByteBuf, v: *mut $computed_type) -> bool {
1031             let buf = match deserialize(view_byte_buf(input)) {
1032                 Ok(buf) => buf,
1033                 Err(..) => return false,
1034             };
1036             std::ptr::write(v, buf);
1037             true
1038         }
1039     };
1042 impl_basic_serde_funcs!(
1043     Servo_LengthPercentage_Serialize,
1044     Servo_LengthPercentage_Deserialize,
1045     computed::LengthPercentage
1048 impl_basic_serde_funcs!(
1049     Servo_StyleRotate_Serialize,
1050     Servo_StyleRotate_Deserialize,
1051     computed::transform::Rotate
1054 impl_basic_serde_funcs!(
1055     Servo_StyleScale_Serialize,
1056     Servo_StyleScale_Deserialize,
1057     computed::transform::Scale
1060 impl_basic_serde_funcs!(
1061     Servo_StyleTranslate_Serialize,
1062     Servo_StyleTranslate_Deserialize,
1063     computed::transform::Translate
1066 impl_basic_serde_funcs!(
1067     Servo_StyleTransform_Serialize,
1068     Servo_StyleTransform_Deserialize,
1069     computed::transform::Transform
1072 impl_basic_serde_funcs!(
1073     Servo_StyleOffsetPath_Serialize,
1074     Servo_StyleOffsetPath_Deserialize,
1075     computed::motion::OffsetPath
1078 impl_basic_serde_funcs!(
1079     Servo_StyleOffsetRotate_Serialize,
1080     Servo_StyleOffsetRotate_Deserialize,
1081     computed::motion::OffsetRotate
1084 impl_basic_serde_funcs!(
1085     Servo_StylePositionOrAuto_Serialize,
1086     Servo_StylePositionOrAuto_Deserialize,
1087     computed::position::PositionOrAuto
1090 impl_basic_serde_funcs!(
1091     Servo_StyleOffsetPosition_Serialize,
1092     Servo_StyleOffsetPosition_Deserialize,
1093     computed::motion::OffsetPosition
1096 impl_basic_serde_funcs!(
1097     Servo_StyleComputedTimingFunction_Serialize,
1098     Servo_StyleComputedTimingFunction_Deserialize,
1099     ComputedTimingFunction
1102 // Return the ComputedValues by a base ComputedValues and the rules.
1103 fn resolve_rules_for_element_with_context<'a>(
1104     element: GeckoElement<'a>,
1105     mut context: StyleContext<GeckoElement<'a>>,
1106     rules: StrongRuleNode,
1107     original_computed_values: &ComputedValues,
1108 ) -> Arc<ComputedValues> {
1109     use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};
1111     // This currently ignores visited styles, which seems acceptable, as
1112     // existing browsers don't appear to animate visited styles.
1113     let inputs = CascadeInputs {
1114         rules: Some(rules),
1115         visited_rules: None,
1116         flags: original_computed_values.flags.for_cascade_inputs(),
1117     };
1119     // Actually `PseudoElementResolution` doesn't matter.
1120     let mut resolver = StyleResolverForElement::new(
1121         element,
1122         &mut context,
1123         RuleInclusion::All,
1124         PseudoElementResolution::IfApplicable,
1125     );
1126     resolver
1127         .cascade_style_and_visited_with_default_parents(inputs)
1128         .0
1131 #[no_mangle]
1132 pub extern "C" fn Servo_AnimationValueMap_Create() -> *mut AnimationValueMap {
1133     Box::into_raw(Box::default())
1136 #[no_mangle]
1137 pub unsafe extern "C" fn Servo_AnimationValueMap_Drop(value_map: *mut AnimationValueMap) {
1138     let _ = Box::from_raw(value_map);
1141 #[no_mangle]
1142 pub extern "C" fn Servo_AnimationValueMap_GetValue(
1143     value_map: &AnimationValueMap,
1144     property_id: &structs::AnimatedPropertyID,
1145 ) -> Strong<AnimationValue> {
1146     let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
1147         Some(property) => property,
1148         None => return Strong::null(),
1149     };
1150     value_map
1151         .get(&property)
1152         .map_or(Strong::null(), |value| Arc::new(value.clone()).into())
1155 #[no_mangle]
1156 pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
1157     raw_style_set: &PerDocumentStyleData,
1158     element: &RawGeckoElement,
1159     computed_values: &ComputedValues,
1160     snapshots: *const ServoElementSnapshotTable,
1161 ) -> Strong<ComputedValues> {
1162     debug_assert!(!snapshots.is_null());
1163     let computed_values = unsafe { ArcBorrow::from_ref(computed_values) };
1165     let rules = match computed_values.rules {
1166         None => return computed_values.clone_arc().into(),
1167         Some(ref rules) => rules,
1168     };
1170     let doc_data = raw_style_set.borrow();
1171     let without_animations_rules = doc_data.stylist.rule_tree().remove_animation_rules(rules);
1172     if without_animations_rules == *rules {
1173         return computed_values.clone_arc().into();
1174     }
1176     let element = GeckoElement(element);
1178     let global_style_data = &*GLOBAL_STYLE_DATA;
1179     let guard = global_style_data.shared_lock.read();
1180     let shared = create_shared_context(
1181         &global_style_data,
1182         &guard,
1183         &doc_data.stylist,
1184         TraversalFlags::empty(),
1185         unsafe { &*snapshots },
1186     );
1187     let mut tlc = ThreadLocalStyleContext::new();
1188     let context = StyleContext {
1189         shared: &shared,
1190         thread_local: &mut tlc,
1191     };
1193     resolve_rules_for_element_with_context(
1194         element,
1195         context,
1196         without_animations_rules,
1197         &computed_values,
1198     )
1199     .into()
1202 #[repr(C)]
1203 #[derive(Default)]
1204 pub struct ShouldTransitionResult {
1205     should_animate: bool,
1206     old_transition_value_matches: bool,
1209 #[inline]
1210 fn is_transitionable(prop: PropertyDeclarationId, behavior: computed::TransitionBehavior) -> bool {
1211     if !prop.is_animatable() {
1212         return false;
1213     }
1215     match behavior {
1216         computed::TransitionBehavior::Normal => !prop.is_discrete_animatable(),
1217         // If transition-behavior is allow-discrete, transitionable is the same as animatable.
1218         computed::TransitionBehavior::AllowDiscrete => true,
1219     }
1222 #[no_mangle]
1223 pub extern "C" fn Servo_ComputedValues_ShouldTransition(
1224     old: &ComputedValues,
1225     new: &ComputedValues,
1226     prop: &structs::AnimatedPropertyID,
1227     behavior: computed::TransitionBehavior,
1228     old_transition_value: Option<&AnimationValue>,
1229     start: &mut structs::RefPtr<AnimationValue>,
1230     end: &mut structs::RefPtr<AnimationValue>,
1231 ) -> ShouldTransitionResult {
1232     let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
1233         return Default::default();
1234     };
1235     let prop = prop.as_borrowed();
1236     if !is_transitionable(prop, behavior) {
1237         return Default::default();
1238     }
1240     let Some(new_value) = AnimationValue::from_computed_values(prop, new) else {
1241         return Default::default();
1242     };
1244     if let Some(old_transition_value) = old_transition_value {
1245         if *old_transition_value == new_value {
1246             return ShouldTransitionResult {
1247                 should_animate: false,
1248                 old_transition_value_matches: true,
1249             };
1250         }
1251     }
1253     let Some(old_value) = AnimationValue::from_computed_values(prop, old) else {
1254         return Default::default();
1255     };
1256     if old_value == new_value
1257         || (matches!(behavior, computed::TransitionBehavior::Normal)
1258             && !old_value.interpolable_with(&new_value))
1259     {
1260         return Default::default();
1261     }
1263     start.set_arc(Arc::new(old_value));
1264     end.set_arc(Arc::new(new_value));
1266     ShouldTransitionResult {
1267         should_animate: true,
1268         old_transition_value_matches: false,
1269     }
1272 #[no_mangle]
1273 pub extern "C" fn Servo_ComputedValues_TransitionValueMatches(
1274     style: &ComputedValues,
1275     prop: &structs::AnimatedPropertyID,
1276     transition_value: &AnimationValue,
1277 ) -> bool {
1278     let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
1279         return false;
1280     };
1281     // Note: the running transitions should be transitionable, so it is always allow-discrete.
1282     let prop = prop.as_borrowed();
1283     if !is_transitionable(prop, computed::TransitionBehavior::AllowDiscrete) {
1284         return false;
1285     }
1286     let Some(value) = AnimationValue::from_computed_values(prop, style) else {
1287         return false;
1288     };
1289     value == *transition_value
1292 #[no_mangle]
1293 pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(
1294     computed_values: &ComputedValues,
1295     property_id: &structs::AnimatedPropertyID,
1296 ) -> Strong<AnimationValue> {
1297     let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
1298         Some(property) => property,
1299         None => return Strong::null(),
1300     };
1301     match AnimationValue::from_computed_values(property.as_borrowed(), &computed_values) {
1302         Some(v) => Arc::new(v).into(),
1303         None => Strong::null(),
1304     }
1307 #[no_mangle]
1308 pub extern "C" fn Servo_ResolveLogicalProperty(
1309     property_id: nsCSSPropertyID,
1310     style: &ComputedValues,
1311 ) -> nsCSSPropertyID {
1312     let longhand = LonghandId::from_nscsspropertyid(property_id)
1313         .expect("We shouldn't need to care about shorthands");
1315     longhand
1316         .to_physical(style.writing_mode)
1317         .to_nscsspropertyid()
1320 #[no_mangle]
1321 pub unsafe extern "C" fn Servo_Property_LookupEnabledForAllContent(
1322     prop: &nsACString,
1323 ) -> nsCSSPropertyID {
1324     match PropertyId::parse_enabled_for_all_content(prop.as_str_unchecked()) {
1325         Ok(p) => p.to_nscsspropertyid_resolving_aliases(),
1326         Err(..) => nsCSSPropertyID::eCSSProperty_UNKNOWN,
1327     }
1330 #[no_mangle]
1331 pub unsafe extern "C" fn Servo_Property_GetName(
1332     prop: nsCSSPropertyID,
1333     out_length: *mut u32,
1334 ) -> *const u8 {
1335     let (ptr, len) = match NonCustomPropertyId::from_nscsspropertyid(prop) {
1336         Some(p) => {
1337             let name = p.name();
1338             (name.as_bytes().as_ptr(), name.len())
1339         },
1340         None => (ptr::null(), 0),
1341     };
1343     *out_length = len as u32;
1344     ptr
1347 macro_rules! parse_enabled_property_name {
1348     ($prop_name:ident, $found:ident, $default:expr) => {{
1349         let prop_name = $prop_name.as_str_unchecked();
1350         match PropertyId::parse_enabled_for_all_content(prop_name) {
1351             Ok(p) => {
1352                 *$found = true;
1353                 p
1354             },
1355             Err(..) => {
1356                 *$found = false;
1357                 return $default;
1358             },
1359         }
1360     }};
1363 #[no_mangle]
1364 pub unsafe extern "C" fn Servo_Property_IsShorthand(
1365     prop_name: &nsACString,
1366     found: *mut bool,
1367 ) -> bool {
1368     let prop_id = parse_enabled_property_name!(prop_name, found, false);
1369     prop_id.is_shorthand()
1372 #[no_mangle]
1373 pub unsafe extern "C" fn Servo_Property_IsInherited(
1374     per_doc_data: &PerDocumentStyleData,
1375     prop_name: &nsACString,
1376 ) -> bool {
1377     let prop_name = prop_name.as_str_unchecked();
1378     let prop_id = match PropertyId::parse_enabled_for_all_content(prop_name) {
1379         Ok(id) => id,
1380         Err(_) => return false,
1381     };
1382     let longhand_id = match prop_id {
1383         PropertyId::Custom(property_name) => {
1384             let stylist = &per_doc_data.borrow().stylist;
1385             return stylist
1386                 .get_custom_property_registration(&property_name)
1387                 .inherits();
1388         },
1389         PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
1390             Ok(lh) => lh,
1391             Err(sh) => sh.longhands().next().unwrap(),
1392         },
1393     };
1394     longhand_id.inherited()
1397 #[no_mangle]
1398 pub unsafe extern "C" fn Servo_Property_SupportsType(
1399     prop_name: &nsACString,
1400     ty: u8,
1401     found: *mut bool,
1402 ) -> bool {
1403     let prop_id = parse_enabled_property_name!(prop_name, found, false);
1404     prop_id.supports_type(ty)
1407 // TODO(emilio): We could use ThinVec instead of nsTArray.
1408 #[no_mangle]
1409 pub unsafe extern "C" fn Servo_Property_GetCSSValuesForProperty(
1410     prop_name: &nsACString,
1411     found: *mut bool,
1412     result: &mut nsTArray<nsString>,
1413 ) {
1414     let prop_id = parse_enabled_property_name!(prop_name, found, ());
1415     // Use B-tree set for unique and sorted result.
1416     let mut values = BTreeSet::<&'static str>::new();
1417     prop_id.collect_property_completion_keywords(&mut |list| values.extend(list.iter()));
1419     let mut extras = vec![];
1420     if values.contains("transparent") {
1421         // This is a special value devtools use to avoid inserting the
1422         // long list of color keywords. We need to prepend it to values.
1423         extras.push("COLOR");
1424     }
1426     let len = extras.len() + values.len();
1427     bindings::Gecko_ResizeTArrayForStrings(result, len as u32);
1429     for (src, dest) in extras.iter().chain(values.iter()).zip(result.iter_mut()) {
1430         dest.write_str(src).unwrap();
1431     }
1434 #[no_mangle]
1435 pub extern "C" fn Servo_Property_IsAnimatable(prop: &structs::AnimatedPropertyID) -> bool {
1436     PropertyId::from_gecko_animated_property_id(prop).map_or(false, |p| p.is_animatable())
1439 #[no_mangle]
1440 pub extern "C" fn Servo_Property_IsDiscreteAnimatable(property: nsCSSPropertyID) -> bool {
1441     match LonghandId::from_nscsspropertyid(property) {
1442         Some(longhand) => longhand.is_discrete_animatable(),
1443         None => return false,
1444     }
1447 #[no_mangle]
1448 pub extern "C" fn Servo_Element_ClearData(element: &RawGeckoElement) {
1449     unsafe { GeckoElement(element).clear_data() };
1452 #[no_mangle]
1453 pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(
1454     malloc_size_of: GeckoMallocSizeOf,
1455     malloc_enclosing_size_of: GeckoMallocSizeOf,
1456     seen_ptrs: *mut SeenPtrs,
1457     element: &RawGeckoElement,
1458 ) -> usize {
1459     let element = GeckoElement(element);
1460     let borrow = element.borrow_data();
1461     if let Some(data) = borrow {
1462         let have_seen_ptr = move |ptr| unsafe { Gecko_HaveSeenPtr(seen_ptrs, ptr) };
1463         let mut ops = MallocSizeOfOps::new(
1464             malloc_size_of.unwrap(),
1465             Some(malloc_enclosing_size_of.unwrap()),
1466             Some(Box::new(have_seen_ptr)),
1467         );
1468         (*data).size_of_excluding_cvs(&mut ops)
1469     } else {
1470         0
1471     }
1474 #[no_mangle]
1475 pub extern "C" fn Servo_Element_GetMaybeOutOfDateStyle(
1476     element: &RawGeckoElement,
1477 ) -> *const ComputedValues {
1478     let element = GeckoElement(element);
1479     let data = match element.borrow_data() {
1480         Some(d) => d,
1481         None => return ptr::null(),
1482     };
1483     &**data.styles.primary() as *const _
1486 #[no_mangle]
1487 pub extern "C" fn Servo_Element_GetMaybeOutOfDatePseudoStyle(
1488     element: &RawGeckoElement,
1489     index: usize,
1490 ) -> *const ComputedValues {
1491     let element = GeckoElement(element);
1492     let data = match element.borrow_data() {
1493         Some(d) => d,
1494         None => return ptr::null(),
1495     };
1496     match data.styles.pseudos.as_array()[index].as_ref() {
1497         Some(style) => &**style as *const _,
1498         None => ptr::null(),
1499     }
1502 #[no_mangle]
1503 pub extern "C" fn Servo_Element_IsDisplayNone(element: &RawGeckoElement) -> bool {
1504     let element = GeckoElement(element);
1505     let data = element
1506         .get_data()
1507         .expect("Invoking Servo_Element_IsDisplayNone on unstyled element");
1509     // This function is hot, so we bypass the AtomicRefCell.
1510     //
1511     // It would be nice to also assert that we're not in the servo traversal,
1512     // but this function is called at various intermediate checkpoints when
1513     // managing the traversal on the Gecko side.
1514     debug_assert!(is_main_thread());
1515     unsafe { &*data.as_ptr() }.styles.is_display_none()
1518 #[no_mangle]
1519 pub extern "C" fn Servo_Element_IsDisplayContents(element: &RawGeckoElement) -> bool {
1520     let element = GeckoElement(element);
1521     let data = element
1522         .get_data()
1523         .expect("Invoking Servo_Element_IsDisplayContents on unstyled element");
1525     debug_assert!(is_main_thread());
1526     unsafe { &*data.as_ptr() }
1527         .styles
1528         .primary()
1529         .get_box()
1530         .clone_display()
1531         .is_contents()
1534 #[no_mangle]
1535 pub extern "C" fn Servo_Element_IsPrimaryStyleReusedViaRuleNode(element: &RawGeckoElement) -> bool {
1536     let element = GeckoElement(element);
1537     let data = element
1538         .borrow_data()
1539         .expect("Invoking Servo_Element_IsPrimaryStyleReusedViaRuleNode on unstyled element");
1540     data.flags
1541         .contains(data::ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE)
1544 fn mode_to_origin(mode: SheetParsingMode) -> Origin {
1545     match mode {
1546         SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
1547         SheetParsingMode::eUserSheetFeatures => Origin::User,
1548         SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
1549     }
1552 #[no_mangle]
1553 pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> Strong<StylesheetContents> {
1554     let global_style_data = &*GLOBAL_STYLE_DATA;
1555     let origin = mode_to_origin(mode);
1556     let shared_lock = &global_style_data.shared_lock;
1557     StylesheetContents::from_str(
1558         "",
1559         unsafe { dummy_url_data() }.clone(),
1560         origin,
1561         shared_lock,
1562         /* loader = */ None,
1563         None,
1564         QuirksMode::NoQuirks,
1565         /* use_counters = */ None,
1566         AllowImportRules::Yes,
1567         /* sanitization_data = */ None,
1568     )
1569     .into()
1572 /// Note: The load_data corresponds to this sheet, and is passed as the parent
1573 /// load data for child sheet loads. It may be null for certain cases where we
1574 /// know we won't have child loads.
1575 #[no_mangle]
1576 pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8Bytes(
1577     loader: *mut Loader,
1578     stylesheet: *mut DomStyleSheet,
1579     load_data: *mut SheetLoadData,
1580     bytes: &nsACString,
1581     mode: SheetParsingMode,
1582     extra_data: *mut URLExtraData,
1583     quirks_mode: nsCompatibility,
1584     reusable_sheets: *mut LoaderReusableStyleSheets,
1585     use_counters: Option<&UseCounters>,
1586     allow_import_rules: AllowImportRules,
1587     sanitization_kind: SanitizationKind,
1588     sanitized_output: Option<&mut nsAString>,
1589 ) -> Strong<StylesheetContents> {
1590     let global_style_data = &*GLOBAL_STYLE_DATA;
1591     let input = bytes.as_str_unchecked();
1593     let reporter = ErrorReporter::new(stylesheet, loader, extra_data);
1594     let url_data = UrlExtraData::from_ptr_ref(&extra_data);
1595     let loader = if loader.is_null() {
1596         None
1597     } else {
1598         debug_assert!(
1599             sanitized_output.is_none(),
1600             "Shouldn't trigger @import loads for sanitization",
1601         );
1602         Some(StylesheetLoader::new(
1603             loader,
1604             stylesheet,
1605             load_data,
1606             reusable_sheets,
1607         ))
1608     };
1610     // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
1611     let loader: Option<&dyn StyleStylesheetLoader> = match loader {
1612         None => None,
1613         Some(ref s) => Some(s),
1614     };
1616     let mut sanitization_data = SanitizationData::new(sanitization_kind);
1618     let contents = StylesheetContents::from_str(
1619         input,
1620         url_data.clone(),
1621         mode_to_origin(mode),
1622         &global_style_data.shared_lock,
1623         loader,
1624         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
1625         quirks_mode.into(),
1626         use_counters,
1627         allow_import_rules,
1628         sanitization_data.as_mut(),
1629     );
1631     if let Some(data) = sanitization_data {
1632         sanitized_output
1633             .unwrap()
1634             .assign_utf8(data.take().as_bytes());
1635     }
1637     contents.into()
1640 #[no_mangle]
1641 pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
1642     load_data: *mut SheetLoadDataHolder,
1643     extra_data: *mut URLExtraData,
1644     bytes: &nsACString,
1645     mode: SheetParsingMode,
1646     quirks_mode: nsCompatibility,
1647     should_record_use_counters: bool,
1648     allow_import_rules: AllowImportRules,
1649 ) {
1650     let load_data = RefPtr::new(load_data);
1651     let extra_data = UrlExtraData::new(extra_data);
1653     let mut sheet_bytes = nsCString::new();
1654     sheet_bytes.assign(bytes);
1656     let async_parser = AsyncStylesheetParser::new(
1657         load_data,
1658         extra_data,
1659         sheet_bytes,
1660         mode_to_origin(mode),
1661         quirks_mode.into(),
1662         should_record_use_counters,
1663         allow_import_rules,
1664     );
1666     if let Some(thread_pool) = STYLE_THREAD_POOL.pool().as_ref() {
1667         thread_pool.spawn(|| {
1668             gecko_profiler_label!(Layout, CSSParsing);
1669             async_parser.parse();
1670         });
1671     } else {
1672         async_parser.parse();
1673     }
1676 #[no_mangle]
1677 pub unsafe extern "C" fn Servo_ShutdownThreadPool() {
1678     debug_assert!(is_main_thread() && !is_in_servo_traversal());
1679     StyleThreadPool::shutdown();
1682 #[no_mangle]
1683 pub unsafe extern "C" fn Servo_ThreadPool_GetThreadHandles(
1684     handles: &mut ThinVec<PlatformThreadHandle>,
1685 ) {
1686     StyleThreadPool::get_thread_handles(handles);
1689 #[no_mangle]
1690 pub unsafe extern "C" fn Servo_StyleSheet_FromSharedData(
1691     extra_data: *mut URLExtraData,
1692     shared_rules: &LockedCssRules,
1693 ) -> Strong<StylesheetContents> {
1694     StylesheetContents::from_shared_data(
1695         Arc::from_raw_addrefed(shared_rules),
1696         Origin::UserAgent,
1697         UrlExtraData::new(extra_data),
1698         QuirksMode::NoQuirks,
1699     )
1700     .into()
1703 #[no_mangle]
1704 pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
1705     raw_data: &PerDocumentStyleData,
1706     sheet: *const DomStyleSheet,
1707 ) {
1708     let global_style_data = &*GLOBAL_STYLE_DATA;
1709     let mut data = raw_data.borrow_mut();
1710     let data = &mut *data;
1711     let guard = global_style_data.shared_lock.read();
1712     let sheet = unsafe { GeckoStyleSheet::new(sheet) };
1713     data.stylist.append_stylesheet(sheet, &guard);
1716 #[no_mangle]
1717 pub extern "C" fn Servo_AuthorStyles_Create() -> *mut AuthorStyles {
1718     Box::into_raw(Box::new(AuthorStyles::new()))
1721 #[no_mangle]
1722 pub unsafe extern "C" fn Servo_AuthorStyles_Drop(styles: *mut AuthorStyles) {
1723     let _ = Box::from_raw(styles);
1726 #[no_mangle]
1727 pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet(
1728     styles: &mut AuthorStyles,
1729     sheet: *const DomStyleSheet,
1730 ) {
1731     let global_style_data = &*GLOBAL_STYLE_DATA;
1732     let guard = global_style_data.shared_lock.read();
1733     let sheet = GeckoStyleSheet::new(sheet);
1734     styles.stylesheets.append_stylesheet(None, sheet, &guard);
1737 #[no_mangle]
1738 pub unsafe extern "C" fn Servo_AuthorStyles_InsertStyleSheetBefore(
1739     styles: &mut AuthorStyles,
1740     sheet: *const DomStyleSheet,
1741     before_sheet: *const DomStyleSheet,
1742 ) {
1743     let global_style_data = &*GLOBAL_STYLE_DATA;
1744     let guard = global_style_data.shared_lock.read();
1745     styles.stylesheets.insert_stylesheet_before(
1746         None,
1747         GeckoStyleSheet::new(sheet),
1748         GeckoStyleSheet::new(before_sheet),
1749         &guard,
1750     );
1753 #[no_mangle]
1754 pub unsafe extern "C" fn Servo_AuthorStyles_RemoveStyleSheet(
1755     styles: &mut AuthorStyles,
1756     sheet: *const DomStyleSheet,
1757 ) {
1758     let global_style_data = &*GLOBAL_STYLE_DATA;
1759     let guard = global_style_data.shared_lock.read();
1760     styles
1761         .stylesheets
1762         .remove_stylesheet(None, GeckoStyleSheet::new(sheet), &guard);
1765 #[no_mangle]
1766 pub extern "C" fn Servo_AuthorStyles_ForceDirty(styles: &mut AuthorStyles) {
1767     styles.stylesheets.force_dirty();
1770 #[no_mangle]
1771 pub extern "C" fn Servo_AuthorStyles_IsDirty(styles: &AuthorStyles) -> bool {
1772     styles.stylesheets.dirty()
1775 #[no_mangle]
1776 pub extern "C" fn Servo_AuthorStyles_Flush(
1777     styles: &mut AuthorStyles,
1778     document_set: &PerDocumentStyleData,
1779 ) {
1780     // Try to avoid the atomic borrow below if possible.
1781     if !styles.stylesheets.dirty() {
1782         return;
1783     }
1785     let global_style_data = &*GLOBAL_STYLE_DATA;
1786     let guard = global_style_data.shared_lock.read();
1788     let mut document_data = document_set.borrow_mut();
1790     // TODO(emilio): This is going to need an element or something to do proper
1791     // invalidation in Shadow roots.
1792     styles.flush::<GeckoElement>(&mut document_data.stylist, &guard);
1795 #[no_mangle]
1796 pub extern "C" fn Servo_StyleSet_RemoveUniqueEntriesFromAuthorStylesCache(
1797     document_set: &PerDocumentStyleData,
1798 ) {
1799     let mut document_data = document_set.borrow_mut();
1800     document_data
1801         .stylist
1802         .remove_unique_author_data_cache_entries();
1805 #[no_mangle]
1806 pub unsafe extern "C" fn Servo_DeclarationBlock_SizeOfIncludingThis(
1807     malloc_size_of: GeckoMallocSizeOf,
1808     malloc_enclosing_size_of: GeckoMallocSizeOf,
1809     declarations: &LockedDeclarationBlock,
1810 ) -> usize {
1811     use malloc_size_of::MallocSizeOf;
1812     use malloc_size_of::MallocUnconditionalShallowSizeOf;
1814     let global_style_data = &*GLOBAL_STYLE_DATA;
1815     let guard = global_style_data.shared_lock.read();
1817     let mut ops = MallocSizeOfOps::new(
1818         malloc_size_of.unwrap(),
1819         Some(malloc_enclosing_size_of.unwrap()),
1820         None,
1821     );
1823     ArcBorrow::from_ref(declarations).with_arc(|declarations| {
1824         let mut n = 0;
1825         n += declarations.unconditional_shallow_size_of(&mut ops);
1826         n += declarations.read_with(&guard).size_of(&mut ops);
1827         n
1828     })
1831 #[no_mangle]
1832 pub unsafe extern "C" fn Servo_AuthorStyles_SizeOfIncludingThis(
1833     malloc_size_of: GeckoMallocSizeOf,
1834     malloc_enclosing_size_of: GeckoMallocSizeOf,
1835     styles: &AuthorStyles,
1836 ) -> usize {
1837     // We cannot `use` MallocSizeOf at the top level, otherwise the compiler
1838     // would complain in `Servo_StyleSheet_SizeOfIncludingThis` for `size_of`
1839     // there.
1840     use malloc_size_of::MallocSizeOf;
1841     let malloc_size_of = malloc_size_of.unwrap();
1842     let malloc_size_of_this = malloc_size_of(styles as *const AuthorStyles as *const c_void);
1844     let mut ops = MallocSizeOfOps::new(
1845         malloc_size_of,
1846         Some(malloc_enclosing_size_of.unwrap()),
1847         None,
1848     );
1849     malloc_size_of_this + styles.size_of(&mut ops)
1852 #[no_mangle]
1853 pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
1854     document_set: &PerDocumentStyleData,
1855     non_document_styles: &mut nsTArray<&mut AuthorStyles>,
1856     may_affect_default_style: bool,
1857 ) -> structs::MediumFeaturesChangedResult {
1858     let global_style_data = &*GLOBAL_STYLE_DATA;
1859     let guard = global_style_data.shared_lock.read();
1861     // NOTE(emilio): We don't actually need to flush the stylist here and ensure
1862     // it's up to date.
1863     //
1864     // In case it isn't we would trigger a rebuild + restyle as needed too.
1865     //
1866     // We need to ensure the default computed values are up to date though,
1867     // because those can influence the result of media query evaluation.
1868     let mut document_data = document_set.borrow_mut();
1870     if may_affect_default_style {
1871         document_data.stylist.device_mut().reset_computed_values();
1872     }
1873     let guards = StylesheetGuards::same(&guard);
1875     let origins_in_which_rules_changed = document_data
1876         .stylist
1877         .media_features_change_changed_style(&guards, document_data.stylist.device());
1879     let affects_document_rules = !origins_in_which_rules_changed.is_empty();
1880     if affects_document_rules {
1881         document_data
1882             .stylist
1883             .force_stylesheet_origins_dirty(origins_in_which_rules_changed);
1884     }
1886     let mut affects_non_document_rules = false;
1887     for author_styles in &mut **non_document_styles {
1888         let affected_style = author_styles.stylesheets.iter().any(|sheet| {
1889             !author_styles.data.media_feature_affected_matches(
1890                 sheet,
1891                 &guards.author,
1892                 document_data.stylist.device(),
1893                 document_data.stylist.quirks_mode(),
1894             )
1895         });
1896         if affected_style {
1897             affects_non_document_rules = true;
1898             author_styles.stylesheets.force_dirty();
1899         }
1900     }
1902     structs::MediumFeaturesChangedResult {
1903         mAffectsDocumentRules: affects_document_rules,
1904         mAffectsNonDocumentRules: affects_non_document_rules,
1905     }
1908 #[no_mangle]
1909 pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
1910     raw_data: &PerDocumentStyleData,
1911     sheet: *const DomStyleSheet,
1912     before_sheet: *const DomStyleSheet,
1913 ) {
1914     let global_style_data = &*GLOBAL_STYLE_DATA;
1915     let mut data = raw_data.borrow_mut();
1916     let data = &mut *data;
1917     let guard = global_style_data.shared_lock.read();
1918     let sheet = unsafe { GeckoStyleSheet::new(sheet) };
1919     data.stylist.insert_stylesheet_before(
1920         sheet,
1921         unsafe { GeckoStyleSheet::new(before_sheet) },
1922         &guard,
1923     );
1926 #[no_mangle]
1927 pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(
1928     raw_data: &PerDocumentStyleData,
1929     sheet: *const DomStyleSheet,
1930 ) {
1931     let global_style_data = &*GLOBAL_STYLE_DATA;
1932     let mut data = raw_data.borrow_mut();
1933     let data = &mut *data;
1934     let guard = global_style_data.shared_lock.read();
1935     let sheet = unsafe { GeckoStyleSheet::new(sheet) };
1936     data.stylist.remove_stylesheet(sheet, &guard);
1939 #[no_mangle]
1940 pub unsafe extern "C" fn Servo_StyleSet_GetSheetAt(
1941     raw_data: &PerDocumentStyleData,
1942     origin: Origin,
1943     index: usize,
1944 ) -> *const DomStyleSheet {
1945     let data = raw_data.borrow();
1946     data.stylist
1947         .sheet_at(origin, index)
1948         .map_or(ptr::null(), |s| s.raw())
1951 #[no_mangle]
1952 pub unsafe extern "C" fn Servo_StyleSet_GetSheetCount(
1953     raw_data: &PerDocumentStyleData,
1954     origin: Origin,
1955 ) -> usize {
1956     let data = raw_data.borrow();
1957     data.stylist.sheet_count(origin)
1960 #[no_mangle]
1961 pub unsafe extern "C" fn Servo_StyleSet_FlushStyleSheets(
1962     raw_data: &PerDocumentStyleData,
1963     doc_element: Option<&RawGeckoElement>,
1964     snapshots: *const ServoElementSnapshotTable,
1965 ) {
1966     let global_style_data = &*GLOBAL_STYLE_DATA;
1967     let guard = global_style_data.shared_lock.read();
1968     let mut data = raw_data.borrow_mut();
1969     let doc_element = doc_element.map(GeckoElement);
1971     let have_invalidations = data.flush_stylesheets(&guard, doc_element, snapshots.as_ref());
1973     if have_invalidations && doc_element.is_some() {
1974         // The invalidation machinery propagates the bits up, but we still need
1975         // to tell the Gecko restyle root machinery about it.
1976         bindings::Gecko_NoteDirtySubtreeForInvalidation(doc_element.unwrap().0);
1977     }
1980 #[no_mangle]
1981 pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
1982     raw_data: &PerDocumentStyleData,
1983     changed_origins: OriginFlags,
1984 ) {
1985     let mut data = raw_data.borrow_mut();
1986     data.stylist
1987         .force_stylesheet_origins_dirty(OriginSet::from(changed_origins));
1990 #[no_mangle]
1991 pub extern "C" fn Servo_StyleSet_SetAuthorStyleDisabled(
1992     raw_data: &PerDocumentStyleData,
1993     author_style_disabled: bool,
1994 ) {
1995     let mut data = raw_data.borrow_mut();
1996     let enabled = if author_style_disabled {
1997         AuthorStylesEnabled::No
1998     } else {
1999         AuthorStylesEnabled::Yes
2000     };
2001     data.stylist.set_author_styles_enabled(enabled);
2004 #[no_mangle]
2005 pub extern "C" fn Servo_StyleSet_UsesFontMetrics(raw_data: &PerDocumentStyleData) -> bool {
2006     let doc_data = raw_data;
2007     doc_data.borrow().stylist.device().used_font_metrics()
2010 #[no_mangle]
2011 pub extern "C" fn Servo_StyleSheet_HasRules(raw_contents: &StylesheetContents) -> bool {
2012     let global_style_data = &*GLOBAL_STYLE_DATA;
2013     let guard = global_style_data.shared_lock.read();
2014     !raw_contents.rules.read_with(&guard).0.is_empty()
2017 #[no_mangle]
2018 pub extern "C" fn Servo_StyleSheet_GetRules(sheet: &StylesheetContents) -> Strong<LockedCssRules> {
2019     sheet.rules.clone().into()
2022 #[no_mangle]
2023 pub extern "C" fn Servo_StyleSheet_Clone(
2024     contents: &StylesheetContents,
2025     reference_sheet: *const DomStyleSheet,
2026 ) -> Strong<StylesheetContents> {
2027     use style::shared_lock::{DeepCloneParams, DeepCloneWithLock};
2028     let global_style_data = &*GLOBAL_STYLE_DATA;
2029     let guard = global_style_data.shared_lock.read();
2030     let params = DeepCloneParams { reference_sheet };
2032     Arc::new(contents.deep_clone_with_lock(&global_style_data.shared_lock, &guard, &params)).into()
2035 #[no_mangle]
2036 pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
2037     malloc_size_of: GeckoMallocSizeOf,
2038     malloc_enclosing_size_of: GeckoMallocSizeOf,
2039     sheet: &StylesheetContents,
2040 ) -> usize {
2041     let global_style_data = &*GLOBAL_STYLE_DATA;
2042     let guard = global_style_data.shared_lock.read();
2043     let mut ops = MallocSizeOfOps::new(
2044         malloc_size_of.unwrap(),
2045         Some(malloc_enclosing_size_of.unwrap()),
2046         None,
2047     );
2048     // TODO(emilio): We're not measuring the size of the Arc<StylesheetContents>
2049     // allocation itself here.
2050     sheet.size_of(&guard, &mut ops)
2053 #[no_mangle]
2054 pub extern "C" fn Servo_StyleSheet_GetOrigin(sheet: &StylesheetContents) -> Origin {
2055     sheet.origin
2058 #[no_mangle]
2059 pub extern "C" fn Servo_StyleSheet_GetSourceMapURL(
2060     contents: &StylesheetContents,
2061     result: &mut nsACString,
2062 ) {
2063     let url_opt = contents.source_map_url.read();
2064     if let Some(ref url) = *url_opt {
2065         result.assign(url);
2066     }
2069 #[no_mangle]
2070 pub extern "C" fn Servo_StyleSheet_GetSourceURL(
2071     contents: &StylesheetContents,
2072     result: &mut nsACString,
2073 ) {
2074     let url_opt = contents.source_url.read();
2075     if let Some(ref url) = *url_opt {
2076         result.assign(url);
2077     }
2080 fn with_maybe_worker_shared_lock<R>(func: impl FnOnce(&SharedRwLock) -> R) -> R {
2081     if is_dom_worker_thread() {
2082         DOM_WORKER_RWLOCK.with(func)
2083     } else {
2084         func(&GLOBAL_STYLE_DATA.shared_lock)
2085     }
2088 fn read_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
2089 where
2090     F: FnOnce(&T) -> R,
2092     debug_assert!(!is_dom_worker_thread());
2093     let global_style_data = &*GLOBAL_STYLE_DATA;
2094     let guard = global_style_data.shared_lock.read();
2095     func(raw.read_with(&guard))
2098 fn read_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
2099 where
2100     F: FnOnce(&T) -> R,
2102     with_maybe_worker_shared_lock(|lock| {
2103         let guard = lock.read();
2104         func(raw.read_with(&guard))
2105     })
2108 #[cfg(debug_assertions)]
2109 unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
2110 where
2111     F: FnOnce(&T) -> R,
2113     debug_assert!(is_main_thread() && !is_in_servo_traversal());
2114     read_locked_arc(raw, func)
2117 #[cfg(not(debug_assertions))]
2118 unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
2119 where
2120     F: FnOnce(&T) -> R,
2122     debug_assert!(!is_dom_worker_thread());
2123     func(raw.read_unchecked())
2126 fn write_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
2127 where
2128     F: FnOnce(&mut T) -> R,
2130     debug_assert!(!is_dom_worker_thread());
2131     let global_style_data = &*GLOBAL_STYLE_DATA;
2132     let mut guard = global_style_data.shared_lock.write();
2133     func(raw.write_with(&mut guard))
2136 fn write_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
2137 where
2138     F: FnOnce(&mut T) -> R,
2140     with_maybe_worker_shared_lock(|lock| {
2141         let mut guard = lock.write();
2142         func(raw.write_with(&mut guard))
2143     })
2146 #[no_mangle]
2147 pub extern "C" fn Servo_CssRules_ListTypes(rules: &LockedCssRules, result: &mut nsTArray<usize>) {
2148     read_locked_arc(rules, |rules: &CssRules| {
2149         result.assign_from_iter_pod(rules.0.iter().map(|rule| rule.rule_type() as usize));
2150     })
2153 #[no_mangle]
2154 pub extern "C" fn Servo_CssRules_InsertRule(
2155     rules: &LockedCssRules,
2156     contents: &StylesheetContents,
2157     rule: &nsACString,
2158     index: u32,
2159     containing_rule_types: u32,
2160     loader: *mut Loader,
2161     allow_import_rules: AllowImportRules,
2162     gecko_stylesheet: *mut DomStyleSheet,
2163     rule_type: &mut CssRuleType,
2164 ) -> nsresult {
2165     let loader = if loader.is_null() {
2166         None
2167     } else {
2168         Some(StylesheetLoader::new(
2169             loader,
2170             gecko_stylesheet,
2171             ptr::null_mut(),
2172             ptr::null_mut(),
2173         ))
2174     };
2175     let loader = loader
2176         .as_ref()
2177         .map(|loader| loader as &dyn StyleStylesheetLoader);
2178     let rule = unsafe { rule.as_str_unchecked() };
2180     let global_style_data = &*GLOBAL_STYLE_DATA;
2181     let result = rules.insert_rule(
2182         &global_style_data.shared_lock,
2183         rule,
2184         contents,
2185         index as usize,
2186         CssRuleTypes::from_bits(containing_rule_types),
2187         loader,
2188         allow_import_rules,
2189     );
2191     match result {
2192         Ok(new_rule) => {
2193             *rule_type = new_rule.rule_type();
2194             nsresult::NS_OK
2195         },
2196         Err(err) => err.into(),
2197     }
2200 #[no_mangle]
2201 pub extern "C" fn Servo_CssRules_DeleteRule(rules: &LockedCssRules, index: u32) -> nsresult {
2202     write_locked_arc(rules, |rules: &mut CssRules| {
2203         match rules.remove_rule(index as usize) {
2204             Ok(_) => nsresult::NS_OK,
2205             Err(err) => err.into(),
2206         }
2207     })
2210 trait MaybeLocked<Target> {
2211     fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a Target;
2214 impl<T> MaybeLocked<T> for T {
2215     fn maybe_locked_read<'a>(&'a self, _: &'a SharedRwLockReadGuard) -> &'a T {
2216         self
2217     }
2220 impl<T> MaybeLocked<T> for Locked<T> {
2221     fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {
2222         self.read_with(guard)
2223     }
2226 macro_rules! impl_basic_rule_funcs_without_getter {
2227     {
2228         ($rule_type:ty, $maybe_locked_rule_type:ty),
2229         debug: $debug:ident,
2230         to_css: $to_css:ident,
2231     } => {
2232         #[cfg(debug_assertions)]
2233         #[no_mangle]
2234         pub extern "C" fn $debug(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
2235             let global_style_data = &*GLOBAL_STYLE_DATA;
2236             let guard = global_style_data.shared_lock.read();
2237             let rule: &$rule_type = rule.maybe_locked_read(&guard);
2238             write!(result, "{:?}", *rule).unwrap();
2239         }
2241         #[cfg(not(debug_assertions))]
2242         #[no_mangle]
2243         pub extern "C" fn $debug(_: &$maybe_locked_rule_type, _: &mut nsACString) {
2244             unreachable!()
2245         }
2247         #[no_mangle]
2248         pub extern "C" fn $to_css(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
2249             let global_style_data = &*GLOBAL_STYLE_DATA;
2250             let guard = global_style_data.shared_lock.read();
2251             let rule: &$rule_type = rule.maybe_locked_read(&guard);
2252             rule.to_css(&guard, result).unwrap();
2253         }
2254     }
2257 macro_rules! impl_basic_rule_funcs {
2258     { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
2259         getter: $getter:ident,
2260         debug: $debug:ident,
2261         to_css: $to_css:ident,
2262         changed: $changed:ident,
2263     } => {
2264         #[no_mangle]
2265         pub extern "C" fn $getter(
2266             rules: &LockedCssRules,
2267             index: u32,
2268             line: &mut u32,
2269             column: &mut u32,
2270         ) -> Strong<$maybe_locked_rule_type> {
2271             let global_style_data = &*GLOBAL_STYLE_DATA;
2272             let guard = global_style_data.shared_lock.read();
2273             let rules = rules.read_with(&guard);
2274             let index = index as usize;
2276             if index >= rules.0.len() {
2277                 return Strong::null();
2278             }
2280             match rules.0[index] {
2281                 CssRule::$name(ref arc) => {
2282                     let rule: &$rule_type = (&**arc).maybe_locked_read(&guard);
2283                     let location = rule.source_location;
2284                     *line = location.line as u32;
2285                     *column = location.column as u32;
2286                     arc.clone().into()
2287                 },
2288                 _ => {
2289                     Strong::null()
2290                 }
2291             }
2292         }
2294         #[no_mangle]
2295         pub extern "C" fn $changed(
2296             styleset: &PerDocumentStyleData,
2297             rule: &$maybe_locked_rule_type,
2298             sheet: &DomStyleSheet,
2299             change_kind: RuleChangeKind,
2300         ) {
2301             let mut data = styleset.borrow_mut();
2302             let data = &mut *data;
2303             let global_style_data = &*GLOBAL_STYLE_DATA;
2304             let guard = global_style_data.shared_lock.read();
2305             // TODO(emilio): Would be nice not to deal with refcount bumps here,
2306             // but it's probably not a huge deal.
2307             let rule = unsafe { CssRule::$name(Arc::from_raw_addrefed(rule)) };
2308             let sheet = unsafe { GeckoStyleSheet::new(sheet) };
2309             data.stylist.rule_changed(&sheet, &rule, &guard, change_kind);
2310         }
2312         impl_basic_rule_funcs_without_getter! {
2313             ($rule_type, $maybe_locked_rule_type),
2314             debug: $debug,
2315             to_css: $to_css,
2316         }
2317     }
2320 macro_rules! impl_group_rule_funcs {
2321     { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
2322       get_rules: $get_rules:ident,
2323       $($basic:tt)+
2324     } => {
2325         impl_basic_rule_funcs! { ($name, $rule_type, $maybe_locked_rule_type), $($basic)+ }
2327         #[no_mangle]
2328         pub extern "C" fn $get_rules(rule: &$maybe_locked_rule_type) -> Strong<LockedCssRules> {
2329             let global_style_data = &*GLOBAL_STYLE_DATA;
2330             let guard = global_style_data.shared_lock.read();
2331             let rule: &$rule_type = rule.maybe_locked_read(&guard);
2332             rule.rules.clone().into()
2333         }
2334     }
2337 impl_basic_rule_funcs! { (Style, StyleRule, Locked<StyleRule>),
2338     getter: Servo_CssRules_GetStyleRuleAt,
2339     debug: Servo_StyleRule_Debug,
2340     to_css: Servo_StyleRule_GetCssText,
2341     changed: Servo_StyleSet_StyleRuleChanged,
2344 #[no_mangle]
2345 pub extern "C" fn Servo_StyleRule_EnsureRules(
2346     rule: &LockedStyleRule,
2347     read_only: bool,
2348 ) -> Strong<LockedCssRules> {
2349     let global_style_data = &*GLOBAL_STYLE_DATA;
2350     let lock = &global_style_data.shared_lock;
2351     if read_only {
2352         let guard = lock.read();
2353         if let Some(ref rules) = rule.read_with(&guard).rules {
2354             return rules.clone().into();
2355         }
2356         return CssRules::new(vec![], lock).into();
2357     }
2358     let mut guard = lock.write();
2359     rule.write_with(&mut guard)
2360         .rules
2361         .get_or_insert_with(|| CssRules::new(vec![], lock))
2362         .clone()
2363         .into()
2366 impl_basic_rule_funcs! { (Import, ImportRule, Locked<ImportRule>),
2367     getter: Servo_CssRules_GetImportRuleAt,
2368     debug: Servo_ImportRule_Debug,
2369     to_css: Servo_ImportRule_GetCssText,
2370     changed: Servo_StyleSet_ImportRuleChanged,
2373 impl_basic_rule_funcs_without_getter! { (Keyframe, Locked<Keyframe>),
2374     debug: Servo_Keyframe_Debug,
2375     to_css: Servo_Keyframe_GetCssText,
2378 impl_basic_rule_funcs! { (Keyframes, KeyframesRule, Locked<KeyframesRule>),
2379     getter: Servo_CssRules_GetKeyframesRuleAt,
2380     debug: Servo_KeyframesRule_Debug,
2381     to_css: Servo_KeyframesRule_GetCssText,
2382     changed: Servo_StyleSet_KeyframesRuleChanged,
2385 impl_group_rule_funcs! { (Media, MediaRule, MediaRule),
2386     get_rules: Servo_MediaRule_GetRules,
2387     getter: Servo_CssRules_GetMediaRuleAt,
2388     debug: Servo_MediaRule_Debug,
2389     to_css: Servo_MediaRule_GetCssText,
2390     changed: Servo_StyleSet_MediaRuleChanged,
2393 impl_basic_rule_funcs! { (Namespace, NamespaceRule, NamespaceRule),
2394     getter: Servo_CssRules_GetNamespaceRuleAt,
2395     debug: Servo_NamespaceRule_Debug,
2396     to_css: Servo_NamespaceRule_GetCssText,
2397     changed: Servo_StyleSet_NamespaceRuleChanged,
2400 impl_basic_rule_funcs! { (Page, PageRule, Locked<PageRule>),
2401     getter: Servo_CssRules_GetPageRuleAt,
2402     debug: Servo_PageRule_Debug,
2403     to_css: Servo_PageRule_GetCssText,
2404     changed: Servo_StyleSet_PageRuleChanged,
2407 impl_basic_rule_funcs! { (Property, PropertyRule, PropertyRule),
2408     getter: Servo_CssRules_GetPropertyRuleAt,
2409     debug: Servo_PropertyRule_Debug,
2410     to_css: Servo_PropertyRule_GetCssText,
2411     changed: Servo_StyleSet_PropertyRuleChanged,
2414 impl_group_rule_funcs! { (Supports, SupportsRule, SupportsRule),
2415     get_rules: Servo_SupportsRule_GetRules,
2416     getter: Servo_CssRules_GetSupportsRuleAt,
2417     debug: Servo_SupportsRule_Debug,
2418     to_css: Servo_SupportsRule_GetCssText,
2419     changed: Servo_StyleSet_SupportsRuleChanged,
2422 impl_group_rule_funcs! { (Container, ContainerRule, ContainerRule),
2423     get_rules: Servo_ContainerRule_GetRules,
2424     getter: Servo_CssRules_GetContainerRuleAt,
2425     debug: Servo_ContainerRule_Debug,
2426     to_css: Servo_ContainerRule_GetCssText,
2427     changed: Servo_StyleSet_ContainerRuleChanged,
2430 impl_group_rule_funcs! { (LayerBlock, LayerBlockRule, LayerBlockRule),
2431     get_rules: Servo_LayerBlockRule_GetRules,
2432     getter: Servo_CssRules_GetLayerBlockRuleAt,
2433     debug: Servo_LayerBlockRule_Debug,
2434     to_css: Servo_LayerBlockRule_GetCssText,
2435     changed: Servo_StyleSet_LayerBlockRuleChanged,
2438 impl_basic_rule_funcs! { (LayerStatement, LayerStatementRule, LayerStatementRule),
2439     getter: Servo_CssRules_GetLayerStatementRuleAt,
2440     debug: Servo_LayerStatementRule_Debug,
2441     to_css: Servo_LayerStatementRule_GetCssText,
2442     changed: Servo_StyleSet_LayerStatementRuleChanged,
2445 impl_group_rule_funcs! { (Document, DocumentRule, DocumentRule),
2446     get_rules: Servo_DocumentRule_GetRules,
2447     getter: Servo_CssRules_GetDocumentRuleAt,
2448     debug: Servo_DocumentRule_Debug,
2449     to_css: Servo_DocumentRule_GetCssText,
2450     changed: Servo_StyleSet_DocumentRuleChanged,
2453 impl_basic_rule_funcs! { (FontFeatureValues, FontFeatureValuesRule, FontFeatureValuesRule),
2454     getter: Servo_CssRules_GetFontFeatureValuesRuleAt,
2455     debug: Servo_FontFeatureValuesRule_Debug,
2456     to_css: Servo_FontFeatureValuesRule_GetCssText,
2457     changed: Servo_StyleSet_FontFeatureValuesRuleChanged,
2460 impl_basic_rule_funcs! { (FontPaletteValues, FontPaletteValuesRule, FontPaletteValuesRule),
2461     getter: Servo_CssRules_GetFontPaletteValuesRuleAt,
2462     debug: Servo_FontPaletteValuesRule_Debug,
2463     to_css: Servo_FontPaletteValuesRule_GetCssText,
2464     changed: Servo_StyleSet_FontPaletteValuesRuleChanged,
2467 impl_basic_rule_funcs! { (FontFace, FontFaceRule, Locked<FontFaceRule>),
2468     getter: Servo_CssRules_GetFontFaceRuleAt,
2469     debug: Servo_FontFaceRule_Debug,
2470     to_css: Servo_FontFaceRule_GetCssText,
2471     changed: Servo_StyleSet_FontFaceRuleChanged,
2474 impl_basic_rule_funcs! { (CounterStyle, CounterStyleRule, Locked<CounterStyleRule>),
2475     getter: Servo_CssRules_GetCounterStyleRuleAt,
2476     debug: Servo_CounterStyleRule_Debug,
2477     to_css: Servo_CounterStyleRule_GetCssText,
2478     changed: Servo_StyleSet_CounterStyleRuleChanged,
2481 #[no_mangle]
2482 pub extern "C" fn Servo_StyleRule_GetStyle(
2483     rule: &LockedStyleRule,
2484 ) -> Strong<LockedDeclarationBlock> {
2485     read_locked_arc(rule, |rule: &StyleRule| rule.block.clone().into())
2488 #[no_mangle]
2489 pub extern "C" fn Servo_StyleRule_SetStyle(
2490     rule: &LockedStyleRule,
2491     declarations: &LockedDeclarationBlock,
2492 ) {
2493     write_locked_arc(rule, |rule: &mut StyleRule| {
2494         rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
2495     })
2498 #[no_mangle]
2499 pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: &LockedStyleRule, result: &mut nsACString) {
2500     read_locked_arc(rule, |rule| rule.selectors.to_css(result).unwrap());
2503 fn desugared_selector_list(rules: &ThinVec<&LockedStyleRule>) -> SelectorList {
2504     let mut selectors: Option<SelectorList> = None;
2505     for rule in rules.iter().rev() {
2506         selectors = Some(read_locked_arc(rule, |rule| match selectors {
2507             Some(ref s) => rule.selectors.replace_parent_selector(s),
2508             None => rule.selectors.clone(),
2509         }));
2510     }
2511     selectors.expect("Empty rule chain?")
2514 #[no_mangle]
2515 pub extern "C" fn Servo_StyleRule_GetSelectorDataAtIndex(
2516     rules: &ThinVec<&LockedStyleRule>,
2517     index: u32,
2518     text: Option<&mut nsACString>,
2519     specificity: Option<&mut u64>,
2520 ) {
2521     let selectors = desugared_selector_list(rules);
2522     let Some(selector) = selectors.slice().get(index as usize) else {
2523         return;
2524     };
2525     if let Some(text) = text {
2526         selector.to_css(text).unwrap();
2527     }
2528     if let Some(specificity) = specificity {
2529         *specificity = selector.specificity() as u64;
2530     }
2533 #[no_mangle]
2534 pub extern "C" fn Servo_StyleRule_GetSelectorCount(rule: &LockedStyleRule) -> u32 {
2535     read_locked_arc(rule, |rule| rule.selectors.len() as u32)
2538 #[no_mangle]
2539 pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(
2540     rules: &ThinVec<&LockedStyleRule>,
2541     element: &RawGeckoElement,
2542     index: u32,
2543     host: Option<&RawGeckoElement>,
2544     pseudo_type: PseudoStyleType,
2545     relevant_link_visited: bool,
2546 ) -> bool {
2547     use selectors::matching::{
2548         matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,
2549     };
2550     let selectors = desugared_selector_list(rules);
2551     let Some(selector) = selectors.slice().get(index as usize) else {
2552         return false;
2553     };
2554     let mut matching_mode = MatchingMode::Normal;
2555     match PseudoElement::from_pseudo_type(pseudo_type, None) {
2556         Some(pseudo) => {
2557             // We need to make sure that the requested pseudo element type
2558             // matches the selector pseudo element type before proceeding.
2559             match selector.pseudo_element() {
2560                 Some(selector_pseudo) if *selector_pseudo == pseudo => {
2561                     matching_mode = MatchingMode::ForStatelessPseudoElement
2562                 },
2563                 _ => return false,
2564             };
2565         },
2566         None => {
2567             // Do not attempt to match if a pseudo element is requested and
2568             // this is not a pseudo element selector, or vice versa.
2569             if selector.has_pseudo_element() {
2570                 return false;
2571             }
2572         },
2573     };
2575     let element = GeckoElement(element);
2576     let host = host.map(GeckoElement);
2577     let quirks_mode = element.as_node().owner_doc().quirks_mode();
2578     let mut selector_caches = SelectorCaches::default();
2579     let visited_mode = if relevant_link_visited {
2580         VisitedHandlingMode::RelevantLinkVisited
2581     } else {
2582         VisitedHandlingMode::AllLinksUnvisited
2583     };
2584     let mut ctx = MatchingContext::new_for_visited(
2585         matching_mode,
2586         /* bloom_filter = */ None,
2587         &mut selector_caches,
2588         visited_mode,
2589         quirks_mode,
2590         NeedsSelectorFlags::No,
2591         MatchingForInvalidation::No,
2592     );
2593     ctx.with_shadow_host(host, |ctx| {
2594         matches_selector(selector, 0, None, &element, ctx)
2595     })
2598 pub type SelectorList = selectors::SelectorList<style::gecko::selector_parser::SelectorImpl>;
2600 #[no_mangle]
2601 pub extern "C" fn Servo_StyleRule_SetSelectorText(
2602     contents: &StylesheetContents,
2603     rule: &LockedStyleRule,
2604     text: &nsACString,
2605 ) -> bool {
2606     let value_str = unsafe { text.as_str_unchecked() };
2608     write_locked_arc(rule, |rule: &mut StyleRule| {
2609         use selectors::parser::ParseRelative;
2610         use style::selector_parser::SelectorParser;
2612         let namespaces = contents.namespaces.read();
2613         let url_data = contents.url_data.read();
2614         let parser = SelectorParser {
2615             stylesheet_origin: contents.origin,
2616             namespaces: &namespaces,
2617             url_data: &url_data,
2618             for_supports_rule: false,
2619         };
2621         // TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style
2622         // rule?
2623         let mut parser_input = ParserInput::new(&value_str);
2624         match SelectorList::parse(
2625             &parser,
2626             &mut Parser::new(&mut parser_input),
2627             ParseRelative::No,
2628         ) {
2629             Ok(selectors) => {
2630                 rule.selectors = selectors;
2631                 true
2632             },
2633             Err(_) => false,
2634         }
2635     })
2638 #[no_mangle]
2639 pub unsafe extern "C" fn Servo_SelectorList_Closest(
2640     element: &RawGeckoElement,
2641     selectors: &SelectorList,
2642 ) -> *const RawGeckoElement {
2643     use style::dom_apis;
2645     let element = GeckoElement(element);
2646     let quirks_mode = element.as_node().owner_doc().quirks_mode();
2647     dom_apis::element_closest(element, &selectors, quirks_mode).map_or(ptr::null(), |e| e.0)
2650 #[no_mangle]
2651 pub unsafe extern "C" fn Servo_SelectorList_Matches(
2652     element: &RawGeckoElement,
2653     selectors: &SelectorList,
2654 ) -> bool {
2655     use style::dom_apis;
2657     let element = GeckoElement(element);
2658     let quirks_mode = element.as_node().owner_doc().quirks_mode();
2659     dom_apis::element_matches(&element, &selectors, quirks_mode)
2662 #[no_mangle]
2663 pub unsafe extern "C" fn Servo_SelectorList_QueryFirst(
2664     node: &RawGeckoNode,
2665     selectors: &SelectorList,
2666     may_use_invalidation: bool,
2667 ) -> *const RawGeckoElement {
2668     use style::dom_apis::{self, MayUseInvalidation, QueryFirst};
2670     let node = GeckoNode(node);
2671     let mut result = None;
2673     let may_use_invalidation = if may_use_invalidation {
2674         MayUseInvalidation::Yes
2675     } else {
2676         MayUseInvalidation::No
2677     };
2679     dom_apis::query_selector::<GeckoElement, QueryFirst>(
2680         node,
2681         &selectors,
2682         &mut result,
2683         may_use_invalidation,
2684     );
2686     result.map_or(ptr::null(), |e| e.0)
2689 #[no_mangle]
2690 pub unsafe extern "C" fn Servo_SelectorList_QueryAll(
2691     node: &RawGeckoNode,
2692     selectors: &SelectorList,
2693     content_list: *mut structs::nsSimpleContentList,
2694     may_use_invalidation: bool,
2695 ) {
2696     use style::dom_apis::{self, MayUseInvalidation, QueryAll};
2698     let node = GeckoNode(node);
2699     let mut result = SmallVec::new();
2701     let may_use_invalidation = if may_use_invalidation {
2702         MayUseInvalidation::Yes
2703     } else {
2704         MayUseInvalidation::No
2705     };
2707     dom_apis::query_selector::<GeckoElement, QueryAll>(
2708         node,
2709         &selectors,
2710         &mut result,
2711         may_use_invalidation,
2712     );
2714     if !result.is_empty() {
2715         // NOTE(emilio): This relies on a slice of GeckoElement having the same
2716         // memory representation than a slice of element pointers.
2717         bindings::Gecko_ContentList_AppendAll(
2718             content_list,
2719             result.as_ptr() as *mut *const _,
2720             result.len(),
2721         )
2722     }
2725 #[no_mangle]
2726 pub extern "C" fn Servo_ImportRule_GetHref(rule: &LockedImportRule, result: &mut nsAString) {
2727     read_locked_arc(rule, |rule: &ImportRule| {
2728         write!(result, "{}", rule.url.as_str()).unwrap();
2729     })
2732 #[no_mangle]
2733 pub extern "C" fn Servo_ImportRule_GetLayerName(rule: &LockedImportRule, result: &mut nsACString) {
2734     // https://w3c.github.io/csswg-drafts/cssom/#dom-cssimportrule-layername
2735     read_locked_arc(rule, |rule: &ImportRule| match rule.layer {
2736         ImportLayer::Named(ref name) => name.to_css(&mut CssWriter::new(result)).unwrap(), // "return the layer name declared in the at-rule itself"
2737         ImportLayer::Anonymous => {}, // "or an empty string if the layer is anonymous"
2738         ImportLayer::None => result.set_is_void(true), // "or null if the at-rule does not declare a layer"
2739     })
2742 #[no_mangle]
2743 pub extern "C" fn Servo_ImportRule_GetSupportsText(
2744     rule: &LockedImportRule,
2745     result: &mut nsACString,
2746 ) {
2747     read_locked_arc(rule, |rule: &ImportRule| match rule.supports {
2748         Some(ref supports) => supports
2749             .condition
2750             .to_css(&mut CssWriter::new(result))
2751             .unwrap(),
2752         None => result.set_is_void(true),
2753     })
2756 #[no_mangle]
2757 pub extern "C" fn Servo_ImportRule_GetSheet(rule: &LockedImportRule) -> *const DomStyleSheet {
2758     read_locked_arc(rule, |rule: &ImportRule| {
2759         rule.stylesheet
2760             .as_sheet()
2761             .map_or(ptr::null(), |s| s.raw() as *const DomStyleSheet)
2762     })
2765 #[no_mangle]
2766 pub unsafe extern "C" fn Servo_ImportRule_SetSheet(
2767     rule: &LockedImportRule,
2768     sheet: *mut DomStyleSheet,
2769 ) {
2770     write_locked_arc(rule, |rule: &mut ImportRule| {
2771         rule.stylesheet = ImportSheet::new(GeckoStyleSheet::new(sheet));
2772     })
2775 #[no_mangle]
2776 pub extern "C" fn Servo_Keyframe_GetKeyText(keyframe: &LockedKeyframe, result: &mut nsACString) {
2777     read_locked_arc(keyframe, |keyframe: &Keyframe| {
2778         keyframe
2779             .selector
2780             .to_css(&mut CssWriter::new(result))
2781             .unwrap()
2782     })
2785 #[no_mangle]
2786 pub extern "C" fn Servo_Keyframe_SetKeyText(keyframe: &LockedKeyframe, text: &nsACString) -> bool {
2787     let text = unsafe { text.as_str_unchecked() };
2788     let mut input = ParserInput::new(&text);
2789     if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) {
2790         write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
2791             keyframe.selector = selector;
2792         });
2793         true
2794     } else {
2795         false
2796     }
2799 #[no_mangle]
2800 pub extern "C" fn Servo_Keyframe_GetStyle(
2801     keyframe: &LockedKeyframe,
2802 ) -> Strong<LockedDeclarationBlock> {
2803     read_locked_arc(keyframe, |keyframe: &Keyframe| {
2804         keyframe.block.clone().into()
2805     })
2808 #[no_mangle]
2809 pub extern "C" fn Servo_Keyframe_SetStyle(
2810     keyframe: &LockedKeyframe,
2811     declarations: &LockedDeclarationBlock,
2812 ) {
2813     write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
2814         keyframe.block = unsafe { Arc::from_raw_addrefed(declarations) };
2815     })
2818 #[no_mangle]
2819 pub extern "C" fn Servo_KeyframesRule_GetName(rule: &LockedKeyframesRule) -> *mut nsAtom {
2820     read_locked_arc(rule, |rule: &KeyframesRule| rule.name.as_atom().as_ptr())
2823 #[no_mangle]
2824 pub unsafe extern "C" fn Servo_KeyframesRule_SetName(
2825     rule: &LockedKeyframesRule,
2826     name: *mut nsAtom,
2827 ) {
2828     write_locked_arc(rule, |rule: &mut KeyframesRule| {
2829         rule.name = KeyframesName::from_atom(Atom::from_addrefed(name));
2830     })
2833 #[no_mangle]
2834 pub extern "C" fn Servo_KeyframesRule_GetCount(rule: &LockedKeyframesRule) -> u32 {
2835     read_locked_arc(rule, |rule: &KeyframesRule| rule.keyframes.len() as u32)
2838 #[no_mangle]
2839 pub extern "C" fn Servo_KeyframesRule_GetKeyframeAt(
2840     rule: &LockedKeyframesRule,
2841     index: u32,
2842     line: &mut u32,
2843     column: &mut u32,
2844 ) -> Strong<LockedKeyframe> {
2845     let global_style_data = &*GLOBAL_STYLE_DATA;
2846     let guard = global_style_data.shared_lock.read();
2847     let key = rule.read_with(&guard).keyframes[index as usize].clone();
2848     let location = key.read_with(&guard).source_location;
2849     *line = location.line as u32;
2850     *column = location.column as u32;
2851     key.into()
2854 #[no_mangle]
2855 pub extern "C" fn Servo_KeyframesRule_FindRule(
2856     rule: &LockedKeyframesRule,
2857     key: &nsACString,
2858 ) -> u32 {
2859     let key = unsafe { key.as_str_unchecked() };
2860     let global_style_data = &*GLOBAL_STYLE_DATA;
2861     let guard = global_style_data.shared_lock.read();
2862     rule.read_with(&guard)
2863         .find_rule(&guard, key)
2864         .map(|index| index as u32)
2865         .unwrap_or(u32::max_value())
2868 #[no_mangle]
2869 pub extern "C" fn Servo_KeyframesRule_AppendRule(
2870     rule: &LockedKeyframesRule,
2871     contents: &StylesheetContents,
2872     css: &nsACString,
2873 ) -> bool {
2874     let css = unsafe { css.as_str_unchecked() };
2875     let global_style_data = &*GLOBAL_STYLE_DATA;
2877     match Keyframe::parse(css, &contents, &global_style_data.shared_lock) {
2878         Ok(keyframe) => {
2879             write_locked_arc(rule, |rule: &mut KeyframesRule| {
2880                 rule.keyframes.push(keyframe);
2881             });
2882             true
2883         },
2884         Err(..) => false,
2885     }
2888 #[no_mangle]
2889 pub extern "C" fn Servo_KeyframesRule_DeleteRule(rule: &LockedKeyframesRule, index: u32) {
2890     write_locked_arc(rule, |rule: &mut KeyframesRule| {
2891         rule.keyframes.remove(index as usize);
2892     })
2895 #[no_mangle]
2896 pub extern "C" fn Servo_MediaRule_GetMedia(rule: &MediaRule) -> Strong<LockedMediaList> {
2897     rule.media_queries.clone().into()
2900 #[no_mangle]
2901 pub extern "C" fn Servo_NamespaceRule_GetPrefix(rule: &NamespaceRule) -> *mut nsAtom {
2902     rule.prefix
2903         .as_ref()
2904         .map_or(atom!("").as_ptr(), |a| a.as_ptr())
2907 #[no_mangle]
2908 pub extern "C" fn Servo_NamespaceRule_GetURI(rule: &NamespaceRule) -> *mut nsAtom {
2909     rule.url.0.as_ptr()
2912 #[no_mangle]
2913 pub extern "C" fn Servo_PageRule_GetStyle(rule: &LockedPageRule) -> Strong<LockedDeclarationBlock> {
2914     read_locked_arc(rule, |rule: &PageRule| rule.block.clone().into())
2917 #[no_mangle]
2918 pub extern "C" fn Servo_PageRule_SetStyle(
2919     rule: &LockedPageRule,
2920     declarations: &LockedDeclarationBlock,
2921 ) {
2922     write_locked_arc(rule, |rule: &mut PageRule| {
2923         rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
2924     })
2927 #[no_mangle]
2928 pub extern "C" fn Servo_PageRule_GetSelectorText(rule: &LockedPageRule, result: &mut nsACString) {
2929     read_locked_arc(rule, |rule: &PageRule| {
2930         rule.selectors.to_css(&mut CssWriter::new(result)).unwrap();
2931     })
2934 #[no_mangle]
2935 pub extern "C" fn Servo_PageRule_SetSelectorText(
2936     contents: &StylesheetContents,
2937     rule: &LockedPageRule,
2938     text: &nsACString,
2939 ) -> bool {
2940     let value_str = unsafe { text.as_str_unchecked() };
2942     write_locked_arc(rule, |rule: &mut PageRule| {
2943         use style::stylesheets::PageSelectors;
2945         let mut parser_input = ParserInput::new(&value_str);
2946         let mut parser = Parser::new(&mut parser_input);
2948         // Ensure that a blank input results in empty page selectors
2949         if parser.is_exhausted() {
2950             rule.selectors = PageSelectors::default();
2951             return true;
2952         }
2954         let url_data = contents.url_data.read();
2955         let context = ParserContext::new(
2956             Origin::Author,
2957             &url_data,
2958             None,
2959             ParsingMode::DEFAULT,
2960             QuirksMode::NoQuirks,
2961             /* namespaces = */ Default::default(),
2962             None,
2963             None,
2964         );
2966         match parser.parse_entirely(|i| PageSelectors::parse(&context, i)) {
2967             Ok(selectors) => {
2968                 rule.selectors = selectors;
2969                 true
2970             },
2971             Err(_) => false,
2972         }
2973     })
2976 #[no_mangle]
2977 pub extern "C" fn Servo_PropertyRule_GetName(rule: &PropertyRule, result: &mut nsACString) {
2978     write!(result, "--{}", rule.name.0).unwrap();
2981 #[no_mangle]
2982 pub extern "C" fn Servo_PropertyRule_GetSyntax(rule: &PropertyRule, result: &mut nsACString) {
2983     if let Some(syntax) = rule.data.syntax.specified_string() {
2984         result.assign(syntax);
2985     } else {
2986         debug_assert!(false, "Rule without specified syntax?");
2987     }
2990 #[no_mangle]
2991 pub extern "C" fn Servo_PropertyRule_GetInherits(rule: &PropertyRule) -> bool {
2992     rule.inherits()
2995 #[no_mangle]
2996 pub extern "C" fn Servo_PropertyRule_GetInitialValue(
2997     rule: &PropertyRule,
2998     result: &mut nsACString,
2999 ) -> bool {
3000     rule.data
3001         .initial_value
3002         .to_css(&mut CssWriter::new(result))
3003         .unwrap();
3004     rule.data.initial_value.is_some()
3007 #[no_mangle]
3008 pub extern "C" fn Servo_SupportsRule_GetConditionText(
3009     rule: &SupportsRule,
3010     result: &mut nsACString,
3011 ) {
3012     rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
3015 #[no_mangle]
3016 pub extern "C" fn Servo_ContainerRule_GetConditionText(
3017     rule: &ContainerRule,
3018     result: &mut nsACString,
3019 ) {
3020     rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
3023 #[no_mangle]
3024 pub extern "C" fn Servo_ContainerRule_GetContainerQuery(
3025     rule: &ContainerRule,
3026     result: &mut nsACString,
3027 ) {
3028     rule.query_condition()
3029         .to_css(&mut CssWriter::new(result))
3030         .unwrap();
3033 #[no_mangle]
3034 pub extern "C" fn Servo_ContainerRule_QueryContainerFor(
3035     rule: &ContainerRule,
3036     element: &RawGeckoElement,
3037 ) -> *const RawGeckoElement {
3038     rule.condition
3039         .find_container(GeckoElement(element), None)
3040         .map_or(ptr::null(), |result| result.element.0)
3043 #[no_mangle]
3044 pub extern "C" fn Servo_ContainerRule_GetContainerName(
3045     rule: &ContainerRule,
3046     result: &mut nsACString,
3047 ) {
3048     let name = rule.container_name();
3049     if !name.is_none() {
3050         name.to_css(&mut CssWriter::new(result)).unwrap();
3051     }
3054 #[no_mangle]
3055 pub extern "C" fn Servo_DocumentRule_GetConditionText(
3056     rule: &DocumentRule,
3057     result: &mut nsACString,
3058 ) {
3059     rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
3062 #[no_mangle]
3063 pub extern "C" fn Servo_FontFeatureValuesRule_GetFontFamily(
3064     rule: &FontFeatureValuesRule,
3065     result: &mut nsACString,
3066 ) {
3067     rule.family_names
3068         .to_css(&mut CssWriter::new(result))
3069         .unwrap();
3072 #[no_mangle]
3073 pub extern "C" fn Servo_FontFeatureValuesRule_GetValueText(
3074     rule: &FontFeatureValuesRule,
3075     result: &mut nsACString,
3076 ) {
3077     rule.value_to_css(&mut CssWriter::new(result)).unwrap();
3080 #[no_mangle]
3081 pub extern "C" fn Servo_FontPaletteValuesRule_GetName(
3082     rule: &FontPaletteValuesRule,
3083     result: &mut nsACString,
3084 ) {
3085     rule.name.to_css(&mut CssWriter::new(result)).unwrap()
3088 #[no_mangle]
3089 pub extern "C" fn Servo_FontPaletteValuesRule_GetFontFamily(
3090     rule: &FontPaletteValuesRule,
3091     result: &mut nsACString,
3092 ) {
3093     if !rule.family_names.is_empty() {
3094         rule.family_names
3095             .to_css(&mut CssWriter::new(result))
3096             .unwrap()
3097     }
3100 #[no_mangle]
3101 pub extern "C" fn Servo_FontPaletteValuesRule_GetBasePalette(
3102     rule: &FontPaletteValuesRule,
3103     result: &mut nsACString,
3104 ) {
3105     rule.base_palette
3106         .to_css(&mut CssWriter::new(result))
3107         .unwrap()
3110 #[no_mangle]
3111 pub extern "C" fn Servo_FontPaletteValuesRule_GetOverrideColors(
3112     rule: &FontPaletteValuesRule,
3113     result: &mut nsACString,
3114 ) {
3115     if !rule.override_colors.is_empty() {
3116         rule.override_colors
3117             .to_css(&mut CssWriter::new(result))
3118             .unwrap()
3119     }
3122 #[no_mangle]
3123 pub extern "C" fn Servo_FontFaceRule_CreateEmpty() -> Strong<LockedFontFaceRule> {
3124     // XXX This is not great. We should split FontFace descriptor data
3125     // from the rule, so that we don't need to create the rule like this
3126     // and the descriptor data itself can be hold in UniquePtr from the
3127     // Gecko side. See bug 1450904.
3128     with_maybe_worker_shared_lock(|lock| {
3129         Arc::new(lock.wrap(FontFaceRule::empty(SourceLocation { line: 0, column: 0 }))).into()
3130     })
3133 #[no_mangle]
3134 pub unsafe extern "C" fn Servo_FontFaceRule_Clone(
3135     rule: &LockedFontFaceRule,
3136 ) -> Strong<LockedFontFaceRule> {
3137     let clone = read_locked_arc_worker(rule, |rule: &FontFaceRule| rule.clone());
3138     with_maybe_worker_shared_lock(|lock| Arc::new(lock.wrap(clone)).into())
3141 #[no_mangle]
3142 pub unsafe extern "C" fn Servo_FontFaceRule_GetSourceLocation(
3143     rule: &LockedFontFaceRule,
3144     line: *mut u32,
3145     column: *mut u32,
3146 ) {
3147     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3148         let location = rule.source_location;
3149         *line.as_mut().unwrap() = location.line as u32;
3150         *column.as_mut().unwrap() = location.column as u32;
3151     });
3154 macro_rules! apply_font_desc_list {
3155     ($apply_macro:ident) => {
3156         $apply_macro! {
3157             valid: [
3158                 eCSSFontDesc_Family => family,
3159                 eCSSFontDesc_Style => style,
3160                 eCSSFontDesc_Weight => weight,
3161                 eCSSFontDesc_Stretch => stretch,
3162                 eCSSFontDesc_Src => sources,
3163                 eCSSFontDesc_UnicodeRange => unicode_range,
3164                 eCSSFontDesc_FontFeatureSettings => feature_settings,
3165                 eCSSFontDesc_FontVariationSettings => variation_settings,
3166                 eCSSFontDesc_FontLanguageOverride => language_override,
3167                 eCSSFontDesc_Display => display,
3168                 eCSSFontDesc_AscentOverride => ascent_override,
3169                 eCSSFontDesc_DescentOverride => descent_override,
3170                 eCSSFontDesc_LineGapOverride => line_gap_override,
3171                 eCSSFontDesc_SizeAdjust => size_adjust,
3172             ]
3173             invalid: [
3174                 eCSSFontDesc_UNKNOWN,
3175                 eCSSFontDesc_COUNT,
3176             ]
3177         }
3178     };
3181 #[no_mangle]
3182 pub unsafe extern "C" fn Servo_FontFaceRule_Length(rule: &LockedFontFaceRule) -> u32 {
3183     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3184         let mut result = 0;
3185         macro_rules! count_values {
3186             (
3187                 valid: [$($v_enum_name:ident => $field:ident,)*]
3188                 invalid: [$($i_enum_name:ident,)*]
3189             ) => {
3190                 $(if rule.$field.is_some() {
3191                     result += 1;
3192                 })*
3193             }
3194         }
3195         apply_font_desc_list!(count_values);
3196         result
3197     })
3200 #[no_mangle]
3201 pub unsafe extern "C" fn Servo_FontFaceRule_IndexGetter(
3202     rule: &LockedFontFaceRule,
3203     index: u32,
3204 ) -> nsCSSFontDesc {
3205     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3206         let mut count = 0;
3207         macro_rules! lookup_index {
3208             (
3209                 valid: [$($v_enum_name:ident => $field:ident,)*]
3210                 invalid: [$($i_enum_name:ident,)*]
3211             ) => {
3212                 $(if rule.$field.is_some() {
3213                     count += 1;
3214                     if count - 1 == index {
3215                         return nsCSSFontDesc::$v_enum_name;
3216                     }
3217                 })*
3218             }
3219         }
3220         apply_font_desc_list!(lookup_index);
3221         return nsCSSFontDesc::eCSSFontDesc_UNKNOWN;
3222     })
3225 #[no_mangle]
3226 pub unsafe extern "C" fn Servo_FontFaceRule_GetDeclCssText(
3227     rule: &LockedFontFaceRule,
3228     result: &mut nsACString,
3229 ) {
3230     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3231         rule.decl_to_css(result).unwrap();
3232     })
3235 macro_rules! simple_font_descriptor_getter_impl {
3236     ($rule:ident, $out:ident, $field:ident, $compute:ident) => {
3237         read_locked_arc_worker($rule, |rule: &FontFaceRule| {
3238             match rule.$field {
3239                 None => return false,
3240                 Some(ref f) => *$out = f.$compute(),
3241             }
3242             true
3243         })
3244     };
3247 #[no_mangle]
3248 pub extern "C" fn Servo_FontFaceRule_GetFontWeight(
3249     rule: &LockedFontFaceRule,
3250     out: &mut font_face::ComputedFontWeightRange,
3251 ) -> bool {
3252     simple_font_descriptor_getter_impl!(rule, out, weight, compute)
3255 #[no_mangle]
3256 pub extern "C" fn Servo_FontFaceRule_GetFontStretch(
3257     rule: &LockedFontFaceRule,
3258     out: &mut font_face::ComputedFontStretchRange,
3259 ) -> bool {
3260     simple_font_descriptor_getter_impl!(rule, out, stretch, compute)
3263 #[no_mangle]
3264 pub extern "C" fn Servo_FontFaceRule_GetFontStyle(
3265     rule: &LockedFontFaceRule,
3266     out: &mut font_face::ComputedFontStyleDescriptor,
3267 ) -> bool {
3268     simple_font_descriptor_getter_impl!(rule, out, style, compute)
3271 #[no_mangle]
3272 pub extern "C" fn Servo_FontFaceRule_GetFontDisplay(
3273     rule: &LockedFontFaceRule,
3274     out: &mut font_face::FontDisplay,
3275 ) -> bool {
3276     simple_font_descriptor_getter_impl!(rule, out, display, clone)
3279 #[no_mangle]
3280 pub extern "C" fn Servo_FontFaceRule_GetFontLanguageOverride(
3281     rule: &LockedFontFaceRule,
3282     out: &mut computed::FontLanguageOverride,
3283 ) -> bool {
3284     simple_font_descriptor_getter_impl!(rule, out, language_override, clone)
3287 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
3288 // rather than an actual percentage value.
3289 #[no_mangle]
3290 pub extern "C" fn Servo_FontFaceRule_GetAscentOverride(
3291     rule: &LockedFontFaceRule,
3292     out: &mut computed::Percentage,
3293 ) -> bool {
3294     simple_font_descriptor_getter_impl!(rule, out, ascent_override, compute)
3297 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
3298 // rather than an actual percentage value.
3299 #[no_mangle]
3300 pub extern "C" fn Servo_FontFaceRule_GetDescentOverride(
3301     rule: &LockedFontFaceRule,
3302     out: &mut computed::Percentage,
3303 ) -> bool {
3304     simple_font_descriptor_getter_impl!(rule, out, descent_override, compute)
3307 // Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
3308 // rather than an actual percentage value.
3309 #[no_mangle]
3310 pub extern "C" fn Servo_FontFaceRule_GetLineGapOverride(
3311     rule: &LockedFontFaceRule,
3312     out: &mut computed::Percentage,
3313 ) -> bool {
3314     simple_font_descriptor_getter_impl!(rule, out, line_gap_override, compute)
3317 #[no_mangle]
3318 pub extern "C" fn Servo_FontFaceRule_GetSizeAdjust(
3319     rule: &LockedFontFaceRule,
3320     out: &mut computed::Percentage,
3321 ) -> bool {
3322     simple_font_descriptor_getter_impl!(rule, out, size_adjust, compute)
3325 #[no_mangle]
3326 pub unsafe extern "C" fn Servo_FontFaceRule_GetFamilyName(
3327     rule: &LockedFontFaceRule,
3328 ) -> *mut nsAtom {
3329     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3330         // TODO(emilio): font-family is a mandatory descriptor, can't we unwrap
3331         // here, and remove the null-checks in Gecko?
3332         rule.family
3333             .as_ref()
3334             .map_or(ptr::null_mut(), |f| f.name.as_ptr())
3335     })
3338 #[no_mangle]
3339 pub unsafe extern "C" fn Servo_FontFaceRule_GetUnicodeRanges(
3340     rule: &LockedFontFaceRule,
3341     out_len: *mut usize,
3342 ) -> *const UnicodeRange {
3343     *out_len = 0;
3344     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3345         let ranges = match rule.unicode_range {
3346             Some(ref ranges) => ranges,
3347             None => return ptr::null(),
3348         };
3349         *out_len = ranges.len();
3350         ranges.as_ptr() as *const _
3351     })
3354 #[no_mangle]
3355 pub unsafe extern "C" fn Servo_FontFaceRule_GetSources(
3356     rule: &LockedFontFaceRule,
3357     out: *mut nsTArray<FontFaceSourceListComponent>,
3358 ) {
3359     let out = &mut *out;
3360     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3361         let sources = match rule.sources {
3362             Some(ref s) => s,
3363             None => return,
3364         };
3365         let len = sources.0.iter().fold(0, |acc, src| {
3366             acc + match *src {
3367                 Source::Url(ref url) => {
3368                     (if url.format_hint.is_some() { 2 } else { 1 }) +
3369                         (if url.tech_flags.is_empty() { 0 } else { 1 })
3370                 },
3371                 Source::Local(_) => 1,
3372             }
3373         });
3375         out.set_len(len as u32);
3377         let mut iter = out.iter_mut();
3379         {
3380             let mut set_next = |component: FontFaceSourceListComponent| {
3381                 *iter.next().expect("miscalculated length") = component;
3382             };
3384             for source in sources.0.iter() {
3385                 match *source {
3386                     Source::Url(ref url) => {
3387                         set_next(FontFaceSourceListComponent::Url(&url.url));
3388                         if let Some(hint) = &url.format_hint {
3389                             match hint {
3390                                 FontFaceSourceFormat::Keyword(kw) => {
3391                                     set_next(FontFaceSourceListComponent::FormatHintKeyword(*kw))
3392                                 },
3393                                 FontFaceSourceFormat::String(s) => {
3394                                     set_next(FontFaceSourceListComponent::FormatHintString {
3395                                         length: s.len(),
3396                                         utf8_bytes: s.as_ptr(),
3397                                     })
3398                                 },
3399                             }
3400                         }
3401                         if !url.tech_flags.is_empty() {
3402                             set_next(FontFaceSourceListComponent::TechFlags(url.tech_flags));
3403                         }
3404                     },
3405                     Source::Local(ref name) => {
3406                         set_next(FontFaceSourceListComponent::Local(name.name.as_ptr()));
3407                     },
3408                 }
3409             }
3410         }
3412         assert!(iter.next().is_none(), "miscalculated");
3413     })
3416 #[no_mangle]
3417 pub unsafe extern "C" fn Servo_FontFaceRule_GetVariationSettings(
3418     rule: &LockedFontFaceRule,
3419     variations: *mut nsTArray<structs::gfxFontVariation>,
3420 ) {
3421     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3422         let source_variations = match rule.variation_settings {
3423             Some(ref v) => v,
3424             None => return,
3425         };
3427         (*variations).set_len(source_variations.0.len() as u32);
3428         for (target, source) in (*variations).iter_mut().zip(source_variations.0.iter()) {
3429             *target = structs::gfxFontVariation {
3430                 mTag: source.tag.0,
3431                 mValue: source.value.get(),
3432             };
3433         }
3434     });
3437 #[no_mangle]
3438 pub unsafe extern "C" fn Servo_FontFaceRule_GetFeatureSettings(
3439     rule: &LockedFontFaceRule,
3440     features: *mut nsTArray<structs::gfxFontFeature>,
3441 ) {
3442     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3443         let source_features = match rule.feature_settings {
3444             Some(ref v) => v,
3445             None => return,
3446         };
3448         (*features).set_len(source_features.0.len() as u32);
3449         for (target, source) in (*features).iter_mut().zip(source_features.0.iter()) {
3450             *target = structs::gfxFontFeature {
3451                 mTag: source.tag.0,
3452                 mValue: source.value.value() as u32,
3453             };
3454         }
3455     });
3458 #[no_mangle]
3459 pub extern "C" fn Servo_FontFaceRule_GetDescriptorCssText(
3460     rule: &LockedFontFaceRule,
3461     desc: nsCSSFontDesc,
3462     result: &mut nsACString,
3463 ) {
3464     read_locked_arc_worker(rule, |rule: &FontFaceRule| {
3465         let mut writer = CssWriter::new(result);
3466         macro_rules! to_css_text {
3467             (
3468                 valid: [$($v_enum_name:ident => $field:ident,)*]
3469                 invalid: [$($i_enum_name:ident,)*]
3470             ) => {
3471                 match desc {
3472                     $(
3473                         nsCSSFontDesc::$v_enum_name => {
3474                             if let Some(ref value) = rule.$field {
3475                                 value.to_css(&mut writer).unwrap();
3476                             }
3477                         }
3478                     )*
3479                     $(
3480                         nsCSSFontDesc::$i_enum_name => {
3481                             debug_assert!(false, "not a valid font descriptor");
3482                         }
3483                     )*
3484                 }
3485             }
3486         }
3487         apply_font_desc_list!(to_css_text)
3488     })
3491 #[no_mangle]
3492 pub unsafe extern "C" fn Servo_FontFaceRule_SetDescriptor(
3493     rule: &LockedFontFaceRule,
3494     desc: nsCSSFontDesc,
3495     value: &nsACString,
3496     data: *mut URLExtraData,
3497     out_changed: *mut bool,
3498 ) -> bool {
3499     let value = value.as_str_unchecked();
3500     let mut input = ParserInput::new(&value);
3501     let mut parser = Parser::new(&mut input);
3502     let url_data = UrlExtraData::from_ptr_ref(&data);
3503     let context = ParserContext::new(
3504         Origin::Author,
3505         url_data,
3506         Some(CssRuleType::FontFace),
3507         ParsingMode::DEFAULT,
3508         QuirksMode::NoQuirks,
3509         /* namespaces = */ Default::default(),
3510         None,
3511         None,
3512     );
3514     write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
3515         macro_rules! to_css_text {
3516             (
3517                 valid: [$($v_enum_name:ident => $field:ident,)*]
3518                 invalid: [$($i_enum_name:ident,)*]
3519             ) => {
3520                 match desc {
3521                     $(
3522                         nsCSSFontDesc::$v_enum_name => {
3523                             if let Ok(value) = parser.parse_entirely(|i| Parse::parse(&context, i)) {
3524                                 let result = Some(value);
3525                                 *out_changed = result != rule.$field;
3526                                 rule.$field = result;
3527                                 true
3528                             } else {
3529                                 false
3530                             }
3531                         }
3532                     )*
3533                     $(
3534                         nsCSSFontDesc::$i_enum_name => {
3535                             debug_assert!(false, "not a valid font descriptor");
3536                             false
3537                         }
3538                     )*
3539                 }
3540             }
3541         }
3542         apply_font_desc_list!(to_css_text)
3543     })
3546 #[no_mangle]
3547 pub unsafe extern "C" fn Servo_FontFaceRule_ResetDescriptor(
3548     rule: &LockedFontFaceRule,
3549     desc: nsCSSFontDesc,
3550 ) {
3551     write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
3552         macro_rules! reset_desc {
3553             (
3554                 valid: [$($v_enum_name:ident => $field:ident,)*]
3555                 invalid: [$($i_enum_name:ident,)*]
3556             ) => {
3557                 match desc {
3558                     $(nsCSSFontDesc::$v_enum_name => rule.$field = None,)*
3559                     $(nsCSSFontDesc::$i_enum_name => debug_assert!(false, "not a valid font descriptor"),)*
3560                 }
3561             }
3562         }
3563         apply_font_desc_list!(reset_desc)
3564     })
3567 #[no_mangle]
3568 pub unsafe extern "C" fn Servo_CounterStyleRule_GetName(
3569     rule: &LockedCounterStyleRule,
3570 ) -> *mut nsAtom {
3571     read_locked_arc(rule, |rule: &CounterStyleRule| rule.name().0.as_ptr())
3574 #[no_mangle]
3575 pub unsafe extern "C" fn Servo_CounterStyleRule_SetName(
3576     rule: &LockedCounterStyleRule,
3577     value: &nsACString,
3578 ) -> bool {
3579     let value = value.as_str_unchecked();
3580     let mut input = ParserInput::new(&value);
3581     let mut parser = Parser::new(&mut input);
3582     match parser.parse_entirely(counter_style::parse_counter_style_name_definition) {
3583         Ok(name) => {
3584             write_locked_arc(rule, |rule: &mut CounterStyleRule| rule.set_name(name));
3585             true
3586         },
3587         Err(_) => false,
3588     }
3591 #[no_mangle]
3592 pub unsafe extern "C" fn Servo_CounterStyleRule_GetGeneration(
3593     rule: &LockedCounterStyleRule,
3594 ) -> u32 {
3595     read_locked_arc(rule, |rule: &CounterStyleRule| rule.generation())
3598 fn symbol_to_string(s: &counter_style::Symbol) -> nsString {
3599     match *s {
3600         counter_style::Symbol::String(ref s) => nsString::from(&**s),
3601         counter_style::Symbol::Ident(ref i) => nsString::from(i.0.as_slice()),
3602     }
3605 // TODO(emilio): Cbindgen could be used to simplify a bunch of code here.
3606 #[no_mangle]
3607 pub unsafe extern "C" fn Servo_CounterStyleRule_GetPad(
3608     rule: &LockedCounterStyleRule,
3609     width: &mut i32,
3610     symbol: &mut nsString,
3611 ) -> bool {
3612     read_locked_arc(rule, |rule: &CounterStyleRule| {
3613         let pad = match rule.pad() {
3614             Some(pad) => pad,
3615             None => return false,
3616         };
3617         *width = pad.0.value();
3618         *symbol = symbol_to_string(&pad.1);
3619         true
3620     })
3623 fn get_symbol(s: Option<&counter_style::Symbol>, out: &mut nsString) -> bool {
3624     let s = match s {
3625         Some(s) => s,
3626         None => return false,
3627     };
3628     *out = symbol_to_string(s);
3629     true
3632 #[no_mangle]
3633 pub unsafe extern "C" fn Servo_CounterStyleRule_GetPrefix(
3634     rule: &LockedCounterStyleRule,
3635     out: &mut nsString,
3636 ) -> bool {
3637     read_locked_arc(rule, |rule: &CounterStyleRule| {
3638         get_symbol(rule.prefix(), out)
3639     })
3642 #[no_mangle]
3643 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSuffix(
3644     rule: &LockedCounterStyleRule,
3645     out: &mut nsString,
3646 ) -> bool {
3647     read_locked_arc(rule, |rule: &CounterStyleRule| {
3648         get_symbol(rule.suffix(), out)
3649     })
3652 #[no_mangle]
3653 pub unsafe extern "C" fn Servo_CounterStyleRule_GetNegative(
3654     rule: &LockedCounterStyleRule,
3655     prefix: &mut nsString,
3656     suffix: &mut nsString,
3657 ) -> bool {
3658     read_locked_arc(rule, |rule: &CounterStyleRule| {
3659         let negative = match rule.negative() {
3660             Some(n) => n,
3661             None => return false,
3662         };
3663         *prefix = symbol_to_string(&negative.0);
3664         *suffix = match negative.1 {
3665             Some(ref s) => symbol_to_string(s),
3666             None => nsString::new(),
3667         };
3668         true
3669     })
3672 #[repr(u8)]
3673 pub enum IsOrdinalInRange {
3674     Auto,
3675     InRange,
3676     NotInRange,
3677     NoOrdinalSpecified,
3680 #[no_mangle]
3681 pub unsafe extern "C" fn Servo_CounterStyleRule_IsInRange(
3682     rule: &LockedCounterStyleRule,
3683     ordinal: i32,
3684 ) -> IsOrdinalInRange {
3685     use style::counter_style::CounterBound;
3686     read_locked_arc(rule, |rule: &CounterStyleRule| {
3687         let range = match rule.range() {
3688             Some(r) => r,
3689             None => return IsOrdinalInRange::NoOrdinalSpecified,
3690         };
3692         if range.0.is_empty() {
3693             return IsOrdinalInRange::Auto;
3694         }
3696         let in_range = range.0.iter().any(|r| {
3697             if let CounterBound::Integer(start) = r.start {
3698                 if start.value() > ordinal {
3699                     return false;
3700                 }
3701             }
3703             if let CounterBound::Integer(end) = r.end {
3704                 if end.value() < ordinal {
3705                     return false;
3706                 }
3707             }
3709             true
3710         });
3712         if in_range {
3713             IsOrdinalInRange::InRange
3714         } else {
3715             IsOrdinalInRange::NotInRange
3716         }
3717     })
3720 #[no_mangle]
3721 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSymbols(
3722     rule: &LockedCounterStyleRule,
3723     symbols: &mut style::OwnedSlice<nsString>,
3724 ) {
3725     read_locked_arc(rule, |rule: &CounterStyleRule| {
3726         *symbols = match rule.symbols() {
3727             Some(s) => s.0.iter().map(symbol_to_string).collect(),
3728             None => style::OwnedSlice::default(),
3729         };
3730     })
3733 #[repr(C)]
3734 pub struct AdditiveSymbol {
3735     pub weight: i32,
3736     pub symbol: nsString,
3739 #[no_mangle]
3740 pub unsafe extern "C" fn Servo_CounterStyleRule_GetAdditiveSymbols(
3741     rule: &LockedCounterStyleRule,
3742     symbols: &mut style::OwnedSlice<AdditiveSymbol>,
3743 ) {
3744     read_locked_arc(rule, |rule: &CounterStyleRule| {
3745         *symbols = match rule.additive_symbols() {
3746             Some(s) => {
3747                 s.0.iter()
3748                     .map(|s| AdditiveSymbol {
3749                         weight: s.weight.value(),
3750                         symbol: symbol_to_string(&s.symbol),
3751                     })
3752                     .collect()
3753             },
3754             None => style::OwnedSlice::default(),
3755         };
3756     })
3759 #[repr(C, u8)]
3760 pub enum CounterSpeakAs {
3761     None,
3762     Auto,
3763     Bullets,
3764     Numbers,
3765     Words,
3766     Ident(*mut nsAtom),
3769 #[no_mangle]
3770 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSpeakAs(
3771     rule: &LockedCounterStyleRule,
3772     out: &mut CounterSpeakAs,
3773 ) {
3774     use style::counter_style::SpeakAs;
3775     *out = read_locked_arc(rule, |rule: &CounterStyleRule| {
3776         let speak_as = match rule.speak_as() {
3777             Some(s) => s,
3778             None => return CounterSpeakAs::None,
3779         };
3780         match *speak_as {
3781             SpeakAs::Auto => CounterSpeakAs::Auto,
3782             SpeakAs::Bullets => CounterSpeakAs::Bullets,
3783             SpeakAs::Numbers => CounterSpeakAs::Numbers,
3784             SpeakAs::Words => CounterSpeakAs::Words,
3785             SpeakAs::Other(ref other) => CounterSpeakAs::Ident(other.0.as_ptr()),
3786         }
3787     });
3790 #[repr(u8)]
3791 pub enum CounterSystem {
3792     Cyclic = 0,
3793     Numeric,
3794     Alphabetic,
3795     Symbolic,
3796     Additive,
3797     Fixed,
3798     Extends,
3801 #[no_mangle]
3802 pub unsafe extern "C" fn Servo_CounterStyleRule_GetSystem(
3803     rule: &LockedCounterStyleRule,
3804 ) -> CounterSystem {
3805     use style::counter_style::System;
3806     read_locked_arc(rule, |rule: &CounterStyleRule| {
3807         match *rule.resolved_system() {
3808             System::Cyclic => CounterSystem::Cyclic,
3809             System::Numeric => CounterSystem::Numeric,
3810             System::Alphabetic => CounterSystem::Alphabetic,
3811             System::Symbolic => CounterSystem::Symbolic,
3812             System::Additive => CounterSystem::Additive,
3813             System::Fixed { .. } => CounterSystem::Fixed,
3814             System::Extends(_) => CounterSystem::Extends,
3815         }
3816     })
3819 #[no_mangle]
3820 pub unsafe extern "C" fn Servo_CounterStyleRule_GetExtended(
3821     rule: &LockedCounterStyleRule,
3822 ) -> *mut nsAtom {
3823     read_locked_arc(rule, |rule: &CounterStyleRule| {
3824         match *rule.resolved_system() {
3825             counter_style::System::Extends(ref name) => name.0.as_ptr(),
3826             _ => {
3827                 debug_assert!(false, "Not extends system");
3828                 ptr::null_mut()
3829             },
3830         }
3831     })
3834 #[no_mangle]
3835 pub unsafe extern "C" fn Servo_CounterStyleRule_GetFixedFirstValue(
3836     rule: &LockedCounterStyleRule,
3837 ) -> i32 {
3838     read_locked_arc(rule, |rule: &CounterStyleRule| {
3839         match *rule.resolved_system() {
3840             counter_style::System::Fixed { first_symbol_value } => {
3841                 first_symbol_value.map_or(1, |v| v.value())
3842             },
3843             _ => {
3844                 debug_assert!(false, "Not fixed system");
3845                 0
3846             },
3847         }
3848     })
3851 #[no_mangle]
3852 pub unsafe extern "C" fn Servo_CounterStyleRule_GetFallback(
3853     rule: &LockedCounterStyleRule,
3854 ) -> *mut nsAtom {
3855     read_locked_arc(rule, |rule: &CounterStyleRule| {
3856         rule.fallback().map_or(ptr::null_mut(), |i| i.0 .0.as_ptr())
3857     })
3860 macro_rules! counter_style_descriptors {
3861     {
3862         valid: [
3863             $($desc:ident => $getter:ident / $setter:ident,)+
3864         ]
3865         invalid: [
3866             $($i_desc:ident,)+
3867         ]
3868     } => {
3869         #[no_mangle]
3870         pub unsafe extern "C" fn Servo_CounterStyleRule_GetDescriptorCssText(
3871             rule: &LockedCounterStyleRule,
3872             desc: nsCSSCounterDesc,
3873             result: &mut nsACString,
3874         ) {
3875             let mut writer = CssWriter::new(result);
3876             read_locked_arc(rule, |rule: &CounterStyleRule| {
3877                 match desc {
3878                     $(nsCSSCounterDesc::$desc => {
3879                         if let Some(value) = rule.$getter() {
3880                             value.to_css(&mut writer).unwrap();
3881                         }
3882                     })+
3883                     $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
3884                 }
3885             });
3886         }
3888         #[no_mangle]
3889         pub unsafe extern "C" fn Servo_CounterStyleRule_SetDescriptor(
3890             rule: &LockedCounterStyleRule,
3891             desc: nsCSSCounterDesc,
3892             value: &nsACString,
3893         ) -> bool {
3894             let value = value.as_str_unchecked();
3895             let mut input = ParserInput::new(&value);
3896             let mut parser = Parser::new(&mut input);
3897             let url_data = dummy_url_data();
3898             let context = ParserContext::new(
3899                 Origin::Author,
3900                 url_data,
3901                 Some(CssRuleType::CounterStyle),
3902                 ParsingMode::DEFAULT,
3903                 QuirksMode::NoQuirks,
3904                 /* namespaces = */ Default::default(),
3905                 None,
3906                 None,
3907             );
3909             write_locked_arc(rule, |rule: &mut CounterStyleRule| {
3910                 match desc {
3911                     $(nsCSSCounterDesc::$desc => {
3912                         match parser.parse_entirely(|i| Parse::parse(&context, i)) {
3913                             Ok(value) => rule.$setter(value),
3914                             Err(_) => false,
3915                         }
3916                     })+
3917                     $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
3918                 }
3919             })
3920         }
3921     }
3924 counter_style_descriptors! {
3925     valid: [
3926         eCSSCounterDesc_System => system / set_system,
3927         eCSSCounterDesc_Symbols => symbols / set_symbols,
3928         eCSSCounterDesc_AdditiveSymbols => additive_symbols / set_additive_symbols,
3929         eCSSCounterDesc_Negative => negative / set_negative,
3930         eCSSCounterDesc_Prefix => prefix / set_prefix,
3931         eCSSCounterDesc_Suffix => suffix / set_suffix,
3932         eCSSCounterDesc_Range => range / set_range,
3933         eCSSCounterDesc_Pad => pad / set_pad,
3934         eCSSCounterDesc_Fallback => fallback / set_fallback,
3935         eCSSCounterDesc_SpeakAs => speak_as / set_speak_as,
3936     ]
3937     invalid: [
3938         eCSSCounterDesc_UNKNOWN,
3939         eCSSCounterDesc_COUNT,
3940     ]
3943 #[no_mangle]
3944 pub unsafe extern "C" fn Servo_ComputedValues_GetForPageContent(
3945     raw_data: &PerDocumentStyleData,
3946     page_name: *const nsAtom,
3947     pseudos: PagePseudoClassFlags,
3948 ) -> Strong<ComputedValues> {
3949     let global_style_data = &*GLOBAL_STYLE_DATA;
3950     let guard = global_style_data.shared_lock.read();
3951     let guards = StylesheetGuards::same(&guard);
3952     let data = raw_data.borrow_mut();
3953     let cascade_data = data.stylist.cascade_data();
3955     let mut extra_declarations = vec![];
3956     let iter = data.stylist.iter_extra_data_origins_rev();
3957     let name = if !page_name.is_null() {
3958         Some(Atom::from_raw(page_name as *mut nsAtom))
3959     } else {
3960         None
3961     };
3962     for (data, origin) in iter {
3963         data.pages.match_and_append_rules(
3964             &mut extra_declarations,
3965             origin,
3966             &guards,
3967             cascade_data,
3968             &name,
3969             pseudos,
3970         );
3971     }
3973     let rule_node = data.stylist.rule_node_for_precomputed_pseudo(
3974         &guards,
3975         &PseudoElement::PageContent,
3976         extra_declarations,
3977     );
3979     data.stylist
3980         .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
3981             &guards,
3982             &PseudoElement::PageContent,
3983             None,
3984             rule_node,
3985         )
3986         .into()
3989 #[no_mangle]
3990 pub unsafe extern "C" fn Servo_ComputedValues_GetForAnonymousBox(
3991     parent_style_or_null: Option<&ComputedValues>,
3992     pseudo: PseudoStyleType,
3993     raw_data: &PerDocumentStyleData,
3994 ) -> Strong<ComputedValues> {
3995     let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
3996     debug_assert!(pseudo.is_anon_box());
3997     debug_assert_ne!(pseudo, PseudoElement::PageContent);
3998     let global_style_data = &*GLOBAL_STYLE_DATA;
3999     let guard = global_style_data.shared_lock.read();
4000     let guards = StylesheetGuards::same(&guard);
4001     let data = raw_data.borrow_mut();
4002     let rule_node = data
4003         .stylist
4004         .rule_node_for_precomputed_pseudo(&guards, &pseudo, vec![]);
4006     data.stylist
4007         .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
4008             &guards,
4009             &pseudo,
4010             parent_style_or_null.map(|x| &*x),
4011             rule_node,
4012         )
4013         .into()
4016 fn get_functional_pseudo_parameter_atom(
4017     functional_pseudo_parameter: *mut nsAtom,
4018 ) -> Option<AtomIdent> {
4019     if functional_pseudo_parameter.is_null() {
4020         None
4021     } else {
4022         Some(AtomIdent::new(unsafe {
4023             Atom::from_raw(functional_pseudo_parameter)
4024         }))
4025     }
4028 #[no_mangle]
4029 pub extern "C" fn Servo_ResolvePseudoStyle(
4030     element: &RawGeckoElement,
4031     pseudo_type: PseudoStyleType,
4032     functional_pseudo_parameter: *mut nsAtom,
4033     is_probe: bool,
4034     inherited_style: Option<&ComputedValues>,
4035     raw_data: &PerDocumentStyleData,
4036 ) -> Strong<ComputedValues> {
4037     let element = GeckoElement(element);
4038     let doc_data = raw_data.borrow();
4040     debug!(
4041         "Servo_ResolvePseudoStyle: {:?} {:?}, is_probe: {}",
4042         element,
4043         PseudoElement::from_pseudo_type(
4044             pseudo_type,
4045             get_functional_pseudo_parameter_atom(functional_pseudo_parameter)
4046         ),
4047         is_probe
4048     );
4050     let data = element.borrow_data();
4052     let data = match data.as_ref() {
4053         Some(data) if data.has_styles() => data,
4054         _ => {
4055             // FIXME(bholley, emilio): Assert against this.
4056             //
4057             // Known offender is nsMathMLmoFrame::MarkIntrinsicISizesDirty,
4058             // which goes and does a bunch of work involving style resolution.
4059             //
4060             // Bug 1403865 tracks fixing it, and potentially adding an assert
4061             // here instead.
4062             warn!("Calling Servo_ResolvePseudoStyle on unstyled element");
4063             return if is_probe {
4064                 Strong::null()
4065             } else {
4066                 doc_data.default_computed_values().clone().into()
4067             };
4068         },
4069     };
4071     let pseudo_element = PseudoElement::from_pseudo_type(
4072         pseudo_type,
4073         get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
4074     )
4075     .expect("ResolvePseudoStyle with a non-pseudo?");
4077     let matching_fn = |pseudo: &PseudoElement| *pseudo == pseudo_element;
4079     let global_style_data = &*GLOBAL_STYLE_DATA;
4080     let guard = global_style_data.shared_lock.read();
4081     let style = get_pseudo_style(
4082         &guard,
4083         element,
4084         &pseudo_element,
4085         RuleInclusion::All,
4086         &data.styles,
4087         inherited_style,
4088         &doc_data.stylist,
4089         is_probe,
4090         /* matching_func = */
4091         if pseudo_element.is_highlight() {
4092             Some(&matching_fn)
4093         } else {
4094             None
4095         },
4096     );
4098     match style {
4099         Some(s) => s.into(),
4100         None => {
4101             debug_assert!(is_probe);
4102             Strong::null()
4103         },
4104     }
4107 fn debug_atom_array(atoms: &nsTArray<structs::RefPtr<nsAtom>>) -> String {
4108     let mut result = String::from("[");
4109     for atom in atoms.iter() {
4110         if atom.mRawPtr.is_null() {
4111             result += "(null), ";
4112         } else {
4113             let atom = unsafe { WeakAtom::new(atom.mRawPtr) };
4114             write!(result, "{}, ", atom).unwrap();
4115         }
4116     }
4117     result.push(']');
4118     result
4121 #[no_mangle]
4122 pub extern "C" fn Servo_ComputedValues_ResolveXULTreePseudoStyle(
4123     element: &RawGeckoElement,
4124     pseudo_tag: *mut nsAtom,
4125     inherited_style: &ComputedValues,
4126     input_word: &nsTArray<structs::RefPtr<nsAtom>>,
4127     raw_data: &PerDocumentStyleData,
4128 ) -> Strong<ComputedValues> {
4129     let element = GeckoElement(element);
4130     let data = element
4131         .borrow_data()
4132         .expect("Calling ResolveXULTreePseudoStyle on unstyled element?");
4134     let pseudo = unsafe {
4135         Atom::with(pseudo_tag, |atom| {
4136             PseudoElement::from_tree_pseudo_atom(atom, Box::new([]))
4137         })
4138         .expect("ResolveXULTreePseudoStyle with a non-tree pseudo?")
4139     };
4140     let doc_data = raw_data.borrow();
4142     debug!(
4143         "ResolveXULTreePseudoStyle: {:?} {:?} {}",
4144         element,
4145         pseudo,
4146         debug_atom_array(input_word)
4147     );
4149     let matching_fn = |pseudo: &PseudoElement| {
4150         let args = pseudo
4151             .tree_pseudo_args()
4152             .expect("Not a tree pseudo-element?");
4153         args.iter()
4154             .all(|atom| input_word.iter().any(|item| atom.as_ptr() == item.mRawPtr))
4155     };
4157     let global_style_data = &*GLOBAL_STYLE_DATA;
4158     let guard = global_style_data.shared_lock.read();
4159     get_pseudo_style(
4160         &guard,
4161         element,
4162         &pseudo,
4163         RuleInclusion::All,
4164         &data.styles,
4165         Some(inherited_style),
4166         &doc_data.stylist,
4167         /* is_probe = */ false,
4168         Some(&matching_fn),
4169     )
4170     .unwrap()
4171     .into()
4174 #[no_mangle]
4175 pub extern "C" fn Servo_SetExplicitStyle(element: &RawGeckoElement, style: &ComputedValues) {
4176     let element = GeckoElement(element);
4177     debug!("Servo_SetExplicitStyle: {:?}", element);
4178     // We only support this API for initial styling. There's no reason it couldn't
4179     // work for other things, we just haven't had a reason to do so.
4180     debug_assert!(!element.has_data());
4181     let mut data = unsafe { element.ensure_data() };
4182     data.styles.primary = Some(unsafe { Arc::from_raw_addrefed(style) });
4185 fn get_pseudo_style(
4186     guard: &SharedRwLockReadGuard,
4187     element: GeckoElement,
4188     pseudo: &PseudoElement,
4189     rule_inclusion: RuleInclusion,
4190     styles: &ElementStyles,
4191     inherited_styles: Option<&ComputedValues>,
4192     stylist: &Stylist,
4193     is_probe: bool,
4194     matching_func: Option<&dyn Fn(&PseudoElement) -> bool>,
4195 ) -> Option<Arc<ComputedValues>> {
4196     let style = match pseudo.cascade_type() {
4197         PseudoElementCascadeType::Eager => {
4198             match *pseudo {
4199                 PseudoElement::FirstLetter => {
4200                     styles.pseudos.get(&pseudo).map(|pseudo_styles| {
4201                         // inherited_styles can be None when doing lazy resolution
4202                         // (e.g. for computed style) or when probing.  In that case
4203                         // we just inherit from our element, which is what Gecko
4204                         // does in that situation.  What should actually happen in
4205                         // the computed style case is a bit unclear.
4206                         let inherited_styles = inherited_styles.unwrap_or(styles.primary());
4207                         let guards = StylesheetGuards::same(guard);
4208                         let inputs = CascadeInputs::new_from_style(pseudo_styles);
4209                         stylist.compute_pseudo_element_style_with_inputs(
4210                             inputs,
4211                             pseudo,
4212                             &guards,
4213                             Some(inherited_styles),
4214                             Some(element),
4215                         )
4216                     })
4217                 },
4218                 _ => {
4219                     // Unfortunately, we can't assert that inherited_styles, if
4220                     // present, is pointer-equal to styles.primary(), or even
4221                     // equal in any meaningful way.  The way it can fail is as
4222                     // follows.  Say we append an element with a ::before,
4223                     // ::after, or ::first-line to a parent with a ::first-line,
4224                     // such that the element ends up on the first line of the
4225                     // parent (e.g. it's an inline-block in the case it has a
4226                     // ::first-line, or any container in the ::before/::after
4227                     // cases).  Then gecko will update its frame's style to
4228                     // inherit from the parent's ::first-line.  The next time we
4229                     // try to get the ::before/::after/::first-line style for
4230                     // the kid, we'll likely pass in the frame's style as
4231                     // inherited_styles, but that's not pointer-identical to
4232                     // styles.primary(), because it got reparented.
4233                     //
4234                     // Now in practice this turns out to be OK, because all the
4235                     // cases in which there's a mismatch go ahead and reparent
4236                     // styles again as needed to make sure the ::first-line
4237                     // affects all the things it should affect.  But it makes it
4238                     // impossible to assert anything about the two styles
4239                     // matching here, unfortunately.
4240                     styles.pseudos.get(&pseudo).cloned()
4241                 },
4242             }
4243         },
4244         PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
4245         PseudoElementCascadeType::Lazy => {
4246             debug_assert!(
4247                 inherited_styles.is_none() ||
4248                     ptr::eq(inherited_styles.unwrap(), &**styles.primary())
4249             );
4250             let originating_element_style = styles.primary();
4251             let guards = StylesheetGuards::same(guard);
4252             stylist.lazily_compute_pseudo_element_style(
4253                 &guards,
4254                 element,
4255                 &pseudo,
4256                 rule_inclusion,
4257                 originating_element_style,
4258                 is_probe,
4259                 matching_func,
4260             )
4261         },
4262     };
4264     if is_probe {
4265         return style;
4266     }
4268     Some(style.unwrap_or_else(|| {
4269         StyleBuilder::for_inheritance(
4270             stylist.device(),
4271             Some(stylist),
4272             Some(styles.primary()),
4273             Some(pseudo),
4274         )
4275         .build()
4276     }))
4279 #[no_mangle]
4280 pub unsafe extern "C" fn Servo_ComputedValues_Inherit(
4281     raw_data: &PerDocumentStyleData,
4282     pseudo: PseudoStyleType,
4283     parent_style_context: Option<&ComputedValues>,
4284     target: structs::InheritTarget,
4285 ) -> Strong<ComputedValues> {
4286     let data = raw_data.borrow();
4288     let for_text = target == structs::InheritTarget::Text;
4289     let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
4290     debug_assert!(pseudo.is_anon_box());
4292     let mut style = StyleBuilder::for_inheritance(
4293         data.stylist.device(),
4294         Some(&data.stylist),
4295         parent_style_context,
4296         Some(&pseudo),
4297     );
4299     if for_text {
4300         StyleAdjuster::new(&mut style).adjust_for_text();
4301     }
4303     style.build().into()
4306 #[no_mangle]
4307 pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(
4308     values: &ComputedValues,
4309 ) -> bool {
4310     let ui = values.get_ui();
4311     ui.specifies_animations() || ui.specifies_transitions()
4314 #[no_mangle]
4315 pub extern "C" fn Servo_ComputedValues_GetStyleRuleList(
4316     values: &ComputedValues,
4317     rules: &mut ThinVec<*const LockedStyleRule>,
4318 ) {
4319     let rule_node = match values.rules {
4320         Some(ref r) => r,
4321         None => return,
4322     };
4324     for node in rule_node.self_and_ancestors() {
4325         let style_rule = match node.style_source().and_then(|x| x.as_rule()) {
4326             Some(rule) => rule,
4327             _ => continue,
4328         };
4330         // For the rules with any important declaration, we insert them into
4331         // rule tree twice, one for normal level and another for important
4332         // level. So, we skip the important one to keep the specificity order of
4333         // rules.
4334         if node.importance().important() {
4335             continue;
4336         }
4338         rules.push(&*style_rule);
4339     }
4342 /// println_stderr!() calls Gecko's printf_stderr(), which, unlike eprintln!(),
4343 /// will funnel output to Android logcat.
4344 #[cfg(feature = "gecko_debug")]
4345 macro_rules! println_stderr {
4346     ($($e:expr),+) => {
4347         {
4348             let mut s = nsCString::new();
4349             write!(s, $($e),+).unwrap();
4350             s.write_char('\n').unwrap();
4351             unsafe { bindings::Gecko_PrintfStderr(&s); }
4352         }
4353     }
4356 #[cfg(feature = "gecko_debug")]
4357 fn dump_properties_and_rules(cv: &ComputedValues, properties: &LonghandIdSet) {
4358     println_stderr!("  Properties:");
4359     for p in properties.iter() {
4360         let mut v = nsCString::new();
4361         cv.computed_or_resolved_value(p, None, &mut v).unwrap();
4362         println_stderr!("    {:?}: {}", p, v);
4363     }
4364     dump_rules(cv);
4367 #[cfg(feature = "gecko_debug")]
4368 fn dump_rules(cv: &ComputedValues) {
4369     println_stderr!("  Rules({:?}):", cv.pseudo());
4370     let global_style_data = &*GLOBAL_STYLE_DATA;
4371     let guard = global_style_data.shared_lock.read();
4372     if let Some(rules) = cv.rules.as_ref() {
4373         for rn in rules.self_and_ancestors() {
4374             if rn.importance().important() {
4375                 continue;
4376             }
4377             if let Some(d) = rn.style_source().and_then(|s| s.as_declarations()) {
4378                 println_stderr!("    [DeclarationBlock: {:?}]", d);
4379             }
4380             if let Some(r) = rn.style_source().and_then(|s| s.as_rule()) {
4381                 let mut s = nsCString::new();
4382                 r.read_with(&guard).to_css(&guard, &mut s).unwrap();
4383                 println_stderr!("    {}", s);
4384             }
4385         }
4386     }
4389 #[cfg(feature = "gecko_debug")]
4390 #[no_mangle]
4391 pub extern "C" fn Servo_ComputedValues_EqualForCachedAnonymousContentStyle(
4392     a: &ComputedValues,
4393     b: &ComputedValues,
4394 ) -> bool {
4395     let mut differing_properties = a.differing_properties(b);
4397     // Ignore any difference in -x-lang, which we can't override in the rules in scrollbars.css,
4398     // but which makes no difference for the anonymous content subtrees we cache style for.
4399     differing_properties.remove(LonghandId::XLang);
4400     // Similarly, -x-lang can influence the font-family fallback we have for the initial
4401     // font-family so remove it as well.
4402     differing_properties.remove(LonghandId::FontFamily);
4403     // We reset font-size to an explicit pixel value, and thus it can get affected by our inherited
4404     // effective zoom. But we don't care about it for the same reason as above.
4405     differing_properties.remove(LonghandId::FontSize);
4407     // Ignore any difference in pref-controlled, inherited properties.  These properties may or may
4408     // not be set by the 'all' declaration in scrollbars.css, depending on whether the pref was
4409     // enabled at the time the UA sheets were parsed.
4410     //
4411     // If you add a new pref-controlled, inherited property, it must be defined with
4412     // `has_effect_on_gecko_scrollbars=False` to declare that different values of this property on
4413     // a <scrollbar> element or its descendant scrollbar part elements should have no effect on
4414     // their rendering and behavior.
4415     //
4416     // If you do need a pref-controlled, inherited property to have an effect on these elements,
4417     // then you will need to add some checks to the
4418     // nsIAnonymousContentCreator::CreateAnonymousContent implementations of nsHTMLScrollFrame and
4419     // nsScrollbarFrame to clear the AnonymousContentKey if a non-initial value is used.
4420     differing_properties.remove_all(&LonghandIdSet::has_no_effect_on_gecko_scrollbars());
4422     if !differing_properties.is_empty() {
4423         println_stderr!("Actual style:");
4424         dump_properties_and_rules(a, &differing_properties);
4425         println_stderr!("Expected style:");
4426         dump_properties_and_rules(b, &differing_properties);
4427     }
4429     differing_properties.is_empty()
4432 #[cfg(feature = "gecko_debug")]
4433 #[no_mangle]
4434 pub extern "C" fn Servo_ComputedValues_DumpMatchedRules(s: &ComputedValues) {
4435     dump_rules(s);
4438 #[no_mangle]
4439 pub extern "C" fn Servo_ComputedValues_BlockifiedDisplay(
4440     style: &ComputedValues,
4441     is_root_element: bool,
4442 ) -> u16 {
4443     let display = style.get_box().clone_display();
4444     let blockified_display = display.equivalent_block_display(is_root_element);
4445     blockified_display.to_u16()
4448 #[no_mangle]
4449 pub extern "C" fn Servo_StyleSet_Init(doc: &structs::Document) -> *mut PerDocumentStyleData {
4450     let data = Box::new(PerDocumentStyleData::new(doc));
4452     // Do this here rather than in Servo_Initialize since we need a document to
4453     // get the default computed values from.
4454     style::properties::generated::gecko::assert_initial_values_match(&data);
4456     Box::into_raw(data) as *mut PerDocumentStyleData
4459 #[no_mangle]
4460 pub unsafe extern "C" fn Servo_StyleSet_Drop(data: *mut PerDocumentStyleData) {
4461     let _ = Box::from_raw(data);
4464 #[no_mangle]
4465 pub extern "C" fn Servo_StyleSet_RebuildCachedData(raw_data: &PerDocumentStyleData) {
4466     let mut data = raw_data.borrow_mut();
4467     data.stylist.device_mut().rebuild_cached_data();
4468     data.undisplayed_style_cache.clear();
4471 #[no_mangle]
4472 pub unsafe extern "C" fn Servo_StyleSet_CompatModeChanged(raw_data: &PerDocumentStyleData) {
4473     let mut data = raw_data.borrow_mut();
4474     let quirks_mode = data.stylist.device().document().mCompatMode;
4475     data.stylist.set_quirks_mode(quirks_mode.into());
4478 fn parse_property_into(
4479     declarations: &mut SourcePropertyDeclaration,
4480     property_id: PropertyId,
4481     value: &nsACString,
4482     origin: Origin,
4483     url_data: &UrlExtraData,
4484     parsing_mode: ParsingMode,
4485     quirks_mode: QuirksMode,
4486     rule_type: CssRuleType,
4487     reporter: Option<&dyn ParseErrorReporter>,
4488 ) -> Result<(), ()> {
4489     let value = unsafe { value.as_str_unchecked() };
4491     if let Some(non_custom) = property_id.non_custom_id() {
4492         if !non_custom.allowed_in_rule(rule_type.into()) {
4493             return Err(());
4494         }
4495     }
4497     parse_one_declaration_into(
4498         declarations,
4499         property_id,
4500         value,
4501         origin,
4502         url_data,
4503         reporter,
4504         parsing_mode,
4505         quirks_mode,
4506         rule_type,
4507     )
4510 #[no_mangle]
4511 pub unsafe extern "C" fn Servo_ParseProperty(
4512     property: &structs::AnimatedPropertyID,
4513     value: &nsACString,
4514     data: *mut URLExtraData,
4515     parsing_mode: ParsingMode,
4516     quirks_mode: nsCompatibility,
4517     loader: *mut Loader,
4518     rule_type: CssRuleType,
4519 ) -> Strong<LockedDeclarationBlock> {
4520     let id = get_property_id_from_animatedpropertyid!(property, Strong::null());
4521     let mut declarations = SourcePropertyDeclaration::default();
4522     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
4523     let data = UrlExtraData::from_ptr_ref(&data);
4524     let result = parse_property_into(
4525         &mut declarations,
4526         id,
4527         value,
4528         Origin::Author,
4529         data,
4530         parsing_mode,
4531         quirks_mode.into(),
4532         rule_type,
4533         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
4534     );
4536     match result {
4537         Ok(()) => {
4538             let global_style_data = &*GLOBAL_STYLE_DATA;
4539             let mut block = PropertyDeclarationBlock::new();
4540             block.extend(declarations.drain(), Importance::Normal);
4541             Arc::new(global_style_data.shared_lock.wrap(block)).into()
4542         },
4543         Err(_) => Strong::null(),
4544     }
4547 #[no_mangle]
4548 pub extern "C" fn Servo_ParseEasing(
4549     easing: &nsACString,
4550     output: &mut ComputedTimingFunction,
4551 ) -> bool {
4552     use style::properties::longhands::transition_timing_function;
4554     let context = ParserContext::new(
4555         Origin::Author,
4556         unsafe { dummy_url_data() },
4557         Some(CssRuleType::Style),
4558         ParsingMode::DEFAULT,
4559         QuirksMode::NoQuirks,
4560         /* namespaces = */ Default::default(),
4561         None,
4562         None,
4563     );
4564     let easing = easing.to_string();
4565     let mut input = ParserInput::new(&easing);
4566     let mut parser = Parser::new(&mut input);
4567     let result =
4568         parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
4569     match result {
4570         Ok(parsed_easing) => {
4571             *output = parsed_easing.to_computed_value_without_context();
4572             true
4573         },
4574         Err(_) => false,
4575     }
4578 #[no_mangle]
4579 pub extern "C" fn Servo_SerializeEasing(easing: &ComputedTimingFunction, output: &mut nsACString) {
4580     easing.to_css(&mut CssWriter::new(output)).unwrap();
4583 #[no_mangle]
4584 pub extern "C" fn Servo_GetProperties_Overriding_Animation(
4585     element: &RawGeckoElement,
4586     list: &nsTArray<nsCSSPropertyID>,
4587     set: &mut structs::nsCSSPropertyIDSet,
4588 ) {
4589     let element = GeckoElement(element);
4590     let element_data = match element.borrow_data() {
4591         Some(data) => data,
4592         None => return,
4593     };
4594     let global_style_data = &*GLOBAL_STYLE_DATA;
4595     let guard = global_style_data.shared_lock.read();
4596     let guards = StylesheetGuards::same(&guard);
4597     let (overridden, custom) = element_data
4598         .styles
4599         .primary()
4600         .rules()
4601         .get_properties_overriding_animations(&guards);
4602     for p in list.iter() {
4603         match NonCustomPropertyId::from_nscsspropertyid(*p) {
4604             Some(property) => {
4605                 if let Some(id) = property.as_longhand() {
4606                     if overridden.contains(id) {
4607                         unsafe { Gecko_AddPropertyToSet(set, *p) };
4608                     }
4609                 }
4610             },
4611             None => {
4612                 if *p == nsCSSPropertyID::eCSSPropertyExtra_variable && custom {
4613                     unsafe { Gecko_AddPropertyToSet(set, *p) };
4614                 }
4615             },
4616         }
4617     }
4620 #[no_mangle]
4621 pub extern "C" fn Servo_MatrixTransform_Operate(
4622     matrix_operator: MatrixTransformOperator,
4623     from: *const structs::Matrix4x4Components,
4624     to: *const structs::Matrix4x4Components,
4625     progress: f64,
4626     output: *mut structs::Matrix4x4Components,
4627 ) {
4628     use self::MatrixTransformOperator::{Accumulate, Interpolate};
4629     use style::values::computed::transform::Matrix3D;
4631     let from = Matrix3D::from(unsafe { from.as_ref() }.expect("not a valid 'from' matrix"));
4632     let to = Matrix3D::from(unsafe { to.as_ref() }.expect("not a valid 'to' matrix"));
4633     let result = match matrix_operator {
4634         Interpolate => from.animate(&to, Procedure::Interpolate { progress }),
4635         Accumulate => from.animate(
4636             &to,
4637             Procedure::Accumulate {
4638                 count: progress as u64,
4639             },
4640         ),
4641     };
4643     let output = unsafe { output.as_mut() }.expect("not a valid 'output' matrix");
4644     if let Ok(result) = result {
4645         *output = result.into();
4646     } else if progress < 0.5 {
4647         *output = from.clone().into();
4648     } else {
4649         *output = to.clone().into();
4650     }
4653 #[no_mangle]
4654 pub unsafe extern "C" fn Servo_ParseStyleAttribute(
4655     data: &nsACString,
4656     raw_extra_data: *mut URLExtraData,
4657     quirks_mode: nsCompatibility,
4658     loader: *mut Loader,
4659     rule_type: CssRuleType,
4660 ) -> Strong<LockedDeclarationBlock> {
4661     let global_style_data = &*GLOBAL_STYLE_DATA;
4662     let value = data.as_str_unchecked();
4663     let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
4664     let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
4665     Arc::new(global_style_data.shared_lock.wrap(parse_style_attribute(
4666         value,
4667         url_data,
4668         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
4669         quirks_mode.into(),
4670         rule_type,
4671     )))
4672     .into()
4675 #[no_mangle]
4676 pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> Strong<LockedDeclarationBlock> {
4677     let global_style_data = &*GLOBAL_STYLE_DATA;
4678     Arc::new(
4679         global_style_data
4680             .shared_lock
4681             .wrap(PropertyDeclarationBlock::new()),
4682     )
4683     .into()
4686 #[no_mangle]
4687 pub extern "C" fn Servo_DeclarationBlock_Clear(declarations: &LockedDeclarationBlock) {
4688     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
4689         decls.clear();
4690     });
4693 #[no_mangle]
4694 pub extern "C" fn Servo_DeclarationBlock_Clone(
4695     declarations: &LockedDeclarationBlock,
4696 ) -> Strong<LockedDeclarationBlock> {
4697     let global_style_data = &*GLOBAL_STYLE_DATA;
4698     let guard = global_style_data.shared_lock.read();
4699     Arc::new(
4700         global_style_data
4701             .shared_lock
4702             .wrap(declarations.read_with(&guard).clone()),
4703     )
4704     .into()
4707 #[no_mangle]
4708 pub extern "C" fn Servo_DeclarationBlock_Equals(
4709     a: &LockedDeclarationBlock,
4710     b: &LockedDeclarationBlock,
4711 ) -> bool {
4712     let global_style_data = &*GLOBAL_STYLE_DATA;
4713     let guard = global_style_data.shared_lock.read();
4714     a.read_with(&guard).declarations() == b.read_with(&guard).declarations()
4717 #[no_mangle]
4718 pub extern "C" fn Servo_DeclarationBlock_GetCssText(
4719     declarations: &LockedDeclarationBlock,
4720     result: &mut nsACString,
4721 ) {
4722     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4723         decls.to_css(result).unwrap()
4724     })
4727 #[no_mangle]
4728 pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue(
4729     decls: &LockedDeclarationBlock,
4730     property_id: &structs::AnimatedPropertyID,
4731     buffer: &mut nsACString,
4732     computed_values: Option<&ComputedValues>,
4733     data: &PerDocumentStyleData,
4734 ) {
4735     let property_id = get_property_id_from_animatedpropertyid!(property_id, ());
4737     let global_style_data = &*GLOBAL_STYLE_DATA;
4738     let guard = global_style_data.shared_lock.read();
4739     let data = data.borrow();
4740     let rv = decls.read_with(&guard).single_value_to_css(
4741         &property_id,
4742         buffer,
4743         computed_values,
4744         &data.stylist,
4745     );
4746     debug_assert!(rv.is_ok());
4749 #[no_mangle]
4750 pub unsafe extern "C" fn Servo_SerializeFontValueForCanvas(
4751     declarations: &LockedDeclarationBlock,
4752     buffer: &mut nsACString,
4753 ) {
4754     use style::properties::shorthands::font;
4755     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4756         let longhands = match font::LonghandsToSerialize::from_iter(decls.declarations().iter()) {
4757             Ok(l) => l,
4758             Err(()) => {
4759                 warn!("Unexpected property!");
4760                 return;
4761             },
4762         };
4764         let rv = longhands.to_css(&mut CssWriter::new(buffer));
4765         debug_assert!(rv.is_ok());
4766     })
4769 #[no_mangle]
4770 pub extern "C" fn Servo_DeclarationBlock_Count(declarations: &LockedDeclarationBlock) -> u32 {
4771     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4772         decls.declarations().len() as u32
4773     })
4776 #[no_mangle]
4777 pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(
4778     declarations: &LockedDeclarationBlock,
4779     index: u32,
4780     result: &mut nsACString,
4781 ) -> bool {
4782     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4783         if let Some(decl) = decls.declarations().get(index as usize) {
4784             result.assign(&decl.id().name());
4785             true
4786         } else {
4787             false
4788         }
4789     })
4792 macro_rules! get_property_id_from_property {
4793     ($property: ident, $ret: expr) => {{
4794         let property = $property.as_str_unchecked();
4795         match PropertyId::parse_enabled_for_all_content(property) {
4796             Ok(property_id) => property_id,
4797             Err(_) => return $ret,
4798         }
4799     }};
4802 unsafe fn get_property_value(
4803     declarations: &LockedDeclarationBlock,
4804     property_id: PropertyId,
4805     value: &mut nsACString,
4806 ) {
4807     // This callsite is hot enough that the lock acquisition shows up in profiles.
4808     // Using an unchecked read here improves our performance by ~10% on the
4809     // microbenchmark in bug 1355599.
4810     read_locked_arc_unchecked(declarations, |decls: &PropertyDeclarationBlock| {
4811         decls.property_value_to_css(&property_id, value).unwrap();
4812     })
4815 #[no_mangle]
4816 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValue(
4817     declarations: &LockedDeclarationBlock,
4818     property: &nsACString,
4819     value: &mut nsACString,
4820 ) {
4821     get_property_value(
4822         declarations,
4823         get_property_id_from_property!(property, ()),
4824         value,
4825     )
4828 #[no_mangle]
4829 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValueById(
4830     declarations: &LockedDeclarationBlock,
4831     property: nsCSSPropertyID,
4832     value: &mut nsACString,
4833 ) {
4834     get_property_value(
4835         declarations,
4836         get_property_id_from_nscsspropertyid!(property, ()),
4837         value,
4838     )
4841 #[no_mangle]
4842 pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(
4843     declarations: &LockedDeclarationBlock,
4844     property: &nsACString,
4845 ) -> bool {
4846     let property_id = get_property_id_from_property!(property, false);
4847     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
4848         decls.property_priority(&property_id).important()
4849     })
4852 #[inline(always)]
4853 fn set_property_to_declarations(
4854     non_custom_property_id: Option<NonCustomPropertyId>,
4855     block: &LockedDeclarationBlock,
4856     parsed_declarations: &mut SourcePropertyDeclaration,
4857     before_change_closure: DeclarationBlockMutationClosure,
4858     importance: Importance,
4859 ) -> bool {
4860     let mut updates = Default::default();
4861     let will_change = read_locked_arc(block, |decls: &PropertyDeclarationBlock| {
4862         decls.prepare_for_update(&parsed_declarations, importance, &mut updates)
4863     });
4864     if !will_change {
4865         return false;
4866     }
4868     before_change_closure.invoke(non_custom_property_id);
4869     write_locked_arc(block, |decls: &mut PropertyDeclarationBlock| {
4870         decls.update(parsed_declarations.drain(), importance, &mut updates)
4871     });
4872     true
4875 fn set_property(
4876     declarations: &LockedDeclarationBlock,
4877     property_id: PropertyId,
4878     value: &nsACString,
4879     is_important: bool,
4880     data: &UrlExtraData,
4881     parsing_mode: ParsingMode,
4882     quirks_mode: QuirksMode,
4883     loader: *mut Loader,
4884     rule_type: CssRuleType,
4885     before_change_closure: DeclarationBlockMutationClosure,
4886 ) -> bool {
4887     let mut source_declarations = SourcePropertyDeclaration::default();
4888     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data.ptr());
4889     let non_custom_property_id = property_id.non_custom_id();
4890     let result = parse_property_into(
4891         &mut source_declarations,
4892         property_id,
4893         value,
4894         Origin::Author,
4895         data,
4896         parsing_mode,
4897         quirks_mode,
4898         rule_type,
4899         reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
4900     );
4902     if result.is_err() {
4903         return false;
4904     }
4906     let importance = if is_important {
4907         Importance::Important
4908     } else {
4909         Importance::Normal
4910     };
4912     set_property_to_declarations(
4913         non_custom_property_id,
4914         declarations,
4915         &mut source_declarations,
4916         before_change_closure,
4917         importance,
4918     )
4921 #[no_mangle]
4922 pub unsafe extern "C" fn Servo_DeclarationBlock_SetProperty(
4923     declarations: &LockedDeclarationBlock,
4924     property: &nsACString,
4925     value: &nsACString,
4926     is_important: bool,
4927     data: *mut URLExtraData,
4928     parsing_mode: ParsingMode,
4929     quirks_mode: nsCompatibility,
4930     loader: *mut Loader,
4931     rule_type: CssRuleType,
4932     before_change_closure: DeclarationBlockMutationClosure,
4933 ) -> bool {
4934     set_property(
4935         declarations,
4936         get_property_id_from_property!(property, false),
4937         value,
4938         is_important,
4939         UrlExtraData::from_ptr_ref(&data),
4940         parsing_mode,
4941         quirks_mode.into(),
4942         loader,
4943         rule_type,
4944         before_change_closure,
4945     )
4948 #[no_mangle]
4949 pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue(
4950     declarations: &LockedDeclarationBlock,
4951     animation_value: &AnimationValue,
4952     before_change_closure: DeclarationBlockMutationClosure,
4953 ) -> bool {
4954     let non_custom_property_id = match animation_value.id() {
4955         PropertyDeclarationId::Longhand(id) => Some(id.into()),
4956         PropertyDeclarationId::Custom(_) => None,
4957     };
4958     let mut source_declarations = SourcePropertyDeclaration::with_one(animation_value.uncompute());
4960     set_property_to_declarations(
4961         non_custom_property_id,
4962         declarations,
4963         &mut source_declarations,
4964         before_change_closure,
4965         Importance::Normal,
4966     )
4969 #[no_mangle]
4970 pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyById(
4971     declarations: &LockedDeclarationBlock,
4972     property: nsCSSPropertyID,
4973     value: &nsACString,
4974     is_important: bool,
4975     data: *mut URLExtraData,
4976     parsing_mode: ParsingMode,
4977     quirks_mode: nsCompatibility,
4978     loader: *mut Loader,
4979     rule_type: CssRuleType,
4980     before_change_closure: DeclarationBlockMutationClosure,
4981 ) -> bool {
4982     set_property(
4983         declarations,
4984         get_property_id_from_nscsspropertyid!(property, false),
4985         value,
4986         is_important,
4987         UrlExtraData::from_ptr_ref(&data),
4988         parsing_mode,
4989         quirks_mode.into(),
4990         loader,
4991         rule_type,
4992         before_change_closure,
4993     )
4996 fn remove_property(
4997     declarations: &LockedDeclarationBlock,
4998     property_id: PropertyId,
4999     before_change_closure: DeclarationBlockMutationClosure,
5000 ) -> bool {
5001     let first_declaration = read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
5002         decls.first_declaration_to_remove(&property_id)
5003     });
5005     let first_declaration = match first_declaration {
5006         Some(i) => i,
5007         None => return false,
5008     };
5010     before_change_closure.invoke(property_id.non_custom_id());
5011     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5012         decls.remove_property(&property_id, first_declaration)
5013     });
5015     true
5018 #[no_mangle]
5019 pub unsafe extern "C" fn Servo_DeclarationBlock_RemoveProperty(
5020     declarations: &LockedDeclarationBlock,
5021     property: &nsACString,
5022     before_change_closure: DeclarationBlockMutationClosure,
5023 ) -> bool {
5024     remove_property(
5025         declarations,
5026         get_property_id_from_property!(property, false),
5027         before_change_closure,
5028     )
5031 #[no_mangle]
5032 pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(
5033     declarations: &LockedDeclarationBlock,
5034     property: nsCSSPropertyID,
5035     before_change_closure: DeclarationBlockMutationClosure,
5036 ) -> bool {
5037     remove_property(
5038         declarations,
5039         get_property_id_from_nscsspropertyid!(property, false),
5040         before_change_closure,
5041     )
5044 #[no_mangle]
5045 pub extern "C" fn Servo_MediaList_Create() -> Strong<LockedMediaList> {
5046     let global_style_data = &*GLOBAL_STYLE_DATA;
5047     Arc::new(global_style_data.shared_lock.wrap(MediaList::empty())).into()
5050 #[no_mangle]
5051 pub extern "C" fn Servo_MediaList_DeepClone(list: &LockedMediaList) -> Strong<LockedMediaList> {
5052     let global_style_data = &*GLOBAL_STYLE_DATA;
5053     read_locked_arc(list, |list: &MediaList| {
5054         Arc::new(global_style_data.shared_lock.wrap(list.clone())).into()
5055     })
5058 #[no_mangle]
5059 pub extern "C" fn Servo_MediaList_Matches(
5060     list: &LockedMediaList,
5061     raw_data: &PerDocumentStyleData,
5062 ) -> bool {
5063     let per_doc_data = raw_data.borrow();
5064     read_locked_arc(list, |list: &MediaList| {
5065         list.evaluate(
5066             per_doc_data.stylist.device(),
5067             per_doc_data.stylist.quirks_mode(),
5068         )
5069     })
5072 #[no_mangle]
5073 pub extern "C" fn Servo_DeclarationBlock_HasCSSWideKeyword(
5074     declarations: &LockedDeclarationBlock,
5075     property: nsCSSPropertyID,
5076 ) -> bool {
5077     let property_id = get_property_id_from_nscsspropertyid!(property, false);
5078     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
5079         decls.has_css_wide_keyword(&property_id)
5080     })
5083 #[no_mangle]
5084 pub extern "C" fn Servo_MediaList_GetText(list: &LockedMediaList, result: &mut nsACString) {
5085     read_locked_arc(list, |list: &MediaList| {
5086         list.to_css(&mut CssWriter::new(result)).unwrap();
5087     })
5090 #[no_mangle]
5091 pub unsafe extern "C" fn Servo_MediaList_SetText(
5092     list: &LockedMediaList,
5093     text: &nsACString,
5094     caller_type: CallerType,
5095 ) {
5096     let text = text.as_str_unchecked();
5098     let mut input = ParserInput::new(&text);
5099     let mut parser = Parser::new(&mut input);
5100     let url_data = dummy_url_data();
5102     // TODO(emilio): If the need for `CallerType` appears in more places,
5103     // consider adding an explicit member in `ParserContext` instead of doing
5104     // this (or adding a dummy "chrome://" url data).
5105     //
5106     // For media query parsing it's effectively the same, so for now...
5107     let origin = match caller_type {
5108         CallerType::System => Origin::UserAgent,
5109         CallerType::NonSystem => Origin::Author,
5110     };
5112     let context = ParserContext::new(
5113         origin,
5114         url_data,
5115         Some(CssRuleType::Media),
5116         ParsingMode::DEFAULT,
5117         QuirksMode::NoQuirks,
5118         /* namespaces = */ Default::default(),
5119         // TODO(emilio): Looks like error reporting could be useful here?
5120         None,
5121         None,
5122     );
5124     write_locked_arc(list, |list: &mut MediaList| {
5125         *list = MediaList::parse(&context, &mut parser);
5126     })
5129 #[no_mangle]
5130 pub extern "C" fn Servo_MediaList_IsViewportDependent(list: &LockedMediaList) -> bool {
5131     read_locked_arc(list, |list: &MediaList| list.is_viewport_dependent())
5134 #[no_mangle]
5135 pub extern "C" fn Servo_MediaList_GetLength(list: &LockedMediaList) -> u32 {
5136     read_locked_arc(list, |list: &MediaList| list.media_queries.len() as u32)
5139 #[no_mangle]
5140 pub extern "C" fn Servo_MediaList_GetMediumAt(
5141     list: &LockedMediaList,
5142     index: u32,
5143     result: &mut nsACString,
5144 ) -> bool {
5145     read_locked_arc(list, |list: &MediaList| {
5146         let media_query = match list.media_queries.get(index as usize) {
5147             Some(mq) => mq,
5148             None => return false,
5149         };
5150         media_query.to_css(&mut CssWriter::new(result)).unwrap();
5151         true
5152     })
5155 #[no_mangle]
5156 pub extern "C" fn Servo_MediaList_AppendMedium(list: &LockedMediaList, new_medium: &nsACString) {
5157     let new_medium = unsafe { new_medium.as_str_unchecked() };
5158     let url_data = unsafe { dummy_url_data() };
5159     let context = ParserContext::new(
5160         Origin::Author,
5161         url_data,
5162         Some(CssRuleType::Media),
5163         ParsingMode::DEFAULT,
5164         QuirksMode::NoQuirks,
5165         /* namespaces = */ Default::default(),
5166         None,
5167         None,
5168     );
5169     write_locked_arc(list, |list: &mut MediaList| {
5170         list.append_medium(&context, new_medium);
5171     })
5174 #[no_mangle]
5175 pub extern "C" fn Servo_MediaList_DeleteMedium(
5176     list: &LockedMediaList,
5177     old_medium: &nsACString,
5178 ) -> bool {
5179     let old_medium = unsafe { old_medium.as_str_unchecked() };
5180     let url_data = unsafe { dummy_url_data() };
5181     let context = ParserContext::new(
5182         Origin::Author,
5183         url_data,
5184         Some(CssRuleType::Media),
5185         ParsingMode::DEFAULT,
5186         QuirksMode::NoQuirks,
5187         /* namespaces = */ Default::default(),
5188         None,
5189         None,
5190     );
5191     write_locked_arc(list, |list: &mut MediaList| {
5192         list.delete_medium(&context, old_medium)
5193     })
5196 #[no_mangle]
5197 pub extern "C" fn Servo_MediaList_SizeOfIncludingThis(
5198     malloc_size_of: GeckoMallocSizeOf,
5199     malloc_enclosing_size_of: GeckoMallocSizeOf,
5200     list: &LockedMediaList,
5201 ) -> usize {
5202     use malloc_size_of::MallocSizeOf;
5203     use malloc_size_of::MallocUnconditionalShallowSizeOf;
5205     let global_style_data = &*GLOBAL_STYLE_DATA;
5206     let guard = global_style_data.shared_lock.read();
5208     let mut ops = MallocSizeOfOps::new(
5209         malloc_size_of.unwrap(),
5210         Some(malloc_enclosing_size_of.unwrap()),
5211         None,
5212     );
5214     unsafe { ArcBorrow::from_ref(list) }.with_arc(|list| {
5215         let mut n = 0;
5216         n += list.unconditional_shallow_size_of(&mut ops);
5217         n += list.read_with(&guard).size_of(&mut ops);
5218         n
5219     })
5222 macro_rules! get_longhand_from_id {
5223     ($id:expr) => {
5224         match LonghandId::from_nscsspropertyid($id) {
5225             Some(lh) => lh,
5226             _ => panic!("stylo: unknown presentation property with id"),
5227         }
5228     };
5231 macro_rules! match_wrap_declared {
5232     ($longhand:ident, $($property:ident => $inner:expr,)*) => (
5233         match $longhand {
5234             $(
5235                 LonghandId::$property => PropertyDeclaration::$property($inner),
5236             )*
5237             _ => {
5238                 panic!("stylo: Don't know how to handle presentation property");
5239             }
5240         }
5241     )
5244 #[no_mangle]
5245 pub extern "C" fn Servo_DeclarationBlock_PropertyIsSet(
5246     declarations: &LockedDeclarationBlock,
5247     property: nsCSSPropertyID,
5248 ) -> bool {
5249     read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
5250         decls.contains(PropertyDeclarationId::Longhand(get_longhand_from_id!(
5251             property
5252         )))
5253     })
5256 #[no_mangle]
5257 pub unsafe extern "C" fn Servo_DeclarationBlock_SetIdentStringValue(
5258     declarations: &LockedDeclarationBlock,
5259     property: nsCSSPropertyID,
5260     value: *mut nsAtom,
5261 ) {
5262     use style::properties::longhands::_x_lang::computed_value::T as Lang;
5263     use style::properties::PropertyDeclaration;
5265     let long = get_longhand_from_id!(property);
5266     let prop = match_wrap_declared! { long,
5267         XLang => Lang(Atom::from_raw(value)),
5268     };
5269     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5270         decls.push(prop, Importance::Normal);
5271     })
5274 #[no_mangle]
5275 #[allow(unreachable_code)]
5276 pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(
5277     declarations: &LockedDeclarationBlock,
5278     property: nsCSSPropertyID,
5279     value: i32,
5280 ) {
5281     use num_traits::FromPrimitive;
5282     use style::properties::longhands;
5283     use style::properties::PropertyDeclaration;
5284     use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword};
5285     use style::values::generics::font::FontStyle;
5286     use style::values::specified::{
5287         table::CaptionSide, BorderStyle, Clear, Display, Float, TextAlign, TextEmphasisPosition,
5288         TextTransform,
5289     };
5291     fn get_from_computed<T>(value: u32) -> T
5292     where
5293         T: ToComputedValue,
5294         T::ComputedValue: FromPrimitive,
5295     {
5296         T::from_computed_value(&T::ComputedValue::from_u32(value).unwrap())
5297     }
5299     let long = get_longhand_from_id!(property);
5300     let value = value as u32;
5302     let prop = match_wrap_declared! { long,
5303         MozUserModify => longhands::_moz_user_modify::SpecifiedValue::from_gecko_keyword(value),
5304         Direction => get_from_computed::<longhands::direction::SpecifiedValue>(value),
5305         Display => get_from_computed::<Display>(value),
5306         Float => get_from_computed::<Float>(value),
5307         Clear => get_from_computed::<Clear>(value),
5308         VerticalAlign => VerticalAlign::Keyword(VerticalAlignKeyword::from_u32(value).unwrap()),
5309         TextAlign => get_from_computed::<TextAlign>(value),
5310         TextEmphasisPosition => TextEmphasisPosition::from_bits_retain(value as u8),
5311         FontSize => {
5312             // We rely on Gecko passing in font-size values (0...7) here.
5313             longhands::font_size::SpecifiedValue::from_html_size(value as u8)
5314         },
5315         FontStyle => {
5316             style::values::specified::FontStyle::Specified(if value == structs::NS_FONT_STYLE_ITALIC {
5317                 FontStyle::Italic
5318             } else {
5319                 debug_assert_eq!(value, structs::NS_FONT_STYLE_NORMAL);
5320                 FontStyle::Normal
5321             })
5322         },
5323         FontWeight => longhands::font_weight::SpecifiedValue::from_gecko_keyword(value),
5324         ListStyleType => Box::new(longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value)),
5325         MathStyle => longhands::math_style::SpecifiedValue::from_gecko_keyword(value),
5326         MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value),
5327         WhiteSpaceCollapse => get_from_computed::<longhands::white_space_collapse::SpecifiedValue>(value),
5328         TextWrapMode => get_from_computed::<longhands::text_wrap_mode::SpecifiedValue>(value),
5329         CaptionSide => get_from_computed::<CaptionSide>(value),
5330         BorderTopStyle => get_from_computed::<BorderStyle>(value),
5331         BorderRightStyle => get_from_computed::<BorderStyle>(value),
5332         BorderBottomStyle => get_from_computed::<BorderStyle>(value),
5333         BorderLeftStyle => get_from_computed::<BorderStyle>(value),
5334         TextTransform => {
5335             debug_assert_eq!(value, structs::StyleTextTransformCase_None as u32);
5336             TextTransform::none()
5337         },
5338     };
5339     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5340         decls.push(prop, Importance::Normal);
5341     })
5344 #[no_mangle]
5345 pub extern "C" fn Servo_DeclarationBlock_SetIntValue(
5346     declarations: &LockedDeclarationBlock,
5347     property: nsCSSPropertyID,
5348     value: i32,
5349 ) {
5350     use style::properties::PropertyDeclaration;
5351     use style::values::specified::Integer;
5353     let long = get_longhand_from_id!(property);
5354     let prop = match_wrap_declared! { long,
5355         XSpan => Integer::new(value),
5356     };
5357     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5358         decls.push(prop, Importance::Normal);
5359     })
5362 #[no_mangle]
5363 pub extern "C" fn Servo_DeclarationBlock_SetMathDepthValue(
5364     declarations: &LockedDeclarationBlock,
5365     value: i32,
5366     is_relative: bool,
5367 ) {
5368     use style::properties::longhands::math_depth::SpecifiedValue as MathDepth;
5369     use style::properties::PropertyDeclaration;
5371     let integer_value = style::values::specified::Integer::new(value);
5372     let prop = PropertyDeclaration::MathDepth(if is_relative {
5373         MathDepth::Add(integer_value)
5374     } else {
5375         MathDepth::Absolute(integer_value)
5376     });
5377     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5378         decls.push(prop, Importance::Normal);
5379     })
5382 #[no_mangle]
5383 pub extern "C" fn Servo_DeclarationBlock_SetCounterResetListItem(
5384     declarations: &LockedDeclarationBlock,
5385     counter_value: i32,
5386     is_reversed: bool,
5387 ) {
5388     use style::properties::PropertyDeclaration;
5389     use style::values::generics::counters::{CounterPair, CounterReset};
5391     let prop = PropertyDeclaration::CounterReset(CounterReset::new(vec![CounterPair {
5392         name: CustomIdent(atom!("list-item")),
5393         value: style::values::specified::Integer::new(counter_value),
5394         is_reversed,
5395     }]));
5396     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5397         decls.push(prop, Importance::Normal);
5398     })
5401 #[no_mangle]
5402 pub extern "C" fn Servo_DeclarationBlock_SetCounterSetListItem(
5403     declarations: &LockedDeclarationBlock,
5404     counter_value: i32,
5405 ) {
5406     use style::properties::PropertyDeclaration;
5407     use style::values::generics::counters::{CounterPair, CounterSet};
5409     let prop = PropertyDeclaration::CounterSet(CounterSet::new(vec![CounterPair {
5410         name: CustomIdent(atom!("list-item")),
5411         value: style::values::specified::Integer::new(counter_value),
5412         is_reversed: false,
5413     }]));
5414     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5415         decls.push(prop, Importance::Normal);
5416     })
5419 #[no_mangle]
5420 pub extern "C" fn Servo_DeclarationBlock_SetPixelValue(
5421     declarations: &LockedDeclarationBlock,
5422     property: nsCSSPropertyID,
5423     value: f32,
5424 ) {
5425     use style::properties::longhands::border_spacing::SpecifiedValue as BorderSpacing;
5426     use style::properties::PropertyDeclaration;
5427     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5428     use style::values::generics::NonNegative;
5429     use style::values::specified::length::{
5430         LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
5431     };
5432     use style::values::specified::{BorderCornerRadius, BorderSideWidth};
5434     let long = get_longhand_from_id!(property);
5435     let nocalc = NoCalcLength::from_px(value);
5436     let lp = LengthPercentage::Length(nocalc);
5437     let lp_or_auto = LengthPercentageOrAuto::LengthPercentage(lp.clone());
5438     let prop = match_wrap_declared! { long,
5439         Height => Size::LengthPercentage(NonNegative(lp)),
5440         Width => Size::LengthPercentage(NonNegative(lp)),
5441         BorderTopWidth => BorderSideWidth::from_px(value),
5442         BorderRightWidth => BorderSideWidth::from_px(value),
5443         BorderBottomWidth => BorderSideWidth::from_px(value),
5444         BorderLeftWidth => BorderSideWidth::from_px(value),
5445         MarginTop => lp_or_auto,
5446         MarginRight => lp_or_auto,
5447         MarginBottom => lp_or_auto,
5448         MarginLeft => lp_or_auto,
5449         PaddingTop => NonNegative(lp),
5450         PaddingRight => NonNegative(lp),
5451         PaddingBottom => NonNegative(lp),
5452         PaddingLeft => NonNegative(lp),
5453         BorderSpacing => {
5454             let v = NonNegativeLength::from(nocalc);
5455             Box::new(BorderSpacing::new(v.clone(), v))
5456         },
5457         BorderTopLeftRadius => {
5458             let length = NonNegativeLengthPercentage::from(nocalc);
5459             Box::new(BorderCornerRadius::new(length.clone(), length))
5460         },
5461         BorderTopRightRadius => {
5462             let length = NonNegativeLengthPercentage::from(nocalc);
5463             Box::new(BorderCornerRadius::new(length.clone(), length))
5464         },
5465         BorderBottomLeftRadius => {
5466             let length = NonNegativeLengthPercentage::from(nocalc);
5467             Box::new(BorderCornerRadius::new(length.clone(), length))
5468         },
5469         BorderBottomRightRadius => {
5470             let length = NonNegativeLengthPercentage::from(nocalc);
5471             Box::new(BorderCornerRadius::new(length.clone(), length))
5472         },
5473     };
5474     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5475         decls.push(prop, Importance::Normal);
5476     })
5479 #[no_mangle]
5480 pub extern "C" fn Servo_DeclarationBlock_SetLengthValue(
5481     declarations: &LockedDeclarationBlock,
5482     property: nsCSSPropertyID,
5483     value: f32,
5484     unit: structs::nsCSSUnit,
5485 ) {
5486     use style::properties::PropertyDeclaration;
5487     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5488     use style::values::generics::NonNegative;
5489     use style::values::specified::length::{
5490         FontRelativeLength, LengthPercentage, ViewportPercentageLength,
5491     };
5492     use style::values::specified::FontSize;
5494     let long = get_longhand_from_id!(property);
5495     let nocalc = match unit {
5496         structs::nsCSSUnit::eCSSUnit_EM => {
5497             NoCalcLength::FontRelative(FontRelativeLength::Em(value))
5498         },
5499         structs::nsCSSUnit::eCSSUnit_XHeight => {
5500             NoCalcLength::FontRelative(FontRelativeLength::Ex(value))
5501         },
5502         structs::nsCSSUnit::eCSSUnit_RootEM => {
5503             NoCalcLength::FontRelative(FontRelativeLength::Rem(value))
5504         },
5505         structs::nsCSSUnit::eCSSUnit_Char => {
5506             NoCalcLength::FontRelative(FontRelativeLength::Ch(value))
5507         },
5508         structs::nsCSSUnit::eCSSUnit_Ideographic => {
5509             NoCalcLength::FontRelative(FontRelativeLength::Ic(value))
5510         },
5511         structs::nsCSSUnit::eCSSUnit_CapHeight => {
5512             NoCalcLength::FontRelative(FontRelativeLength::Cap(value))
5513         },
5514         structs::nsCSSUnit::eCSSUnit_Pixel => NoCalcLength::Absolute(AbsoluteLength::Px(value)),
5515         structs::nsCSSUnit::eCSSUnit_Inch => NoCalcLength::Absolute(AbsoluteLength::In(value)),
5516         structs::nsCSSUnit::eCSSUnit_Centimeter => {
5517             NoCalcLength::Absolute(AbsoluteLength::Cm(value))
5518         },
5519         structs::nsCSSUnit::eCSSUnit_Millimeter => {
5520             NoCalcLength::Absolute(AbsoluteLength::Mm(value))
5521         },
5522         structs::nsCSSUnit::eCSSUnit_Point => NoCalcLength::Absolute(AbsoluteLength::Pt(value)),
5523         structs::nsCSSUnit::eCSSUnit_Pica => NoCalcLength::Absolute(AbsoluteLength::Pc(value)),
5524         structs::nsCSSUnit::eCSSUnit_Quarter => NoCalcLength::Absolute(AbsoluteLength::Q(value)),
5525         structs::nsCSSUnit::eCSSUnit_VW => {
5526             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value))
5527         },
5528         structs::nsCSSUnit::eCSSUnit_VH => {
5529             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value))
5530         },
5531         structs::nsCSSUnit::eCSSUnit_VMin => {
5532             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value))
5533         },
5534         structs::nsCSSUnit::eCSSUnit_VMax => {
5535             NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value))
5536         },
5537         _ => unreachable!("Unknown unit passed to SetLengthValue"),
5538     };
5540     let prop = match_wrap_declared! { long,
5541         Width => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5542         Height => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5543         X =>  LengthPercentage::Length(nocalc),
5544         Y =>  LengthPercentage::Length(nocalc),
5545         Cx => LengthPercentage::Length(nocalc),
5546         Cy => LengthPercentage::Length(nocalc),
5547         R =>  NonNegative(LengthPercentage::Length(nocalc)),
5548         Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5549         Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
5550         FontSize => FontSize::Length(LengthPercentage::Length(nocalc)),
5551     };
5552     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5553         decls.push(prop, Importance::Normal);
5554     })
5557 #[no_mangle]
5558 pub extern "C" fn Servo_DeclarationBlock_SetPathValue(
5559     declarations: &LockedDeclarationBlock,
5560     property: nsCSSPropertyID,
5561     path: &nsTArray<f32>,
5562 ) {
5563     use style::properties::PropertyDeclaration;
5564     use style::values::specified::DProperty;
5566     // 1. Decode the path data from SVG.
5567     let path = match specified::SVGPathData::decode_from_f32_array(path) {
5568         Ok(p) => p,
5569         Err(()) => return,
5570     };
5572     // 2. Set decoded path into style.
5573     let long = get_longhand_from_id!(property);
5574     let prop = match_wrap_declared! { long,
5575         D => if path.0.is_empty() { DProperty::None } else { DProperty::Path(path) },
5576     };
5577     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5578         decls.push(prop, Importance::Normal);
5579     })
5582 #[no_mangle]
5583 pub extern "C" fn Servo_DeclarationBlock_SetPercentValue(
5584     declarations: &LockedDeclarationBlock,
5585     property: nsCSSPropertyID,
5586     value: f32,
5587 ) {
5588     use style::properties::PropertyDeclaration;
5589     use style::values::computed::Percentage;
5590     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5591     use style::values::generics::NonNegative;
5592     use style::values::specified::length::LengthPercentage;
5593     use style::values::specified::FontSize;
5595     let long = get_longhand_from_id!(property);
5596     let pc = Percentage(value);
5597     let lp = LengthPercentage::Percentage(pc);
5598     let lp_or_auto = LengthPercentageOrAuto::LengthPercentage(lp.clone());
5600     let prop = match_wrap_declared! { long,
5601         Height => Size::LengthPercentage(NonNegative(lp)),
5602         Width => Size::LengthPercentage(NonNegative(lp)),
5603         X =>  lp,
5604         Y =>  lp,
5605         Cx => lp,
5606         Cy => lp,
5607         R =>  NonNegative(lp),
5608         Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
5609         Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
5610         MarginTop => lp_or_auto,
5611         MarginRight => lp_or_auto,
5612         MarginBottom => lp_or_auto,
5613         MarginLeft => lp_or_auto,
5614         FontSize => FontSize::Length(LengthPercentage::Percentage(pc)),
5615     };
5616     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5617         decls.push(prop, Importance::Normal);
5618     })
5621 #[no_mangle]
5622 pub extern "C" fn Servo_DeclarationBlock_SetAutoValue(
5623     declarations: &LockedDeclarationBlock,
5624     property: nsCSSPropertyID,
5625 ) {
5626     use style::properties::PropertyDeclaration;
5627     use style::values::generics::length::{LengthPercentageOrAuto, Size};
5629     let long = get_longhand_from_id!(property);
5630     let auto = LengthPercentageOrAuto::Auto;
5632     let prop = match_wrap_declared! { long,
5633         Height => Size::auto(),
5634         Width => Size::auto(),
5635         MarginTop => auto,
5636         MarginRight => auto,
5637         MarginBottom => auto,
5638         MarginLeft => auto,
5639         AspectRatio => specified::AspectRatio::auto(),
5640     };
5641     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5642         decls.push(prop, Importance::Normal);
5643     })
5646 #[no_mangle]
5647 pub extern "C" fn Servo_DeclarationBlock_SetCurrentColor(
5648     declarations: &LockedDeclarationBlock,
5649     property: nsCSSPropertyID,
5650 ) {
5651     use style::properties::PropertyDeclaration;
5652     use style::values::specified::Color;
5654     let long = get_longhand_from_id!(property);
5655     let cc = Color::currentcolor();
5657     let prop = match_wrap_declared! { long,
5658         BorderTopColor => cc,
5659         BorderRightColor => cc,
5660         BorderBottomColor => cc,
5661         BorderLeftColor => cc,
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_SetColorValue(
5670     declarations: &LockedDeclarationBlock,
5671     property: nsCSSPropertyID,
5672     value: structs::nscolor,
5673 ) {
5674     use style::gecko::values::convert_nscolor_to_absolute_color;
5675     use style::properties::longhands;
5676     use style::properties::PropertyDeclaration;
5677     use style::values::specified::Color;
5679     let long = get_longhand_from_id!(property);
5680     let rgba = convert_nscolor_to_absolute_color(value);
5681     let color = Color::from_absolute_color(rgba);
5683     let prop = match_wrap_declared! { long,
5684         BorderTopColor => color,
5685         BorderRightColor => color,
5686         BorderBottomColor => color,
5687         BorderLeftColor => color,
5688         Color => longhands::color::SpecifiedValue(color),
5689         BackgroundColor => color,
5690     };
5691     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5692         decls.push(prop, Importance::Normal);
5693     })
5696 #[no_mangle]
5697 pub unsafe extern "C" fn Servo_DeclarationBlock_SetFontFamily(
5698     declarations: &LockedDeclarationBlock,
5699     value: &nsACString,
5700 ) {
5701     use style::properties::longhands::font_family::SpecifiedValue as FontFamily;
5702     use style::properties::PropertyDeclaration;
5704     let string = value.as_str_unchecked();
5705     let mut input = ParserInput::new(&string);
5706     let mut parser = Parser::new(&mut input);
5707     let context = ParserContext::new(
5708         Origin::Author,
5709         dummy_url_data(),
5710         Some(CssRuleType::Style),
5711         ParsingMode::DEFAULT,
5712         QuirksMode::NoQuirks,
5713         /* namespaces = */ Default::default(),
5714         None,
5715         None,
5716     );
5717     let result = FontFamily::parse(&context, &mut parser);
5718     if let Ok(family) = result {
5719         if parser.is_exhausted() {
5720             let decl = PropertyDeclaration::FontFamily(family);
5721             write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5722                 decls.push(decl, Importance::Normal);
5723             })
5724         }
5725     }
5728 #[no_mangle]
5729 pub unsafe extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(
5730     declarations: &LockedDeclarationBlock,
5731     value: &nsACString,
5732     raw_extra_data: *mut URLExtraData,
5733 ) {
5734     use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage;
5735     use style::properties::PropertyDeclaration;
5736     use style::stylesheets::CorsMode;
5737     use style::values::generics::image::Image;
5738     use style::values::specified::url::SpecifiedImageUrl;
5740     let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
5741     let string = value.as_str_unchecked();
5742     let context = ParserContext::new(
5743         Origin::Author,
5744         url_data,
5745         Some(CssRuleType::Style),
5746         ParsingMode::DEFAULT,
5747         QuirksMode::NoQuirks,
5748         /* namespaces = */ Default::default(),
5749         None,
5750         None,
5751     );
5752     let url = SpecifiedImageUrl::parse_from_string(string.into(), &context, CorsMode::None);
5753     let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(vec![Image::Url(url)].into()));
5754     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5755         decls.push(decl, Importance::Normal);
5756     });
5759 #[no_mangle]
5760 pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(
5761     declarations: &LockedDeclarationBlock,
5762 ) {
5763     use style::properties::PropertyDeclaration;
5764     use style::values::specified::text::TextDecorationLine;
5766     let decoration = TextDecorationLine::COLOR_OVERRIDE;
5767     let decl = PropertyDeclaration::TextDecorationLine(decoration);
5768     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5769         decls.push(decl, Importance::Normal);
5770     })
5773 #[no_mangle]
5774 pub extern "C" fn Servo_DeclarationBlock_SetAspectRatio(
5775     declarations: &LockedDeclarationBlock,
5776     width: f32,
5777     height: f32,
5778 ) {
5779     use style::properties::PropertyDeclaration;
5780     use style::values::generics::position::AspectRatio;
5782     let decl = PropertyDeclaration::AspectRatio(AspectRatio::from_mapped_ratio(width, height));
5783     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
5784         decls.push(decl, Importance::Normal);
5785     })
5788 #[no_mangle]
5789 pub extern "C" fn Servo_CSSSupports2(property: &nsACString, value: &nsACString) -> bool {
5790     let id = unsafe { get_property_id_from_property!(property, false) };
5792     let mut declarations = SourcePropertyDeclaration::default();
5793     parse_property_into(
5794         &mut declarations,
5795         id,
5796         value,
5797         Origin::Author,
5798         unsafe { dummy_url_data() },
5799         ParsingMode::DEFAULT,
5800         QuirksMode::NoQuirks,
5801         CssRuleType::Style,
5802         None,
5803     )
5804     .is_ok()
5807 #[no_mangle]
5808 pub extern "C" fn Servo_CSSSupports(
5809     cond: &nsACString,
5810     ua_origin: bool,
5811     chrome_sheet: bool,
5812     quirks: bool,
5813 ) -> bool {
5814     let condition = unsafe { cond.as_str_unchecked() };
5815     let mut input = ParserInput::new(&condition);
5816     let mut input = Parser::new(&mut input);
5817     let cond = match input.parse_entirely(parse_condition_or_declaration) {
5818         Ok(c) => c,
5819         Err(..) => return false,
5820     };
5822     let origin = if ua_origin {
5823         Origin::UserAgent
5824     } else {
5825         Origin::Author
5826     };
5827     let url_data = unsafe {
5828         UrlExtraData::from_ptr_ref(if chrome_sheet {
5829             &DUMMY_CHROME_URL_DATA
5830         } else {
5831             &DUMMY_URL_DATA
5832         })
5833     };
5834     let quirks_mode = if quirks {
5835         QuirksMode::Quirks
5836     } else {
5837         QuirksMode::NoQuirks
5838     };
5840     // NOTE(emilio): The supports API is not associated to any stylesheet,
5841     // so the fact that there is no namespace map here is fine.
5842     let context = ParserContext::new(
5843         origin,
5844         url_data,
5845         Some(CssRuleType::Style),
5846         ParsingMode::DEFAULT,
5847         quirks_mode,
5848         /* namespaces = */ Default::default(),
5849         None,
5850         None,
5851     );
5853     cond.eval(&context)
5856 #[no_mangle]
5857 pub extern "C" fn Servo_CSSSupportsForImport(after_rule: &nsACString) -> bool {
5858     let condition = unsafe { after_rule.as_str_unchecked() };
5859     let mut input = ParserInput::new(&condition);
5860     let mut input = Parser::new(&mut input);
5862     // NOTE(emilio): The supports API is not associated to any stylesheet,
5863     // so the fact that there is no namespace map here is fine.
5864     let mut context = ParserContext::new(
5865         Origin::Author,
5866         unsafe { dummy_url_data() },
5867         Some(CssRuleType::Style),
5868         ParsingMode::DEFAULT,
5869         QuirksMode::NoQuirks,
5870         /* namespaces = */ Default::default(),
5871         None,
5872         None,
5873     );
5875     let (_layer, supports) = ImportRule::parse_layer_and_supports(&mut input, &mut context);
5877     supports.map_or(true, |s| s.enabled)
5880 #[no_mangle]
5881 pub unsafe extern "C" fn Servo_NoteExplicitHints(
5882     element: &RawGeckoElement,
5883     restyle_hint: RestyleHint,
5884     change_hint: nsChangeHint,
5885 ) {
5886     GeckoElement(element).note_explicit_hints(restyle_hint, change_hint);
5889 #[no_mangle]
5890 pub extern "C" fn Servo_TakeChangeHint(element: &RawGeckoElement, was_restyled: &mut bool) -> u32 {
5891     let element = GeckoElement(element);
5893     let damage = match element.mutate_data() {
5894         Some(mut data) => {
5895             *was_restyled = data.is_restyle();
5897             let damage = data.damage;
5898             data.clear_restyle_state();
5899             damage
5900         },
5901         None => {
5902             warn!("Trying to get change hint from unstyled element");
5903             *was_restyled = false;
5904             GeckoRestyleDamage::empty()
5905         },
5906     };
5908     debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage);
5909     // We'd like to return `nsChangeHint` here, but bindgen bitfield enums don't
5910     // work as return values with the Linux 32-bit ABI at the moment because
5911     // they wrap the value in a struct, so for now just unwrap it.
5912     damage.as_change_hint().0
5915 #[no_mangle]
5916 pub extern "C" fn Servo_ResolveStyle(element: &RawGeckoElement) -> Strong<ComputedValues> {
5917     let element = GeckoElement(element);
5918     debug!("Servo_ResolveStyle: {:?}", element);
5919     let data = element
5920         .borrow_data()
5921         .expect("Resolving style on unstyled element");
5923     debug_assert!(
5924         element.has_current_styles(&*data),
5925         "Resolving style on {:?} without current styles: {:?}",
5926         element,
5927         data
5928     );
5929     data.styles.primary().clone().into()
5932 #[no_mangle]
5933 pub extern "C" fn Servo_ResolveStyleLazily(
5934     element: &RawGeckoElement,
5935     pseudo_type: PseudoStyleType,
5936     functional_pseudo_parameter: *mut nsAtom,
5937     rule_inclusion: StyleRuleInclusion,
5938     snapshots: *const ServoElementSnapshotTable,
5939     cache_generation: u64,
5940     can_use_cache: bool,
5941     raw_data: &PerDocumentStyleData,
5942 ) -> Strong<ComputedValues> {
5943     debug_assert!(!snapshots.is_null());
5944     let global_style_data = &*GLOBAL_STYLE_DATA;
5945     let guard = global_style_data.shared_lock.read();
5946     let element = GeckoElement(element);
5947     let mut data = raw_data.borrow_mut();
5948     let data = &mut *data;
5949     let rule_inclusion = RuleInclusion::from(rule_inclusion);
5950     let pseudo_element = PseudoElement::from_pseudo_type(
5951         pseudo_type,
5952         get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
5953     );
5955     let matching_fn = |pseudo: &PseudoElement| match pseudo_element {
5956         Some(ref p) => *pseudo == *p,
5957         _ => false,
5958     };
5960     if cache_generation != data.undisplayed_style_cache_generation {
5961         data.undisplayed_style_cache.clear();
5962         data.undisplayed_style_cache_generation = cache_generation;
5963     }
5965     let stylist = &data.stylist;
5966     let finish = |styles: &ElementStyles, is_probe: bool| -> Option<Arc<ComputedValues>> {
5967         match pseudo_element {
5968             Some(ref pseudo) => {
5969                 get_pseudo_style(
5970                     &guard,
5971                     element,
5972                     pseudo,
5973                     rule_inclusion,
5974                     styles,
5975                     /* inherited_styles = */ None,
5976                     &stylist,
5977                     is_probe,
5978                     if pseudo.is_highlight() {
5979                         Some(&matching_fn)
5980                     } else {
5981                         None
5982                     },
5983                 )
5984             },
5985             None => Some(styles.primary().clone()),
5986         }
5987     };
5989     let is_before_or_after = pseudo_element
5990         .as_ref()
5991         .map_or(false, |p| p.is_before_or_after());
5993     // In the common case we already have the style. Check that before setting
5994     // up all the computation machinery.
5995     //
5996     // Also, only probe in the ::before or ::after case, since their styles may
5997     // not be in the `ElementData`, given they may exist but not be applicable
5998     // to generate an actual pseudo-element (like, having a `content: none`).
5999     if rule_inclusion == RuleInclusion::All {
6000         let styles = element.borrow_data().and_then(|d| {
6001             if d.has_styles() {
6002                 finish(&d.styles, is_before_or_after)
6003             } else {
6004                 None
6005             }
6006         });
6007         if let Some(result) = styles {
6008             return result.into();
6009         }
6010         if pseudo_element.is_none() && can_use_cache {
6011             if let Some(style) = data.undisplayed_style_cache.get(&element.opaque()) {
6012                 return style.clone().into();
6013             }
6014         }
6015     }
6017     // We don't have the style ready. Go ahead and compute it as necessary.
6018     let shared = create_shared_context(
6019         &global_style_data,
6020         &guard,
6021         &stylist,
6022         TraversalFlags::empty(),
6023         unsafe { &*snapshots },
6024     );
6025     let mut tlc = ThreadLocalStyleContext::new();
6026     let mut context = StyleContext {
6027         shared: &shared,
6028         thread_local: &mut tlc,
6029     };
6031     let styles = resolve_style(
6032         &mut context,
6033         element,
6034         rule_inclusion,
6035         pseudo_element.as_ref(),
6036         if can_use_cache {
6037             Some(&mut data.undisplayed_style_cache)
6038         } else {
6039             None
6040         },
6041     );
6043     finish(&styles, /* is_probe = */ false)
6044         .expect("We're not probing, so we should always get a style back")
6045         .into()
6048 #[no_mangle]
6049 pub extern "C" fn Servo_ReparentStyle(
6050     style_to_reparent: &ComputedValues,
6051     parent_style: &ComputedValues,
6052     layout_parent_style: &ComputedValues,
6053     element: Option<&RawGeckoElement>,
6054     raw_data: &PerDocumentStyleData,
6055 ) -> Strong<ComputedValues> {
6056     use style::properties::FirstLineReparenting;
6058     let global_style_data = &*GLOBAL_STYLE_DATA;
6059     let guard = global_style_data.shared_lock.read();
6060     let doc_data = raw_data.borrow();
6061     let inputs = CascadeInputs::new_from_style(style_to_reparent);
6062     let pseudo = style_to_reparent.pseudo();
6063     let element = element.map(GeckoElement);
6065     doc_data
6066         .stylist
6067         .cascade_style_and_visited(
6068             element,
6069             pseudo.as_ref(),
6070             inputs,
6071             &StylesheetGuards::same(&guard),
6072             Some(parent_style),
6073             Some(layout_parent_style),
6074             FirstLineReparenting::Yes { style_to_reparent },
6075             /* rule_cache = */ None,
6076             &mut RuleCacheConditions::default(),
6077         )
6078         .into()
6081 #[cfg(feature = "gecko_debug")]
6082 fn simulate_compute_values_failure(property: &PropertyValuePair) -> bool {
6083     let p = &property.mProperty;
6084     let id = get_property_id_from_animatedpropertyid!(p, false);
6085     id.as_shorthand().is_ok() && property.mSimulateComputeValuesFailure
6088 #[cfg(not(feature = "gecko_debug"))]
6089 fn simulate_compute_values_failure(_: &PropertyValuePair) -> bool {
6090     false
6093 fn create_context_for_animation<'a>(
6094     per_doc_data: &'a PerDocumentStyleDataImpl,
6095     style: &'a ComputedValues,
6096     parent_style: Option<&'a ComputedValues>,
6097     for_smil_animation: bool,
6098     rule_cache_conditions: &'a mut RuleCacheConditions,
6099     container_size_query: ContainerSizeQuery<'a>,
6100 ) -> Context<'a> {
6101     Context::new_for_animation(
6102         StyleBuilder::for_animation(
6103             per_doc_data.stylist.device(),
6104             Some(&per_doc_data.stylist),
6105             style,
6106             parent_style,
6107         ),
6108         for_smil_animation,
6109         per_doc_data.stylist.quirks_mode(),
6110         rule_cache_conditions,
6111         container_size_query,
6112     )
6115 struct PropertyAndIndex {
6116     property: PropertyId,
6117     index: usize,
6120 struct PrioritizedPropertyIter<'a> {
6121     properties: &'a [PropertyValuePair],
6122     sorted_property_indices: Vec<PropertyAndIndex>,
6123     curr: usize,
6126 impl<'a> PrioritizedPropertyIter<'a> {
6127     fn new(properties: &'a [PropertyValuePair]) -> PrioritizedPropertyIter {
6128         use style::values::animated::compare_property_priority;
6130         // If we fail to convert a nsCSSPropertyID into a PropertyId we
6131         // shouldn't fail outright but instead by treating that property as the
6132         // 'all' property we make it sort last.
6133         let mut sorted_property_indices: Vec<PropertyAndIndex> = properties
6134             .iter()
6135             .enumerate()
6136             .map(|(index, pair)| {
6137                 let property = PropertyId::from_gecko_animated_property_id(&pair.mProperty)
6138                     .unwrap_or(PropertyId::NonCustom(ShorthandId::All.into()));
6139                 PropertyAndIndex { property, index }
6140             })
6141             .collect();
6142         sorted_property_indices.sort_by(|a, b| compare_property_priority(&a.property, &b.property));
6144         PrioritizedPropertyIter {
6145             properties,
6146             sorted_property_indices,
6147             curr: 0,
6148         }
6149     }
6151     fn reset(&mut self) {
6152         self.curr = 0;
6153     }
6156 impl<'a> Iterator for PrioritizedPropertyIter<'a> {
6157     type Item = &'a PropertyValuePair;
6159     fn next(&mut self) -> Option<&'a PropertyValuePair> {
6160         if self.curr >= self.sorted_property_indices.len() {
6161             return None;
6162         }
6163         self.curr += 1;
6164         Some(&self.properties[self.sorted_property_indices[self.curr - 1].index])
6165     }
6168 #[no_mangle]
6169 pub extern "C" fn Servo_GetComputedKeyframeValues(
6170     keyframes: &nsTArray<structs::Keyframe>,
6171     element: &RawGeckoElement,
6172     pseudo_type: PseudoStyleType,
6173     style: &ComputedValues,
6174     raw_data: &PerDocumentStyleData,
6175     computed_keyframes: &mut nsTArray<structs::ComputedKeyframeValues>,
6176 ) {
6177     use style::applicable_declarations::CascadePriority;
6178     use style::custom_properties::CustomPropertiesBuilder;
6179     use style::properties::PropertyDeclaration;
6180     let data = raw_data.borrow();
6181     let element = GeckoElement(element);
6182     let pseudo = PseudoElement::from_pseudo_type(pseudo_type, None);
6183     let parent_element = if pseudo.is_none() {
6184         element.inheritance_parent()
6185     } else {
6186         Some(element)
6187     };
6188     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
6189     let parent_style = parent_data
6190         .as_ref()
6191         .map(|d| d.styles.primary())
6192         .map(|x| &**x);
6194     let container_size_query =
6195         ContainerSizeQuery::for_element(element, parent_style, pseudo.is_some());
6196     let mut conditions = Default::default();
6197     let mut context = create_context_for_animation(
6198         &data,
6199         &style,
6200         parent_style,
6201         /* for_smil_animation = */ false,
6202         &mut conditions,
6203         container_size_query,
6204     );
6206     let restriction = pseudo.and_then(|p| p.property_restriction());
6208     let global_style_data = &*GLOBAL_STYLE_DATA;
6209     let guard = global_style_data.shared_lock.read();
6210     let default_values = data.default_computed_values();
6212     for (index, keyframe) in keyframes.iter().enumerate() {
6213         let ref mut animation_values = computed_keyframes[index];
6215         let mut seen = PropertyDeclarationIdSet::default();
6216         let mut iter = PrioritizedPropertyIter::new(&keyframe.mPropertyValues);
6218         // FIXME (bug 1883255): This is pretty much a hack. Instead, the AnimatedValue should be
6219         // better integrated in the cascade.
6220         {
6221             let mut builder = CustomPropertiesBuilder::new_with_properties(
6222                 &data.stylist,
6223                 style.custom_properties().clone(),
6224                 &mut context,
6225             );
6226             let priority = CascadePriority::same_tree_author_normal_at_root_layer();
6227             for property in &mut iter {
6228                 let is_custom =
6229                     match PropertyId::from_gecko_animated_property_id(&property.mProperty) {
6230                         Some(PropertyId::Custom(..)) => true,
6231                         _ => false,
6232                     };
6233                 if !is_custom {
6234                     break; // Custom props are guaranteed to sort earlier.
6235                 }
6236                 if property.mServoDeclarationBlock.mRawPtr.is_null() {
6237                     continue;
6238                 }
6239                 let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
6240                 let guard = declarations.read_with(&guard);
6241                 for decl in guard.normal_declaration_iter() {
6242                     if let PropertyDeclaration::Custom(ref declaration) = *decl {
6243                         builder.cascade(declaration, priority);
6244                     }
6245                 }
6246             }
6247             iter.reset();
6248             let _deferred = builder.build(DeferFontRelativeCustomPropertyResolution::No);
6249             debug_assert!(
6250                 _deferred.is_none(),
6251                 "Custom property processing deferred despite specifying otherwise?"
6252             );
6253         };
6255         let mut property_index = 0;
6256         for property in iter {
6257             if simulate_compute_values_failure(property) {
6258                 continue;
6259             }
6261             let mut maybe_append_animation_value =
6262                 |property: PropertyDeclarationId, value: Option<AnimationValue>| {
6263                     debug_assert!(!property.is_logical());
6264                     debug_assert!(property.is_animatable());
6266                     // 'display' is only animatable from SMIL
6267                     if property == PropertyDeclarationId::Longhand(LonghandId::Display) {
6268                         return;
6269                     }
6271                     // Skip restricted properties
6272                     if restriction.map_or(false, |r| !property.flags().contains(r)) {
6273                         return;
6274                     }
6276                     if seen.contains(property) {
6277                         return;
6278                     }
6279                     seen.insert(property);
6281                     // This is safe since we immediately write to the uninitialized values.
6282                     unsafe {
6283                         animation_values.set_len((property_index + 1) as u32);
6284                         ptr::write(
6285                             &mut animation_values[property_index],
6286                             structs::PropertyStyleAnimationValuePair {
6287                                 mProperty: property
6288                                     .to_gecko_animated_property_id(/* owned = */ true),
6289                                 mValue: structs::AnimationValue {
6290                                     mServo: value.map_or(structs::RefPtr::null(), |v| {
6291                                         structs::RefPtr::from_arc(Arc::new(v))
6292                                     }),
6293                                 },
6294                             },
6295                         );
6296                     }
6297                     property_index += 1;
6298                 };
6300             if property.mServoDeclarationBlock.mRawPtr.is_null() {
6301                 if let Some(prop) =
6302                     OwnedPropertyDeclarationId::from_gecko_animated_property_id(&property.mProperty)
6303                 {
6304                     maybe_append_animation_value(prop.as_borrowed(), None);
6305                 }
6306                 continue;
6307             }
6309             let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
6310             let guard = declarations.read_with(&guard);
6311             let iter = guard.to_animation_value_iter(&mut context, &default_values);
6313             for value in iter {
6314                 maybe_append_animation_value(value.id(), Some(value.clone()));
6315             }
6316         }
6317     }
6320 #[no_mangle]
6321 pub extern "C" fn Servo_GetAnimationValues(
6322     declarations: &LockedDeclarationBlock,
6323     element: &RawGeckoElement,
6324     style: &ComputedValues,
6325     raw_data: &PerDocumentStyleData,
6326     animation_values: &mut ThinVec<structs::RefPtr<AnimationValue>>,
6327 ) {
6328     let data = raw_data.borrow();
6329     let element = GeckoElement(element);
6330     let parent_element = element.inheritance_parent();
6331     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
6332     let parent_style = parent_data
6333         .as_ref()
6334         .map(|d| d.styles.primary())
6335         .map(|x| &**x);
6337     let container_size_query =
6338         ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
6339     let mut conditions = Default::default();
6340     let mut context = create_context_for_animation(
6341         &data,
6342         &style,
6343         parent_style,
6344         /* for_smil_animation = */ true,
6345         &mut conditions,
6346         container_size_query,
6347     );
6349     let default_values = data.default_computed_values();
6350     let global_style_data = &*GLOBAL_STYLE_DATA;
6351     let guard = global_style_data.shared_lock.read();
6353     let guard = declarations.read_with(&guard);
6354     let iter = guard.to_animation_value_iter(&mut context, &default_values);
6355     animation_values.extend(iter.map(|v| structs::RefPtr::from_arc(Arc::new(v))));
6358 #[no_mangle]
6359 pub extern "C" fn Servo_AnimationValue_GetPropertyId(
6360     value: &AnimationValue,
6361     property_id: &mut structs::AnimatedPropertyID,
6362 ) {
6363     *property_id = value.id().to_gecko_animated_property_id(/* owned = */ true);
6366 #[no_mangle]
6367 pub extern "C" fn Servo_AnimationValue_Compute(
6368     element: &RawGeckoElement,
6369     declarations: &LockedDeclarationBlock,
6370     style: &ComputedValues,
6371     raw_data: &PerDocumentStyleData,
6372 ) -> Strong<AnimationValue> {
6373     let data = raw_data.borrow();
6375     let element = GeckoElement(element);
6376     let parent_element = element.inheritance_parent();
6377     let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
6378     let parent_style = parent_data
6379         .as_ref()
6380         .map(|d| d.styles.primary())
6381         .map(|x| &**x);
6383     let container_size_query =
6384         ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
6385     let mut conditions = Default::default();
6386     let mut context = create_context_for_animation(
6387         &data,
6388         style,
6389         parent_style,
6390         /* for_smil_animation = */ false,
6391         &mut conditions,
6392         container_size_query,
6393     );
6395     let default_values = data.default_computed_values();
6396     let global_style_data = &*GLOBAL_STYLE_DATA;
6397     let guard = global_style_data.shared_lock.read();
6398     // We only compute the first element in declarations.
6399     match declarations
6400         .read_with(&guard)
6401         .declaration_importance_iter()
6402         .next()
6403     {
6404         Some((decl, imp)) if imp == Importance::Normal => {
6405             let animation = AnimationValue::from_declaration(decl, &mut context, default_values);
6406             animation.map_or(Strong::null(), |value| Arc::new(value).into())
6407         },
6408         _ => Strong::null(),
6409     }
6412 #[no_mangle]
6413 pub extern "C" fn Servo_AssertTreeIsClean(root: &RawGeckoElement) {
6414     if !cfg!(feature = "gecko_debug") {
6415         panic!("Calling Servo_AssertTreeIsClean in release build");
6416     }
6418     let root = GeckoElement(root);
6419     debug!("Servo_AssertTreeIsClean: ");
6420     debug!("{:?}", ShowSubtreeData(root.as_node()));
6422     fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) {
6423         debug_assert!(
6424             !el.has_dirty_descendants() && !el.has_animation_only_dirty_descendants(),
6425             "{:?} has still dirty bit {:?} or animation-only dirty bit {:?}",
6426             el,
6427             el.has_dirty_descendants(),
6428             el.has_animation_only_dirty_descendants()
6429         );
6430         for child in el.traversal_children() {
6431             if let Some(child) = child.as_element() {
6432                 assert_subtree_is_clean(child);
6433             }
6434         }
6435     }
6437     assert_subtree_is_clean(root);
6440 #[no_mangle]
6441 pub extern "C" fn Servo_IsWorkerThread() -> bool {
6442     thread_state::get().is_worker()
6445 enum Offset {
6446     Zero,
6447     One,
6450 fn fill_in_missing_keyframe_values(
6451     all_properties: &PropertyDeclarationIdSet,
6452     timing_function: &ComputedTimingFunction,
6453     properties_at_offset: &PropertyDeclarationIdSet,
6454     offset: Offset,
6455     keyframes: &mut nsTArray<structs::Keyframe>,
6456 ) {
6457     // Return early if all animated properties are already set.
6458     if properties_at_offset.contains_all(all_properties) {
6459         return;
6460     }
6462     // Use auto for missing keyframes.
6463     // FIXME: This may be a spec issue in css-animations-2 because the spec says the default
6464     // keyframe-specific composite is replace, but web-animations-1 uses auto. Use auto now so we
6465     // use the value of animation-composition of the element, for missing keyframes.
6466     // https://github.com/w3c/csswg-drafts/issues/7476
6467     let composition = structs::CompositeOperationOrAuto::Auto;
6468     let keyframe = match offset {
6469         Offset::Zero => unsafe {
6470             Gecko_GetOrCreateInitialKeyframe(keyframes, timing_function, composition)
6471         },
6472         Offset::One => unsafe {
6473             Gecko_GetOrCreateFinalKeyframe(keyframes, timing_function, composition)
6474         },
6475     };
6477     // Append properties that have not been set at this offset.
6478     for property in all_properties.iter() {
6479         if !properties_at_offset.contains(property) {
6480             unsafe {
6481                 Gecko_AppendPropertyValuePair(
6482                     &mut *(*keyframe).mPropertyValues,
6483                     &property.to_gecko_animated_property_id(/* owned = */ false),
6484                 );
6485             }
6486         }
6487     }
6490 #[no_mangle]
6491 pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName(
6492     raw_data: &PerDocumentStyleData,
6493     element: &RawGeckoElement,
6494     style: &ComputedValues,
6495     name: *mut nsAtom,
6496     inherited_timing_function: &ComputedTimingFunction,
6497     keyframes: &mut nsTArray<structs::Keyframe>,
6498 ) -> bool {
6499     use style::gecko_bindings::structs::CompositeOperationOrAuto;
6500     use style::values::computed::AnimationComposition;
6502     debug_assert!(keyframes.len() == 0, "keyframes should be initially empty");
6504     let element = GeckoElement(element);
6505     let data = raw_data.borrow();
6506     let name = Atom::from_raw(name);
6508     let animation = match data.stylist.get_animation(&name, element) {
6509         Some(animation) => animation,
6510         None => return false,
6511     };
6513     let global_style_data = &*GLOBAL_STYLE_DATA;
6514     let guard = global_style_data.shared_lock.read();
6516     let mut properties_set_at_current_offset = PropertyDeclarationIdSet::default();
6517     let mut properties_set_at_start = PropertyDeclarationIdSet::default();
6518     let mut properties_set_at_end = PropertyDeclarationIdSet::default();
6519     let mut has_complete_initial_keyframe = false;
6520     let mut has_complete_final_keyframe = false;
6521     let mut current_offset = -1.;
6523     let writing_mode = style.writing_mode;
6525     // Iterate over the keyframe rules backwards so we can drop overridden
6526     // properties (since declarations in later rules override those in earlier
6527     // ones).
6528     for step in animation.steps.iter().rev() {
6529         if step.start_percentage.0 != current_offset {
6530             properties_set_at_current_offset.clear();
6531             current_offset = step.start_percentage.0;
6532         }
6534         // Override timing_function if the keyframe has an animation-timing-function.
6535         let timing_function = match step.get_animation_timing_function(&guard) {
6536             Some(val) => val.to_computed_value_without_context(),
6537             None => (*inherited_timing_function).clone(),
6538         };
6540         // Override composite operation if the keyframe has an animation-composition.
6541         let composition =
6542             step.get_animation_composition(&guard)
6543                 .map_or(CompositeOperationOrAuto::Auto, |val| match val {
6544                     AnimationComposition::Replace => CompositeOperationOrAuto::Replace,
6545                     AnimationComposition::Add => CompositeOperationOrAuto::Add,
6546                     AnimationComposition::Accumulate => CompositeOperationOrAuto::Accumulate,
6547                 });
6549         // Look for an existing keyframe with the same offset, timing function, and compsition, or
6550         // else add a new keyframe at the beginning of the keyframe array.
6551         let keyframe = Gecko_GetOrCreateKeyframeAtStart(
6552             keyframes,
6553             step.start_percentage.0 as f32,
6554             &timing_function,
6555             composition,
6556         );
6558         match step.value {
6559             KeyframesStepValue::ComputedValues => {
6560                 // In KeyframesAnimation::from_keyframes if there is no 0% or
6561                 // 100% keyframe at all, we will create a 'ComputedValues' step
6562                 // to represent that all properties animated by the keyframes
6563                 // animation should be set to the underlying computed value for
6564                 // that keyframe.
6565                 let mut seen = PropertyDeclarationIdSet::default();
6566                 for property in animation.properties_changed.iter() {
6567                     let property = property.to_physical(writing_mode);
6568                     if seen.contains(property) {
6569                         continue;
6570                     }
6571                     seen.insert(property);
6573                     Gecko_AppendPropertyValuePair(
6574                         &mut *(*keyframe).mPropertyValues,
6575                         &property.to_gecko_animated_property_id(/* owned = */ false),
6576                     );
6577                 }
6578                 if current_offset == 0.0 {
6579                     has_complete_initial_keyframe = true;
6580                 } else if current_offset == 1.0 {
6581                     has_complete_final_keyframe = true;
6582                 }
6583             },
6584             KeyframesStepValue::Declarations { ref block } => {
6585                 let guard = block.read_with(&guard);
6587                 // Filter out non-animatable properties and properties with
6588                 // !important.
6589                 //
6590                 // Also, iterate in reverse to respect the source order in case
6591                 // there are logical and physical longhands in the same block.
6592                 for declaration in guard.normal_declaration_iter().rev() {
6593                     let id = declaration.id().to_physical(writing_mode);
6595                     // Skip non-animatable properties, including the 'display' property because
6596                     // although it is animatable from SMIL, it should not be animatable from CSS
6597                     // Animations.
6598                     if !id.is_animatable() ||
6599                         id == PropertyDeclarationId::Longhand(LonghandId::Display)
6600                     {
6601                         continue;
6602                     }
6604                     if properties_set_at_current_offset.contains(id) {
6605                         continue;
6606                     }
6608                     let pair = Gecko_AppendPropertyValuePair(
6609                         &mut *(*keyframe).mPropertyValues,
6610                         &id.to_gecko_animated_property_id(/* owned = */ false),
6611                     );
6613                     (*pair).mServoDeclarationBlock.set_arc(Arc::new(
6614                         global_style_data
6615                             .shared_lock
6616                             .wrap(PropertyDeclarationBlock::with_one(
6617                                 declaration.to_physical(writing_mode),
6618                                 Importance::Normal,
6619                             )),
6620                     ));
6622                     if current_offset == 0.0 {
6623                         properties_set_at_start.insert(id);
6624                     } else if current_offset == 1.0 {
6625                         properties_set_at_end.insert(id);
6626                     }
6627                     properties_set_at_current_offset.insert(id);
6628                 }
6629             },
6630         }
6631     }
6633     let mut properties_changed = PropertyDeclarationIdSet::default();
6634     for property in animation.properties_changed.iter() {
6635         properties_changed.insert(property.to_physical(writing_mode));
6636     }
6638     // Append property values that are missing in the initial or the final keyframes.
6639     if !has_complete_initial_keyframe {
6640         fill_in_missing_keyframe_values(
6641             &properties_changed,
6642             inherited_timing_function,
6643             &properties_set_at_start,
6644             Offset::Zero,
6645             keyframes,
6646         );
6647     }
6648     if !has_complete_final_keyframe {
6649         fill_in_missing_keyframe_values(
6650             &properties_changed,
6651             inherited_timing_function,
6652             &properties_set_at_end,
6653             Offset::One,
6654             keyframes,
6655         );
6656     }
6657     true
6660 #[no_mangle]
6661 pub extern "C" fn Servo_StyleSet_GetFontFaceRules(
6662     raw_data: &PerDocumentStyleData,
6663     rules: &mut ThinVec<structs::nsFontFaceRuleContainer>,
6664 ) {
6665     let data = raw_data.borrow();
6666     debug_assert_eq!(rules.len(), 0);
6668     // Reversed iterator because Gecko expects rules to appear sorted
6669     // UserAgent first, Author last.
6670     let font_face_iter = data
6671         .stylist
6672         .iter_extra_data_origins_rev()
6673         .flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
6675     rules.extend(font_face_iter.map(|(&(ref rule, _layer_id), origin)| {
6676         structs::nsFontFaceRuleContainer {
6677             mRule: structs::RefPtr::from_arc(rule.clone()),
6678             mOrigin: origin,
6679         }
6680     }))
6683 // XXX Ideally this should return a Option<&LockedCounterStyleRule>,
6684 // but we cannot, because the value from AtomicRefCell::borrow() can only
6685 // live in this function, and thus anything derived from it cannot get the
6686 // same lifetime as raw_data in parameter. See bug 1451543.
6687 #[no_mangle]
6688 pub unsafe extern "C" fn Servo_StyleSet_GetCounterStyleRule(
6689     raw_data: &PerDocumentStyleData,
6690     name: *mut nsAtom,
6691 ) -> *const LockedCounterStyleRule {
6692     let data = raw_data.borrow();
6693     Atom::with(name, |name| {
6694         data.stylist
6695             .iter_extra_data_origins()
6696             .find_map(|(d, _)| d.counter_styles.get(name))
6697             .map_or(ptr::null(), |rule| &**rule as *const _)
6698     })
6701 #[no_mangle]
6702 pub extern "C" fn Servo_StyleSet_BuildFontFeatureValueSet(
6703     raw_data: &PerDocumentStyleData,
6704 ) -> *mut gfxFontFeatureValueSet {
6705     let data = raw_data.borrow();
6707     let has_rule = data
6708         .stylist
6709         .iter_extra_data_origins()
6710         .any(|(d, _)| !d.font_feature_values.is_empty());
6712     if !has_rule {
6713         return ptr::null_mut();
6714     }
6716     let font_feature_values_iter = data
6717         .stylist
6718         .iter_extra_data_origins_rev()
6719         .flat_map(|(d, _)| d.font_feature_values.iter());
6721     let set = unsafe { Gecko_ConstructFontFeatureValueSet() };
6722     for &(ref rule, _) in font_feature_values_iter {
6723         rule.set_at_rules(set);
6724     }
6725     set
6728 #[no_mangle]
6729 pub extern "C" fn Servo_StyleSet_BuildFontPaletteValueSet(
6730     raw_data: &PerDocumentStyleData,
6731 ) -> *mut FontPaletteValueSet {
6732     let data = raw_data.borrow();
6734     let has_rule = data
6735         .stylist
6736         .iter_extra_data_origins()
6737         .any(|(d, _)| !d.font_palette_values.is_empty());
6739     if !has_rule {
6740         return ptr::null_mut();
6741     }
6743     let font_palette_values_iter = data
6744         .stylist
6745         .iter_extra_data_origins_rev()
6746         .flat_map(|(d, _)| d.font_palette_values.iter());
6748     let set = unsafe { Gecko_ConstructFontPaletteValueSet() };
6749     for &(ref rule, _) in font_palette_values_iter {
6750         rule.to_gecko_palette_value_set(set);
6751     }
6752     set
6755 #[no_mangle]
6756 pub extern "C" fn Servo_StyleSet_ResolveForDeclarations(
6757     raw_data: &PerDocumentStyleData,
6758     parent_style_context: Option<&ComputedValues>,
6759     declarations: &LockedDeclarationBlock,
6760 ) -> Strong<ComputedValues> {
6761     let doc_data = raw_data.borrow();
6762     let global_style_data = &*GLOBAL_STYLE_DATA;
6763     let guard = global_style_data.shared_lock.read();
6764     let guards = StylesheetGuards::same(&guard);
6766     let parent_style = match parent_style_context {
6767         Some(parent) => &*parent,
6768         None => doc_data.default_computed_values(),
6769     };
6771     doc_data
6772         .stylist
6773         .compute_for_declarations::<GeckoElement>(&guards, parent_style, unsafe {
6774             Arc::from_raw_addrefed(declarations)
6775         })
6776         .into()
6779 #[no_mangle]
6780 pub extern "C" fn Servo_StyleSet_AddSizeOfExcludingThis(
6781     malloc_size_of: GeckoMallocSizeOf,
6782     malloc_enclosing_size_of: GeckoMallocSizeOf,
6783     sizes: *mut ServoStyleSetSizes,
6784     raw_data: &PerDocumentStyleData,
6785 ) {
6786     let data = raw_data.borrow_mut();
6787     let mut ops = MallocSizeOfOps::new(
6788         malloc_size_of.unwrap(),
6789         Some(malloc_enclosing_size_of.unwrap()),
6790         None,
6791     );
6792     let sizes = unsafe { sizes.as_mut() }.unwrap();
6793     data.add_size_of(&mut ops, sizes);
6796 #[no_mangle]
6797 pub extern "C" fn Servo_UACache_AddSizeOf(
6798     malloc_size_of: GeckoMallocSizeOf,
6799     malloc_enclosing_size_of: GeckoMallocSizeOf,
6800     sizes: *mut ServoStyleSetSizes,
6801 ) {
6802     let mut ops = MallocSizeOfOps::new(
6803         malloc_size_of.unwrap(),
6804         Some(malloc_enclosing_size_of.unwrap()),
6805         None,
6806     );
6807     let sizes = unsafe { sizes.as_mut() }.unwrap();
6808     add_size_of_ua_cache(&mut ops, sizes);
6811 #[no_mangle]
6812 pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(
6813     raw_data: &PerDocumentStyleData,
6814     element: &RawGeckoElement,
6815     local_name: *mut nsAtom,
6816 ) -> bool {
6817     let data = raw_data.borrow();
6818     let element = GeckoElement(element);
6820     unsafe {
6821         AtomIdent::with(local_name, |atom| {
6822             data.stylist.any_applicable_rule_data(element, |data| {
6823                 data.might_have_attribute_dependency(atom)
6824             })
6825         })
6826     }
6829 #[no_mangle]
6830 pub extern "C" fn Servo_StyleSet_MightHaveNthOfIDDependency(
6831     raw_data: &PerDocumentStyleData,
6832     element: &RawGeckoElement,
6833     old_id: *mut nsAtom,
6834     new_id: *mut nsAtom,
6835 ) -> bool {
6836     let data = raw_data.borrow();
6837     let element = GeckoElement(element);
6839     data.stylist.any_applicable_rule_data(element, |data| {
6840         [old_id, new_id]
6841             .iter()
6842             .filter(|id| !id.is_null())
6843             .any(|id| unsafe {
6844                 AtomIdent::with(*id, |atom| data.might_have_nth_of_id_dependency(atom))
6845             }) ||
6846             data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("id")))
6847     })
6850 #[no_mangle]
6851 pub extern "C" fn Servo_StyleSet_MightHaveNthOfClassDependency(
6852     raw_data: &PerDocumentStyleData,
6853     element: &RawGeckoElement,
6854     snapshots: &ServoElementSnapshotTable,
6855 ) -> bool {
6856     let data = raw_data.borrow();
6857     let element = GeckoElement(element);
6859     data.stylist.any_applicable_rule_data(element, |data| {
6860         classes_changed(&element, snapshots)
6861             .iter()
6862             .any(|atom| data.might_have_nth_of_class_dependency(atom)) ||
6863             data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("class")))
6864     })
6867 #[no_mangle]
6868 pub extern "C" fn Servo_StyleSet_MightHaveNthOfAttributeDependency(
6869     raw_data: &PerDocumentStyleData,
6870     element: &RawGeckoElement,
6871     local_name: *mut nsAtom,
6872 ) -> bool {
6873     let data = raw_data.borrow();
6874     let element = GeckoElement(element);
6876     unsafe {
6877         AtomIdent::with(local_name, |atom| {
6878             data.stylist.any_applicable_rule_data(element, |data| {
6879                 data.might_have_nth_of_attribute_dependency(atom)
6880             })
6881         })
6882     }
6885 fn on_siblings_invalidated(element: GeckoElement) {
6886     let parent = element
6887         .traversal_parent()
6888         .expect("How could we invalidate siblings without a common parent?");
6889     unsafe {
6890         parent.set_dirty_descendants();
6891         bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
6892     }
6895 fn restyle_for_nth_of(element: GeckoElement, flags: ElementSelectorFlags) {
6896     debug_assert!(
6897         !flags.is_empty(),
6898         "Calling restyle for nth but no relevant flag is set."
6899     );
6900     fn invalidate_siblings_of(
6901         element: GeckoElement,
6902         get_sibling: fn(GeckoElement) -> Option<GeckoElement>,
6903     ) {
6904         let mut sibling = get_sibling(element);
6905         while let Some(sib) = sibling {
6906             if let Some(mut data) = sib.mutate_data() {
6907                 data.hint.insert(RestyleHint::restyle_subtree());
6908             }
6909             sibling = get_sibling(sib);
6910         }
6911     }
6913     if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
6914         invalidate_siblings_of(element, |e| e.prev_sibling_element());
6915     }
6916     if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
6917         invalidate_siblings_of(element, |e| e.next_sibling_element());
6918     }
6919     on_siblings_invalidated(element);
6922 fn relative_selector_invalidated_at(element: GeckoElement, result: &InvalidationResult) {
6923     if result.has_invalidated_siblings() {
6924         on_siblings_invalidated(element);
6925     } else if result.has_invalidated_descendants() {
6926         unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
6927     } else if result.has_invalidated_self() {
6928         unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
6929         let flags = element
6930             .parent_element()
6931             .map_or(ElementSelectorFlags::empty(), |e| e.slow_selector_flags());
6932         // We invalidated up to the anchor, and it has a flag for nth-of invalidation.
6933         if !flags.is_empty() {
6934             restyle_for_nth_of(element, flags);
6935         }
6936     }
6939 fn add_relative_selector_attribute_dependency<'a>(
6940     element: &GeckoElement<'a>,
6941     scope: &Option<OpaqueElement>,
6942     invalidation_map: &'a RelativeSelectorInvalidationMap,
6943     attribute: &AtomIdent,
6944     collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
6945 ) {
6946     match invalidation_map
6947         .map
6948         .other_attribute_affecting_selectors
6949         .get(attribute)
6950     {
6951         Some(v) => {
6952             for dependency in v {
6953                 collector.add_dependency(dependency, *element, *scope);
6954             }
6955         },
6956         None => (),
6957     };
6960 fn inherit_relative_selector_search_direction(
6961     parent: Option<GeckoElement>,
6962     prev_sibling: Option<GeckoElement>,
6963 ) -> ElementSelectorFlags {
6964     let mut inherited = ElementSelectorFlags::empty();
6965     if let Some(parent) = parent {
6966         if let Some(direction) = parent.relative_selector_search_direction() {
6967             inherited |= direction
6968                 .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR);
6969         }
6970     }
6971     if let Some(sibling) = prev_sibling {
6972         if let Some(direction) = sibling.relative_selector_search_direction() {
6973             // Inherit both, for e.g. a sibling with `:has(~.sibling .descendant)`
6974             inherited |= direction.intersection(
6975                 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING,
6976             );
6977         }
6978     }
6979     inherited
6982 #[no_mangle]
6983 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorIDDependency(
6984     raw_data: &PerDocumentStyleData,
6985     element: &RawGeckoElement,
6986     old_id: *mut nsAtom,
6987     new_id: *mut nsAtom,
6988     snapshots: &ServoElementSnapshotTable,
6989 ) {
6990     let data = raw_data.borrow();
6991     let element = GeckoElement(element);
6993     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
6994     let invalidator = RelativeSelectorInvalidator {
6995         element,
6996         quirks_mode,
6997         snapshot_table: Some(snapshots),
6998         invalidated: relative_selector_invalidated_at,
6999         sibling_traversal_map: SiblingTraversalMap::default(),
7000         _marker: std::marker::PhantomData,
7001     };
7003     invalidator.invalidate_relative_selectors_for_this(
7004         &data.stylist,
7005         |element, scope, data, quirks_mode, collector| {
7006             let invalidation_map = data.relative_selector_invalidation_map();
7007             relative_selector_dependencies_for_id(
7008                 old_id,
7009                 new_id,
7010                 element,
7011                 scope,
7012                 quirks_mode,
7013                 &invalidation_map,
7014                 collector,
7015             );
7016             add_relative_selector_attribute_dependency(
7017                 element,
7018                 &scope,
7019                 invalidation_map,
7020                 &AtomIdent(atom!("id")),
7021                 collector,
7022             );
7023         },
7024     );
7027 #[no_mangle]
7028 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorClassDependency(
7029     raw_data: &PerDocumentStyleData,
7030     element: &RawGeckoElement,
7031     snapshots: &ServoElementSnapshotTable,
7032 ) {
7033     let data = raw_data.borrow();
7034     let element = GeckoElement(element);
7035     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7036     let invalidator = RelativeSelectorInvalidator {
7037         element,
7038         quirks_mode,
7039         snapshot_table: Some(snapshots),
7040         invalidated: relative_selector_invalidated_at,
7041         sibling_traversal_map: SiblingTraversalMap::default(),
7042         _marker: std::marker::PhantomData,
7043     };
7045     invalidator.invalidate_relative_selectors_for_this(
7046         &data.stylist,
7047         |element, scope, data, quirks_mode, mut collector| {
7048             let invalidation_map = data.relative_selector_invalidation_map();
7050             relative_selector_dependencies_for_class(
7051                 &classes_changed(element, snapshots),
7052                 &element,
7053                 scope,
7054                 quirks_mode,
7055                 invalidation_map,
7056                 collector,
7057             );
7058             add_relative_selector_attribute_dependency(
7059                 element,
7060                 &scope,
7061                 invalidation_map,
7062                 &AtomIdent(atom!("class")),
7063                 &mut collector,
7064             );
7065         },
7066     );
7069 #[no_mangle]
7070 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorAttributeDependency(
7071     raw_data: &PerDocumentStyleData,
7072     element: &RawGeckoElement,
7073     local_name: *mut nsAtom,
7074     snapshots: &ServoElementSnapshotTable,
7075 ) {
7076     let data = raw_data.borrow();
7077     let element = GeckoElement(element);
7079     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7080     unsafe {
7081         AtomIdent::with(local_name, |atom| {
7082             let invalidator = RelativeSelectorInvalidator {
7083                 element,
7084                 quirks_mode,
7085                 snapshot_table: Some(snapshots),
7086                 invalidated: relative_selector_invalidated_at,
7087                 sibling_traversal_map: SiblingTraversalMap::default(),
7088                 _marker: std::marker::PhantomData,
7089             };
7091             invalidator.invalidate_relative_selectors_for_this(
7092                 &data.stylist,
7093                 |element, scope, data, _quirks_mode, mut collector| {
7094                     add_relative_selector_attribute_dependency(
7095                         element,
7096                         &scope,
7097                         data.relative_selector_invalidation_map(),
7098                         atom,
7099                         &mut collector,
7100                     );
7101                 },
7102             );
7103         })
7104     }
7107 #[no_mangle]
7108 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorStateDependency(
7109     raw_data: &PerDocumentStyleData,
7110     element: &RawGeckoElement,
7111     state: u64,
7112     snapshots: &ServoElementSnapshotTable,
7113 ) {
7114     let element = GeckoElement(element);
7116     let state = match ElementState::from_bits(state) {
7117         Some(state) => state,
7118         None => return,
7119     };
7120     let data = raw_data.borrow();
7121     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7123     let invalidator = RelativeSelectorInvalidator {
7124         element,
7125         quirks_mode,
7126         snapshot_table: Some(snapshots),
7127         invalidated: relative_selector_invalidated_at,
7128         sibling_traversal_map: SiblingTraversalMap::default(),
7129         _marker: std::marker::PhantomData,
7130     };
7132     invalidator.invalidate_relative_selectors_for_this(
7133         &data.stylist,
7134         |element, scope, data, quirks_mode, collector| {
7135             let invalidation_map = data.relative_selector_invalidation_map();
7136             invalidation_map
7137                 .map
7138                 .state_affecting_selectors
7139                 .lookup_with_additional(*element, quirks_mode, None, &[], state, |dependency| {
7140                     if !dependency.state.intersects(state) {
7141                         return true;
7142                     }
7143                     collector.add_dependency(&dependency.dep, *element, scope);
7144                     true
7145                 });
7146         },
7147     );
7150 fn invalidate_relative_selector_prev_sibling_side_effect(
7151     prev_sibling: GeckoElement,
7152     quirks_mode: QuirksMode,
7153     sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
7154     stylist: &Stylist,
7155 ) {
7156     let invalidator = RelativeSelectorInvalidator {
7157         element: prev_sibling,
7158         quirks_mode,
7159         snapshot_table: None,
7160         invalidated: relative_selector_invalidated_at,
7161         sibling_traversal_map,
7162         _marker: std::marker::PhantomData,
7163     };
7164     invalidator.invalidate_relative_selectors_for_dom_mutation(
7165         false,
7166         &stylist,
7167         ElementSelectorFlags::empty(),
7168         DomMutationOperation::SideEffectPrevSibling,
7169     );
7172 fn invalidate_relative_selector_next_sibling_side_effect(
7173     next_sibling: GeckoElement,
7174     quirks_mode: QuirksMode,
7175     sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
7176     stylist: &Stylist,
7177 ) {
7178     let invalidator = RelativeSelectorInvalidator {
7179         element: next_sibling,
7180         quirks_mode,
7181         snapshot_table: None,
7182         invalidated: relative_selector_invalidated_at,
7183         sibling_traversal_map,
7184         _marker: std::marker::PhantomData,
7185     };
7186     invalidator.invalidate_relative_selectors_for_dom_mutation(
7187         false,
7188         &stylist,
7189         ElementSelectorFlags::empty(),
7190         DomMutationOperation::SideEffectNextSibling,
7191     );
7194 fn invalidate_relative_selector_ts_dependency(
7195     raw_data: &PerDocumentStyleData,
7196     element: GeckoElement,
7197     state: TSStateForInvalidation,
7198 ) {
7199     let data = raw_data.borrow();
7200     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7202     let invalidator = RelativeSelectorInvalidator {
7203         element,
7204         quirks_mode,
7205         snapshot_table: None,
7206         invalidated: relative_selector_invalidated_at,
7207         sibling_traversal_map: SiblingTraversalMap::default(),
7208         _marker: std::marker::PhantomData,
7209     };
7211     invalidator.invalidate_relative_selectors_for_this(
7212         &data.stylist,
7213         |element, scope, data, quirks_mode, collector| {
7214             let invalidation_map = data.relative_selector_invalidation_map();
7215             invalidation_map
7216                 .ts_state_to_selector
7217                 .lookup_with_additional(
7218                     *element,
7219                     quirks_mode,
7220                     None,
7221                     &[],
7222                     ElementState::empty(),
7223                     |dependency| {
7224                         if !dependency.state.intersects(state) {
7225                             return true;
7226                         }
7227                         collector.add_dependency(&dependency.dep, *element, scope);
7228                         true
7229                     },
7230                 );
7231         },
7232     );
7235 #[no_mangle]
7236 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorEmptyDependency(
7237     raw_data: &PerDocumentStyleData,
7238     element: &RawGeckoElement,
7239 ) {
7240     invalidate_relative_selector_ts_dependency(
7241         raw_data,
7242         GeckoElement(element),
7243         TSStateForInvalidation::EMPTY,
7244     );
7247 #[no_mangle]
7248 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthEdgeDependency(
7249     raw_data: &PerDocumentStyleData,
7250     element: &RawGeckoElement,
7251 ) {
7252     invalidate_relative_selector_ts_dependency(
7253         raw_data,
7254         GeckoElement(element),
7255         TSStateForInvalidation::NTH_EDGE,
7256     );
7259 #[no_mangle]
7260 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthDependencyFromSibling(
7261     raw_data: &PerDocumentStyleData,
7262     element: &RawGeckoElement,
7263 ) {
7264     let mut element = Some(GeckoElement(element));
7266     // Short of doing the actual matching, any of the siblings can match the selector, so we
7267     // have to try invalidating against all of them.
7268     while let Some(sibling) = element {
7269         invalidate_relative_selector_ts_dependency(raw_data, sibling, TSStateForInvalidation::NTH);
7270         element = sibling.next_sibling_element();
7271     }
7274 #[no_mangle]
7275 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForInsertion(
7276     raw_data: &PerDocumentStyleData,
7277     element: &RawGeckoElement,
7278 ) {
7279     let element = GeckoElement(element);
7280     let data = raw_data.borrow();
7281     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7283     let inherited = inherit_relative_selector_search_direction(
7284         element.parent_element(),
7285         element.prev_sibling_element(),
7286     );
7287     // Technically, we're not handling breakouts, where the anchor is a (later-sibling) descendant.
7288     // For descendant case, we're ok since it's a descendant of an element yet to be styled.
7289     // For later-sibling descendant, `HAS_SLOW_SELECTOR_LATER_SIBLINGS` is set anyway.
7290     if inherited.is_empty() {
7291         return;
7292     }
7294     // Ok, we could've been inserted between two sibling elements that were connected
7295     // through next sibling. This can happen in two ways:
7296     // * `.a:has(+ .b)`
7297     // * `:has(.. .a + .b ..)`
7298     // Note that the previous sibling may be the anchor, and not part of the invalidation chain.
7299     // Either way, there must be siblings to both sides of the element being inserted
7300     // to consider it.
7301     match (
7302         element.prev_sibling_element(),
7303         element.next_sibling_element(),
7304     ) {
7305         (Some(prev_sibling), Some(next_sibling)) => 'sibling: {
7306             // If the prev sibling is not on the sibling search path, skip.
7307             if prev_sibling
7308                 .relative_selector_search_direction()
7309                 .map_or(true, |direction| {
7310                     !direction.intersects(
7311                         ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
7312                     )
7313                 })
7314             {
7315                 break 'sibling;
7316             }
7317             element.apply_selector_flags(
7318                 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
7319             );
7320             invalidate_relative_selector_prev_sibling_side_effect(
7321                 prev_sibling,
7322                 quirks_mode,
7323                 SiblingTraversalMap::new(
7324                     prev_sibling,
7325                     prev_sibling.prev_sibling_element(),
7326                     element.next_sibling_element(),
7327                 ), // Pretend this inserted element isn't here.
7328                 &data.stylist,
7329             );
7330             invalidate_relative_selector_next_sibling_side_effect(
7331                 next_sibling,
7332                 quirks_mode,
7333                 SiblingTraversalMap::new(
7334                     next_sibling,
7335                     Some(prev_sibling),
7336                     next_sibling.next_sibling_element(),
7337                 ),
7338                 &data.stylist,
7339             );
7340         },
7341         _ => (),
7342     };
7344     let invalidator = RelativeSelectorInvalidator {
7345         element,
7346         quirks_mode,
7347         snapshot_table: None,
7348         invalidated: relative_selector_invalidated_at,
7349         sibling_traversal_map: SiblingTraversalMap::default(),
7350         _marker: std::marker::PhantomData,
7351     };
7353     invalidator.invalidate_relative_selectors_for_dom_mutation(
7354         true,
7355         &data.stylist,
7356         inherited,
7357         DomMutationOperation::Insert,
7358     );
7361 #[no_mangle]
7362 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForAppend(
7363     raw_data: &PerDocumentStyleData,
7364     first_node: &RawGeckoNode,
7365 ) {
7366     let first_node = GeckoNode(first_node);
7367     let inherited = inherit_relative_selector_search_direction(
7368         first_node.parent_element(),
7369         first_node.prev_sibling_element(),
7370     );
7371     if inherited.is_empty() {
7372         return;
7373     }
7374     let first_element = if let Some(e) = first_node.as_element() {
7375         e
7376     } else if let Some(e) = first_node.next_sibling_element() {
7377         e
7378     } else {
7379         return;
7380     };
7381     let data = raw_data.borrow();
7382     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7384     let mut element = Some(first_element);
7385     while let Some(e) = element {
7386         let invalidator = RelativeSelectorInvalidator {
7387             element: e,
7388             quirks_mode,
7389             snapshot_table: None,
7390             sibling_traversal_map: SiblingTraversalMap::default(),
7391             invalidated: relative_selector_invalidated_at,
7392             _marker: std::marker::PhantomData,
7393         };
7394         invalidator.invalidate_relative_selectors_for_dom_mutation(
7395             true,
7396             &data.stylist,
7397             inherited,
7398             DomMutationOperation::Append,
7399         );
7400         element = e.next_sibling_element();
7401     }
7404 fn get_siblings_of_element<'e>(
7405     element: GeckoElement<'e>,
7406     following_node: &'e Option<GeckoNode<'e>>,
7407 ) -> (Option<GeckoElement<'e>>, Option<GeckoElement<'e>>) {
7408     let node = match following_node {
7409         Some(n) => n,
7410         None => {
7411             return match element.as_node().parent_node() {
7412                 Some(p) => (p.last_child_element(), None),
7413                 None => (None, None),
7414             }
7415         },
7416     };
7418     (node.prev_sibling_element(), node.next_sibling_element())
7421 #[no_mangle]
7422 pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForRemoval(
7423     raw_data: &PerDocumentStyleData,
7424     element: &RawGeckoElement,
7425     following_node: Option<&RawGeckoNode>,
7426 ) {
7427     let element = GeckoElement(element);
7429     // This element was in-tree, so we can safely say that if it was not on
7430     // the relative selector search path, its removal will not invalidate any
7431     // relative selector.
7432     if element.relative_selector_search_direction().is_none() {
7433         return;
7434     }
7435     let following_node = following_node.map(GeckoNode);
7436     let (prev_sibling, next_sibling) = get_siblings_of_element(element, &following_node);
7437     let data = raw_data.borrow();
7438     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7440     let inherited =
7441         inherit_relative_selector_search_direction(element.parent_element(), prev_sibling);
7442     if inherited.is_empty() {
7443         return;
7444     }
7446     // Same comment as insertion applies.
7447     match (prev_sibling, next_sibling) {
7448         (Some(prev_sibling), Some(next_sibling)) => {
7449             invalidate_relative_selector_prev_sibling_side_effect(
7450                 prev_sibling,
7451                 quirks_mode,
7452                 SiblingTraversalMap::default(),
7453                 &data.stylist,
7454             );
7455             invalidate_relative_selector_next_sibling_side_effect(
7456                 next_sibling,
7457                 quirks_mode,
7458                 SiblingTraversalMap::default(),
7459                 &data.stylist,
7460             );
7461         },
7462         _ => (),
7463     };
7464     let invalidator = RelativeSelectorInvalidator {
7465         element,
7466         quirks_mode,
7467         snapshot_table: None,
7468         sibling_traversal_map: SiblingTraversalMap::new(element, prev_sibling, next_sibling),
7469         invalidated: relative_selector_invalidated_at,
7470         _marker: std::marker::PhantomData,
7471     };
7472     invalidator.invalidate_relative_selectors_for_dom_mutation(
7473         true,
7474         &data.stylist,
7475         inherited,
7476         DomMutationOperation::Remove,
7477     );
7480 #[no_mangle]
7481 pub extern "C" fn Servo_StyleSet_HasStateDependency(
7482     raw_data: &PerDocumentStyleData,
7483     element: &RawGeckoElement,
7484     state: u64,
7485 ) -> bool {
7486     let element = GeckoElement(element);
7488     let state = ElementState::from_bits_retain(state);
7489     let data = raw_data.borrow();
7491     data.stylist
7492         .any_applicable_rule_data(element, |data| data.has_state_dependency(state))
7495 #[no_mangle]
7496 pub extern "C" fn Servo_StyleSet_HasNthOfStateDependency(
7497     raw_data: &PerDocumentStyleData,
7498     element: &RawGeckoElement,
7499     state: u64,
7500 ) -> bool {
7501     let element = GeckoElement(element);
7503     let state = ElementState::from_bits_retain(state);
7504     let data = raw_data.borrow();
7506     data.stylist
7507         .any_applicable_rule_data(element, |data| data.has_nth_of_state_dependency(state))
7510 #[no_mangle]
7511 pub extern "C" fn Servo_StyleSet_RestyleSiblingsForNthOf(element: &RawGeckoElement, flags: u32) {
7512     let flags = slow_selector_flags_from_node_selector_flags(flags);
7513     let element = GeckoElement(element);
7514     restyle_for_nth_of(element, flags);
7517 #[no_mangle]
7518 pub extern "C" fn Servo_StyleSet_HasDocumentStateDependency(
7519     raw_data: &PerDocumentStyleData,
7520     state: u64,
7521 ) -> bool {
7522     let state = DocumentState::from_bits_retain(state);
7523     let data = raw_data.borrow();
7525     data.stylist.has_document_state_dependency(state)
7528 fn computed_or_resolved_value(
7529     style: &ComputedValues,
7530     prop: nsCSSPropertyID,
7531     context: Option<&style::values::resolved::Context>,
7532     value: &mut nsACString,
7533 ) {
7534     if let Some(longhand) = LonghandId::from_nscsspropertyid(prop) {
7535         return style
7536             .computed_or_resolved_value(longhand, context, value)
7537             .unwrap();
7538     }
7540     let shorthand =
7541         ShorthandId::from_nscsspropertyid(prop).expect("Not a shorthand nor a longhand?");
7542     let mut block = PropertyDeclarationBlock::new();
7543     for longhand in shorthand.longhands() {
7544         block.push(
7545             style.computed_or_resolved_declaration(longhand, context),
7546             Importance::Normal,
7547         );
7548     }
7549     block.shorthand_to_css(shorthand, value).unwrap();
7552 #[no_mangle]
7553 pub unsafe extern "C" fn Servo_GetComputedValue(
7554     style: &ComputedValues,
7555     prop: nsCSSPropertyID,
7556     value: &mut nsACString,
7557 ) {
7558     computed_or_resolved_value(style, prop, None, value)
7561 #[no_mangle]
7562 pub unsafe extern "C" fn Servo_GetResolvedValue(
7563     style: &ComputedValues,
7564     prop: nsCSSPropertyID,
7565     raw_data: &PerDocumentStyleData,
7566     element: &RawGeckoElement,
7567     value: &mut nsACString,
7568 ) {
7569     use style::values::resolved;
7571     let data = raw_data.borrow();
7572     let device = data.stylist.device();
7573     let context = resolved::Context {
7574         style,
7575         device,
7576         element_info: resolved::ResolvedElementInfo {
7577             element: GeckoElement(element),
7578         },
7579     };
7581     computed_or_resolved_value(style, prop, Some(&context), value)
7584 #[no_mangle]
7585 pub unsafe extern "C" fn Servo_GetCustomPropertyValue(
7586     computed_values: &ComputedValues,
7587     raw_style_set: &PerDocumentStyleData,
7588     name: &nsACString,
7589     value: &mut nsACString,
7590 ) -> bool {
7591     let doc_data = raw_style_set.borrow();
7592     let name = Atom::from(name.as_str_unchecked());
7593     let custom_registration = doc_data.stylist.get_custom_property_registration(&name);
7594     let computed_value = if custom_registration.inherits() {
7595         computed_values.custom_properties.inherited.get(&name)
7596     } else {
7597         computed_values.custom_properties.non_inherited.get(&name)
7598     };
7600     if let Some(v) = computed_value {
7601         v.to_css(&mut CssWriter::new(value)).unwrap();
7602         true
7603     } else {
7604         false
7605     }
7608 #[no_mangle]
7609 pub extern "C" fn Servo_GetCustomPropertiesCount(computed_values: &ComputedValues) -> u32 {
7610     // Just expose the custom property items from custom_properties.inherited
7611     // and custom_properties.non_inherited.
7612     let properties = computed_values.custom_properties();
7613     properties.inherited.len() as u32 + properties.non_inherited.len() as u32
7616 #[no_mangle]
7617 pub extern "C" fn Servo_GetCustomPropertyNameAt(
7618     computed_values: &ComputedValues,
7619     index: u32,
7620 ) -> *mut nsAtom {
7621     match &computed_values
7622         .custom_properties
7623         .property_at(index as usize)
7624     {
7625         Some((name, _value)) => name.as_ptr(),
7626         None => ptr::null_mut(),
7627     }
7630 #[no_mangle]
7631 pub extern "C" fn Servo_CssUrl_IsLocalRef(url: &url::CssUrl) -> bool {
7632     url.is_fragment()
7635 fn relative_selector_dependencies_for_id<'a>(
7636     old_id: *const nsAtom,
7637     new_id: *const nsAtom,
7638     element: &GeckoElement<'a>,
7639     scope: Option<OpaqueElement>,
7640     quirks_mode: QuirksMode,
7641     invalidation_map: &'a RelativeSelectorInvalidationMap,
7642     collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
7643 ) {
7644     [old_id, new_id]
7645         .iter()
7646         .filter(|id| !id.is_null())
7647         .for_each(|id| unsafe {
7648             AtomIdent::with(*id, |atom| {
7649                 match invalidation_map.map.id_to_selector.get(atom, quirks_mode) {
7650                     Some(v) => {
7651                         for dependency in v {
7652                             collector.add_dependency(dependency, *element, scope);
7653                         }
7654                     },
7655                     None => (),
7656                 };
7657             })
7658         });
7661 fn relative_selector_dependencies_for_class<'a>(
7662     classes_changed: &SmallVec<[Atom; 8]>,
7663     element: &GeckoElement<'a>,
7664     scope: Option<OpaqueElement>,
7665     quirks_mode: QuirksMode,
7666     invalidation_map: &'a RelativeSelectorInvalidationMap,
7667     collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
7668 ) {
7669     classes_changed.iter().for_each(|atom| {
7670         match invalidation_map
7671             .map
7672             .class_to_selector
7673             .get(atom, quirks_mode)
7674         {
7675             Some(v) => {
7676                 for dependency in v {
7677                     collector.add_dependency(dependency, *element, scope);
7678                 }
7679             },
7680             None => (),
7681         };
7682     });
7685 fn process_relative_selector_invalidations(
7686     element: &GeckoElement,
7687     snapshot_table: &ServoElementSnapshotTable,
7688     data: &PerDocumentStyleDataImpl,
7689 ) {
7690     let snapshot = match snapshot_table.get(element) {
7691         None => return,
7692         Some(s) => s,
7693     };
7694     let mut states = None;
7695     let mut classes = None;
7697     let quirks_mode: QuirksMode = data.stylist.quirks_mode();
7698     let invalidator = RelativeSelectorInvalidator {
7699         element: *element,
7700         quirks_mode,
7701         invalidated: relative_selector_invalidated_at,
7702         sibling_traversal_map: SiblingTraversalMap::default(),
7703         snapshot_table: Some(snapshot_table),
7704         _marker: std::marker::PhantomData,
7705     };
7707     invalidator.invalidate_relative_selectors_for_this(
7708         &data.stylist,
7709         |element, scope, data, quirks_mode, collector| {
7710             let invalidation_map = data.relative_selector_invalidation_map();
7711             let states = *states.get_or_insert_with(|| {
7712                 ElementWrapper::new(*element, snapshot_table).state_changes()
7713             });
7714             let classes = classes.get_or_insert_with(|| classes_changed(element, snapshot_table));
7715             if snapshot.id_changed() {
7716                 relative_selector_dependencies_for_id(
7717                     element
7718                         .id()
7719                         .map(|id| id.as_ptr().cast_const())
7720                         .unwrap_or(ptr::null()),
7721                     snapshot
7722                         .id_attr()
7723                         .map(|id| id.as_ptr().cast_const())
7724                         .unwrap_or(ptr::null()),
7725                     element,
7726                     scope,
7727                     quirks_mode,
7728                     invalidation_map,
7729                     collector,
7730                 );
7731             }
7732             relative_selector_dependencies_for_class(
7733                 &classes,
7734                 element,
7735                 scope,
7736                 quirks_mode,
7737                 invalidation_map,
7738                 collector,
7739             );
7740             snapshot.each_attr_changed(|attr| {
7741                 add_relative_selector_attribute_dependency(
7742                     element,
7743                     &scope,
7744                     invalidation_map,
7745                     attr,
7746                     collector,
7747                 )
7748             });
7749             invalidation_map
7750                 .map
7751                 .state_affecting_selectors
7752                 .lookup_with_additional(*element, quirks_mode, None, &[], states, |dependency| {
7753                     if !dependency.state.intersects(states) {
7754                         return true;
7755                     }
7756                     collector.add_dependency(&dependency.dep, *element, scope);
7757                     true
7758                 });
7759         },
7760     );
7763 #[no_mangle]
7764 pub extern "C" fn Servo_ProcessInvalidations(
7765     set: &PerDocumentStyleData,
7766     element: &RawGeckoElement,
7767     snapshots: *const ServoElementSnapshotTable,
7768 ) {
7769     debug_assert!(!snapshots.is_null());
7771     let element = GeckoElement(element);
7772     debug_assert!(element.has_snapshot());
7773     debug_assert!(!element.handled_snapshot());
7775     let snapshot_table = unsafe { &*snapshots };
7776     let per_doc_data = set.borrow();
7777     process_relative_selector_invalidations(&element, snapshot_table, &per_doc_data);
7779     let mut data = element.mutate_data();
7780     if data.is_none() {
7781         // Snapshot for unstyled element is really only meant for relative selector
7782         // invalidation, so this is fine.
7783         return;
7784     }
7786     let global_style_data = &*GLOBAL_STYLE_DATA;
7787     let guard = global_style_data.shared_lock.read();
7788     let per_doc_data = set.borrow();
7789     let shared_style_context = create_shared_context(
7790         &global_style_data,
7791         &guard,
7792         &per_doc_data.stylist,
7793         TraversalFlags::empty(),
7794         snapshot_table,
7795     );
7796     let mut data = data.as_mut().map(|d| &mut **d);
7798     let mut selector_caches = SelectorCaches::default();
7799     if let Some(ref mut data) = data {
7800         // FIXME(emilio): Ideally we could share the nth-index-cache across all
7801         // the elements?
7802         let result = data.invalidate_style_if_needed(
7803             element,
7804             &shared_style_context,
7805             None,
7806             &mut selector_caches,
7807         );
7809         if result.has_invalidated_siblings() {
7810             let parent = element
7811                 .traversal_parent()
7812                 .expect("How could we invalidate siblings without a common parent?");
7813             unsafe {
7814                 parent.set_dirty_descendants();
7815                 bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
7816             }
7817         } else if result.has_invalidated_descendants() {
7818             unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
7819         } else if result.has_invalidated_self() {
7820             unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
7821         }
7822     }
7825 #[no_mangle]
7826 pub extern "C" fn Servo_HasPendingRestyleAncestor(
7827     element: &RawGeckoElement,
7828     may_need_to_flush_layout: bool,
7829 ) -> bool {
7830     let mut has_yet_to_be_styled = false;
7831     let mut element = Some(GeckoElement(element));
7832     while let Some(e) = element {
7833         if e.has_any_animation() {
7834             return true;
7835         }
7837         // If the element needs a frame, it means that we haven't styled it yet
7838         // after it got inserted in the document, and thus we may need to do
7839         // that for transitions and animations to trigger.
7840         //
7841         // This is a fast path in the common case, but `has_yet_to_be_styled` is
7842         // the real check for this.
7843         if e.needs_frame() {
7844             return true;
7845         }
7847         let data = e.borrow_data();
7848         if let Some(ref data) = data {
7849             if !data.hint.is_empty() {
7850                 return true;
7851             }
7852             if has_yet_to_be_styled && !data.styles.is_display_none() {
7853                 return true;
7854             }
7855             // Ideally, DOM mutations wouldn't affect layout trees of siblings.
7856             //
7857             // In practice, this can happen because Gecko deals pretty badly
7858             // with some kinds of content insertion and removals.
7859             //
7860             // If we may need to flush layout, we need frames to accurately
7861             // determine whether we'll actually flush, so if we have to
7862             // reconstruct we need to flush style, which is what will take care
7863             // of ensuring that frames are constructed, even if the style itself
7864             // is up-to-date.
7865             if may_need_to_flush_layout && data.damage.contains(GeckoRestyleDamage::reconstruct()) {
7866                 return true;
7867             }
7868         }
7869         has_yet_to_be_styled = data.is_none();
7871         element = e.traversal_parent();
7872     }
7873     false
7876 #[no_mangle]
7877 pub unsafe extern "C" fn Servo_SelectorList_Parse(
7878     selector_list: &nsACString,
7879     is_chrome: bool,
7880 ) -> *mut SelectorList {
7881     use style::selector_parser::SelectorParser;
7883     let url_data = UrlExtraData::from_ptr_ref(if is_chrome {
7884         &DUMMY_CHROME_URL_DATA
7885     } else {
7886         &DUMMY_URL_DATA
7887     });
7889     let input = selector_list.as_str_unchecked();
7890     let selector_list = match SelectorParser::parse_author_origin_no_namespace(&input, url_data) {
7891         Ok(selector_list) => selector_list,
7892         Err(..) => return ptr::null_mut(),
7893     };
7895     Box::into_raw(Box::new(selector_list))
7898 #[no_mangle]
7899 pub unsafe extern "C" fn Servo_SelectorList_Drop(list: *mut SelectorList) {
7900     let _ = Box::from_raw(list);
7903 #[no_mangle]
7904 pub unsafe extern "C" fn Servo_IsValidCSSColor(value: &nsACString) -> bool {
7905     let mut input = ParserInput::new(value.as_str_unchecked());
7906     let mut input = Parser::new(&mut input);
7907     let context = ParserContext::new(
7908         Origin::Author,
7909         dummy_url_data(),
7910         Some(CssRuleType::Style),
7911         ParsingMode::DEFAULT,
7912         QuirksMode::NoQuirks,
7913         /* namespaces = */ Default::default(),
7914         None,
7915         None,
7916     );
7917     specified::Color::is_valid(&context, &mut input)
7920 #[no_mangle]
7921 pub unsafe extern "C" fn Servo_ComputeColor(
7922     raw_data: Option<&PerDocumentStyleData>,
7923     current_color: structs::nscolor,
7924     value: &nsACString,
7925     result_color: &mut structs::nscolor,
7926     was_current_color: *mut bool,
7927     loader: *mut Loader,
7928 ) -> bool {
7929     let mut input = ParserInput::new(value.as_str_unchecked());
7930     let mut input = Parser::new(&mut input);
7931     let reporter = loader.as_mut().and_then(|loader| {
7932         // Make an ErrorReporter that will report errors as being "from DOM".
7933         ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
7934     });
7936     let context = ParserContext::new(
7937         Origin::Author,
7938         dummy_url_data(),
7939         Some(CssRuleType::Style),
7940         ParsingMode::DEFAULT,
7941         QuirksMode::NoQuirks,
7942         /* namespaces = */ Default::default(),
7943         reporter.as_ref().map(|e| e as &dyn ParseErrorReporter),
7944         None,
7945     );
7947     let data;
7948     let device = match raw_data {
7949         Some(d) => {
7950             data = d.borrow();
7951             Some(data.stylist.device())
7952         },
7953         None => None,
7954     };
7956     let computed = match specified::Color::parse_and_compute(&context, &mut input, device) {
7957         Some(c) => c,
7958         None => return false,
7959     };
7961     let current_color = style::gecko::values::convert_nscolor_to_absolute_color(current_color);
7962     if !was_current_color.is_null() {
7963         *was_current_color = computed.is_currentcolor();
7964     }
7966     let rgba = computed.resolve_to_absolute(&current_color);
7967     *result_color = style::gecko::values::convert_absolute_color_to_nscolor(&rgba);
7968     true
7971 #[no_mangle]
7972 pub unsafe extern "C" fn Servo_ColorTo(
7973     from_color: &nsACString,
7974     to_color_space: &nsACString,
7975     result_color: &mut nsACString,
7976     result_components: &mut nsTArray<f32>,
7977     result_adjusted: &mut bool,
7978     loader: *mut Loader,
7979 ) -> bool {
7980     // Figure out the color space.
7981     let mut input = ParserInput::new(to_color_space.as_str_unchecked());
7982     let mut input = Parser::new(&mut input);
7983     let to_color_space = match ColorSpace::parse(&mut input) {
7984         Ok(color_space) => color_space,
7985         Err(_) => {
7986             // Can't parse the color space? Fail the conversion.
7987             return false;
7988         },
7989     };
7991     let mut input = ParserInput::new(from_color.as_str_unchecked());
7992     let mut input = Parser::new(&mut input);
7994     let reporter = loader.as_mut().and_then(|loader| {
7995         // Make an ErrorReporter that will report errors as being "from DOM".
7996         ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
7997     });
7999     let context = ParserContext::new(
8000         Origin::Author,
8001         dummy_url_data(),
8002         Some(CssRuleType::Style),
8003         ParsingMode::DEFAULT,
8004         QuirksMode::NoQuirks,
8005         /* namespaces = */ Default::default(),
8006         reporter.as_ref().map(|e| e as &dyn ParseErrorReporter),
8007         None,
8008     );
8010     let specified = match specified::Color::parse(&context, &mut input) {
8011         Ok(color) => color,
8012         Err(_) => return false,
8013     };
8015     let color = match specified {
8016         specified::Color::Absolute(ref absolute) => &absolute.color,
8017         _ => {
8018             // Can't do anything with a non-absolute color from here, so we
8019             // fail the conversion.
8020             return false;
8021         },
8022     };
8024     let color = color.to_color_space(to_color_space);
8025     let mut s = String::new();
8026     color
8027         .write_author_preferred_value(&mut CssWriter::new(&mut s))
8028         .unwrap();
8029     result_color.assign(&s);
8031     result_components.assign_from_iter_pod(color.raw_components().iter().copied());
8033     // For now we don't do gamut mapping, so always false.
8034     *result_adjusted = false;
8036     true
8039 #[no_mangle]
8040 pub extern "C" fn Servo_ResolveColor(
8041     color: &computed::Color,
8042     foreground: &style::color::AbsoluteColor,
8043 ) -> style::color::AbsoluteColor {
8044     color.resolve_to_absolute(foreground)
8047 #[no_mangle]
8048 pub extern "C" fn Servo_ResolveCalcLengthPercentage(
8049     calc: &computed::length_percentage::CalcLengthPercentage,
8050     basis: f32,
8051 ) -> f32 {
8052     calc.resolve(computed::Length::new(basis)).px()
8055 #[no_mangle]
8056 pub extern "C" fn Servo_ConvertColorSpace(
8057     color: &AbsoluteColor,
8058     color_space: ColorSpace,
8059 ) -> AbsoluteColor {
8060     color.to_color_space(color_space)
8063 #[no_mangle]
8064 pub unsafe extern "C" fn Servo_IntersectionObserverRootMargin_Parse(
8065     value: &nsACString,
8066     result: *mut IntersectionObserverRootMargin,
8067 ) -> bool {
8068     let value = value.as_str_unchecked();
8069     let result = result.as_mut().unwrap();
8071     let mut input = ParserInput::new(&value);
8072     let mut parser = Parser::new(&mut input);
8074     let url_data = dummy_url_data();
8075     let context = ParserContext::new(
8076         Origin::Author,
8077         url_data,
8078         Some(CssRuleType::Style),
8079         ParsingMode::DEFAULT,
8080         QuirksMode::NoQuirks,
8081         /* namespaces = */ Default::default(),
8082         None,
8083         None,
8084     );
8086     let margin = parser.parse_entirely(|p| IntersectionObserverRootMargin::parse(&context, p));
8087     match margin {
8088         Ok(margin) => {
8089             *result = margin;
8090             true
8091         },
8092         Err(..) => false,
8093     }
8096 #[no_mangle]
8097 pub extern "C" fn Servo_IntersectionObserverRootMargin_ToString(
8098     root_margin: &IntersectionObserverRootMargin,
8099     result: &mut nsACString,
8100 ) {
8101     let mut writer = CssWriter::new(result);
8102     root_margin.to_css(&mut writer).unwrap();
8105 #[no_mangle]
8106 pub extern "C" fn Servo_ParseTransformIntoMatrix(
8107     value: &nsACString,
8108     contain_3d: &mut bool,
8109     result: &mut structs::Matrix4x4Components,
8110 ) -> bool {
8111     use style::properties::longhands::transform;
8113     let string = unsafe { value.as_str_unchecked() };
8114     let mut input = ParserInput::new(&string);
8115     let mut parser = Parser::new(&mut input);
8116     let context = ParserContext::new(
8117         Origin::Author,
8118         unsafe { dummy_url_data() },
8119         Some(CssRuleType::Style),
8120         ParsingMode::DEFAULT,
8121         QuirksMode::NoQuirks,
8122         /* namespaces = */ Default::default(),
8123         None,
8124         None,
8125     );
8127     let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
8128         Ok(t) => t,
8129         Err(..) => return false,
8130     };
8132     let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
8133         Ok(result) => result,
8134         Err(..) => return false,
8135     };
8137     *result = m.to_array();
8138     *contain_3d = is_3d;
8139     true
8142 #[no_mangle]
8143 pub extern "C" fn Servo_ParseFilters(
8144     value: &nsACString,
8145     ignore_urls: bool,
8146     data: *mut URLExtraData,
8147     out: &mut style::OwnedSlice<Filter>,
8148 ) -> bool {
8149     use style::values::specified::effects::SpecifiedFilter;
8151     let string = unsafe { value.as_str_unchecked() };
8152     let mut input = ParserInput::new(&string);
8153     let mut parser = Parser::new(&mut input);
8154     let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
8155     let context = ParserContext::new(
8156         Origin::Author,
8157         url_data,
8158         None,
8159         ParsingMode::DEFAULT,
8160         QuirksMode::NoQuirks,
8161         /* namespaces = */ Default::default(),
8162         None,
8163         None,
8164     );
8166     let mut filters = vec![];
8168     if parser
8169         .try_parse(|i| i.expect_ident_matching("none"))
8170         .is_ok()
8171     {
8172         return parser.expect_exhausted().is_ok();
8173     }
8175     if parser.is_exhausted() {
8176         return false;
8177     }
8179     while !parser.is_exhausted() {
8180         let specified_filter = match SpecifiedFilter::parse(&context, &mut parser) {
8181             Ok(f) => f,
8182             Err(..) => return false,
8183         };
8185         let filter = match specified_filter.to_computed_value_without_context() {
8186             Ok(f) => f,
8187             Err(..) => return false,
8188         };
8190         if ignore_urls && matches!(filter, Filter::Url(_)) {
8191             continue;
8192         }
8194         filters.push(filter);
8195     }
8197     *out = style::OwnedSlice::from(filters);
8198     true
8201 #[no_mangle]
8202 pub unsafe extern "C" fn Servo_ParseFontShorthandForMatching(
8203     value: &nsACString,
8204     data: *mut URLExtraData,
8205     family: &mut FontFamilyList,
8206     style: &mut FontStyle,
8207     stretch: &mut FontStretch,
8208     weight: &mut FontWeight,
8209     size: Option<&mut f32>,
8210     small_caps: Option<&mut bool>,
8211 ) -> bool {
8212     use style::properties::shorthands::font;
8213     use style::values::generics::font::FontStyle as GenericFontStyle;
8214     use style::values::specified::font as specified;
8216     let string = value.as_str_unchecked();
8217     let mut input = ParserInput::new(&string);
8218     let mut parser = Parser::new(&mut input);
8219     let url_data = UrlExtraData::from_ptr_ref(&data);
8220     let context = ParserContext::new(
8221         Origin::Author,
8222         url_data,
8223         Some(CssRuleType::FontFace),
8224         ParsingMode::DEFAULT,
8225         QuirksMode::NoQuirks,
8226         /* namespaces = */ Default::default(),
8227         None,
8228         None,
8229     );
8231     let font = match parser.parse_entirely(|f| font::parse_value(&context, f)) {
8232         Ok(f) => f,
8233         Err(..) => return false,
8234     };
8236     // The system font is not acceptable, so we return false.
8237     match font.font_family {
8238         specified::FontFamily::Values(list) => *family = list,
8239         specified::FontFamily::System(_) => return false,
8240     }
8242     let specified_font_style = match font.font_style {
8243         specified::FontStyle::Specified(ref s) => s,
8244         specified::FontStyle::System(_) => return false,
8245     };
8247     *style = match *specified_font_style {
8248         GenericFontStyle::Normal => FontStyle::NORMAL,
8249         GenericFontStyle::Italic => FontStyle::ITALIC,
8250         GenericFontStyle::Oblique(ref angle) => FontStyle::oblique(angle.degrees()),
8251     };
8253     *stretch = match font.font_stretch {
8254         specified::FontStretch::Keyword(ref k) => k.compute(),
8255         specified::FontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()),
8256         specified::FontStretch::System(_) => return false,
8257     };
8259     *weight = match font.font_weight {
8260         specified::FontWeight::Absolute(w) => w.compute(),
8261         // Resolve relative font weights against the initial of font-weight
8262         // (normal, which is equivalent to 400).
8263         specified::FontWeight::Bolder => FontWeight::normal().bolder(),
8264         specified::FontWeight::Lighter => FontWeight::normal().lighter(),
8265         specified::FontWeight::System(_) => return false,
8266     };
8268     // XXX This is unfinished; see values::specified::FontSize::ToComputedValue
8269     // for a more complete implementation (but we can't use it as-is).
8270     if let Some(size) = size {
8271         *size = match font.font_size {
8272             specified::FontSize::Length(lp) => {
8273                 use style::values::generics::transform::ToAbsoluteLength;
8274                 match lp.to_pixel_length(None) {
8275                     Ok(len) => len,
8276                     Err(..) => return false,
8277                 }
8278             },
8279             specified::FontSize::Keyword(info) => {
8280                 let keyword = if info.kw != specified::FontSizeKeyword::Math {
8281                     info.kw
8282                 } else {
8283                     specified::FontSizeKeyword::Medium
8284                 };
8285                 // Map absolute-size keywords to sizes.
8286                 // TODO: Maybe get a meaningful quirks / base size from the caller?
8287                 let quirks_mode = QuirksMode::NoQuirks;
8288                 keyword
8289                     .to_length_without_context(
8290                         quirks_mode,
8291                         computed::Length::new(specified::FONT_MEDIUM_PX),
8292                     )
8293                     .0
8294                     .px()
8295             },
8296             // smaller, larger not currently supported
8297             specified::FontSize::Smaller |
8298             specified::FontSize::Larger |
8299             specified::FontSize::System(_) => {
8300                 return false;
8301             },
8302         };
8303     }
8305     if let Some(small_caps) = small_caps {
8306         use style::computed_values::font_variant_caps::T::SmallCaps;
8307         *small_caps = font.font_variant_caps == SmallCaps;
8308     }
8310     true
8313 #[no_mangle]
8314 pub unsafe extern "C" fn Servo_SourceSizeList_Parse(value: &nsACString) -> *mut SourceSizeList {
8315     let value = value.as_str_unchecked();
8316     let mut input = ParserInput::new(value);
8317     let mut parser = Parser::new(&mut input);
8319     let context = ParserContext::new(
8320         Origin::Author,
8321         dummy_url_data(),
8322         Some(CssRuleType::Style),
8323         ParsingMode::DEFAULT,
8324         QuirksMode::NoQuirks,
8325         /* namespaces = */ Default::default(),
8326         None,
8327         None,
8328     );
8330     // NB: Intentionally not calling parse_entirely.
8331     let list = SourceSizeList::parse(&context, &mut parser);
8332     Box::into_raw(Box::new(list))
8335 #[no_mangle]
8336 pub unsafe extern "C" fn Servo_SourceSizeList_Evaluate(
8337     raw_data: &PerDocumentStyleData,
8338     list: Option<&SourceSizeList>,
8339 ) -> i32 {
8340     let doc_data = raw_data.borrow();
8341     let device = doc_data.stylist.device();
8342     let quirks_mode = doc_data.stylist.quirks_mode();
8344     let result = match list {
8345         Some(list) => list.evaluate(device, quirks_mode),
8346         None => SourceSizeList::empty().evaluate(device, quirks_mode),
8347     };
8349     result.0
8352 #[no_mangle]
8353 pub unsafe extern "C" fn Servo_SourceSizeList_Drop(list: *mut SourceSizeList) {
8354     let _ = Box::from_raw(list);
8357 #[no_mangle]
8358 pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges(
8359     root: &RawGeckoElement,
8360     document_style: &PerDocumentStyleData,
8361     non_document_styles: &nsTArray<&AuthorStyles>,
8362     states_changed: u64,
8363 ) {
8364     use style::invalidation::element::document_state::DocumentStateInvalidationProcessor;
8365     use style::invalidation::element::invalidator::TreeStyleInvalidator;
8367     let document_data = document_style.borrow();
8369     let iter = document_data
8370         .stylist
8371         .iter_origins()
8372         .map(|(data, _origin)| data)
8373         .chain(
8374             non_document_styles
8375                 .iter()
8376                 .map(|author_styles| &*author_styles.data),
8377         );
8379     let mut selector_caches = SelectorCaches::default();
8380     let root = GeckoElement(root);
8381     let mut processor = DocumentStateInvalidationProcessor::new(
8382         iter,
8383         DocumentState::from_bits_retain(states_changed),
8384         &mut selector_caches,
8385         root.as_node().owner_doc().quirks_mode(),
8386     );
8388     let result =
8389         TreeStyleInvalidator::new(root, /* stack_limit_checker = */ None, &mut processor)
8390             .invalidate();
8392     debug_assert!(!result.has_invalidated_siblings(), "How in the world?");
8393     if result.has_invalidated_descendants() {
8394         bindings::Gecko_NoteDirtySubtreeForInvalidation(root.0);
8395     } else if result.has_invalidated_self() {
8396         bindings::Gecko_NoteDirtyElement(root.0);
8397     }
8400 #[no_mangle]
8401 pub unsafe extern "C" fn Servo_PseudoClass_GetStates(name: &nsACString) -> u64 {
8402     let name = name.as_str_unchecked();
8403     match NonTSPseudoClass::parse_non_functional(name) {
8404         None => 0,
8405         // Ignore :any-link since it contains both visited and unvisited state.
8406         Some(NonTSPseudoClass::AnyLink) => 0,
8407         Some(pseudo_class) => pseudo_class.state_flag().bits(),
8408     }
8411 #[no_mangle]
8412 pub unsafe extern "C" fn Servo_UseCounters_Create() -> *mut UseCounters {
8413     Box::into_raw(Box::<UseCounters>::default())
8416 #[no_mangle]
8417 pub unsafe extern "C" fn Servo_UseCounters_Drop(c: *mut UseCounters) {
8418     let _ = Box::from_raw(c);
8421 #[no_mangle]
8422 pub unsafe extern "C" fn Servo_UseCounters_Merge(
8423     doc_counters: &UseCounters,
8424     sheet_counters: &UseCounters,
8425 ) {
8426     doc_counters.merge(sheet_counters)
8429 #[no_mangle]
8430 pub unsafe extern "C" fn Servo_IsPropertyIdRecordedInUseCounter(
8431     use_counters: &UseCounters,
8432     id: nsCSSPropertyID,
8433 ) -> bool {
8434     let id = NonCustomPropertyId::from_nscsspropertyid(id).unwrap();
8435     use_counters.non_custom_properties.recorded(id)
8438 #[no_mangle]
8439 pub unsafe extern "C" fn Servo_IsUnknownPropertyRecordedInUseCounter(
8440     use_counters: &UseCounters,
8441     p: CountedUnknownProperty,
8442 ) -> bool {
8443     use_counters.counted_unknown_properties.recorded(p)
8446 #[no_mangle]
8447 pub unsafe extern "C" fn Servo_IsCssPropertyRecordedInUseCounter(
8448     use_counters: &UseCounters,
8449     property: &nsACString,
8450     known_prop: *mut bool,
8451 ) -> bool {
8452     *known_prop = false;
8454     let prop_name = property.as_str_unchecked();
8455     if let Ok(p) = PropertyId::parse_unchecked_for_testing(prop_name) {
8456         if let Some(id) = p.non_custom_id() {
8457             *known_prop = true;
8458             return use_counters.non_custom_properties.recorded(id);
8459         }
8460     }
8462     if let Some(p) = CountedUnknownProperty::parse_for_testing(prop_name) {
8463         *known_prop = true;
8464         return use_counters.counted_unknown_properties.recorded(p);
8465     }
8467     false
8470 #[no_mangle]
8471 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Create(
8472     buffer: *mut u8,
8473     len: usize,
8474 ) -> *mut SharedMemoryBuilder {
8475     Box::into_raw(Box::new(SharedMemoryBuilder::new(buffer, len)))
8478 #[no_mangle]
8479 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_AddStylesheet(
8480     builder: &mut SharedMemoryBuilder,
8481     contents: &StylesheetContents,
8482     error_message: &mut nsACString,
8483 ) -> *const LockedCssRules {
8484     // Assert some things we assume when we create a style sheet from shared
8485     // memory.
8486     debug_assert_eq!(contents.origin, Origin::UserAgent);
8487     debug_assert_eq!(contents.quirks_mode, QuirksMode::NoQuirks);
8488     debug_assert!(contents.source_map_url.read().is_none());
8489     debug_assert!(contents.source_url.read().is_none());
8491     match builder.write(&contents.rules) {
8492         Ok(rules_ptr) => &**rules_ptr,
8493         Err(message) => {
8494             error_message.assign(&message);
8495             ptr::null()
8496         },
8497     }
8500 #[no_mangle]
8501 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_GetLength(
8502     builder: &SharedMemoryBuilder,
8503 ) -> usize {
8504     builder.len()
8507 #[no_mangle]
8508 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Drop(builder: *mut SharedMemoryBuilder) {
8509     let _ = Box::from_raw(builder);
8512 #[no_mangle]
8513 pub unsafe extern "C" fn Servo_StyleArcSlice_EmptyPtr() -> *mut c_void {
8514     style_traits::arc_slice::ArcSlice::<u64>::leaked_empty_ptr()
8517 #[no_mangle]
8518 pub unsafe extern "C" fn Servo_LoadData_GetLazy(
8519     source: &url::LoadDataSource,
8520 ) -> *const url::LoadData {
8521     source.get()
8524 #[no_mangle]
8525 pub extern "C" fn Servo_LengthPercentage_ToCss(
8526     lp: &computed::LengthPercentage,
8527     result: &mut nsACString,
8528 ) {
8529     lp.to_css(&mut CssWriter::new(result)).unwrap();
8532 #[no_mangle]
8533 pub extern "C" fn Servo_FontStyle_ToCss(s: &FontStyle, result: &mut nsACString) {
8534     s.to_css(&mut CssWriter::new(result)).unwrap()
8537 #[no_mangle]
8538 pub extern "C" fn Servo_FontWeight_ToCss(w: &FontWeight, result: &mut nsACString) {
8539     w.to_css(&mut CssWriter::new(result)).unwrap()
8542 #[no_mangle]
8543 pub extern "C" fn Servo_FontStretch_ToCss(s: &FontStretch, result: &mut nsACString) {
8544     s.to_css(&mut CssWriter::new(result)).unwrap()
8547 #[no_mangle]
8548 pub extern "C" fn Servo_FontStretch_SerializeKeyword(
8549     s: &FontStretch,
8550     result: &mut nsACString,
8551 ) -> bool {
8552     let kw = match s.as_keyword() {
8553         Some(kw) => kw,
8554         None => return false,
8555     };
8556     kw.to_css(&mut CssWriter::new(result)).unwrap();
8557     true
8560 #[no_mangle]
8561 pub unsafe extern "C" fn Servo_CursorKind_Parse(
8562     cursor: &nsACString,
8563     result: &mut computed::ui::CursorKind,
8564 ) -> bool {
8565     match computed::ui::CursorKind::from_ident(cursor.as_str_unchecked()) {
8566         Ok(c) => {
8567             *result = c;
8568             true
8569         },
8570         Err(..) => false,
8571     }
8574 #[no_mangle]
8575 pub extern "C" fn Servo_FontFamily_Generic(generic: GenericFontFamily) -> &'static FontFamily {
8576     FontFamily::generic(generic)
8579 #[no_mangle]
8580 pub extern "C" fn Servo_FontFamily_ForSystemFont(name: &nsACString, out: &mut FontFamily) {
8581     *out = FontFamily::for_system_font(&name.to_utf8());
8584 #[no_mangle]
8585 pub extern "C" fn Servo_FontFamilyList_WithNames(
8586     names: &nsTArray<computed::font::SingleFontFamily>,
8587     out: &mut FontFamilyList,
8588 ) {
8589     *out = FontFamilyList {
8590         list: style_traits::arc_slice::ArcSlice::from_iter(names.iter().cloned()),
8591     };
8594 #[no_mangle]
8595 pub extern "C" fn Servo_FamilyName_Serialize(name: &FamilyName, result: &mut nsACString) {
8596     name.to_css(&mut CssWriter::new(result)).unwrap()
8599 #[no_mangle]
8600 pub extern "C" fn Servo_GenericFontFamily_Parse(input: &nsACString) -> GenericFontFamily {
8601     let context = ParserContext::new(
8602         Origin::Author,
8603         unsafe { dummy_url_data() },
8604         Some(CssRuleType::Style),
8605         ParsingMode::DEFAULT,
8606         QuirksMode::NoQuirks,
8607         /* namespaces = */ Default::default(),
8608         None,
8609         None,
8610     );
8611     let value = input.to_utf8();
8612     let mut input = ParserInput::new(&value);
8613     let mut input = Parser::new(&mut input);
8614     GenericFontFamily::parse(&context, &mut input).unwrap_or(GenericFontFamily::None)
8617 #[no_mangle]
8618 pub extern "C" fn Servo_ColorScheme_Parse(input: &nsACString, out: &mut u8) -> bool {
8619     use style::values::specified::ColorScheme;
8621     let context = ParserContext::new(
8622         Origin::Author,
8623         unsafe { dummy_url_data() },
8624         Some(CssRuleType::Style),
8625         ParsingMode::DEFAULT,
8626         QuirksMode::NoQuirks,
8627         /* namespaces = */ Default::default(),
8628         None,
8629         None,
8630     );
8631     let input = unsafe { input.as_str_unchecked() };
8632     let mut input = ParserInput::new(&input);
8633     let mut input = Parser::new(&mut input);
8634     let scheme = match input.parse_entirely(|i| ColorScheme::parse(&context, i)) {
8635         Ok(scheme) => scheme,
8636         Err(..) => return false,
8637     };
8638     *out = scheme.raw_bits();
8639     true
8642 #[no_mangle]
8643 pub extern "C" fn Servo_LayerBlockRule_GetName(rule: &LayerBlockRule, result: &mut nsACString) {
8644     if let Some(ref name) = rule.name {
8645         name.to_css(&mut CssWriter::new(result)).unwrap()
8646     }
8649 #[no_mangle]
8650 pub extern "C" fn Servo_LayerStatementRule_GetNameCount(rule: &LayerStatementRule) -> usize {
8651     rule.names.len()
8654 #[no_mangle]
8655 pub extern "C" fn Servo_LayerStatementRule_GetNameAt(
8656     rule: &LayerStatementRule,
8657     index: usize,
8658     result: &mut nsACString,
8659 ) {
8660     if let Some(ref name) = rule.names.get(index) {
8661         name.to_css(&mut CssWriter::new(result)).unwrap()
8662     }
8665 #[no_mangle]
8666 pub unsafe extern "C" fn Servo_InvalidateForViewportUnits(
8667     document_style: &PerDocumentStyleData,
8668     root: &RawGeckoElement,
8669     dynamic_only: bool,
8670 ) {
8671     let mut document_data = document_style.borrow_mut();
8672     let ref mut stylist = document_data.stylist;
8673     let device = stylist.device();
8675     if !device.used_viewport_size() {
8676         return;
8677     }
8679     if dynamic_only && !device.used_dynamic_viewport_size() {
8680         return;
8681     }
8683     // If the viewport changed, then initial values containing viewport units need to be recomputed.
8684     if stylist
8685         .get_custom_property_initial_values_flags()
8686         .intersects(ComputedValueFlags::USES_VIEWPORT_UNITS)
8687     {
8688         stylist.rebuild_initial_values_for_custom_properties();
8689     }
8691     if style::invalidation::viewport_units::invalidate(GeckoElement(root)) {
8692         // The invalidation machinery propagates the bits up, but we still need
8693         // to tell the Gecko restyle root machinery about it.
8694         bindings::Gecko_NoteDirtySubtreeForInvalidation(root);
8695     }
8698 #[no_mangle]
8699 pub extern "C" fn Servo_InterpolateColor(
8700     interpolation: ColorInterpolationMethod,
8701     left: &AbsoluteColor,
8702     right: &AbsoluteColor,
8703     progress: f32,
8704 ) -> AbsoluteColor {
8705     style::color::mix::mix(
8706         interpolation,
8707         left,
8708         progress,
8709         right,
8710         1.0 - progress,
8711         ColorMixFlags::empty(),
8712     )
8715 #[no_mangle]
8716 pub extern "C" fn Servo_EasingFunctionAt(
8717     easing_function: &ComputedTimingFunction,
8718     progress: f64,
8719     before_flag: BeforeFlag,
8720 ) -> f64 {
8721     easing_function.calculate_output(progress, before_flag, 1e-7)
8724 fn parse_no_context<'i, F, R>(string: &'i str, parse: F) -> Result<R, ()>
8725 where
8726     F: FnOnce(&ParserContext, &mut Parser<'i, '_>) -> Result<R, ParseError<'i>>,
8728     let context = ParserContext::new(
8729         Origin::Author,
8730         unsafe { dummy_url_data() },
8731         None,
8732         ParsingMode::DEFAULT,
8733         QuirksMode::NoQuirks,
8734         /* namespaces = */ Default::default(),
8735         None,
8736         None,
8737     );
8738     let mut input = ParserInput::new(string);
8739     Parser::new(&mut input)
8740         .parse_entirely(|i| parse(&context, i))
8741         .map_err(|_| ())
8744 #[no_mangle]
8745 // Parse a length without style context (for canvas2d letterSpacing/wordSpacing attributes).
8746 // This accepts absolute lengths, and if a font-metrics-getter function is passed, also
8747 // font-relative ones, but not other units (such as percentages, viewport-relative, etc)
8748 // that would require a full style context to resolve.
8749 pub extern "C" fn Servo_ParseLengthWithoutStyleContext(
8750     len: &nsACString,
8751     out: &mut f32,
8752     get_font_metrics: Option<unsafe extern "C" fn(*mut c_void) -> GeckoFontMetrics>,
8753     getter_context: *mut c_void,
8754 ) -> bool {
8755     let metrics_getter = if let Some(getter) = get_font_metrics {
8756         Some(move || -> GeckoFontMetrics { unsafe { getter(getter_context) } })
8757     } else {
8758         None
8759     };
8760     let value = parse_no_context(unsafe { len.as_str_unchecked() }, specified::Length::parse)
8761         .and_then(|p| p.to_computed_pixel_length_with_font_metrics(metrics_getter));
8762     match value {
8763         Ok(v) => {
8764             *out = v;
8765             true
8766         },
8767         Err(..) => false,
8768     }
8771 #[no_mangle]
8772 pub extern "C" fn Servo_SlowRgbToColorName(r: u8, g: u8, b: u8, result: &mut nsACString) -> bool {
8773     let mut candidates = SmallVec::<[&'static str; 5]>::new();
8774     for (name, color) in cssparser::color::all_named_colors() {
8775         if color == (r, g, b) {
8776             candidates.push(name);
8777         }
8778     }
8779     if candidates.is_empty() {
8780         return false;
8781     }
8782     // DevTools expect the first alphabetically.
8783     candidates.sort();
8784     result.assign(candidates[0]);
8785     true
8788 #[no_mangle]
8789 pub extern "C" fn Servo_ColorNameToRgb(name: &nsACString, out: &mut structs::nscolor) -> bool {
8790     match cssparser::color::parse_named_color(unsafe { name.as_str_unchecked() }) {
8791         Ok((r, g, b)) => {
8792             *out = style::gecko::values::convert_absolute_color_to_nscolor(&AbsoluteColor::new(
8793                 ColorSpace::Srgb,
8794                 r,
8795                 g,
8796                 b,
8797                 1.0,
8798             ));
8799             true
8800         },
8801         _ => false,
8802     }
8805 #[repr(u8)]
8806 pub enum RegisterCustomPropertyResult {
8807     SuccessfullyRegistered,
8808     InvalidName,
8809     AlreadyRegistered,
8810     InvalidSyntax,
8811     NoInitialValue,
8812     InvalidInitialValue,
8813     InitialValueNotComputationallyIndependent,
8816 /// https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function
8817 #[no_mangle]
8818 pub extern "C" fn Servo_RegisterCustomProperty(
8819     per_doc_data: &PerDocumentStyleData,
8820     extra_data: *mut URLExtraData,
8821     name: &nsACString,
8822     syntax: &nsACString,
8823     inherits: bool,
8824     initial_value: Option<&nsACString>,
8825 ) -> RegisterCustomPropertyResult {
8826     use self::RegisterCustomPropertyResult::*;
8827     use style::custom_properties::SpecifiedValue;
8828     use style::properties_and_values::rule::{PropertyRegistrationError, PropertyRuleName};
8829     use style::properties_and_values::syntax::Descriptor;
8831     let mut per_doc_data = per_doc_data.borrow_mut();
8832     let url_data = unsafe { UrlExtraData::from_ptr_ref(&extra_data) };
8833     let name = unsafe { name.as_str_unchecked() };
8834     let syntax = unsafe { syntax.as_str_unchecked() };
8835     let initial_value = initial_value.map(|v| unsafe { v.as_str_unchecked() });
8837     // If name is not a custom property name string, throw a SyntaxError and exit this algorithm.
8838     let name = match style::custom_properties::parse_name(name) {
8839         Ok(n) => Atom::from(n),
8840         Err(()) => return InvalidName,
8841     };
8843     // If property set already contains an entry with name as its property name (compared
8844     // codepoint-wise), throw an InvalidModificationError and exit this algorithm.
8845     if per_doc_data
8846         .stylist
8847         .custom_property_script_registry()
8848         .get(&name)
8849         .is_some()
8850     {
8851         return AlreadyRegistered;
8852     }
8853     // Attempt to consume a syntax definition from syntax. If it returns failure, throw a
8854     // SyntaxError. Otherwise, let syntax definition be the returned syntax definition.
8855     let Ok(syntax) = Descriptor::from_str(syntax, /* preserve_specified = */ false) else {
8856         return InvalidSyntax;
8857     };
8859     let initial_value = match initial_value {
8860         Some(v) => {
8861             let mut input = ParserInput::new(v);
8862             let parsed = Parser::new(&mut input)
8863                 .parse_entirely(|input| {
8864                     input.skip_whitespace();
8865                     SpecifiedValue::parse(input, url_data).map(Arc::new)
8866                 })
8867                 .ok();
8868             if parsed.is_none() {
8869                 return InvalidInitialValue;
8870             }
8871             parsed
8872         },
8873         None => None,
8874     };
8876     if let Err(error) =
8877         PropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())
8878     {
8879         return match error {
8880             PropertyRegistrationError::InitialValueNotComputationallyIndependent => {
8881                 InitialValueNotComputationallyIndependent
8882             },
8883             PropertyRegistrationError::InvalidInitialValue => InvalidInitialValue,
8884             PropertyRegistrationError::NoInitialValue => NoInitialValue,
8885         };
8886     }
8888     per_doc_data
8889         .stylist
8890         .custom_property_script_registry_mut()
8891         .register(PropertyRegistration {
8892             name: PropertyRuleName(name),
8893             data: PropertyRegistrationData {
8894                 syntax,
8895                 inherits: if inherits {
8896                     PropertyInherits::True
8897                 } else {
8898                     PropertyInherits::False
8899                 },
8900                 initial_value,
8901             },
8902             url_data: url_data.clone(),
8903             source_location: SourceLocation { line: 0, column: 0 },
8904         });
8906     per_doc_data
8907         .stylist
8908         .rebuild_initial_values_for_custom_properties();
8910     SuccessfullyRegistered
8913 #[repr(C)]
8914 pub struct PropDef {
8915     // The name of the property.
8916     pub name: Atom,
8917     // The syntax of the property.
8918     pub syntax: nsCString,
8919     // Whether the property inherits.
8920     pub inherits: bool,
8921     pub has_initial_value: bool,
8922     pub initial_value: nsCString,
8923     // True if the property was set with CSS.registerProperty
8924     pub from_js: bool,
8927 impl PropDef {
8928     /// Creates a PropDef from a name and a PropertyRegistration.
8929     pub fn new(name: Atom, property_registration: &PropertyRegistration, from_js: bool) -> Self {
8930         let mut syntax = nsCString::new();
8931         if let Some(spec) = property_registration.data.syntax.specified_string() {
8932             syntax.assign(spec);
8933         } else {
8934             // FIXME: Descriptor::to_css should behave consistently (probably this shouldn't use
8935             // the ToCss trait).
8936             property_registration
8937                 .data
8938                 .syntax
8939                 .to_css(&mut CssWriter::new(&mut syntax))
8940                 .unwrap();
8941         };
8942         let initial_value = property_registration.data.initial_value.to_css_nscstring();
8943         PropDef {
8944             name,
8945             syntax,
8946             inherits: property_registration.data.inherits(),
8947             has_initial_value: property_registration.data.initial_value.is_some(),
8948             initial_value,
8949             from_js,
8950         }
8951     }
8954 #[no_mangle]
8955 pub extern "C" fn Servo_GetRegisteredCustomProperties(
8956     per_doc_data: &PerDocumentStyleData,
8957     custom_properties: &mut ThinVec<PropDef>,
8958 ) {
8959     let stylist = &per_doc_data.borrow().stylist;
8961     custom_properties.extend(
8962         stylist
8963             .custom_property_script_registry()
8964             .get_all()
8965             .iter()
8966             .map(|(name, property_registration)| {
8967                 PropDef::new(name.clone(), property_registration, /* from_js */ true)
8968             }),
8969     );
8971     for (cascade_data, _origin) in stylist.iter_origins() {
8972         custom_properties.extend(cascade_data.custom_property_registrations().iter().map(
8973             |(name, value)| {
8974                 let property_registration = &value.last().unwrap().0;
8975                 PropDef::new(
8976                     name.clone(),
8977                     property_registration,
8978                     /* from_js */
8979                     false,
8980                 )
8981             },
8982         ))
8983     }
8986 #[repr(C)]
8987 pub struct SelectorWarningData {
8988     /// Index to the selector generating the warning.
8989     pub index: usize,
8990     /// Kind of the warning.
8991     pub kind: SelectorWarningKind,
8994 #[no_mangle]
8995 pub extern "C" fn Servo_GetSelectorWarnings(
8996     rule: &LockedStyleRule,
8997     warnings: &mut ThinVec<SelectorWarningData>,
8998 ) {
8999     read_locked_arc(rule, |r| {
9000         for (i, selector) in r.selectors.slice().iter().enumerate() {
9001             for k in SelectorWarningKind::from_selector(selector) {
9002                 warnings.push(SelectorWarningData { index: i, kind: k });
9003             }
9004         }
9005     });
9008 #[no_mangle]
9009 pub extern "C" fn Servo_GetRuleBodyTextOffsets(
9010     initial_text: &nsACString,
9011     result_start_offset: &mut u32,
9012     result_end_offset: &mut u32,
9013 ) -> bool {
9014     let css_text = unsafe { initial_text.as_str_unchecked() };
9015     let mut input = ParserInput::new(&css_text);
9016     let mut input = Parser::new(&mut input);
9018     let mut start_offset = 0;
9019     let mut found_start = false;
9021     // Search forward for the opening brace.
9022     while let Ok(token) = input.next() {
9023         match *token {
9024             Token::CurlyBracketBlock => {
9025                 start_offset = input.position().byte_index();
9026                 found_start = true;
9027                 break;
9028             },
9029             _ => {}
9030         }
9032         if token.is_parse_error() {
9033             return false;
9034         }
9035     }
9038     if !found_start {
9039         return false;
9040     }
9042     let token_start = input.position();
9043     // Parse the nested block to move the parser to the end of the block
9044     let _ = input.parse_nested_block(
9045         |_i| -> Result<(), CssParseError<'_, BasicParseError>> {
9046             Ok(())
9047         }
9048     );
9049     let mut end_offset = input.position().byte_index();
9050     // We're not guaranteed to have a closing bracket, but when we do, we need to move
9051     // the end offset before it.
9052     let token_slice = input.slice_from(token_start);
9053     if token_slice.ends_with("}") {
9054         end_offset = end_offset - 1;
9055     }
9057     *result_start_offset = start_offset as u32;
9058     *result_end_offset = end_offset as u32;
9060     return true;