summaryrefslogtreecommitdiffstats
path: root/servo/ports
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /servo/ports
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/ports')
-rw-r--r--servo/ports/geckolib/Cargo.toml33
-rw-r--r--servo/ports/geckolib/cbindgen.toml1076
-rw-r--r--servo/ports/geckolib/error_reporter.rs514
-rw-r--r--servo/ports/geckolib/glue.rs8921
-rw-r--r--servo/ports/geckolib/lib.rs36
-rw-r--r--servo/ports/geckolib/stylesheet_loader.rs200
-rw-r--r--servo/ports/geckolib/tests/Cargo.toml14
-rw-r--r--servo/ports/geckolib/tests/lib.rs17
-rw-r--r--servo/ports/geckolib/tests/piecewise_linear.rs345
9 files changed, 11156 insertions, 0 deletions
diff --git a/servo/ports/geckolib/Cargo.toml b/servo/ports/geckolib/Cargo.toml
new file mode 100644
index 0000000000..ebf4178014
--- /dev/null
+++ b/servo/ports/geckolib/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "geckoservo"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+license = "MPL-2.0"
+
+[lib]
+name = "geckoservo"
+path = "lib.rs"
+
+[features]
+gecko_debug = ["style/gecko_debug", "nsstring/gecko_debug"]
+gecko_refcount_logging = ["style/gecko_refcount_logging", "servo_arc/gecko_refcount_logging"]
+
+[dependencies]
+atomic_refcell = "0.1"
+bincode = "1.0"
+cssparser = "0.33"
+cstr = "0.2"
+dom = { path = "../../../dom/base/rust" }
+gecko-profiler = { path = "../../../tools/profiler/rust-api" }
+libc = "0.2"
+log = {version = "0.4", features = ["release_max_level_info"]}
+malloc_size_of = {path = "../../components/malloc_size_of"}
+nsstring = {path = "../../../xpcom/rust/nsstring/"}
+num-traits = "0.2"
+selectors = {path = "../../components/selectors"}
+servo_arc = {path = "../../components/servo_arc"}
+smallvec = "1.0"
+style = {path = "../../components/style", features = ["gecko"]}
+style_traits = {path = "../../components/style_traits"}
+thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
+to_shmem = {path = "../../components/to_shmem"}
diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml
new file mode 100644
index 0000000000..b703dc6c92
--- /dev/null
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -0,0 +1,1076 @@
+header = """/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+// See the comment in ServoBindings.h about the same.
+#pragma GCC diagnostic push
+#ifdef __clang__
+# pragma GCC diagnostic ignored "-Wreturn-type-c-linkage"
+#endif
+// Work-around silly windows.h define.
+#pragma push_macro("STRICT")
+#undef STRICT
+#pragma push_macro("TRANSPARENT")
+#undef TRANSPARENT
+"""
+trailer = """
+#pragma pop_macro("STRICT")
+#pragma pop_macro("TRANSPARENT")
+#pragma GCC diagnostic pop
+#include "mozilla/ServoStyleConstsInlines.h"
+"""
+autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
+ * To generate this file:
+ * 1. Get the latest cbindgen using `cargo install --force cbindgen`
+ * a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release
+ * 2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate style -o layout/style/ServoStyleConsts.h`
+ */
+"""
+include_guard = "mozilla_ServoStyleConsts_h"
+include_version = true
+braces = "SameLine"
+line_length = 80
+tab_width = 2
+language = "C++"
+namespaces = ["mozilla"]
+includes = ["mozilla/ServoStyleConstsForwards.h", "mozilla/ServoStyleSet.h"]
+
+[parse]
+parse_deps = true
+extra_bindings = ["style"]
+include = ["style", "app_units", "cssparser", "style_traits", "servo_arc"]
+
+[struct]
+associated_constants_in_body = true
+derive_eq = true
+derive_neq = true
+
+[defines]
+# These will actually never be defined, but is handy to avoid cbindgen
+# generating Servo-only types.
+"feature = servo" = "CBINDGEN_IS_SERVO"
+"feature = servo-layout-2013" = "CBINDGEN_IS_SERVO"
+"feature = servo-layout-2020" = "CBINDGEN_IS_SERVO"
+# These will always be defined.
+"feature = gecko" = "CBINDGEN_IS_GECKO"
+"feature = cbindgen" = "CBINDGEN_IS_GECKO"
+# These depend on the build.
+"target_pointer_width = 32" = "SERVO_32_BITS"
+"target_pointer_width = 64" = "HAVE_64BIT_BUILD"
+"unix" = "XP_UNIX"
+"windows" = "XP_WIN"
+
+[macro_expansion]
+bitflags = true
+
+[enum]
+derive_helper_methods = true
+derive_const_casts = true
+derive_tagged_enum_destructor = true
+derive_tagged_enum_copy_constructor = true
+derive_tagged_enum_copy_assignment = true
+private_default_tagged_enum_constructor = true
+cast_assert_name = "MOZ_DIAGNOSTIC_ASSERT"
+
+[export]
+prefix = "Style"
+exclude = [
+ "NS_LogCtor",
+ "NS_LogDtor",
+ "SelectorList",
+ "AuthorStyles",
+]
+include = [
+ "AnimationTimeline",
+ "AnimationIterationCount",
+ "AnimationDirection",
+ "AnimationFillMode",
+ "AnimationPlayState",
+ "AnimationComposition",
+ "Appearance",
+ "Au",
+ "BreakBetween",
+ "BreakWithin",
+ "BorderStyle",
+ "BoolInteger",
+ "OutlineStyle",
+ "CaptionSide",
+ "FontSizeAdjust",
+ "FontPalette",
+ "ComputedFontStretchRange",
+ "ComputedFontStyleDescriptor",
+ "ComputedFontWeightRange",
+ "ComputedTimingFunction",
+ "ComputedValueFlags",
+ "CursorKind",
+ "Display",
+ "DisplayOutside",
+ "DisplayInside",
+ "DisplayMode",
+ "Platform",
+ "GtkThemeFamily",
+ "PrefersColorScheme",
+ "PrefersContrast",
+ "DynamicRange",
+ "Scripting",
+ "FillRule",
+ "FontDisplay",
+ "FontFaceSourceListComponent",
+ "FontFaceSourceTechFlags",
+ "FontLanguageOverride",
+ "FontSynthesis",
+ "GenericFontFamily",
+ "FontFamily",
+ "FontFamilyNameSyntax",
+ "MozTheme",
+ "OverflowWrap",
+ "OffsetPath",
+ "OffsetPathFunction",
+ "OffsetPosition",
+ "OffsetRotate",
+ "RayFunction",
+ "UnicodeRange",
+ "UserSelect",
+ "Float",
+ "Clear",
+ "OverscrollBehavior",
+ "ScrollSnapAlign",
+ "ScrollSnapAxis",
+ "ScrollSnapStop",
+ "ScrollSnapStrictness",
+ "ScrollSnapType",
+ "ScrollTimelineName",
+ "ViewTimelineInset",
+ "OverflowAnchor",
+ "OverflowClipBox",
+ "Resize",
+ "Overflow",
+ "LengthPercentage",
+ "LetterSpacing",
+ "NonNegativeLengthPercentage",
+ "LengthPercentageOrAuto",
+ "LineHeight",
+ "NonNegativeLengthPercentageOrAuto",
+ "NonNegativeLengthPercentageOrNormal",
+ "LengthOrAuto",
+ "NonNegativeLengthOrAuto",
+ "TextDecorationSkipInk",
+ "TextDecorationLength",
+ "Time",
+ "Rect",
+ "IntersectionObserverRootMargin",
+ "Size",
+ "MaxSize",
+ "FlexBasis",
+ "Position",
+ "PositionOrAuto",
+ "BackgroundSize",
+ "BaselineSource",
+ "BorderImageSlice",
+ "BorderSpacing",
+ "BorderRadius",
+ "ColorScheme",
+ "NonNegativeLengthOrNumberRect",
+ "Perspective",
+ "ZIndex",
+ "TransformBox",
+ "TransformOrigin",
+ "LineBreak",
+ "LineClamp",
+ "WordBreak",
+ "Contain",
+ "ContainerType",
+ "ContainerName",
+ "ContentVisibility",
+ "ContainIntrinsicSize",
+ "Origin",
+ "RestyleHint",
+ "TouchAction",
+ "WillChange",
+ "TextDecorationLine",
+ "MasonryAutoFlow",
+ "MasonryPlacement",
+ "MasonryItemOrder",
+ "TextUnderlinePosition",
+ "TextTransform",
+ "TextJustify",
+ "TextOverflow",
+ "TextIndent",
+ "MozControlCharacterVisibility",
+ "RubyPosition",
+ "Owned",
+ "OwnedOrNull",
+ "Strong",
+ "ScrollbarColor",
+ "CaretColor",
+ "Color",
+ "ColorOrAuto",
+ "SystemColor",
+ "SystemColorScheme",
+ "SystemFont",
+ "GradientItem",
+ "VerticalAlign",
+ "BasicShape",
+ "InsetRect",
+ "ShapeRadius",
+ "ArcSlice",
+ "ForgottenArcSlicePtr",
+ "HeaderWithLength",
+ "MozContextProperties",
+ "Quotes",
+ "BoxShadow",
+ "SimpleShadow",
+ "Transform",
+ "Rotate",
+ "Scale",
+ "Translate",
+ "BorderImageWidth",
+ "ComputedUrl",
+ "ComputedImageUrl",
+ "UrlOrNone",
+ "Filter",
+ "Gradient",
+ "GridTemplateAreas",
+ "GridLine",
+ "TrackSize",
+ "TrackBreadth",
+ "ImplicitGridTracks",
+ "SVGPaint",
+ "SVGPaintKind",
+ "GridTemplateComponent",
+ "TextEmphasisStyle",
+ "TextEmphasisPosition",
+ "FontVariantAlternates",
+ "PaintOrder",
+ "SVGPaintOrder",
+ "ClipRectOrAuto",
+ "CounterReset",
+ "CounterSet",
+ "CounterIncrement",
+ "WritingMode",
+ "Content",
+ "ContentItem",
+ "AlignSelf",
+ "JustifySelf",
+ "AlignItems",
+ "ComputedJustifyItems",
+ "AlignContent",
+ "JustifyContent",
+ "AlignTracks",
+ "JustifyTracks",
+ "TransformStyle",
+ "Image",
+ "ClipPath",
+ "ShapeOutside",
+ "GridAutoFlow",
+ "Cursor",
+ "SVGStrokeDashArray",
+ "SVGLength",
+ "SVGOpacity",
+ "SVGWidth",
+ "TextAlign",
+ "TextAlignLast",
+ "FontSizeKeyword",
+ "AspectRatio",
+ "DefaultFontSizes",
+ "RuleChangeKind",
+ "PageName",
+ "PageOrientation",
+ "PageSize",
+ "DProperty",
+ "ImageRendering",
+ "PrintColorAdjust",
+ "ForcedColorAdjust",
+ "ScrollbarGutter",
+ "ScrollDirection",
+ "HyphenateCharacter",
+ "ComputedLinearStop",
+ "PiecewiseLinearFunction",
+ "BeforeFlag",
+ "XTextScale",
+ "Zoom",
+ "TransitionProperty",
+]
+item_types = ["enums", "structs", "unions", "typedefs", "functions", "constants"]
+renaming_overrides_prefixing = true
+
+# Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
+[export.rename]
+"nscolor" = "nscolor"
+"nsAtom" = "nsAtom"
+"nsIURI" = "nsIURI"
+"imgRequestProxy" = "imgRequestProxy"
+"nsCompatibility" = "nsCompatibility"
+"nsSimpleContentList" = "nsSimpleContentList"
+"nsACString" = "nsACString"
+"nsAString" = "nsAString"
+"nsCString" = "nsCString"
+"nsString" = "nsString"
+"CopyableTArray" = "CopyableTArray"
+"nsTArray" = "nsTArray"
+"nsPresContext" = "nsPresContext"
+"ComputedTiming" = "ComputedTiming"
+"CountedUnknownProperty" = "CountedUnknownProperty"
+"RefPtr" = "RefPtr"
+"nsCSSPropertyID" = "nsCSSPropertyID"
+"nsCSSPropertyIDSet" = "nsCSSPropertyIDSet"
+"AnimatedPropertyID" = "AnimatedPropertyID"
+"nsCSSValueSharedList" = "nsCSSValueSharedList"
+"AnimationPropertySegment" = "AnimationPropertySegment"
+"RawServoAnimationValueTable" = "RawServoAnimationValueTable"
+"nsCSSUnit" = "nsCSSUnit"
+"InheritTarget" = "InheritTarget"
+"PseudoStyleType" = "PseudoStyleType"
+"DeclarationBlockMutationClosure" = "DeclarationBlockMutationClosure"
+"AtomArray" = "AtomArray"
+"UpdateAnimationsTasks" = "UpdateAnimationsTasks"
+"StyleRuleInclusion" = "StyleRuleInclusion"
+"nsFontFaceRuleContainer" = "nsFontFaceRuleContainer"
+"MediumFeaturesChangedResult" = "MediumFeaturesChangedResult"
+"FontSizePrefs" = "FontSizePrefs"
+"nsCSSFontDesc" = "nsCSSFontDesc"
+"nsresult" = "nsresult"
+"gfxFontFeature" = "gfxFontFeature"
+"gfxFontFeatureValueSet" = "gfxFontFeatureValueSet"
+"GeckoFontMetrics" = "GeckoFontMetrics"
+"SeenPtrs" = "SeenPtrs"
+"gfxFontVariation" = "gfxFontVariation"
+"URLExtraData" = "URLExtraData"
+"Keyframe" = "Keyframe"
+"nsChangeHint" = "nsChangeHint"
+"ServoElementSnapshotTable" = "ServoElementSnapshotTable"
+"Keyframe" = "Keyframe"
+"ComputedKeyframeValues" = "ComputedKeyframeValues"
+"OriginFlags" = "OriginFlags"
+"ServoTraversalFlags" = "ServoTraversalFlags"
+"ServoStyleSetSizes" = "ServoStyleSetSizes"
+"BeforeFlag" = "StyleEasingBeforeFlag"
+"FontPaletteValueSet" = "gfx::FontPaletteValueSet"
+"PaletteValues" = "gfx::FontPaletteValueSet::PaletteValues"
+"ThinVec" = "nsTArray"
+"RawPthread" = "pthread_t"
+"RawHandle" = "void*"
+
+[export.body]
+"CSSPixelLength" = """
+ static StyleCSSPixelLength FromPixels(CSSCoord aCoord) { return {aCoord}; }
+ static StyleCSSPixelLength Zero() { return FromPixels(0.0f); }
+
+ inline nscoord ToAppUnits() const;
+ inline bool IsZero() const;
+ CSSCoord ToCSSPixels() const { return _0; }
+ inline void ScaleBy(float);
+ inline StyleCSSPixelLength ScaledBy(float) const;
+"""
+
+"CalcLengthPercentage" = """
+ inline CSSCoord ResolveToCSSPixels(CSSCoord aBasis) const;
+
+ using CoordRounder = nscoord(*)(float);
+ nscoord Resolve(nscoord aBasis, CoordRounder) const;
+"""
+
+"GenericCalcNode" = """
+ void ScaleLengthsBy(float);
+"""
+
+"LengthPercentageUnion" = """
+ using Self = StyleLengthPercentageUnion;
+
+ // TODO(emilio): cbindgen should be able to generate these in the body of the
+ // union, but it seems it's only implemented for structs, not unions.
+ static const uint8_t TAG_CALC = StyleLengthPercentageUnion_TAG_CALC;
+ static const uint8_t TAG_LENGTH = StyleLengthPercentageUnion_TAG_LENGTH;
+ static const uint8_t TAG_PERCENTAGE = StyleLengthPercentageUnion_TAG_PERCENTAGE;
+ static const uint8_t TAG_MASK = StyleLengthPercentageUnion_TAG_MASK;
+
+ private:
+ uint8_t Tag() const {
+ return tag.tag & TAG_MASK;
+ }
+
+ public:
+ // We need to do all this manually because cbingen can't reason about unions.
+ inline StyleLengthPercentageUnion();
+ inline StyleLengthPercentageUnion(const Self&);
+ inline ~StyleLengthPercentageUnion();
+ inline Self& operator=(const Self&);
+
+ inline bool operator==(const Self& aOther) const;
+ inline bool operator!=(const Self& aOther) const;
+
+ inline bool IsLength() const;
+ inline bool IsPercentage() const;
+ inline bool IsCalc() const;
+
+ inline const StyleLength& AsLength() const;
+ inline StyleLength& AsLength();
+
+ inline const StylePercentage& AsPercentage() const;
+ inline StylePercentage& AsPercentage();
+
+ inline const StyleCalcLengthPercentage& AsCalc() const;
+ inline StyleCalcLengthPercentage& AsCalc();
+
+ static inline Self Zero();
+ static inline Self FromAppUnits(nscoord);
+ static inline Self FromPixels(CSSCoord);
+ static inline Self FromPercentage(float);
+
+ inline void ScaleLengthsBy(float);
+ inline bool HasPercent() const;
+ inline bool ConvertsToLength() const;
+ inline nscoord ToLength() const;
+ inline CSSCoord ToLengthInCSSPixels() const;
+ inline bool ConvertsToPercentage() const;
+ inline bool HasLengthAndPercentage() const;
+ inline float ToPercentage() const;
+ inline bool IsDefinitelyZero() const;
+ inline CSSCoord ResolveToCSSPixels(CSSCoord aPercentageBasisInCSSPixels) const;
+ template<typename T> inline CSSCoord ResolveToCSSPixelsWith(T aPercentageGetter) const;
+ template<typename T, typename U>
+ inline nscoord Resolve(T aPercentageGetter, U aRoundingFunction) const;
+ template<typename T>
+ inline nscoord Resolve(nscoord aPercentageBasis, T aRoundingFunction) const;
+ template<typename T> inline nscoord Resolve(T aPercentageGetter) const;
+ inline nscoord Resolve(nscoord aPercentageBasis) const;
+"""
+
+"GenericLengthPercentageOrAuto" = """
+ inline bool ConvertsToLength() const;
+ inline nscoord ToLength() const;
+ inline bool ConvertsToPercentage() const;
+ inline float ToPercentage() const;
+ inline bool HasPercent() const;
+ inline bool HasLengthAndPercentage() const;
+
+ // Just some convenient aliases for LengthOrAuto, to avoid confusing naming.
+ inline bool IsLength() const;
+ inline const StyleLength& AsLength() const;
+
+ static inline StyleGenericLengthPercentageOrAuto Zero();
+"""
+
+"GenericSize" = """
+ inline bool ConvertsToLength() const;
+ inline nscoord ToLength() const;
+ inline bool ConvertsToPercentage() const;
+ inline float ToPercentage() const;
+ inline bool HasPercent() const;
+ inline bool HasLengthAndPercentage() const;
+ inline bool BehavesLikeInitialValueOnBlockAxis() const;
+"""
+
+"GenericFlexBasis" = """
+ inline bool IsAuto() const;
+"""
+
+"GenericMaxSize" = """
+ inline bool ConvertsToLength() const;
+ inline nscoord ToLength() const;
+ inline bool ConvertsToPercentage() const;
+ inline float ToPercentage() const;
+ inline bool HasPercent() const;
+ inline bool HasLengthAndPercentage() const;
+ inline bool BehavesLikeInitialValueOnBlockAxis() const;
+"""
+
+"GenericPosition" = """
+ inline bool HasPercent() const;
+ inline bool DependsOnPositioningAreaSize() const;
+ static inline StyleGenericPosition FromPercentage(float);
+"""
+
+"GenericBackgroundSize" = """
+ bool IsInitialValue() const;
+"""
+
+"Rect" = """
+ template<typename Predicate> inline bool All(Predicate) const;
+ template<typename Predicate> inline bool Any(Predicate) const;
+
+ // Defined in WritingModes.h
+ inline const T& Get(mozilla::Side) const;
+ inline const T& Get(WritingMode, LogicalSide) const;
+ inline const T& GetIStart(WritingMode) const;
+ inline const T& GetBStart(WritingMode) const;
+ inline const T& Start(LogicalAxis, WritingMode) const;
+ inline const T& GetIEnd(WritingMode) const;
+ inline const T& GetBEnd(WritingMode) const;
+ inline const T& End(LogicalAxis, WritingMode) const;
+
+ inline T& Get(mozilla::Side);
+ inline T& Get(WritingMode, LogicalSide);
+ inline T& GetIStart(WritingMode);
+ inline T& GetBStart(WritingMode);
+ inline T& GetIEnd(WritingMode);
+ inline T& GetBEnd(WritingMode);
+
+ static StyleRect WithAllSides(const T& aSide) {
+ return {aSide, aSide, aSide, aSide};
+ }
+"""
+
+"GenericBorderRadius" = """
+ inline const StyleLengthPercentage& Get(HalfCorner) const;
+"""
+
+"RestyleHint" = """
+ static inline StyleRestyleHint RestyleSubtree();
+ static inline StyleRestyleHint RecascadeSubtree();
+ static inline StyleRestyleHint ForAnimations();
+ // Returns true if this change hint is guaranteed to at least recascade all
+ // elements in the subtree of the element it is applied to.
+ inline bool DefinitelyRecascadesAllSubtree() const;
+"""
+
+"TextTransform" = """
+ static inline StyleTextTransform None();
+ inline bool IsNone() const;
+"""
+
+"TextUnderlinePosition" = """
+ inline bool IsAuto() const;
+ inline bool IsFromFont() const;
+ inline bool IsUnder() const;
+ inline bool IsLeft() const;
+ inline bool IsRight() const;
+"""
+
+# TODO(emilio): Add hooks to cbindgen to be able to generate [[nodiscard]]
+# on the functions.
+"Owned" = """
+ UniquePtr<GeckoType> Consume() {
+ UniquePtr<GeckoType> ret(ptr);
+ ptr = nullptr;
+ return ret;
+ }
+"""
+
+"OwnedOrNull" = """
+ UniquePtr<GeckoType> Consume() {
+ UniquePtr<GeckoType> ret(ptr);
+ ptr = nullptr;
+ return ret;
+ }
+"""
+
+"Strong" = """
+ already_AddRefed<GeckoType> Consume() {
+ already_AddRefed<GeckoType> ret(const_cast<GeckoType*>(ptr));
+ ptr = nullptr;
+ return ret;
+ }
+"""
+
+"GenericColor" = """
+ static inline StyleGenericColor FromColor(nscolor);
+
+ static inline StyleGenericColor Transparent();
+ static inline StyleGenericColor Black();
+ static inline StyleGenericColor White();
+
+ bool MaybeTransparent() const;
+
+ /**
+ * Compute the final color, taking into account the foreground color.
+ **/
+ StyleAbsoluteColor ResolveColor(const StyleAbsoluteColor&) const;
+
+ /**
+ * Compute the final color, taking into account the foreground color from the
+ * frame's ComputedStyle.
+ */
+ nscolor CalcColor(const nsIFrame*) const;
+ /**
+ * Compute the final color, taking into account the foreground color from the
+ * style.
+ */
+ nscolor CalcColor(const ComputedStyle&) const;
+ /**
+ * Compute the final color, making the argument the foreground color.
+ */
+ nscolor CalcColor(nscolor) const;
+ nscolor CalcColor(const StyleAbsoluteColor&) const;
+"""
+
+"AbsoluteColor" = """
+ /**
+ * Create a new AbsoluteColor in the sRGB color space in legacy color syntax.
+ */
+ static inline StyleAbsoluteColor SrgbLegacy(float red, float green, float blue, float alpha);
+
+ static inline StyleAbsoluteColor FromColor(nscolor);
+
+ /**
+ * Convert this color into the given color space.
+ */
+ StyleAbsoluteColor ToColorSpace(StyleColorSpace aColorSpace) const;
+
+ /**
+ * Convert this color to an nscolor. The color will be converted to sRGB first
+ * if required.
+ */
+ nscolor ToColor() const;
+"""
+
+"OwnedSlice" = """
+ constexpr StyleOwnedSlice() :
+ ptr((T*)alignof(T)),
+ len(0) {}
+
+ inline void Clear();
+ inline void CopyFrom(const StyleOwnedSlice&);
+ inline void SwapElements(StyleOwnedSlice&);
+
+ StyleOwnedSlice& operator=(const StyleOwnedSlice&);
+ StyleOwnedSlice& operator=(StyleOwnedSlice&&);
+
+ inline StyleOwnedSlice(const StyleOwnedSlice&);
+ inline StyleOwnedSlice(StyleOwnedSlice&&);
+ inline explicit StyleOwnedSlice(Vector<T>&&);
+
+ inline ~StyleOwnedSlice();
+
+ Span<const T> AsSpan() const {
+ return {ptr, len};
+ }
+
+ size_t Length() const {
+ return len;
+ }
+
+ bool IsEmpty() const { return Length() == 0; }
+
+ bool operator==(const StyleOwnedSlice& other) const {
+ return AsSpan() == other.AsSpan();
+ }
+
+ bool operator!=(const StyleOwnedSlice& other) const {
+ return !(*this == other);
+ }
+"""
+
+"ArcInner" = """
+ // Increase the reference count.
+ inline void IncrementRef();
+ // Release the reference count, and return whether the result must be freed or not.
+ [[nodiscard]] inline bool DecrementRef();
+"""
+
+"HeaderSlice" = """
+ StyleHeaderSlice() = delete;
+ StyleHeaderSlice(const StyleHeaderSlice&) = delete;
+
+ inline ~StyleHeaderSlice();
+ inline bool operator==(const StyleHeaderSlice& other) const;
+ inline bool operator!=(const StyleHeaderSlice& other) const;
+
+ inline Span<const T> AsSpan() const;
+ inline size_t Length() const { return len; }
+ inline bool IsEmpty() const { return len == 0; }
+"""
+
+"ArcSlice" = """
+ inline StyleArcSlice();
+ inline explicit StyleArcSlice(const StyleForgottenArcSlicePtr<T>& aPtr);
+ inline Span<const T> AsSpan() const;
+ inline size_t Length() const;
+ inline bool IsEmpty() const;
+"""
+
+"Arc" = """
+ StyleArc() = delete;
+ inline StyleArc(const StyleArc& Other);
+ private:
+ inline void Release();
+ public:
+ explicit StyleArc(decltype(p) aP) : p(aP) {
+ MOZ_DIAGNOSTIC_ASSERT(p, "Arc shouldn't be null");
+ }
+ inline ~StyleArc();
+
+ inline StyleArc& operator=(const StyleArc&);
+ inline StyleArc& operator=(StyleArc&&);
+
+ const T* operator->() const {
+ MOZ_DIAGNOSTIC_ASSERT(p, "Arc shouldn't be null");
+ return &p->data;
+ }
+ const T& operator*() const {
+ MOZ_DIAGNOSTIC_ASSERT(p, "Arc shouldn't be null");
+ return p->data;
+ }
+ bool operator==(const StyleArc& other) const {
+ return p == other.p || *(*this) == *other;
+ }
+ bool operator!=(const StyleArc& other) const {
+ return !(*this == other);
+ }
+"""
+
+"CustomIdent" = """
+ inline nsAtom* AsAtom() const;
+"""
+
+"Atom" = """
+ StyleAtom(size_t) = delete;
+ StyleAtom() = delete;
+
+ inline bool IsStatic() const;
+ inline nsAtom* AsAtom() const;
+
+ private:
+ inline void AddRef();
+ inline void Release();
+
+ public:
+ inline explicit StyleAtom(already_AddRefed<nsAtom>);
+ inline explicit StyleAtom(nsStaticAtom*);
+ inline StyleAtom(const StyleAtom& aOther);
+ inline StyleAtom& operator=(const StyleAtom&);
+ inline ~StyleAtom();
+"""
+
+"OwnedStr" = """
+ inline nsDependentCSubstring AsString() const;
+"""
+
+"GenericTransformOrigin" = """
+ inline bool HasPercent() const;
+"""
+
+"GenericTransform" = """
+ inline Span<const T> Operations() const;
+ inline bool IsNone() const;
+ bool HasPercent() const;
+"""
+
+"Angle" = """
+ inline static StyleAngle Zero();
+ inline float ToDegrees() const;
+ inline double ToRadians() const;
+ StyleAngle operator+(const StyleAngle& aAngle) const {
+ return StyleAngle{_0 + aAngle._0};
+ }
+ StyleAngle operator-(const StyleAngle& aAngle) const {
+ return StyleAngle{_0 - aAngle._0};
+ }
+"""
+
+"CoordPair" = """
+ explicit StyleCoordPair(const gfx::Point& aPoint): x(aPoint.x), y(aPoint.y) {}
+ gfx::Point ConvertsToGfxPoint() const { return gfx::Point(x, y); }
+"""
+
+"TextOverflow" = """
+ StyleTextOverflow()
+ : first(StyleTextOverflowSide::Clip()),
+ second(StyleTextOverflowSide::Clip()),
+ sides_are_logical(true) {}
+"""
+
+"GenericTextIndent" = """
+ StyleGenericTextIndent()
+ : length(StyleLengthPercentage::Zero()),
+ hanging(false),
+ each_line(false) {}
+"""
+
+"UrlExtraData" = """
+ StyleUrlExtraData() = delete;
+
+ // Could be implemented if wanted.
+ StyleUrlExtraData(const StyleUrlExtraData&) = delete;
+ StyleUrlExtraData& operator=(const StyleUrlExtraData&) = delete;
+
+ inline bool IsShared() const;
+
+ inline ~StyleUrlExtraData();
+ inline const URLExtraData& get() const;
+"""
+
+"CssUrlData" = """
+ // Implemented in nsStyleStruct.cpp
+ bool operator==(const StyleCssUrlData& other) const;
+ bool operator!=(const StyleCssUrlData& other) const {
+ return !(*this == other);
+ }
+"""
+
+"LoadData" = """
+ ~StyleLoadData();
+"""
+
+"CssUrl" = """
+ inline nsDependentCSubstring SpecifiedSerialization() const;
+ inline const URLExtraData& ExtraData() const;
+ inline StyleLoadData& LoadData() const;
+ inline nsIURI* GetURI() const;
+"""
+
+"ComputedUrl" = """
+ // Forwarded from CssUrl.
+ inline nsDependentCSubstring SpecifiedSerialization() const;
+ inline const URLExtraData& ExtraData() const;
+ inline nsIURI* GetURI() const;
+ inline StyleLoadData& LoadData() const;
+
+ inline bool IsLocalRef() const;
+ inline bool HasRef() const;
+ inline StyleCorsMode CorsMode() const;
+ already_AddRefed<nsIURI> ResolveLocalRef(nsIURI* aBase) const;
+ already_AddRefed<nsIURI> ResolveLocalRef(const nsIContent* aContent) const;
+
+ // Only relevant for images.
+ inline bool IsImageResolved() const;
+ inline imgRequestProxy* GetImage() const;
+ void ResolveImage(dom::Document&, const StyleComputedUrl* aOldImage);
+"""
+
+"GenericGradient" = """
+ inline bool Repeating() const;
+ bool IsOpaque() const;
+
+ // Return the color interpolation method of the gradient.
+ inline const StyleColorInterpolationMethod& ColorInterpolationMethod() const;
+"""
+
+"GenericGridLine" = """
+ // Returns the `auto` value.
+ inline StyleGenericGridLine();
+ inline bool IsAuto() const;
+ // The line name, or nsGkAtoms::_empty if not present.
+ inline nsAtom* LineName() const;
+"""
+
+"GenericTrackBreadth" = """
+ inline bool HasPercent() const;
+"""
+
+"GenericTrackSize" = """
+ // Implemented in nsGridContainerFrame.cpp
+ inline const StyleGenericTrackBreadth<L>& GetMin() const;
+ inline const StyleGenericTrackBreadth<L>& GetMax() const;
+"""
+
+"GenericGridTemplateComponent" = """
+ inline Maybe<size_t> RepeatAutoIndex() const;
+ inline const StyleGenericTrackRepeat<L, I>* GetRepeatAutoValue() const;
+ inline bool HasRepeatAuto() const;
+ inline Span<const StyleOwnedSlice<StyleCustomIdent>> LineNameLists(bool aIsSubgrid) const;
+ inline Span<const StyleGenericTrackListValue<L, I>> TrackListValues() const;
+"""
+
+"GenericClipRect" = """
+ // Get the layout rect, replacing auto right / bottom values for aAutoSize.
+ inline nsRect ToLayoutRect(nscoord aAutoSize = NS_MAXSIZE) const;
+"""
+
+"TimingFunction" = """
+ public:
+ bool IsLinearKeyword() const { return IsKeyword() && AsKeyword() == StyleTimingKeyword::Linear; }
+ static StyleTimingFunction LinearKeyword() { return Keyword(StyleTimingKeyword::Linear); }
+
+ inline double At(double, bool aBeforeFlag) const;
+ inline void AppendToString(nsACString&) const;
+
+ inline static double GetPortion(const Maybe<StyleTimingFunction>&, double, bool aBeforeFlag);
+"""
+
+"GenericImage" = """
+ public:
+ // Returns the intrinsic resolution of the image.
+ //
+ // The resolution is in dppx, and should be used to impact the intrinsic
+ // size of the image.
+ ImageResolution GetResolution(const ComputedStyle&) const;
+
+ // If this is an image-set(), the final image we've selected, otherwise it
+ // returns *this.
+ const StyleGenericImage& FinalImage() const;
+
+ // Whether this image may have an image request associated with it.
+ bool IsImageRequestType() const;
+
+ // Gets the image request URL.
+ const StyleComputedImageUrl* GetImageRequestURLValue() const;
+
+ // Gets the image data of this image if it has any image request.
+ imgRequestProxy* GetImageRequest() const;
+
+ // Returns true if this image is fully loaded, and its size is calculated.
+ // Always returns true if there's no image request involved and this image
+ // is not `none`.
+ bool IsComplete() const;
+
+ // Returns true if this image has an available size and hasn't errored.
+ // Always returns true if there's no image request involved and this image
+ // is not `none`.
+ bool IsSizeAvailable() const;
+
+ // Returns true if the item is definitely opaque --- i.e., paints every
+ // pixel within its bounds opaquely, and the bounds contains at least a pixel.
+ bool IsOpaque() const;
+
+ // Resolves the underlying image request if any.
+ void ResolveImage(dom::Document&, const StyleGenericImage* aOld);
+
+ // Returns whether this image has been resolved.
+ bool IsResolved() const;
+"""
+
+"Ratio" = """
+ inline AspectRatio ToLayoutRatio(UseBoxSizing aUseBoxSizing) const;
+"""
+
+"GenericAspectRatio" = """
+ bool HasRatio() const { return ratio.IsRatio(); }
+ bool HasFiniteRatio() const { return static_cast<bool>(ToLayoutRatio()); }
+ bool BehavesAsAuto() const { return auto_ || !HasFiniteRatio(); }
+ inline AspectRatio ToLayoutRatio() const;
+
+ static StyleGenericAspectRatio Auto() {
+ return {true, StylePreferredRatio<N>::None()};
+ }
+"""
+
+"FontFamilyList" = """
+ static StyleFontFamilyList WithOneUnquotedFamily(const nsACString&);
+ // Constructs a font family list with a list of names.
+ static StyleFontFamilyList WithNames(nsTArray<StyleSingleFontFamily>&&);
+"""
+
+"SingleFontFamily" = """
+ static StyleSingleFontFamily Parse(const nsACString& aName);
+ void AppendToString(nsACString& aFamily, bool aIncludeQuotes = true) const;
+ bool IsNamedFamily(const nsAString&) const;
+"""
+
+"TimelineOrKeyframesName" = """
+ public:
+ inline nsAtom* AsAtom() const;
+"""
+
+"FontWeight" = """
+ SERVO_FIXED_POINT_HELPERS(StyleFontWeight, uint16_t, StyleFONT_WEIGHT_FRACTION_BITS);
+ bool IsNormal() const { return *this == NORMAL; }
+
+ inline bool IsBold() const;
+"""
+
+"FontStyle" = """
+ SERVO_FIXED_POINT_HELPERS(StyleFontStyle, int16_t, StyleFONT_STYLE_FRACTION_BITS);
+ bool IsNormal() const { return *this == NORMAL; }
+ inline bool IsItalic() const;
+ inline bool IsOblique() const;
+ inline float ObliqueAngle() const; // Only for use when IsOblique() is true
+ inline float SlantAngle() const; // Returns angle for any font-style, including
+ // normal/italic as well as explicit oblique
+"""
+
+"FontStretch" = """
+ SERVO_FIXED_POINT_HELPERS(StyleFontStretch, uint16_t, StyleFONT_STRETCH_FRACTION_BITS);
+ bool IsNormal() const { return *this == NORMAL; }
+"""
+
+"Zoom" = """
+ SERVO_FIXED_POINT_HELPERS(StyleZoom, uint16_t, StyleZOOM_FRACTION_BITS);
+ inline float Zoom(float) const;
+ inline float Unzoom(float) const;
+ inline nscoord ZoomCoord(nscoord) const;
+ inline nscoord UnzoomCoord(nscoord) const;
+ inline nsSize Zoom(const nsSize&) const;
+ inline nsSize Unzoom(const nsSize&) const;
+ inline nsPoint Zoom(const nsPoint&) const;
+ inline nsPoint Unzoom(const nsPoint&) const;
+ inline nsRect Zoom(const nsRect&) const;
+ inline nsRect Unzoom(const nsRect&) const;
+"""
+
+"AnimationName" = """
+ public:
+ StyleAnimationName() : _0(nsGkAtoms::_empty) {}
+"""
+
+"GenericViewTimelineInset" = """
+ public:
+ inline StyleGenericViewTimelineInset();
+"""
+
+"Time" = """
+ float ToSeconds() const { return seconds; }
+ float ToMilliseconds() const { return seconds * 1000.0f; }
+"""
+
+"FontFaceSourceTechFlags" = """
+ inline static StyleFontFaceSourceTechFlags Empty() {
+ return StyleFontFaceSourceTechFlags{0};
+ }
+"""
+
+"FontPalette" = """
+ inline static StyleFontPalette Normal() {
+ return StyleFontPalette{StyleAtom(nsGkAtoms::normal->ToAddRefed())};
+ }
+"""
+
+"GenericOffsetPath" = """
+ // Return true if the <offset-path> is ray().
+ bool IsRay() const {
+ return IsOffsetPath() && AsOffsetPath().path->IsRay();
+ }
+
+ const StyleRayFunction& AsRay() const {
+ return AsOffsetPath().path->AsRay();
+ }
+
+ // Return true if the <offset-path> is url().
+ bool IsUrl() const {
+ return IsOffsetPath() && AsOffsetPath().path->IsUrl();
+ }
+
+ const StyleComputedUrl& AsUrl() const {
+ return AsOffsetPath().path->AsUrl();
+ }
+
+ // Return true if the <basic-shape> is path().
+ bool IsPath() const {
+ return IsOffsetPath() && AsOffsetPath().path->IsShape() &&
+ AsOffsetPath().path->AsShape().IsPath();
+ }
+
+ const StyleSVGPathData& AsSVGPathData() const {
+ return AsOffsetPath().path->AsShape().AsPath().path;
+ }
+
+ // Return true if this is "<basic-shape> || <coord-box>".
+ bool IsBasicShapeOrCoordBox() const {
+ return IsCoordBox() || (IsOffsetPath() && AsOffsetPath().path->IsShape());
+ }
+"""
+
+"GenericContainIntrinsicSize" = """
+ bool HasAuto() const { return IsAutoLength() || IsAutoNone(); }
+"""
+
+"Display" = """
+ inline StyleDisplayOutside Outside() const;
+ inline StyleDisplayInside Inside() const;
+ inline bool IsListItem() const;
+ inline bool IsInlineFlow() const;
+ inline bool IsInlineInside() const;
+ inline bool IsInlineOutside() const;
+ inline bool IsBlockOutside() const;
+ inline bool IsRuby() const;
+ inline bool IsInternalRuby() const;
+ inline bool IsInternalTable() const;
+ inline bool IsInternalTableExceptCell() const;
+"""
+
+"TransitionProperty" = """
+ bool IsAll() const {
+ return IsNonCustom() &&
+ nsCSSPropertyID(AsNonCustom()._0) == eCSSProperty_all;
+ }
+"""
diff --git a/servo/ports/geckolib/error_reporter.rs b/servo/ports/geckolib/error_reporter.rs
new file mode 100644
index 0000000000..78facf3371
--- /dev/null
+++ b/servo/ports/geckolib/error_reporter.rs
@@ -0,0 +1,514 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Wrapper around Gecko's CSS error reporting mechanism.
+
+#![allow(unsafe_code)]
+
+use cssparser::{serialize_identifier, CowRcStr, ToCss};
+use cssparser::{BasicParseErrorKind, ParseError, ParseErrorKind, SourceLocation, Token};
+use selectors::parser::SelectorParseErrorKind;
+use selectors::SelectorList;
+use std::ffi::CStr;
+use std::ptr;
+use style::error_reporting::{ContextualParseError, ParseErrorReporter};
+use style::gecko_bindings::bindings;
+use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
+use style::gecko_bindings::structs::{nsIURI, Loader, StyleSheet as DomStyleSheet};
+use style::selector_parser::SelectorImpl;
+use style::stylesheets::UrlExtraData;
+use style_traits::{PropertySyntaxParseError, StyleParseErrorKind, ValueParseErrorKind};
+
+pub type ErrorKind<'i> = ParseErrorKind<'i, StyleParseErrorKind<'i>>;
+
+/// An error reporter with all the data we need to report errors.
+pub struct ErrorReporter {
+ window_id: u64,
+ uri: *mut nsIURI,
+}
+
+impl ErrorReporter {
+ /// Create a new instance of the Gecko error reporter, if error reporting is
+ /// enabled.
+ pub fn new(
+ sheet: *mut DomStyleSheet,
+ loader: *mut Loader,
+ extra_data: *mut RawUrlExtraData,
+ ) -> Option<Self> {
+ let mut window_id = 0;
+
+ let enabled =
+ unsafe { bindings::Gecko_ErrorReportingEnabled(sheet, loader, &mut window_id) };
+
+ if !enabled {
+ return None;
+ }
+
+ let uri = unsafe {
+ extra_data
+ .as_ref()
+ .map(|d| d.mBaseURI.raw())
+ .unwrap_or(ptr::null_mut())
+ };
+
+ Some(ErrorReporter { window_id, uri })
+ }
+}
+
+enum ErrorString<'a> {
+ Snippet(CowRcStr<'a>),
+ Ident(CowRcStr<'a>),
+ UnexpectedToken(Token<'a>),
+}
+
+impl<'a> ErrorString<'a> {
+ fn into_str(self) -> CowRcStr<'a> {
+ match self {
+ ErrorString::Snippet(s) => s,
+ ErrorString::UnexpectedToken(t) => t.to_css_string().into(),
+ ErrorString::Ident(i) => {
+ let mut s = String::new();
+ serialize_identifier(&i, &mut s).unwrap();
+ s.into()
+ },
+ }
+ }
+}
+
+#[derive(Debug)]
+enum Action {
+ Nothing,
+ Skip,
+ Drop,
+}
+
+trait ErrorHelpers<'a> {
+ fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>);
+ fn error_params(self) -> ErrorParams<'a>;
+ fn selectors(&self) -> &'a [SelectorList<SelectorImpl>];
+ fn to_gecko_message(&self) -> (Option<&'static CStr>, &'static CStr, Action);
+}
+
+fn extract_error_param<'a>(err: ErrorKind<'a>) -> Option<ErrorString<'a>> {
+ Some(match err {
+ ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
+ ErrorString::UnexpectedToken(t)
+ },
+
+ ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(i)) => {
+ let mut s = String::from("@");
+ serialize_identifier(&i, &mut s).unwrap();
+ ErrorString::Snippet(s.into())
+ },
+
+ ParseErrorKind::Custom(StyleParseErrorKind::OtherInvalidValue(property)) => {
+ ErrorString::Snippet(property)
+ },
+
+ ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(
+ SelectorParseErrorKind::UnexpectedIdent(ident),
+ )) => ErrorString::Ident(ident),
+
+ ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(property)) => {
+ ErrorString::Ident(property)
+ },
+
+ ParseErrorKind::Custom(StyleParseErrorKind::UnexpectedTokenWithinNamespace(token)) => {
+ ErrorString::UnexpectedToken(token)
+ },
+
+ _ => return None,
+ })
+}
+
+struct ErrorParams<'a> {
+ prefix_param: Option<ErrorString<'a>>,
+ main_param: Option<ErrorString<'a>>,
+}
+
+/// If an error parameter is present in the given error, return it. Additionally return
+/// a second parameter if it exists, for use in the prefix for the eventual error message.
+fn extract_error_params<'a>(err: ErrorKind<'a>) -> Option<ErrorParams<'a>> {
+ let (main, prefix) = match err {
+ ParseErrorKind::Custom(StyleParseErrorKind::InvalidColor(property, token)) |
+ ParseErrorKind::Custom(StyleParseErrorKind::InvalidFilter(property, token)) => (
+ Some(ErrorString::Snippet(property.into())),
+ Some(ErrorString::UnexpectedToken(token)),
+ ),
+
+ ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName(ident)) => {
+ (Some(ErrorString::Ident(ident)), None)
+ },
+
+ ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)) |
+ ParseErrorKind::Custom(StyleParseErrorKind::ValueError(
+ ValueParseErrorKind::InvalidColor(token),
+ )) => (Some(ErrorString::UnexpectedToken(token)), None),
+
+ ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err)) => match err {
+ SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t) |
+ SelectorParseErrorKind::BadValueInAttr(t) |
+ SelectorParseErrorKind::ExpectedBarInAttr(t) |
+ SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t) |
+ SelectorParseErrorKind::InvalidQualNameInAttr(t) |
+ SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t) |
+ SelectorParseErrorKind::PseudoElementExpectedIdent(t) |
+ SelectorParseErrorKind::NoIdentForPseudo(t) |
+ SelectorParseErrorKind::ClassNeedsIdent(t) |
+ SelectorParseErrorKind::PseudoElementExpectedColon(t) => {
+ (None, Some(ErrorString::UnexpectedToken(t)))
+ },
+ SelectorParseErrorKind::ExpectedNamespace(namespace) => {
+ (None, Some(ErrorString::Ident(namespace)))
+ },
+ SelectorParseErrorKind::UnsupportedPseudoClassOrElement(p) => {
+ (None, Some(ErrorString::Ident(p)))
+ },
+ SelectorParseErrorKind::EmptySelector | SelectorParseErrorKind::DanglingCombinator => {
+ (None, None)
+ },
+ err => match extract_error_param(ParseErrorKind::Custom(
+ StyleParseErrorKind::SelectorError(err),
+ )) {
+ Some(e) => (Some(e), None),
+ None => return None,
+ },
+ },
+ err => match extract_error_param(err) {
+ Some(e) => (Some(e), None),
+ None => return None,
+ },
+ };
+ Some(ErrorParams {
+ main_param: main,
+ prefix_param: prefix,
+ })
+}
+
+impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
+ fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>) {
+ match self {
+ ContextualParseError::UnsupportedPropertyDeclaration(s, err, _) |
+ ContextualParseError::UnsupportedPropertyDescriptor(s, err) |
+ ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
+ ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
+ ContextualParseError::UnsupportedFontPaletteValuesDescriptor(s, err) |
+ ContextualParseError::InvalidKeyframeRule(s, err) |
+ ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
+ ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
+ ContextualParseError::InvalidRule(s, err) |
+ ContextualParseError::UnsupportedRule(s, err) |
+ ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
+ ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) |
+ ContextualParseError::InvalidMediaRule(s, err) |
+ ContextualParseError::UnsupportedValue(s, err) => (s.into(), err.kind),
+ ContextualParseError::NeverMatchingHostSelector(s) |
+ ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
+ ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) => (
+ s.into(),
+ ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()),
+ ),
+ ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
+ ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
+ ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => (
+ "".into(),
+ ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()),
+ ),
+ }
+ }
+
+ fn error_params(self) -> ErrorParams<'a> {
+ let (s, error) = self.error_data();
+ extract_error_params(error).unwrap_or_else(|| ErrorParams {
+ main_param: Some(ErrorString::Snippet(s)),
+ prefix_param: None,
+ })
+ }
+
+ fn selectors(&self) -> &'a [SelectorList<SelectorImpl>] {
+ match *self {
+ ContextualParseError::UnsupportedPropertyDeclaration(_, _, selectors) => selectors,
+ _ => &[],
+ }
+ }
+
+ fn to_gecko_message(&self) -> (Option<&'static CStr>, &'static CStr, Action) {
+ let (msg, action): (&CStr, Action) = match *self {
+ ContextualParseError::UnsupportedPropertyDeclaration(
+ _,
+ ParseError {
+ kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(_)),
+ ..
+ },
+ _,
+ ) |
+ ContextualParseError::UnsupportedPropertyDeclaration(
+ _,
+ ParseError {
+ kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)),
+ ..
+ },
+ _,
+ ) => (cstr!("PEParseDeclarationDeclExpected"), Action::Skip),
+ ContextualParseError::UnsupportedPropertyDeclaration(
+ _,
+ ParseError {
+ kind: ParseErrorKind::Custom(ref err),
+ ..
+ },
+ _,
+ ) => match *err {
+ StyleParseErrorKind::InvalidColor(_, _) => {
+ return (
+ Some(cstr!("PEColorNotColor")),
+ cstr!("PEValueParsingError"),
+ Action::Drop,
+ );
+ },
+ StyleParseErrorKind::InvalidFilter(_, _) => {
+ return (
+ Some(cstr!("PEExpectedNoneOrURLOrFilterFunction")),
+ cstr!("PEValueParsingError"),
+ Action::Drop,
+ );
+ },
+ StyleParseErrorKind::OtherInvalidValue(_) => {
+ (cstr!("PEValueParsingError"), Action::Drop)
+ },
+ _ => (cstr!("PEUnknownProperty"), Action::Drop),
+ },
+ ContextualParseError::UnsupportedPropertyDeclaration(..) => {
+ (cstr!("PEUnknownProperty"), Action::Drop)
+ },
+ ContextualParseError::UnsupportedFontFaceDescriptor(..) => {
+ (cstr!("PEUnknownFontDesc"), Action::Skip)
+ },
+ ContextualParseError::InvalidKeyframeRule(..) => {
+ (cstr!("PEKeyframeBadName"), Action::Nothing)
+ },
+ ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) => {
+ (cstr!("PEBadSelectorKeyframeRuleIgnored"), Action::Nothing)
+ },
+ ContextualParseError::InvalidRule(
+ _,
+ ParseError {
+ kind:
+ ParseErrorKind::Custom(StyleParseErrorKind::UnexpectedTokenWithinNamespace(_)),
+ ..
+ },
+ ) => (cstr!("PEAtNSUnexpected"), Action::Nothing),
+ ContextualParseError::InvalidRule(
+ _,
+ ParseError {
+ kind: ParseErrorKind::Custom(StyleParseErrorKind::DisallowedImportRule),
+ ..
+ },
+ ) => (cstr!("PEDisallowedImportRule"), Action::Nothing),
+ ContextualParseError::InvalidRule(
+ _,
+ ParseError {
+ kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)),
+ ..
+ },
+ ) => (cstr!("PEUnknownAtRule"), Action::Nothing),
+ ContextualParseError::InvalidRule(_, ref err) => {
+ let prefix = match err.kind {
+ ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(ref err)) => {
+ match *err {
+ SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(_) => {
+ Some(cstr!("PEAttSelUnexpected"))
+ },
+ SelectorParseErrorKind::ExpectedBarInAttr(_) => {
+ Some(cstr!("PEAttSelNoBar"))
+ },
+ SelectorParseErrorKind::BadValueInAttr(_) => {
+ Some(cstr!("PEAttSelBadValue"))
+ },
+ SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(_) => {
+ Some(cstr!("PEAttributeNameOrNamespaceExpected"))
+ },
+ SelectorParseErrorKind::InvalidQualNameInAttr(_) => {
+ Some(cstr!("PEAttributeNameExpected"))
+ },
+ SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(_) => {
+ Some(cstr!("PETypeSelNotType"))
+ },
+ SelectorParseErrorKind::ExpectedNamespace(_) => {
+ Some(cstr!("PEUnknownNamespacePrefix"))
+ },
+ SelectorParseErrorKind::EmptySelector => {
+ Some(cstr!("PESelectorGroupNoSelector"))
+ },
+ SelectorParseErrorKind::DanglingCombinator => {
+ Some(cstr!("PESelectorGroupExtraCombinator"))
+ },
+ SelectorParseErrorKind::UnsupportedPseudoClassOrElement(_) => {
+ Some(cstr!("PEPseudoSelUnknown"))
+ },
+ SelectorParseErrorKind::PseudoElementExpectedColon(_) => {
+ Some(cstr!("PEPseudoSelEndOrUserActionPC"))
+ },
+ SelectorParseErrorKind::NoIdentForPseudo(_) => {
+ Some(cstr!("PEPseudoClassArgNotIdent"))
+ },
+ SelectorParseErrorKind::PseudoElementExpectedIdent(_) => {
+ Some(cstr!("PEPseudoSelBadName"))
+ },
+ SelectorParseErrorKind::ClassNeedsIdent(_) => {
+ Some(cstr!("PEClassSelNotIdent"))
+ },
+ _ => None,
+ }
+ },
+ _ => None,
+ };
+ return (prefix, cstr!("PEBadSelectorRSIgnored"), Action::Nothing);
+ },
+ ContextualParseError::InvalidMediaRule(_, ref err) => {
+ let err: &CStr = match err.kind {
+ ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName(
+ ..,
+ )) => cstr!("PEMQExpectedFeatureName"),
+ ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureValue) => {
+ cstr!("PEMQExpectedFeatureValue")
+ },
+ ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryUnexpectedOperator) => {
+ cstr!("PEMQUnexpectedOperator")
+ },
+ ParseErrorKind::Custom(StyleParseErrorKind::RangedExpressionWithNoValue) => {
+ cstr!("PEMQNoMinMaxWithoutValue")
+ },
+ _ => cstr!("PEMQUnexpectedToken"),
+ };
+ (err, Action::Nothing)
+ },
+ ContextualParseError::UnsupportedRule(..) => (cstr!("PEDeclDropped"), Action::Nothing),
+ ContextualParseError::NeverMatchingHostSelector(..) => {
+ (cstr!("PENeverMatchingHostSelector"), Action::Nothing)
+ },
+ ContextualParseError::UnsupportedViewportDescriptorDeclaration(..) |
+ ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(..) |
+ ContextualParseError::InvalidCounterStyleWithoutSymbols(..) |
+ ContextualParseError::InvalidCounterStyleNotEnoughSymbols(..) |
+ ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
+ ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
+ ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols |
+ ContextualParseError::UnsupportedPropertyDescriptor(..) |
+ ContextualParseError::UnsupportedFontFeatureValuesDescriptor(..) |
+ ContextualParseError::UnsupportedFontPaletteValuesDescriptor(..) |
+ ContextualParseError::InvalidFontFeatureValuesRule(..) => {
+ (cstr!("PEUnknownAtRule"), Action::Skip)
+ },
+ ContextualParseError::UnsupportedValue(_, ParseError { ref kind, .. }) => {
+ match *kind {
+ ParseErrorKind::Custom(StyleParseErrorKind::ValueError(
+ ValueParseErrorKind::InvalidColor(..),
+ )) => (cstr!("PEColorNotColor"), Action::Nothing),
+ ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(ref kind)) => {
+ let name = match kind {
+ PropertySyntaxParseError::EmptyInput => {
+ cstr!("PEPRSyntaxFieldEmptyInput")
+ },
+ PropertySyntaxParseError::ExpectedPipeBetweenComponents => {
+ cstr!("PEPRSyntaxFieldExpectedPipe")
+ },
+ PropertySyntaxParseError::InvalidNameStart => {
+ cstr!("PEPRSyntaxFieldInvalidNameStart")
+ },
+ PropertySyntaxParseError::InvalidName => {
+ cstr!("PEPRSyntaxFieldInvalidName")
+ },
+ PropertySyntaxParseError::UnclosedDataTypeName => {
+ cstr!("PEPRSyntaxFieldUnclosedDataTypeName")
+ },
+ PropertySyntaxParseError::UnexpectedEOF => {
+ cstr!("PEPRSyntaxFieldUnexpectedEOF")
+ },
+ PropertySyntaxParseError::UnknownDataTypeName => {
+ cstr!("PEPRSyntaxFieldUnknownDataTypeName")
+ },
+ };
+ (name, Action::Nothing)
+ },
+ _ => {
+ // Not the best error message, since we weren't parsing
+ // a declaration, just a value. But we don't produce
+ // UnsupportedValue errors other than InvalidColors
+ // currently.
+ debug_assert!(false, "should use a more specific error message");
+ (cstr!("PEDeclDropped"), Action::Nothing)
+ },
+ }
+ },
+ };
+ (None, msg, action)
+ }
+}
+
+impl ErrorReporter {
+ pub fn report(&self, location: SourceLocation, error: ContextualParseError) {
+ let (pre, name, action) = error.to_gecko_message();
+ let suffix = match action {
+ Action::Nothing => ptr::null(),
+ Action::Skip => cstr!("PEDeclSkipped").as_ptr(),
+ Action::Drop => cstr!("PEDeclDropped").as_ptr(),
+ };
+ let selectors = error.selectors();
+ let desugared_selector_list = match selectors.len() {
+ 0 => None,
+ 1 => Some(selectors[0].to_css_string()),
+ _ => {
+ let mut desugared = selectors.last().unwrap().clone();
+ for parent in selectors.iter().rev().skip(1) {
+ desugared = desugared.replace_parent_selector(&parent);
+ }
+ Some(desugared.to_css_string())
+ },
+ };
+ let selector_list_ptr = desugared_selector_list
+ .as_ref()
+ .map_or(ptr::null(), |s| s.as_ptr()) as *const _;
+ let params = error.error_params();
+ let param = params.main_param;
+ let pre_param = params.prefix_param;
+ let param = param.map(|p| p.into_str());
+ let pre_param = pre_param.map(|p| p.into_str());
+ let param_ptr = param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
+ let pre_param_ptr = pre_param.as_ref().map_or(ptr::null(), |p| p.as_ptr());
+ // The CSS source text is unused and will be removed in bug 1381188.
+ let source = "";
+ unsafe {
+ bindings::Gecko_ReportUnexpectedCSSError(
+ self.window_id,
+ self.uri,
+ name.as_ptr() as *const _,
+ param_ptr as *const _,
+ param.as_ref().map_or(0, |p| p.len()) as u32,
+ pre.map_or(ptr::null(), |p| p.as_ptr()) as *const _,
+ pre_param_ptr as *const _,
+ pre_param.as_ref().map_or(0, |p| p.len()) as u32,
+ suffix as *const _,
+ source.as_ptr() as *const _,
+ source.len() as u32,
+ selector_list_ptr,
+ desugared_selector_list
+ .as_ref()
+ .map_or(0, |string| string.len()) as u32,
+ location.line,
+ location.column,
+ );
+ }
+ }
+}
+
+impl ParseErrorReporter for ErrorReporter {
+ fn report_error(
+ &self,
+ _url: &UrlExtraData,
+ location: SourceLocation,
+ error: ContextualParseError,
+ ) {
+ self.report(location, error)
+ }
+}
diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs
new file mode 100644
index 0000000000..a6c4ef1262
--- /dev/null
+++ b/servo/ports/geckolib/glue.rs
@@ -0,0 +1,8921 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use super::error_reporter::ErrorReporter;
+use super::stylesheet_loader::{AsyncStylesheetParser, StylesheetLoader};
+use bincode::{deserialize, serialize};
+use cssparser::ToCss as ParserToCss;
+use cssparser::{Parser, ParserInput, SourceLocation, UnicodeRange};
+use dom::{DocumentState, ElementState};
+use malloc_size_of::MallocSizeOfOps;
+use nsstring::{nsCString, nsString};
+use selectors::matching::{ElementSelectorFlags, MatchingForInvalidation, SelectorCaches};
+use selectors::{Element, OpaqueElement};
+use servo_arc::{Arc, ArcBorrow};
+use smallvec::SmallVec;
+use style::custom_properties::DeferFontRelativeCustomPropertyResolution;
+use std::collections::BTreeSet;
+use std::fmt::Write;
+use std::iter;
+use std::os::raw::c_void;
+use std::ptr;
+use style::color::mix::ColorInterpolationMethod;
+use style::color::{AbsoluteColor, ColorSpace};
+use style::computed_value_flags::ComputedValueFlags;
+use style::context::ThreadLocalStyleContext;
+use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
+use style::counter_style;
+use style::data::{self, ElementStyles};
+use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
+use style::driver;
+use style::error_reporting::{ParseErrorReporter, SelectorWarningKind};
+use style::font_face::{self, FontFaceSourceFormat, FontFaceSourceListComponent, Source};
+use style::gecko::arc_types::{
+ LockedCounterStyleRule, LockedCssRules, LockedDeclarationBlock, LockedFontFaceRule,
+ LockedImportRule, LockedKeyframe, LockedKeyframesRule, LockedMediaList, LockedPageRule,
+ LockedStyleRule,
+};
+use style::gecko::data::{
+ AuthorStyles, GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl,
+};
+use style::gecko::restyle_damage::GeckoRestyleDamage;
+use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
+use style::gecko::snapshot_helpers::classes_changed;
+use style::gecko::traversal::RecalcStyleOnly;
+use style::gecko::url;
+use style::gecko::wrapper::{
+ slow_selector_flags_from_node_selector_flags, GeckoElement, GeckoNode,
+};
+use style::gecko_bindings::bindings;
+use style::gecko_bindings::bindings::nsACString;
+use style::gecko_bindings::bindings::nsAString;
+use style::gecko_bindings::bindings::Gecko_AddPropertyToSet;
+use style::gecko_bindings::bindings::Gecko_AppendPropertyValuePair;
+use style::gecko_bindings::bindings::Gecko_ConstructFontFeatureValueSet;
+use style::gecko_bindings::bindings::Gecko_ConstructFontPaletteValueSet;
+use style::gecko_bindings::bindings::Gecko_GetOrCreateFinalKeyframe;
+use style::gecko_bindings::bindings::Gecko_GetOrCreateInitialKeyframe;
+use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
+use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
+use style::gecko_bindings::structs;
+use style::gecko_bindings::structs::gfx::FontPaletteValueSet;
+use style::gecko_bindings::structs::gfxFontFeatureValueSet;
+use style::gecko_bindings::structs::ipc::ByteBuf;
+use style::gecko_bindings::structs::nsAtom;
+use style::gecko_bindings::structs::nsCSSCounterDesc;
+use style::gecko_bindings::structs::nsCSSFontDesc;
+use style::gecko_bindings::structs::nsCSSPropertyID;
+use style::gecko_bindings::structs::nsChangeHint;
+use style::gecko_bindings::structs::nsCompatibility;
+use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
+use style::gecko_bindings::structs::nsTArray;
+use style::gecko_bindings::structs::nsresult;
+use style::gecko_bindings::structs::CallerType;
+use style::gecko_bindings::structs::CompositeOperation;
+use style::gecko_bindings::structs::DeclarationBlockMutationClosure;
+use style::gecko_bindings::structs::GeckoFontMetrics;
+use style::gecko_bindings::structs::IterationCompositeOperation;
+use style::gecko_bindings::structs::Loader;
+use style::gecko_bindings::structs::LoaderReusableStyleSheets;
+use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
+use style::gecko_bindings::structs::OriginFlags;
+use style::gecko_bindings::structs::PropertyValuePair;
+use style::gecko_bindings::structs::PseudoStyleType;
+use style::gecko_bindings::structs::SeenPtrs;
+use style::gecko_bindings::structs::ServoElementSnapshotTable;
+use style::gecko_bindings::structs::ServoStyleSetSizes;
+use style::gecko_bindings::structs::ServoTraversalFlags;
+use style::gecko_bindings::structs::SheetLoadData;
+use style::gecko_bindings::structs::SheetLoadDataHolder;
+use style::gecko_bindings::structs::SheetParsingMode;
+use style::gecko_bindings::structs::StyleRuleInclusion;
+use style::gecko_bindings::structs::StyleSheet as DomStyleSheet;
+use style::gecko_bindings::structs::URLExtraData;
+use style::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};
+use style::gecko_bindings::sugar::ownership::Strong;
+use style::gecko_bindings::sugar::refptr::RefPtr;
+use style::global_style_data::{
+ GlobalStyleData, PlatformThreadHandle, StyleThreadPool, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL,
+};
+use style::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
+use style::invalidation::element::invalidation_map::{
+ RelativeSelectorInvalidationMap, TSStateForInvalidation,
+};
+use style::invalidation::element::invalidator::{InvalidationResult, SiblingTraversalMap};
+use style::invalidation::element::relative_selector::{
+ DomMutationOperation, RelativeSelectorDependencyCollector, RelativeSelectorInvalidator,
+};
+use style::invalidation::element::restyle_hints::RestyleHint;
+use style::invalidation::stylesheets::RuleChangeKind;
+use style::media_queries::MediaList;
+use style::parser::{Parse, ParserContext};
+#[cfg(feature = "gecko_debug")]
+use style::properties::LonghandIdSet;
+use style::properties::{
+ animated_properties::{AnimationValue, AnimationValueMap},
+ parse_one_declaration_into, parse_style_attribute, ComputedValues, CountedUnknownProperty,
+ Importance, LonghandId, NonCustomPropertyId, OwnedPropertyDeclarationId,
+ PropertyDeclarationBlock, PropertyDeclarationId, PropertyDeclarationIdSet, PropertyId,
+ ShorthandId, SourcePropertyDeclaration, StyleBuilder,
+};
+use style::properties_and_values::registry::{PropertyRegistration, PropertyRegistrationData};
+use style::properties_and_values::rule::Inherits as PropertyInherits;
+use style::rule_cache::RuleCacheConditions;
+use style::rule_tree::StrongRuleNode;
+use style::selector_parser::PseudoElementCascadeType;
+use style::shared_lock::{
+ Locked, SharedRwLock, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard,
+};
+use style::string_cache::{Atom, WeakAtom};
+use style::style_adjuster::StyleAdjuster;
+use style::stylesheets::container_rule::ContainerSizeQuery;
+use style::stylesheets::import_rule::{ImportLayer, ImportSheet};
+use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
+use style::stylesheets::supports_rule::parse_condition_or_declaration;
+use style::stylesheets::{
+ AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleType, CssRuleTypes,
+ CssRules, CssRulesHelpers, DocumentRule, FontFaceRule, FontFeatureValuesRule,
+ FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
+ MediaRule, NamespaceRule, Origin, OriginSet, PagePseudoClassFlags, PageRule, PropertyRule,
+ SanitizationData, SanitizationKind, StyleRule, StylesheetContents,
+ StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData,
+};
+use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
+use style::thread_state;
+use style::traversal::resolve_style;
+use style::traversal::DomTraversal;
+use style::traversal_flags::{self, TraversalFlags};
+use style::use_counters::UseCounters;
+use style::values::animated::{Animate, Procedure, ToAnimatedZero};
+use style::values::computed::easing::ComputedTimingFunction;
+use style::values::computed::effects::Filter;
+use style::values::computed::font::{
+ FamilyName, FontFamily, FontFamilyList, FontStretch, FontStyle, FontWeight, GenericFontFamily,
+};
+use style::values::computed::{self, Context, ToComputedValue};
+use style::values::distance::ComputeSquaredDistance;
+use style::values::generics::color::ColorMixFlags;
+use style::values::generics::easing::BeforeFlag;
+use style::values::specified::gecko::IntersectionObserverRootMargin;
+use style::values::specified::source_size_list::SourceSizeList;
+use style::values::specified::{AbsoluteLength, NoCalcLength};
+use style::values::{specified, AtomIdent, CustomIdent, KeyframesName};
+use style_traits::{CssWriter, ParseError, ParsingMode, ToCss};
+use thin_vec::ThinVec;
+use to_shmem::SharedMemoryBuilder;
+
+trait ClosureHelper {
+ fn invoke(&self, property_id: Option<NonCustomPropertyId>);
+}
+
+impl ClosureHelper for DeclarationBlockMutationClosure {
+ #[inline]
+ fn invoke(&self, property_id: Option<NonCustomPropertyId>) {
+ if let Some(function) = self.function.as_ref() {
+ let gecko_prop_id = match property_id {
+ Some(p) => p.to_nscsspropertyid(),
+ None => nsCSSPropertyID::eCSSPropertyExtra_variable,
+ };
+ unsafe { function(self.data, gecko_prop_id) }
+ }
+ }
+}
+
+/*
+ * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
+ * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
+ * those signatures as well, giving us a second declaration of all the Servo_* functions in this
+ * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
+ * depend on but good enough for our purposes.
+ */
+
+// A dummy url data for where we don't pass url data in.
+static mut DUMMY_URL_DATA: *mut URLExtraData = 0 as *mut _;
+static mut DUMMY_CHROME_URL_DATA: *mut URLExtraData = 0 as *mut _;
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Initialize(
+ dummy_url_data: *mut URLExtraData,
+ dummy_chrome_url_data: *mut URLExtraData,
+) {
+ use style::gecko_bindings::sugar::origin_flags;
+
+ // Pretend that we're a Servo Layout thread, to make some assertions happy.
+ thread_state::initialize(thread_state::ThreadState::LAYOUT);
+
+ // Perform some debug-only runtime assertions.
+ origin_flags::assert_flags_match();
+ traversal_flags::assert_traversal_flags_match();
+ specified::font::assert_variant_east_asian_matches();
+ specified::font::assert_variant_ligatures_matches();
+
+ DUMMY_URL_DATA = dummy_url_data;
+ DUMMY_CHROME_URL_DATA = dummy_chrome_url_data;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Shutdown() {
+ DUMMY_URL_DATA = ptr::null_mut();
+ DUMMY_CHROME_URL_DATA = ptr::null_mut();
+ Stylist::shutdown();
+ url::shutdown();
+}
+
+#[inline(always)]
+unsafe fn dummy_url_data() -> &'static UrlExtraData {
+ UrlExtraData::from_ptr_ref(&DUMMY_URL_DATA)
+}
+
+#[allow(dead_code)]
+fn is_main_thread() -> bool {
+ unsafe { bindings::Gecko_IsMainThread() }
+}
+
+#[allow(dead_code)]
+fn is_dom_worker_thread() -> bool {
+ unsafe { bindings::Gecko_IsDOMWorkerThread() }
+}
+
+thread_local! {
+ /// Thread-local style data for DOM workers
+ static DOM_WORKER_RWLOCK: SharedRwLock = SharedRwLock::new();
+}
+
+#[allow(dead_code)]
+fn is_in_servo_traversal() -> bool {
+ unsafe { bindings::Gecko_IsInServoTraversal() }
+}
+
+fn create_shared_context<'a>(
+ global_style_data: &GlobalStyleData,
+ guard: &'a SharedRwLockReadGuard,
+ stylist: &'a Stylist,
+ traversal_flags: TraversalFlags,
+ snapshot_map: &'a ServoElementSnapshotTable,
+) -> SharedStyleContext<'a> {
+ SharedStyleContext {
+ stylist: &stylist,
+ visited_styles_enabled: stylist.device().visited_styles_enabled(),
+ options: global_style_data.options.clone(),
+ guards: StylesheetGuards::same(guard),
+ current_time_for_animations: 0.0, // Unused for Gecko, at least for now.
+ traversal_flags,
+ snapshot_map,
+ }
+}
+
+fn traverse_subtree(
+ element: GeckoElement,
+ global_style_data: &GlobalStyleData,
+ per_doc_data: &PerDocumentStyleDataImpl,
+ guard: &SharedRwLockReadGuard,
+ traversal_flags: TraversalFlags,
+ snapshots: &ServoElementSnapshotTable,
+) {
+ let shared_style_context = create_shared_context(
+ &global_style_data,
+ &guard,
+ &per_doc_data.stylist,
+ traversal_flags,
+ snapshots,
+ );
+
+ let token = RecalcStyleOnly::pre_traverse(element, &shared_style_context);
+
+ if !token.should_traverse() {
+ return;
+ }
+
+ debug!("Traversing subtree from {:?}", element);
+
+ let thread_pool_holder = &*STYLE_THREAD_POOL;
+ let pool;
+ let thread_pool = if traversal_flags.contains(TraversalFlags::ParallelTraversal) {
+ pool = thread_pool_holder.pool();
+ pool.as_ref()
+ } else {
+ None
+ };
+
+ let traversal = RecalcStyleOnly::new(shared_style_context);
+ driver::traverse_dom(&traversal, token, thread_pool);
+}
+
+/// Traverses the subtree rooted at `root` for restyling.
+///
+/// Returns whether the root was restyled. Whether anything else was restyled or
+/// not can be inferred from the dirty bits in the rest of the tree.
+#[no_mangle]
+pub extern "C" fn Servo_TraverseSubtree(
+ root: &RawGeckoElement,
+ raw_data: &PerDocumentStyleData,
+ snapshots: *const ServoElementSnapshotTable,
+ raw_flags: ServoTraversalFlags,
+) -> bool {
+ let traversal_flags = TraversalFlags::from_bits_retain(raw_flags);
+ debug_assert!(!snapshots.is_null());
+
+ let element = GeckoElement(root);
+
+ debug!("Servo_TraverseSubtree (flags={:?})", traversal_flags);
+ debug!("{:?}", ShowSubtreeData(element.as_node()));
+
+ if cfg!(debug_assertions) {
+ if let Some(parent) = element.traversal_parent() {
+ let data = parent
+ .borrow_data()
+ .expect("Styling element with unstyled parent");
+ assert!(
+ !data.styles.is_display_none(),
+ "Styling element with display: none parent"
+ );
+ }
+ }
+
+ let needs_animation_only_restyle =
+ element.has_animation_only_dirty_descendants() || element.has_animation_restyle_hints();
+
+ let per_doc_data = raw_data.borrow();
+ debug_assert!(!per_doc_data.stylist.stylesheets_have_changed());
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+
+ let was_initial_style = !element.has_data();
+
+ if needs_animation_only_restyle {
+ debug!(
+ "Servo_TraverseSubtree doing animation-only restyle (aodd={})",
+ element.has_animation_only_dirty_descendants()
+ );
+ traverse_subtree(
+ element,
+ &global_style_data,
+ &per_doc_data,
+ &guard,
+ traversal_flags | TraversalFlags::AnimationOnly,
+ unsafe { &*snapshots },
+ );
+ }
+
+ traverse_subtree(
+ element,
+ &global_style_data,
+ &per_doc_data,
+ &guard,
+ traversal_flags,
+ unsafe { &*snapshots },
+ );
+
+ debug!(
+ "Servo_TraverseSubtree complete (dd={}, aodd={}, lfcd={}, lfc={}, data={:?})",
+ element.has_dirty_descendants(),
+ element.has_animation_only_dirty_descendants(),
+ element.descendants_need_frames(),
+ element.needs_frame(),
+ element.borrow_data().unwrap()
+ );
+
+ if was_initial_style {
+ debug_assert!(!element.borrow_data().unwrap().contains_restyle_data());
+ false
+ } else {
+ let element_was_restyled = element.borrow_data().unwrap().contains_restyle_data();
+ element_was_restyled
+ }
+}
+
+/// Checks whether the rule tree has crossed its threshold for unused nodes, and
+/// if so, frees them.
+#[no_mangle]
+pub extern "C" fn Servo_MaybeGCRuleTree(raw_data: &PerDocumentStyleData) {
+ let per_doc_data = raw_data.borrow_mut();
+ per_doc_data.stylist.rule_tree().maybe_gc();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_Interpolate(
+ from: &AnimationValue,
+ to: &AnimationValue,
+ progress: f64,
+) -> Strong<AnimationValue> {
+ if let Ok(value) = from.animate(to, Procedure::Interpolate { progress }) {
+ Arc::new(value).into()
+ } else {
+ Strong::null()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_IsInterpolable(
+ from: &AnimationValue,
+ to: &AnimationValue,
+) -> bool {
+ from.interpolable_with(to)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_Add(
+ a: &AnimationValue,
+ b: &AnimationValue,
+) -> Strong<AnimationValue> {
+ if let Ok(value) = a.animate(b, Procedure::Add) {
+ Arc::new(value).into()
+ } else {
+ Strong::null()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_Accumulate(
+ a: &AnimationValue,
+ b: &AnimationValue,
+ count: u64,
+) -> Strong<AnimationValue> {
+ if let Ok(value) = a.animate(b, Procedure::Accumulate { count }) {
+ Arc::new(value).into()
+ } else {
+ Strong::null()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_GetZeroValue(
+ value_to_match: &AnimationValue,
+) -> Strong<AnimationValue> {
+ if let Ok(zero_value) = value_to_match.to_animated_zero() {
+ Arc::new(zero_value).into()
+ } else {
+ Strong::null()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValues_ComputeDistance(
+ from: &AnimationValue,
+ to: &AnimationValue,
+) -> f64 {
+ // If compute_squared_distance() failed, this function will return negative value
+ // in order to check whether we support the specified paced animation values.
+ from.compute_squared_distance(to).map_or(-1.0, |d| d.sqrt())
+}
+
+/// Compute one of the endpoints for the interpolation interval, compositing it with the
+/// underlying value if needed.
+/// An None returned value means, "Just use endpoint_value as-is."
+/// It is the responsibility of the caller to ensure that |underlying_value| is provided
+/// when it will be used.
+fn composite_endpoint(
+ endpoint_value: Option<&AnimationValue>,
+ composite: CompositeOperation,
+ underlying_value: Option<&AnimationValue>,
+) -> Option<AnimationValue> {
+ match endpoint_value {
+ Some(endpoint_value) => match composite {
+ CompositeOperation::Add => underlying_value
+ .expect("We should have an underlying_value")
+ .animate(endpoint_value, Procedure::Add)
+ .ok(),
+ CompositeOperation::Accumulate => underlying_value
+ .expect("We should have an underlying value")
+ .animate(endpoint_value, Procedure::Accumulate { count: 1 })
+ .ok(),
+ _ => None,
+ },
+ None => underlying_value.map(|v| v.clone()),
+ }
+}
+
+/// Accumulate one of the endpoints of the animation interval.
+/// A returned value of None means, "Just use endpoint_value as-is."
+fn accumulate_endpoint(
+ endpoint_value: Option<&AnimationValue>,
+ composited_value: Option<AnimationValue>,
+ last_value: &AnimationValue,
+ current_iteration: u64,
+) -> Option<AnimationValue> {
+ debug_assert!(
+ endpoint_value.is_some() || composited_value.is_some(),
+ "Should have a suitable value to use"
+ );
+
+ let count = current_iteration;
+ match composited_value {
+ Some(endpoint) => last_value
+ .animate(&endpoint, Procedure::Accumulate { count })
+ .ok()
+ .or(Some(endpoint)),
+ None => last_value
+ .animate(endpoint_value.unwrap(), Procedure::Accumulate { count })
+ .ok(),
+ }
+}
+
+/// Compose the animation segment. We composite it with the underlying_value and last_value if
+/// needed.
+/// The caller is responsible for providing an underlying value and last value
+/// in all situations where there are needed.
+fn compose_animation_segment(
+ segment: &structs::AnimationPropertySegment,
+ underlying_value: Option<&AnimationValue>,
+ last_value: Option<&AnimationValue>,
+ iteration_composite: IterationCompositeOperation,
+ current_iteration: u64,
+ total_progress: f64,
+ segment_progress: f64,
+) -> AnimationValue {
+ // Extract keyframe values.
+ let keyframe_from_value = unsafe { segment.mFromValue.mServo.mRawPtr.as_ref() };
+ let keyframe_to_value = unsafe { segment.mToValue.mServo.mRawPtr.as_ref() };
+ let mut composited_from_value = composite_endpoint(
+ keyframe_from_value,
+ segment.mFromComposite,
+ underlying_value,
+ );
+ let mut composited_to_value =
+ composite_endpoint(keyframe_to_value, segment.mToComposite, underlying_value);
+
+ debug_assert!(
+ keyframe_from_value.is_some() || composited_from_value.is_some(),
+ "Should have a suitable from value to use"
+ );
+ debug_assert!(
+ keyframe_to_value.is_some() || composited_to_value.is_some(),
+ "Should have a suitable to value to use"
+ );
+
+ // Apply iteration composite behavior.
+ if iteration_composite == IterationCompositeOperation::Accumulate && current_iteration > 0 {
+ let last_value = last_value
+ .unwrap_or_else(|| underlying_value.expect("Should have a valid underlying value"));
+
+ composited_from_value = accumulate_endpoint(
+ keyframe_from_value,
+ composited_from_value,
+ last_value,
+ current_iteration,
+ );
+ composited_to_value = accumulate_endpoint(
+ keyframe_to_value,
+ composited_to_value,
+ last_value,
+ current_iteration,
+ );
+ }
+
+ // Use the composited value if there is one, otherwise, use the original keyframe value.
+ let from = composited_from_value
+ .as_ref()
+ .unwrap_or_else(|| keyframe_from_value.unwrap());
+ let to = composited_to_value
+ .as_ref()
+ .unwrap_or_else(|| keyframe_to_value.unwrap());
+
+ if segment.mToKey == segment.mFromKey {
+ return if total_progress < 0. {
+ from.clone()
+ } else {
+ to.clone()
+ };
+ }
+
+ match from.animate(
+ to,
+ Procedure::Interpolate {
+ progress: segment_progress,
+ },
+ ) {
+ Ok(value) => value,
+ _ => {
+ if segment_progress < 0.5 {
+ from.clone()
+ } else {
+ to.clone()
+ }
+ },
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComposeAnimationSegment(
+ segment: &structs::AnimationPropertySegment,
+ underlying_value: Option<&AnimationValue>,
+ last_value: Option<&AnimationValue>,
+ iteration_composite: IterationCompositeOperation,
+ progress: f64,
+ current_iteration: u64,
+) -> Strong<AnimationValue> {
+ let result = compose_animation_segment(
+ segment,
+ underlying_value,
+ last_value,
+ iteration_composite,
+ current_iteration,
+ progress,
+ progress,
+ );
+ Arc::new(result).into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationCompose(
+ value_map: &mut AnimationValueMap,
+ base_values: &structs::RawServoAnimationValueTable,
+ css_property: &structs::AnimatedPropertyID,
+ segment: &structs::AnimationPropertySegment,
+ last_segment: &structs::AnimationPropertySegment,
+ computed_timing: &structs::ComputedTiming,
+ iteration_composite: IterationCompositeOperation,
+) {
+ use style::gecko_bindings::bindings::Gecko_AnimationGetBaseStyle;
+ use style::gecko_bindings::bindings::Gecko_GetPositionInSegment;
+ use style::gecko_bindings::bindings::Gecko_GetProgressFromComputedTiming;
+
+ let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(css_property) {
+ Some(property) if property.as_borrowed().is_animatable() => property,
+ _ => return,
+ };
+
+ // We will need an underlying value if either of the endpoints is null...
+ let need_underlying_value = segment.mFromValue.mServo.mRawPtr.is_null() ||
+ segment.mToValue.mServo.mRawPtr.is_null() ||
+ // ... or if they have a non-replace composite mode ...
+ segment.mFromComposite != CompositeOperation::Replace ||
+ segment.mToComposite != CompositeOperation::Replace ||
+ // ... or if we accumulate onto the last value and it is null.
+ (iteration_composite == IterationCompositeOperation::Accumulate &&
+ computed_timing.mCurrentIteration > 0 &&
+ last_segment.mToValue.mServo.mRawPtr.is_null());
+
+ // If either of the segment endpoints are null, get the underlying value to
+ // use from the current value in the values map (set by a lower-priority
+ // effect), or, if there is no current value, look up the cached base value
+ // for this property.
+ let underlying_value = if need_underlying_value {
+ let previous_composed_value = value_map.get(&property).map(|v| &*v);
+ previous_composed_value
+ .or_else(|| unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property).as_ref() })
+ } else {
+ None
+ };
+
+ if need_underlying_value && underlying_value.is_none() {
+ warn!("Underlying value should be valid when we expect to use it");
+ return;
+ }
+
+ let last_value = unsafe { last_segment.mToValue.mServo.mRawPtr.as_ref() };
+ let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
+ let position = if segment.mToKey == segment.mFromKey {
+ // Note: compose_animation_segment doesn't use this value
+ // if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine.
+ progress
+ } else {
+ unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }
+ };
+
+ let result = compose_animation_segment(
+ segment,
+ underlying_value,
+ last_value,
+ iteration_composite,
+ computed_timing.mCurrentIteration,
+ progress,
+ position,
+ );
+ value_map.insert(property, result);
+}
+
+macro_rules! get_property_id_from_nscsspropertyid {
+ ($property_id: ident, $ret: expr) => {{
+ match PropertyId::from_nscsspropertyid($property_id) {
+ Some(property_id) => property_id,
+ None => {
+ return $ret;
+ },
+ }
+ }};
+}
+
+macro_rules! get_property_id_from_animatedpropertyid {
+ ($property_id: ident, $ret: expr) => {{
+ match PropertyId::from_gecko_animated_property_id($property_id) {
+ Some(property_id) => property_id,
+ None => {
+ return $ret;
+ },
+ }
+ }};
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Serialize(
+ value: &AnimationValue,
+ property: &structs::AnimatedPropertyID,
+ raw_data: &PerDocumentStyleData,
+ buffer: &mut nsACString,
+) {
+ let uncomputed_value = value.uncompute();
+ let data = raw_data.borrow();
+ let rv = PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal)
+ .single_value_to_css(
+ &get_property_id_from_animatedpropertyid!(property, ()),
+ buffer,
+ None,
+ &data.stylist,
+ );
+ debug_assert!(rv.is_ok());
+}
+
+/// Debug: MOZ_DBG for AnimationValue.
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Dump(value: &AnimationValue, result: &mut nsACString) {
+ write!(result, "{:?}", value).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_GetColor(
+ value: &AnimationValue,
+ foreground_color: structs::nscolor,
+) -> structs::nscolor {
+ use style::gecko::values::{
+ convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color,
+ };
+ use style::values::computed::color::Color as ComputedColor;
+ match *value {
+ AnimationValue::BackgroundColor(ref color) => {
+ let computed: ComputedColor = color.clone();
+ let foreground_color = convert_nscolor_to_absolute_color(foreground_color);
+ convert_absolute_color_to_nscolor(&computed.resolve_to_absolute(&foreground_color))
+ },
+ _ => panic!("Other color properties are not supported yet"),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_IsCurrentColor(value: &AnimationValue) -> bool {
+ match *value {
+ AnimationValue::BackgroundColor(ref color) => color.is_currentcolor(),
+ _ => {
+ debug_assert!(false, "Other color properties are not supported yet");
+ false
+ },
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_GetOpacity(value: &AnimationValue) -> f32 {
+ if let AnimationValue::Opacity(opacity) = *value {
+ opacity
+ } else {
+ panic!("The AnimationValue should be Opacity");
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Opacity(opacity: f32) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::Opacity(opacity)).into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Color(
+ color_property: nsCSSPropertyID,
+ color: structs::nscolor,
+) -> Strong<AnimationValue> {
+ use style::gecko::values::convert_nscolor_to_absolute_color;
+ use style::values::animated::color::Color;
+
+ let property = LonghandId::from_nscsspropertyid(color_property)
+ .expect("We don't have shorthand property animation value");
+
+ let animated = convert_nscolor_to_absolute_color(color);
+
+ match property {
+ LonghandId::BackgroundColor => {
+ Arc::new(AnimationValue::BackgroundColor(Color::Absolute(animated))).into()
+ },
+ _ => panic!("Should be background-color property"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetScale(
+ value: &AnimationValue,
+) -> *const computed::Scale {
+ match *value {
+ AnimationValue::Scale(ref value) => value,
+ _ => unreachable!("Expected scale"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetTranslate(
+ value: &AnimationValue,
+) -> *const computed::Translate {
+ match *value {
+ AnimationValue::Translate(ref value) => value,
+ _ => unreachable!("Expected translate"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetRotate(
+ value: &AnimationValue,
+) -> *const computed::Rotate {
+ match *value {
+ AnimationValue::Rotate(ref value) => value,
+ _ => unreachable!("Expected rotate"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
+ value: &AnimationValue,
+) -> *const computed::Transform {
+ match *value {
+ AnimationValue::Transform(ref value) => value,
+ _ => unreachable!("Unsupported transform animation value"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPath(
+ value: &AnimationValue,
+ output: &mut computed::motion::OffsetPath,
+) {
+ use style::values::animated::ToAnimatedValue;
+ match *value {
+ AnimationValue::OffsetPath(ref value) => {
+ *output = ToAnimatedValue::from_animated_value(value.clone())
+ },
+ _ => unreachable!("Expected offset-path"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetDistance(
+ value: &AnimationValue,
+) -> *const computed::LengthPercentage {
+ match *value {
+ AnimationValue::OffsetDistance(ref value) => value,
+ _ => unreachable!("Expected offset-distance"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetRotate(
+ value: &AnimationValue,
+) -> *const computed::motion::OffsetRotate {
+ match *value {
+ AnimationValue::OffsetRotate(ref value) => value,
+ _ => unreachable!("Expected offset-rotate"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetAnchor(
+ value: &AnimationValue,
+) -> *const computed::position::PositionOrAuto {
+ match *value {
+ AnimationValue::OffsetAnchor(ref value) => value,
+ _ => unreachable!("Expected offset-anchor"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPosition(
+ value: &AnimationValue,
+) -> *const computed::motion::OffsetPosition {
+ match *value {
+ AnimationValue::OffsetPosition(ref value) => value,
+ _ => unreachable!("Expected offset-position"),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_IsOffsetPathUrl(value: &AnimationValue) -> bool {
+ use style::values::generics::motion::{GenericOffsetPath, GenericOffsetPathFunction};
+ if let AnimationValue::OffsetPath(ref op) = value {
+ if let GenericOffsetPath::OffsetPath { path, coord_box: _ } = op {
+ return matches!(**path, GenericOffsetPathFunction::Url(_));
+ }
+ }
+ false
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_Rotate(
+ r: &computed::Rotate,
+) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::Rotate(r.clone())).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_Translate(
+ t: &computed::Translate,
+) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::Translate(t.clone())).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_Scale(s: &computed::Scale) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::Scale(s.clone())).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_Transform(
+ transform: &computed::Transform,
+) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::Transform(transform.clone())).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_OffsetPath(
+ p: &computed::motion::OffsetPath,
+) -> Strong<AnimationValue> {
+ use style::values::animated::ToAnimatedValue;
+ Arc::new(AnimationValue::OffsetPath(p.clone().to_animated_value())).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_OffsetDistance(
+ d: &computed::LengthPercentage,
+) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::OffsetDistance(d.clone())).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_OffsetRotate(
+ r: &computed::motion::OffsetRotate,
+) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::OffsetRotate(*r)).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_OffsetAnchor(
+ p: &computed::position::PositionOrAuto,
+) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::OffsetAnchor(p.clone())).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValue_OffsetPosition(
+ p: &computed::motion::OffsetPosition,
+) -> Strong<AnimationValue> {
+ Arc::new(AnimationValue::OffsetPosition(p.clone())).into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_DeepEqual(
+ this: &AnimationValue,
+ other: &AnimationValue,
+) -> bool {
+ this == other
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Uncompute(
+ value: &AnimationValue,
+) -> Strong<LockedDeclarationBlock> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ Arc::new(
+ global_style_data
+ .shared_lock
+ .wrap(PropertyDeclarationBlock::with_one(
+ value.uncompute(),
+ Importance::Normal,
+ )),
+ )
+ .into()
+}
+
+#[inline]
+fn create_byte_buf_from_vec(mut v: Vec<u8>) -> ByteBuf {
+ let w = ByteBuf {
+ mData: v.as_mut_ptr(),
+ mLen: v.len(),
+ mCapacity: v.capacity(),
+ };
+ std::mem::forget(v);
+ w
+}
+
+#[inline]
+fn view_byte_buf(b: &ByteBuf) -> &[u8] {
+ if b.mData.is_null() {
+ debug_assert_eq!(b.mCapacity, 0);
+ return &[];
+ }
+ unsafe { std::slice::from_raw_parts(b.mData, b.mLen) }
+}
+
+macro_rules! impl_basic_serde_funcs {
+ ($ser_name:ident, $de_name:ident, $computed_type:ty) => {
+ #[no_mangle]
+ pub extern "C" fn $ser_name(v: &$computed_type, output: &mut ByteBuf) -> bool {
+ let buf = match serialize(v) {
+ Ok(buf) => buf,
+ Err(..) => return false,
+ };
+
+ *output = create_byte_buf_from_vec(buf);
+ true
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn $de_name(input: &ByteBuf, v: *mut $computed_type) -> bool {
+ let buf = match deserialize(view_byte_buf(input)) {
+ Ok(buf) => buf,
+ Err(..) => return false,
+ };
+
+ std::ptr::write(v, buf);
+ true
+ }
+ };
+}
+
+impl_basic_serde_funcs!(
+ Servo_LengthPercentage_Serialize,
+ Servo_LengthPercentage_Deserialize,
+ computed::LengthPercentage
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleRotate_Serialize,
+ Servo_StyleRotate_Deserialize,
+ computed::transform::Rotate
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleScale_Serialize,
+ Servo_StyleScale_Deserialize,
+ computed::transform::Scale
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleTranslate_Serialize,
+ Servo_StyleTranslate_Deserialize,
+ computed::transform::Translate
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleTransform_Serialize,
+ Servo_StyleTransform_Deserialize,
+ computed::transform::Transform
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleOffsetPath_Serialize,
+ Servo_StyleOffsetPath_Deserialize,
+ computed::motion::OffsetPath
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleOffsetRotate_Serialize,
+ Servo_StyleOffsetRotate_Deserialize,
+ computed::motion::OffsetRotate
+);
+
+impl_basic_serde_funcs!(
+ Servo_StylePositionOrAuto_Serialize,
+ Servo_StylePositionOrAuto_Deserialize,
+ computed::position::PositionOrAuto
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleOffsetPosition_Serialize,
+ Servo_StyleOffsetPosition_Deserialize,
+ computed::motion::OffsetPosition
+);
+
+impl_basic_serde_funcs!(
+ Servo_StyleComputedTimingFunction_Serialize,
+ Servo_StyleComputedTimingFunction_Deserialize,
+ ComputedTimingFunction
+);
+
+// Return the ComputedValues by a base ComputedValues and the rules.
+fn resolve_rules_for_element_with_context<'a>(
+ element: GeckoElement<'a>,
+ mut context: StyleContext<GeckoElement<'a>>,
+ rules: StrongRuleNode,
+ original_computed_values: &ComputedValues,
+) -> Arc<ComputedValues> {
+ use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};
+
+ // This currently ignores visited styles, which seems acceptable, as
+ // existing browsers don't appear to animate visited styles.
+ let inputs = CascadeInputs {
+ rules: Some(rules),
+ visited_rules: None,
+ flags: original_computed_values.flags.for_cascade_inputs(),
+ };
+
+ // Actually `PseudoElementResolution` doesn't matter.
+ let mut resolver = StyleResolverForElement::new(
+ element,
+ &mut context,
+ RuleInclusion::All,
+ PseudoElementResolution::IfApplicable,
+ );
+ resolver
+ .cascade_style_and_visited_with_default_parents(inputs)
+ .0
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValueMap_Create() -> *mut AnimationValueMap {
+ Box::into_raw(Box::default())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AnimationValueMap_Drop(value_map: *mut AnimationValueMap) {
+ let _ = Box::from_raw(value_map);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValueMap_GetValue(
+ value_map: &AnimationValueMap,
+ property_id: &structs::AnimatedPropertyID,
+) -> Strong<AnimationValue> {
+ let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
+ Some(property) => property,
+ None => return Strong::null(),
+ };
+ value_map
+ .get(&property)
+ .map_or(Strong::null(), |value| Arc::new(value.clone()).into())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
+ raw_style_set: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ computed_values: &ComputedValues,
+ snapshots: *const ServoElementSnapshotTable,
+) -> Strong<ComputedValues> {
+ debug_assert!(!snapshots.is_null());
+ let computed_values = unsafe { ArcBorrow::from_ref(computed_values) };
+
+ let rules = match computed_values.rules {
+ None => return computed_values.clone_arc().into(),
+ Some(ref rules) => rules,
+ };
+
+ let doc_data = raw_style_set.borrow();
+ let without_animations_rules = doc_data.stylist.rule_tree().remove_animation_rules(rules);
+ if without_animations_rules == *rules {
+ return computed_values.clone_arc().into();
+ }
+
+ let element = GeckoElement(element);
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let shared = create_shared_context(
+ &global_style_data,
+ &guard,
+ &doc_data.stylist,
+ TraversalFlags::empty(),
+ unsafe { &*snapshots },
+ );
+ let mut tlc = ThreadLocalStyleContext::new();
+ let context = StyleContext {
+ shared: &shared,
+ thread_local: &mut tlc,
+ };
+
+ resolve_rules_for_element_with_context(
+ element,
+ context,
+ without_animations_rules,
+ &computed_values,
+ )
+ .into()
+}
+
+#[repr(C)]
+#[derive(Default)]
+pub struct ShouldTransitionResult {
+ should_animate: bool,
+ old_transition_value_matches: bool,
+}
+
+#[inline]
+fn is_transitionable(prop: PropertyDeclarationId) -> bool {
+ prop.is_animatable() && !prop.is_discrete_animatable()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_ShouldTransition(
+ old: &ComputedValues,
+ new: &ComputedValues,
+ prop: &structs::AnimatedPropertyID,
+ old_transition_value: Option<&AnimationValue>,
+ start: &mut structs::RefPtr<AnimationValue>,
+ end: &mut structs::RefPtr<AnimationValue>,
+) -> ShouldTransitionResult {
+ let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
+ return Default::default();
+ };
+ let prop = prop.as_borrowed();
+ if !is_transitionable(prop) {
+ return Default::default();
+ }
+
+ let Some(new_value) = AnimationValue::from_computed_values(prop, new) else {
+ return Default::default();
+ };
+
+ if let Some(old_transition_value) = old_transition_value {
+ if *old_transition_value == new_value {
+ return ShouldTransitionResult {
+ should_animate: false,
+ old_transition_value_matches: true,
+ };
+ }
+ }
+
+ let Some(old_value) = AnimationValue::from_computed_values(prop, old) else {
+ return Default::default();
+ };
+ if old_value == new_value || !old_value.interpolable_with(&new_value) {
+ return Default::default();
+ }
+
+ start.set_arc(Arc::new(old_value));
+ end.set_arc(Arc::new(new_value));
+
+ ShouldTransitionResult {
+ should_animate: true,
+ old_transition_value_matches: false,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_TransitionValueMatches(
+ style: &ComputedValues,
+ prop: &structs::AnimatedPropertyID,
+ transition_value: &AnimationValue,
+) -> bool {
+ let Some(prop) = OwnedPropertyDeclarationId::from_gecko_animated_property_id(prop) else {
+ return false;
+ };
+ let prop = prop.as_borrowed();
+ if !is_transitionable(prop) {
+ return false;
+ }
+ let Some(value) = AnimationValue::from_computed_values(prop, style) else {
+ return false;
+ };
+ value == *transition_value
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(
+ computed_values: &ComputedValues,
+ property_id: &structs::AnimatedPropertyID,
+) -> Strong<AnimationValue> {
+ let property = match OwnedPropertyDeclarationId::from_gecko_animated_property_id(property_id) {
+ Some(property) => property,
+ None => return Strong::null(),
+ };
+ match AnimationValue::from_computed_values(property.as_borrowed(), &computed_values) {
+ Some(v) => Arc::new(v).into(),
+ None => Strong::null(),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ResolveLogicalProperty(
+ property_id: nsCSSPropertyID,
+ style: &ComputedValues,
+) -> nsCSSPropertyID {
+ let longhand = LonghandId::from_nscsspropertyid(property_id)
+ .expect("We shouldn't need to care about shorthands");
+
+ longhand
+ .to_physical(style.writing_mode)
+ .to_nscsspropertyid()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Property_LookupEnabledForAllContent(
+ prop: &nsACString,
+) -> nsCSSPropertyID {
+ match PropertyId::parse_enabled_for_all_content(prop.as_str_unchecked()) {
+ Ok(p) => p.to_nscsspropertyid_resolving_aliases(),
+ Err(..) => nsCSSPropertyID::eCSSProperty_UNKNOWN,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Property_GetName(
+ prop: nsCSSPropertyID,
+ out_length: *mut u32,
+) -> *const u8 {
+ let (ptr, len) = match NonCustomPropertyId::from_nscsspropertyid(prop) {
+ Some(p) => {
+ let name = p.name();
+ (name.as_bytes().as_ptr(), name.len())
+ },
+ None => (ptr::null(), 0),
+ };
+
+ *out_length = len as u32;
+ ptr
+}
+
+macro_rules! parse_enabled_property_name {
+ ($prop_name:ident, $found:ident, $default:expr) => {{
+ let prop_name = $prop_name.as_str_unchecked();
+ match PropertyId::parse_enabled_for_all_content(prop_name) {
+ Ok(p) => {
+ *$found = true;
+ p
+ },
+ Err(..) => {
+ *$found = false;
+ return $default;
+ },
+ }
+ }};
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Property_IsShorthand(
+ prop_name: &nsACString,
+ found: *mut bool,
+) -> bool {
+ let prop_id = parse_enabled_property_name!(prop_name, found, false);
+ prop_id.is_shorthand()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Property_IsInherited(
+ per_doc_data: &PerDocumentStyleData,
+ prop_name: &nsACString,
+) -> bool {
+ let prop_name = prop_name.as_str_unchecked();
+ let prop_id = match PropertyId::parse_enabled_for_all_content(prop_name) {
+ Ok(id) => id,
+ Err(_) => return false,
+ };
+ let longhand_id = match prop_id {
+ PropertyId::Custom(property_name) => {
+ let stylist = &per_doc_data.borrow().stylist;
+ return stylist.get_custom_property_registration(&property_name).inherits()
+ },
+ PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
+ Ok(lh) => lh,
+ Err(sh) => sh.longhands().next().unwrap(),
+ },
+ };
+ longhand_id.inherited()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Property_SupportsType(
+ prop_name: &nsACString,
+ ty: u8,
+ found: *mut bool,
+) -> bool {
+ let prop_id = parse_enabled_property_name!(prop_name, found, false);
+ prop_id.supports_type(ty)
+}
+
+// TODO(emilio): We could use ThinVec instead of nsTArray.
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Property_GetCSSValuesForProperty(
+ prop_name: &nsACString,
+ found: *mut bool,
+ result: &mut nsTArray<nsString>,
+) {
+ let prop_id = parse_enabled_property_name!(prop_name, found, ());
+ // Use B-tree set for unique and sorted result.
+ let mut values = BTreeSet::<&'static str>::new();
+ prop_id.collect_property_completion_keywords(&mut |list| values.extend(list.iter()));
+
+ let mut extras = vec![];
+ if values.contains("transparent") {
+ // This is a special value devtools use to avoid inserting the
+ // long list of color keywords. We need to prepend it to values.
+ extras.push("COLOR");
+ }
+
+ let len = extras.len() + values.len();
+ bindings::Gecko_ResizeTArrayForStrings(result, len as u32);
+
+ for (src, dest) in extras.iter().chain(values.iter()).zip(result.iter_mut()) {
+ dest.write_str(src).unwrap();
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Property_IsAnimatable(prop: &structs::AnimatedPropertyID) -> bool {
+ PropertyId::from_gecko_animated_property_id(prop).map_or(false, |p| p.is_animatable())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Property_IsDiscreteAnimatable(property: nsCSSPropertyID) -> bool {
+ match LonghandId::from_nscsspropertyid(property) {
+ Some(longhand) => longhand.is_discrete_animatable(),
+ None => return false,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_ClearData(element: &RawGeckoElement) {
+ unsafe { GeckoElement(element).clear_data() };
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_SizeOfExcludingThisAndCVs(
+ malloc_size_of: GeckoMallocSizeOf,
+ malloc_enclosing_size_of: GeckoMallocSizeOf,
+ seen_ptrs: *mut SeenPtrs,
+ element: &RawGeckoElement,
+) -> usize {
+ let element = GeckoElement(element);
+ let borrow = element.borrow_data();
+ if let Some(data) = borrow {
+ let have_seen_ptr = move |ptr| unsafe { Gecko_HaveSeenPtr(seen_ptrs, ptr) };
+ let mut ops = MallocSizeOfOps::new(
+ malloc_size_of.unwrap(),
+ Some(malloc_enclosing_size_of.unwrap()),
+ Some(Box::new(have_seen_ptr)),
+ );
+ (*data).size_of_excluding_cvs(&mut ops)
+ } else {
+ 0
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_GetMaybeOutOfDateStyle(
+ element: &RawGeckoElement,
+) -> *const ComputedValues {
+ let element = GeckoElement(element);
+ let data = match element.borrow_data() {
+ Some(d) => d,
+ None => return ptr::null(),
+ };
+ &**data.styles.primary() as *const _
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_GetMaybeOutOfDatePseudoStyle(
+ element: &RawGeckoElement,
+ index: usize,
+) -> *const ComputedValues {
+ let element = GeckoElement(element);
+ let data = match element.borrow_data() {
+ Some(d) => d,
+ None => return ptr::null(),
+ };
+ match data.styles.pseudos.as_array()[index].as_ref() {
+ Some(style) => &**style as *const _,
+ None => ptr::null(),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_IsDisplayNone(element: &RawGeckoElement) -> bool {
+ let element = GeckoElement(element);
+ let data = element
+ .get_data()
+ .expect("Invoking Servo_Element_IsDisplayNone on unstyled element");
+
+ // This function is hot, so we bypass the AtomicRefCell.
+ //
+ // It would be nice to also assert that we're not in the servo traversal,
+ // but this function is called at various intermediate checkpoints when
+ // managing the traversal on the Gecko side.
+ debug_assert!(is_main_thread());
+ unsafe { &*data.as_ptr() }.styles.is_display_none()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_IsDisplayContents(element: &RawGeckoElement) -> bool {
+ let element = GeckoElement(element);
+ let data = element
+ .get_data()
+ .expect("Invoking Servo_Element_IsDisplayContents on unstyled element");
+
+ debug_assert!(is_main_thread());
+ unsafe { &*data.as_ptr() }
+ .styles
+ .primary()
+ .get_box()
+ .clone_display()
+ .is_contents()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Element_IsPrimaryStyleReusedViaRuleNode(element: &RawGeckoElement) -> bool {
+ let element = GeckoElement(element);
+ let data = element
+ .borrow_data()
+ .expect("Invoking Servo_Element_IsPrimaryStyleReusedViaRuleNode on unstyled element");
+ data.flags
+ .contains(data::ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE)
+}
+
+fn mode_to_origin(mode: SheetParsingMode) -> Origin {
+ match mode {
+ SheetParsingMode::eAuthorSheetFeatures => Origin::Author,
+ SheetParsingMode::eUserSheetFeatures => Origin::User,
+ SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> Strong<StylesheetContents> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let origin = mode_to_origin(mode);
+ let shared_lock = &global_style_data.shared_lock;
+ StylesheetContents::from_str(
+ "",
+ unsafe { dummy_url_data() }.clone(),
+ origin,
+ shared_lock,
+ /* loader = */ None,
+ None,
+ QuirksMode::NoQuirks,
+ /* use_counters = */ None,
+ AllowImportRules::Yes,
+ /* sanitization_data = */ None,
+ )
+ .into()
+}
+
+/// Note: The load_data corresponds to this sheet, and is passed as the parent
+/// load data for child sheet loads. It may be null for certain cases where we
+/// know we won't have child loads.
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8Bytes(
+ loader: *mut Loader,
+ stylesheet: *mut DomStyleSheet,
+ load_data: *mut SheetLoadData,
+ bytes: &nsACString,
+ mode: SheetParsingMode,
+ extra_data: *mut URLExtraData,
+ quirks_mode: nsCompatibility,
+ reusable_sheets: *mut LoaderReusableStyleSheets,
+ use_counters: Option<&UseCounters>,
+ allow_import_rules: AllowImportRules,
+ sanitization_kind: SanitizationKind,
+ sanitized_output: Option<&mut nsAString>,
+) -> Strong<StylesheetContents> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let input = bytes.as_str_unchecked();
+
+ let reporter = ErrorReporter::new(stylesheet, loader, extra_data);
+ let url_data = UrlExtraData::from_ptr_ref(&extra_data);
+ let loader = if loader.is_null() {
+ None
+ } else {
+ debug_assert!(
+ sanitized_output.is_none(),
+ "Shouldn't trigger @import loads for sanitization",
+ );
+ Some(StylesheetLoader::new(
+ loader,
+ stylesheet,
+ load_data,
+ reusable_sheets,
+ ))
+ };
+
+ // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
+ let loader: Option<&dyn StyleStylesheetLoader> = match loader {
+ None => None,
+ Some(ref s) => Some(s),
+ };
+
+ let mut sanitization_data = SanitizationData::new(sanitization_kind);
+
+ let contents = StylesheetContents::from_str(
+ input,
+ url_data.clone(),
+ mode_to_origin(mode),
+ &global_style_data.shared_lock,
+ loader,
+ reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
+ quirks_mode.into(),
+ use_counters,
+ allow_import_rules,
+ sanitization_data.as_mut(),
+ );
+
+ if let Some(data) = sanitization_data {
+ sanitized_output
+ .unwrap()
+ .assign_utf8(data.take().as_bytes());
+ }
+
+ contents.into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
+ load_data: *mut SheetLoadDataHolder,
+ extra_data: *mut URLExtraData,
+ bytes: &nsACString,
+ mode: SheetParsingMode,
+ quirks_mode: nsCompatibility,
+ should_record_use_counters: bool,
+ allow_import_rules: AllowImportRules,
+) {
+ let load_data = RefPtr::new(load_data);
+ let extra_data = UrlExtraData::new(extra_data);
+
+ let mut sheet_bytes = nsCString::new();
+ sheet_bytes.assign(bytes);
+
+ let async_parser = AsyncStylesheetParser::new(
+ load_data,
+ extra_data,
+ sheet_bytes,
+ mode_to_origin(mode),
+ quirks_mode.into(),
+ should_record_use_counters,
+ allow_import_rules,
+ );
+
+ if let Some(thread_pool) = STYLE_THREAD_POOL.pool().as_ref() {
+ thread_pool.spawn(|| {
+ gecko_profiler_label!(Layout, CSSParsing);
+ async_parser.parse();
+ });
+ } else {
+ async_parser.parse();
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ShutdownThreadPool() {
+ debug_assert!(is_main_thread() && !is_in_servo_traversal());
+ StyleThreadPool::shutdown();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ThreadPool_GetThreadHandles(
+ handles: &mut ThinVec<PlatformThreadHandle>,
+) {
+ StyleThreadPool::get_thread_handles(handles);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSheet_FromSharedData(
+ extra_data: *mut URLExtraData,
+ shared_rules: &LockedCssRules,
+) -> Strong<StylesheetContents> {
+ StylesheetContents::from_shared_data(
+ Arc::from_raw_addrefed(shared_rules),
+ Origin::UserAgent,
+ UrlExtraData::new(extra_data),
+ QuirksMode::NoQuirks,
+ )
+ .into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
+ raw_data: &PerDocumentStyleData,
+ sheet: *const DomStyleSheet,
+) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let mut data = raw_data.borrow_mut();
+ let data = &mut *data;
+ let guard = global_style_data.shared_lock.read();
+ let sheet = unsafe { GeckoStyleSheet::new(sheet) };
+ data.stylist.append_stylesheet(sheet, &guard);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AuthorStyles_Create() -> *mut AuthorStyles {
+ Box::into_raw(Box::new(AuthorStyles::new()))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AuthorStyles_Drop(styles: *mut AuthorStyles) {
+ let _ = Box::from_raw(styles);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet(
+ styles: &mut AuthorStyles,
+ sheet: *const DomStyleSheet,
+) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let sheet = GeckoStyleSheet::new(sheet);
+ styles.stylesheets.append_stylesheet(None, sheet, &guard);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AuthorStyles_InsertStyleSheetBefore(
+ styles: &mut AuthorStyles,
+ sheet: *const DomStyleSheet,
+ before_sheet: *const DomStyleSheet,
+) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ styles.stylesheets.insert_stylesheet_before(
+ None,
+ GeckoStyleSheet::new(sheet),
+ GeckoStyleSheet::new(before_sheet),
+ &guard,
+ );
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AuthorStyles_RemoveStyleSheet(
+ styles: &mut AuthorStyles,
+ sheet: *const DomStyleSheet,
+) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ styles
+ .stylesheets
+ .remove_stylesheet(None, GeckoStyleSheet::new(sheet), &guard);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AuthorStyles_ForceDirty(styles: &mut AuthorStyles) {
+ styles.stylesheets.force_dirty();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AuthorStyles_IsDirty(styles: &AuthorStyles) -> bool {
+ styles.stylesheets.dirty()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AuthorStyles_Flush(
+ styles: &mut AuthorStyles,
+ document_set: &PerDocumentStyleData,
+) {
+ // Try to avoid the atomic borrow below if possible.
+ if !styles.stylesheets.dirty() {
+ return;
+ }
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+
+ let mut document_data = document_set.borrow_mut();
+
+ // TODO(emilio): This is going to need an element or something to do proper
+ // invalidation in Shadow roots.
+ styles.flush::<GeckoElement>(&mut document_data.stylist, &guard);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_RemoveUniqueEntriesFromAuthorStylesCache(
+ document_set: &PerDocumentStyleData,
+) {
+ let mut document_data = document_set.borrow_mut();
+ document_data
+ .stylist
+ .remove_unique_author_data_cache_entries();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SizeOfIncludingThis(
+ malloc_size_of: GeckoMallocSizeOf,
+ malloc_enclosing_size_of: GeckoMallocSizeOf,
+ declarations: &LockedDeclarationBlock,
+) -> usize {
+ use malloc_size_of::MallocSizeOf;
+ use malloc_size_of::MallocUnconditionalShallowSizeOf;
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+
+ let mut ops = MallocSizeOfOps::new(
+ malloc_size_of.unwrap(),
+ Some(malloc_enclosing_size_of.unwrap()),
+ None,
+ );
+
+ ArcBorrow::from_ref(declarations).with_arc(|declarations| {
+ let mut n = 0;
+ n += declarations.unconditional_shallow_size_of(&mut ops);
+ n += declarations.read_with(&guard).size_of(&mut ops);
+ n
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_AuthorStyles_SizeOfIncludingThis(
+ malloc_size_of: GeckoMallocSizeOf,
+ malloc_enclosing_size_of: GeckoMallocSizeOf,
+ styles: &AuthorStyles,
+) -> usize {
+ // We cannot `use` MallocSizeOf at the top level, otherwise the compiler
+ // would complain in `Servo_StyleSheet_SizeOfIncludingThis` for `size_of`
+ // there.
+ use malloc_size_of::MallocSizeOf;
+ let malloc_size_of = malloc_size_of.unwrap();
+ let malloc_size_of_this = malloc_size_of(styles as *const AuthorStyles as *const c_void);
+
+ let mut ops = MallocSizeOfOps::new(
+ malloc_size_of,
+ Some(malloc_enclosing_size_of.unwrap()),
+ None,
+ );
+ malloc_size_of_this + styles.size_of(&mut ops)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
+ document_set: &PerDocumentStyleData,
+ non_document_styles: &mut nsTArray<&mut AuthorStyles>,
+ may_affect_default_style: bool,
+) -> structs::MediumFeaturesChangedResult {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+
+ // NOTE(emilio): We don't actually need to flush the stylist here and ensure
+ // it's up to date.
+ //
+ // In case it isn't we would trigger a rebuild + restyle as needed too.
+ //
+ // We need to ensure the default computed values are up to date though,
+ // because those can influence the result of media query evaluation.
+ let mut document_data = document_set.borrow_mut();
+
+ if may_affect_default_style {
+ document_data.stylist.device_mut().reset_computed_values();
+ }
+ let guards = StylesheetGuards::same(&guard);
+
+ let origins_in_which_rules_changed = document_data
+ .stylist
+ .media_features_change_changed_style(&guards, document_data.stylist.device());
+
+ let affects_document_rules = !origins_in_which_rules_changed.is_empty();
+ if affects_document_rules {
+ document_data
+ .stylist
+ .force_stylesheet_origins_dirty(origins_in_which_rules_changed);
+ }
+
+ let mut affects_non_document_rules = false;
+ for author_styles in &mut **non_document_styles {
+ let affected_style = author_styles.stylesheets.iter().any(|sheet| {
+ !author_styles.data.media_feature_affected_matches(
+ sheet,
+ &guards.author,
+ document_data.stylist.device(),
+ document_data.stylist.quirks_mode(),
+ )
+ });
+ if affected_style {
+ affects_non_document_rules = true;
+ author_styles.stylesheets.force_dirty();
+ }
+ }
+
+ structs::MediumFeaturesChangedResult {
+ mAffectsDocumentRules: affects_document_rules,
+ mAffectsNonDocumentRules: affects_non_document_rules,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
+ raw_data: &PerDocumentStyleData,
+ sheet: *const DomStyleSheet,
+ before_sheet: *const DomStyleSheet,
+) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let mut data = raw_data.borrow_mut();
+ let data = &mut *data;
+ let guard = global_style_data.shared_lock.read();
+ let sheet = unsafe { GeckoStyleSheet::new(sheet) };
+ data.stylist.insert_stylesheet_before(
+ sheet,
+ unsafe { GeckoStyleSheet::new(before_sheet) },
+ &guard,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(
+ raw_data: &PerDocumentStyleData,
+ sheet: *const DomStyleSheet,
+) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let mut data = raw_data.borrow_mut();
+ let data = &mut *data;
+ let guard = global_style_data.shared_lock.read();
+ let sheet = unsafe { GeckoStyleSheet::new(sheet) };
+ data.stylist.remove_stylesheet(sheet, &guard);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_GetSheetAt(
+ raw_data: &PerDocumentStyleData,
+ origin: Origin,
+ index: usize,
+) -> *const DomStyleSheet {
+ let data = raw_data.borrow();
+ data.stylist
+ .sheet_at(origin, index)
+ .map_or(ptr::null(), |s| s.raw())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_GetSheetCount(
+ raw_data: &PerDocumentStyleData,
+ origin: Origin,
+) -> usize {
+ let data = raw_data.borrow();
+ data.stylist.sheet_count(origin)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_FlushStyleSheets(
+ raw_data: &PerDocumentStyleData,
+ doc_element: Option<&RawGeckoElement>,
+ snapshots: *const ServoElementSnapshotTable,
+) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let mut data = raw_data.borrow_mut();
+ let doc_element = doc_element.map(GeckoElement);
+
+ let have_invalidations = data.flush_stylesheets(&guard, doc_element, snapshots.as_ref());
+
+ if have_invalidations && doc_element.is_some() {
+ // The invalidation machinery propagates the bits up, but we still need
+ // to tell the Gecko restyle root machinery about it.
+ bindings::Gecko_NoteDirtySubtreeForInvalidation(doc_element.unwrap().0);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
+ raw_data: &PerDocumentStyleData,
+ changed_origins: OriginFlags,
+) {
+ let mut data = raw_data.borrow_mut();
+ data.stylist
+ .force_stylesheet_origins_dirty(OriginSet::from(changed_origins));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_SetAuthorStyleDisabled(
+ raw_data: &PerDocumentStyleData,
+ author_style_disabled: bool,
+) {
+ let mut data = raw_data.borrow_mut();
+ let enabled = if author_style_disabled {
+ AuthorStylesEnabled::No
+ } else {
+ AuthorStylesEnabled::Yes
+ };
+ data.stylist.set_author_styles_enabled(enabled);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_UsesFontMetrics(raw_data: &PerDocumentStyleData) -> bool {
+ let doc_data = raw_data;
+ doc_data.borrow().stylist.device().used_font_metrics()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_HasRules(raw_contents: &StylesheetContents) -> bool {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ !raw_contents.rules.read_with(&guard).0.is_empty()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_GetRules(sheet: &StylesheetContents) -> Strong<LockedCssRules> {
+ sheet.rules.clone().into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_Clone(
+ contents: &StylesheetContents,
+ reference_sheet: *const DomStyleSheet,
+) -> Strong<StylesheetContents> {
+ use style::shared_lock::{DeepCloneParams, DeepCloneWithLock};
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let params = DeepCloneParams { reference_sheet };
+
+ Arc::new(contents.deep_clone_with_lock(&global_style_data.shared_lock, &guard, &params)).into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
+ malloc_size_of: GeckoMallocSizeOf,
+ malloc_enclosing_size_of: GeckoMallocSizeOf,
+ sheet: &StylesheetContents,
+) -> usize {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let mut ops = MallocSizeOfOps::new(
+ malloc_size_of.unwrap(),
+ Some(malloc_enclosing_size_of.unwrap()),
+ None,
+ );
+ // TODO(emilio): We're not measuring the size of the Arc<StylesheetContents>
+ // allocation itself here.
+ sheet.size_of(&guard, &mut ops)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_GetOrigin(sheet: &StylesheetContents) -> Origin {
+ sheet.origin
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_GetSourceMapURL(
+ contents: &StylesheetContents,
+ result: &mut nsACString,
+) {
+ let url_opt = contents.source_map_url.read();
+ if let Some(ref url) = *url_opt {
+ result.assign(url);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSheet_GetSourceURL(
+ contents: &StylesheetContents,
+ result: &mut nsACString,
+) {
+ let url_opt = contents.source_url.read();
+ if let Some(ref url) = *url_opt {
+ result.assign(url);
+ }
+}
+
+fn with_maybe_worker_shared_lock<R>(func: impl FnOnce(&SharedRwLock) -> R) -> R {
+ if is_dom_worker_thread() {
+ DOM_WORKER_RWLOCK.with(func)
+ } else {
+ func(&GLOBAL_STYLE_DATA.shared_lock)
+ }
+}
+
+fn read_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
+where
+ F: FnOnce(&T) -> R,
+{
+ debug_assert!(!is_dom_worker_thread());
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ func(raw.read_with(&guard))
+}
+
+fn read_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
+where
+ F: FnOnce(&T) -> R,
+{
+ with_maybe_worker_shared_lock(|lock| {
+ let guard = lock.read();
+ func(raw.read_with(&guard))
+ })
+}
+
+#[cfg(debug_assertions)]
+unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
+where
+ F: FnOnce(&T) -> R,
+{
+ debug_assert!(is_main_thread() && !is_in_servo_traversal());
+ read_locked_arc(raw, func)
+}
+
+#[cfg(not(debug_assertions))]
+unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &Locked<T>, func: F) -> R
+where
+ F: FnOnce(&T) -> R,
+{
+ debug_assert!(!is_dom_worker_thread());
+ func(raw.read_unchecked())
+}
+
+fn write_locked_arc<T, R, F>(raw: &Locked<T>, func: F) -> R
+where
+ F: FnOnce(&mut T) -> R,
+{
+ debug_assert!(!is_dom_worker_thread());
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let mut guard = global_style_data.shared_lock.write();
+ func(raw.write_with(&mut guard))
+}
+
+fn write_locked_arc_worker<T, R, F>(raw: &Locked<T>, func: F) -> R
+where
+ F: FnOnce(&mut T) -> R,
+{
+ with_maybe_worker_shared_lock(|lock| {
+ let mut guard = lock.write();
+ func(raw.write_with(&mut guard))
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_CssRules_ListTypes(rules: &LockedCssRules, result: &mut nsTArray<usize>) {
+ read_locked_arc(rules, |rules: &CssRules| {
+ result.assign_from_iter_pod(rules.0.iter().map(|rule| rule.rule_type() as usize));
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_CssRules_InsertRule(
+ rules: &LockedCssRules,
+ contents: &StylesheetContents,
+ rule: &nsACString,
+ index: u32,
+ containing_rule_types: u32,
+ loader: *mut Loader,
+ allow_import_rules: AllowImportRules,
+ gecko_stylesheet: *mut DomStyleSheet,
+ rule_type: &mut CssRuleType,
+) -> nsresult {
+ let loader = if loader.is_null() {
+ None
+ } else {
+ Some(StylesheetLoader::new(
+ loader,
+ gecko_stylesheet,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ))
+ };
+ let loader = loader
+ .as_ref()
+ .map(|loader| loader as &dyn StyleStylesheetLoader);
+ let rule = unsafe { rule.as_str_unchecked() };
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let result = rules.insert_rule(
+ &global_style_data.shared_lock,
+ rule,
+ contents,
+ index as usize,
+ CssRuleTypes::from_bits(containing_rule_types),
+ loader,
+ allow_import_rules,
+ );
+
+ match result {
+ Ok(new_rule) => {
+ *rule_type = new_rule.rule_type();
+ nsresult::NS_OK
+ },
+ Err(err) => err.into(),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_CssRules_DeleteRule(rules: &LockedCssRules, index: u32) -> nsresult {
+ write_locked_arc(rules, |rules: &mut CssRules| {
+ match rules.remove_rule(index as usize) {
+ Ok(_) => nsresult::NS_OK,
+ Err(err) => err.into(),
+ }
+ })
+}
+
+trait MaybeLocked<Target> {
+ fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a Target;
+}
+
+impl<T> MaybeLocked<T> for T {
+ fn maybe_locked_read<'a>(&'a self, _: &'a SharedRwLockReadGuard) -> &'a T {
+ self
+ }
+}
+
+impl<T> MaybeLocked<T> for Locked<T> {
+ fn maybe_locked_read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {
+ self.read_with(guard)
+ }
+}
+
+macro_rules! impl_basic_rule_funcs_without_getter {
+ {
+ ($rule_type:ty, $maybe_locked_rule_type:ty),
+ debug: $debug:ident,
+ to_css: $to_css:ident,
+ } => {
+ #[cfg(debug_assertions)]
+ #[no_mangle]
+ pub extern "C" fn $debug(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let rule: &$rule_type = rule.maybe_locked_read(&guard);
+ write!(result, "{:?}", *rule).unwrap();
+ }
+
+ #[cfg(not(debug_assertions))]
+ #[no_mangle]
+ pub extern "C" fn $debug(_: &$maybe_locked_rule_type, _: &mut nsACString) {
+ unreachable!()
+ }
+
+ #[no_mangle]
+ pub extern "C" fn $to_css(rule: &$maybe_locked_rule_type, result: &mut nsACString) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let rule: &$rule_type = rule.maybe_locked_read(&guard);
+ rule.to_css(&guard, result).unwrap();
+ }
+ }
+}
+
+macro_rules! impl_basic_rule_funcs {
+ { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
+ getter: $getter:ident,
+ debug: $debug:ident,
+ to_css: $to_css:ident,
+ changed: $changed:ident,
+ } => {
+ #[no_mangle]
+ pub extern "C" fn $getter(
+ rules: &LockedCssRules,
+ index: u32,
+ line: &mut u32,
+ column: &mut u32,
+ ) -> Strong<$maybe_locked_rule_type> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let rules = rules.read_with(&guard);
+ let index = index as usize;
+
+ if index >= rules.0.len() {
+ return Strong::null();
+ }
+
+ match rules.0[index] {
+ CssRule::$name(ref arc) => {
+ let rule: &$rule_type = (&**arc).maybe_locked_read(&guard);
+ let location = rule.source_location;
+ *line = location.line as u32;
+ *column = location.column as u32;
+ arc.clone().into()
+ },
+ _ => {
+ Strong::null()
+ }
+ }
+ }
+
+ #[no_mangle]
+ pub extern "C" fn $changed(
+ styleset: &PerDocumentStyleData,
+ rule: &$maybe_locked_rule_type,
+ sheet: &DomStyleSheet,
+ change_kind: RuleChangeKind,
+ ) {
+ let mut data = styleset.borrow_mut();
+ let data = &mut *data;
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ // TODO(emilio): Would be nice not to deal with refcount bumps here,
+ // but it's probably not a huge deal.
+ let rule = unsafe { CssRule::$name(Arc::from_raw_addrefed(rule)) };
+ let sheet = unsafe { GeckoStyleSheet::new(sheet) };
+ data.stylist.rule_changed(&sheet, &rule, &guard, change_kind);
+ }
+
+ impl_basic_rule_funcs_without_getter! {
+ ($rule_type, $maybe_locked_rule_type),
+ debug: $debug,
+ to_css: $to_css,
+ }
+ }
+}
+
+macro_rules! impl_group_rule_funcs {
+ { ($name:ident, $rule_type:ty, $maybe_locked_rule_type:ty),
+ get_rules: $get_rules:ident,
+ $($basic:tt)+
+ } => {
+ impl_basic_rule_funcs! { ($name, $rule_type, $maybe_locked_rule_type), $($basic)+ }
+
+ #[no_mangle]
+ pub extern "C" fn $get_rules(rule: &$maybe_locked_rule_type) -> Strong<LockedCssRules> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let rule: &$rule_type = rule.maybe_locked_read(&guard);
+ rule.rules.clone().into()
+ }
+ }
+}
+
+impl_basic_rule_funcs! { (Style, StyleRule, Locked<StyleRule>),
+ getter: Servo_CssRules_GetStyleRuleAt,
+ debug: Servo_StyleRule_Debug,
+ to_css: Servo_StyleRule_GetCssText,
+ changed: Servo_StyleSet_StyleRuleChanged,
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_EnsureRules(
+ rule: &LockedStyleRule,
+ read_only: bool,
+) -> Strong<LockedCssRules> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let lock = &global_style_data.shared_lock;
+ if read_only {
+ let guard = lock.read();
+ if let Some(ref rules) = rule.read_with(&guard).rules {
+ return rules.clone().into();
+ }
+ return CssRules::new(vec![], lock).into();
+ }
+ let mut guard = lock.write();
+ rule.write_with(&mut guard)
+ .rules
+ .get_or_insert_with(|| CssRules::new(vec![], lock))
+ .clone()
+ .into()
+}
+
+impl_basic_rule_funcs! { (Import, ImportRule, Locked<ImportRule>),
+ getter: Servo_CssRules_GetImportRuleAt,
+ debug: Servo_ImportRule_Debug,
+ to_css: Servo_ImportRule_GetCssText,
+ changed: Servo_StyleSet_ImportRuleChanged,
+}
+
+impl_basic_rule_funcs_without_getter! { (Keyframe, Locked<Keyframe>),
+ debug: Servo_Keyframe_Debug,
+ to_css: Servo_Keyframe_GetCssText,
+}
+
+impl_basic_rule_funcs! { (Keyframes, KeyframesRule, Locked<KeyframesRule>),
+ getter: Servo_CssRules_GetKeyframesRuleAt,
+ debug: Servo_KeyframesRule_Debug,
+ to_css: Servo_KeyframesRule_GetCssText,
+ changed: Servo_StyleSet_KeyframesRuleChanged,
+}
+
+impl_group_rule_funcs! { (Media, MediaRule, MediaRule),
+ get_rules: Servo_MediaRule_GetRules,
+ getter: Servo_CssRules_GetMediaRuleAt,
+ debug: Servo_MediaRule_Debug,
+ to_css: Servo_MediaRule_GetCssText,
+ changed: Servo_StyleSet_MediaRuleChanged,
+}
+
+impl_basic_rule_funcs! { (Namespace, NamespaceRule, NamespaceRule),
+ getter: Servo_CssRules_GetNamespaceRuleAt,
+ debug: Servo_NamespaceRule_Debug,
+ to_css: Servo_NamespaceRule_GetCssText,
+ changed: Servo_StyleSet_NamespaceRuleChanged,
+}
+
+impl_basic_rule_funcs! { (Page, PageRule, Locked<PageRule>),
+ getter: Servo_CssRules_GetPageRuleAt,
+ debug: Servo_PageRule_Debug,
+ to_css: Servo_PageRule_GetCssText,
+ changed: Servo_StyleSet_PageRuleChanged,
+}
+
+impl_basic_rule_funcs! { (Property, PropertyRule, PropertyRule),
+ getter: Servo_CssRules_GetPropertyRuleAt,
+ debug: Servo_PropertyRule_Debug,
+ to_css: Servo_PropertyRule_GetCssText,
+ changed: Servo_StyleSet_PropertyRuleChanged,
+}
+
+impl_group_rule_funcs! { (Supports, SupportsRule, SupportsRule),
+ get_rules: Servo_SupportsRule_GetRules,
+ getter: Servo_CssRules_GetSupportsRuleAt,
+ debug: Servo_SupportsRule_Debug,
+ to_css: Servo_SupportsRule_GetCssText,
+ changed: Servo_StyleSet_SupportsRuleChanged,
+}
+
+impl_group_rule_funcs! { (Container, ContainerRule, ContainerRule),
+ get_rules: Servo_ContainerRule_GetRules,
+ getter: Servo_CssRules_GetContainerRuleAt,
+ debug: Servo_ContainerRule_Debug,
+ to_css: Servo_ContainerRule_GetCssText,
+ changed: Servo_StyleSet_ContainerRuleChanged,
+}
+
+impl_group_rule_funcs! { (LayerBlock, LayerBlockRule, LayerBlockRule),
+ get_rules: Servo_LayerBlockRule_GetRules,
+ getter: Servo_CssRules_GetLayerBlockRuleAt,
+ debug: Servo_LayerBlockRule_Debug,
+ to_css: Servo_LayerBlockRule_GetCssText,
+ changed: Servo_StyleSet_LayerBlockRuleChanged,
+}
+
+impl_basic_rule_funcs! { (LayerStatement, LayerStatementRule, LayerStatementRule),
+ getter: Servo_CssRules_GetLayerStatementRuleAt,
+ debug: Servo_LayerStatementRule_Debug,
+ to_css: Servo_LayerStatementRule_GetCssText,
+ changed: Servo_StyleSet_LayerStatementRuleChanged,
+}
+
+impl_group_rule_funcs! { (Document, DocumentRule, DocumentRule),
+ get_rules: Servo_DocumentRule_GetRules,
+ getter: Servo_CssRules_GetDocumentRuleAt,
+ debug: Servo_DocumentRule_Debug,
+ to_css: Servo_DocumentRule_GetCssText,
+ changed: Servo_StyleSet_DocumentRuleChanged,
+}
+
+impl_basic_rule_funcs! { (FontFeatureValues, FontFeatureValuesRule, FontFeatureValuesRule),
+ getter: Servo_CssRules_GetFontFeatureValuesRuleAt,
+ debug: Servo_FontFeatureValuesRule_Debug,
+ to_css: Servo_FontFeatureValuesRule_GetCssText,
+ changed: Servo_StyleSet_FontFeatureValuesRuleChanged,
+}
+
+impl_basic_rule_funcs! { (FontPaletteValues, FontPaletteValuesRule, FontPaletteValuesRule),
+ getter: Servo_CssRules_GetFontPaletteValuesRuleAt,
+ debug: Servo_FontPaletteValuesRule_Debug,
+ to_css: Servo_FontPaletteValuesRule_GetCssText,
+ changed: Servo_StyleSet_FontPaletteValuesRuleChanged,
+}
+
+impl_basic_rule_funcs! { (FontFace, FontFaceRule, Locked<FontFaceRule>),
+ getter: Servo_CssRules_GetFontFaceRuleAt,
+ debug: Servo_FontFaceRule_Debug,
+ to_css: Servo_FontFaceRule_GetCssText,
+ changed: Servo_StyleSet_FontFaceRuleChanged,
+}
+
+impl_basic_rule_funcs! { (CounterStyle, CounterStyleRule, Locked<CounterStyleRule>),
+ getter: Servo_CssRules_GetCounterStyleRuleAt,
+ debug: Servo_CounterStyleRule_Debug,
+ to_css: Servo_CounterStyleRule_GetCssText,
+ changed: Servo_StyleSet_CounterStyleRuleChanged,
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_GetStyle(
+ rule: &LockedStyleRule,
+) -> Strong<LockedDeclarationBlock> {
+ read_locked_arc(rule, |rule: &StyleRule| rule.block.clone().into())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_SetStyle(
+ rule: &LockedStyleRule,
+ declarations: &LockedDeclarationBlock,
+) {
+ write_locked_arc(rule, |rule: &mut StyleRule| {
+ rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: &LockedStyleRule, result: &mut nsACString) {
+ read_locked_arc(rule, |rule| rule.selectors.to_css(result).unwrap());
+}
+
+fn desugared_selector_list(rules: &ThinVec<&LockedStyleRule>) -> SelectorList {
+ let mut selectors: Option<SelectorList> = None;
+ for rule in rules.iter().rev() {
+ selectors = Some(read_locked_arc(rule, |rule| match selectors {
+ Some(ref s) => rule.selectors.replace_parent_selector(s),
+ None => rule.selectors.clone(),
+ }));
+ }
+ selectors.expect("Empty rule chain?")
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_GetSelectorDataAtIndex(
+ rules: &ThinVec<&LockedStyleRule>,
+ index: u32,
+ text: Option<&mut nsACString>,
+ specificity: Option<&mut u64>,
+) {
+ let selectors = desugared_selector_list(rules);
+ let Some(selector) = selectors.slice().get(index as usize) else {
+ return;
+ };
+ if let Some(text) = text {
+ selector.to_css(text).unwrap();
+ }
+ if let Some(specificity) = specificity {
+ *specificity = selector.specificity() as u64;
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_GetSelectorCount(rule: &LockedStyleRule) -> u32 {
+ read_locked_arc(rule, |rule| rule.selectors.len() as u32)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(
+ rules: &ThinVec<&LockedStyleRule>,
+ element: &RawGeckoElement,
+ index: u32,
+ host: Option<&RawGeckoElement>,
+ pseudo_type: PseudoStyleType,
+ relevant_link_visited: bool,
+) -> bool {
+ use selectors::matching::{
+ matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,
+ };
+ let selectors = desugared_selector_list(rules);
+ let Some(selector) = selectors.slice().get(index as usize) else {
+ return false;
+ };
+ let mut matching_mode = MatchingMode::Normal;
+ match PseudoElement::from_pseudo_type(pseudo_type, None) {
+ Some(pseudo) => {
+ // We need to make sure that the requested pseudo element type
+ // matches the selector pseudo element type before proceeding.
+ match selector.pseudo_element() {
+ Some(selector_pseudo) if *selector_pseudo == pseudo => {
+ matching_mode = MatchingMode::ForStatelessPseudoElement
+ },
+ _ => return false,
+ };
+ },
+ None => {
+ // Do not attempt to match if a pseudo element is requested and
+ // this is not a pseudo element selector, or vice versa.
+ if selector.has_pseudo_element() {
+ return false;
+ }
+ },
+ };
+
+ let element = GeckoElement(element);
+ let host = host.map(GeckoElement);
+ let quirks_mode = element.as_node().owner_doc().quirks_mode();
+ let mut selector_caches = SelectorCaches::default();
+ let visited_mode = if relevant_link_visited {
+ VisitedHandlingMode::RelevantLinkVisited
+ } else {
+ VisitedHandlingMode::AllLinksUnvisited
+ };
+ let mut ctx = MatchingContext::new_for_visited(
+ matching_mode,
+ /* bloom_filter = */ None,
+ &mut selector_caches,
+ visited_mode,
+ quirks_mode,
+ NeedsSelectorFlags::No,
+ MatchingForInvalidation::No,
+ );
+ ctx.with_shadow_host(host, |ctx| {
+ matches_selector(selector, 0, None, &element, ctx)
+ })
+}
+
+pub type SelectorList = selectors::SelectorList<style::gecko::selector_parser::SelectorImpl>;
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleRule_SetSelectorText(
+ contents: &StylesheetContents,
+ rule: &LockedStyleRule,
+ text: &nsACString,
+) -> bool {
+ let value_str = unsafe { text.as_str_unchecked() };
+
+ write_locked_arc(rule, |rule: &mut StyleRule| {
+ use selectors::parser::ParseRelative;
+ use style::selector_parser::SelectorParser;
+
+ let namespaces = contents.namespaces.read();
+ let url_data = contents.url_data.read();
+ let parser = SelectorParser {
+ stylesheet_origin: contents.origin,
+ namespaces: &namespaces,
+ url_data: &url_data,
+ for_supports_rule: false,
+ };
+
+ // TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style
+ // rule?
+ let mut parser_input = ParserInput::new(&value_str);
+ match SelectorList::parse(
+ &parser,
+ &mut Parser::new(&mut parser_input),
+ ParseRelative::No,
+ ) {
+ Ok(selectors) => {
+ rule.selectors = selectors;
+ true
+ },
+ Err(_) => false,
+ }
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SelectorList_Closest(
+ element: &RawGeckoElement,
+ selectors: &SelectorList,
+) -> *const RawGeckoElement {
+ use style::dom_apis;
+
+ let element = GeckoElement(element);
+ let quirks_mode = element.as_node().owner_doc().quirks_mode();
+ dom_apis::element_closest(element, &selectors, quirks_mode).map_or(ptr::null(), |e| e.0)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SelectorList_Matches(
+ element: &RawGeckoElement,
+ selectors: &SelectorList,
+) -> bool {
+ use style::dom_apis;
+
+ let element = GeckoElement(element);
+ let quirks_mode = element.as_node().owner_doc().quirks_mode();
+ dom_apis::element_matches(&element, &selectors, quirks_mode)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SelectorList_QueryFirst(
+ node: &RawGeckoNode,
+ selectors: &SelectorList,
+ may_use_invalidation: bool,
+) -> *const RawGeckoElement {
+ use style::dom_apis::{self, MayUseInvalidation, QueryFirst};
+
+ let node = GeckoNode(node);
+ let mut result = None;
+
+ let may_use_invalidation = if may_use_invalidation {
+ MayUseInvalidation::Yes
+ } else {
+ MayUseInvalidation::No
+ };
+
+ dom_apis::query_selector::<GeckoElement, QueryFirst>(
+ node,
+ &selectors,
+ &mut result,
+ may_use_invalidation,
+ );
+
+ result.map_or(ptr::null(), |e| e.0)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SelectorList_QueryAll(
+ node: &RawGeckoNode,
+ selectors: &SelectorList,
+ content_list: *mut structs::nsSimpleContentList,
+ may_use_invalidation: bool,
+) {
+ use style::dom_apis::{self, MayUseInvalidation, QueryAll};
+
+ let node = GeckoNode(node);
+ let mut result = SmallVec::new();
+
+ let may_use_invalidation = if may_use_invalidation {
+ MayUseInvalidation::Yes
+ } else {
+ MayUseInvalidation::No
+ };
+
+ dom_apis::query_selector::<GeckoElement, QueryAll>(
+ node,
+ &selectors,
+ &mut result,
+ may_use_invalidation,
+ );
+
+ if !result.is_empty() {
+ // NOTE(emilio): This relies on a slice of GeckoElement having the same
+ // memory representation than a slice of element pointers.
+ bindings::Gecko_ContentList_AppendAll(
+ content_list,
+ result.as_ptr() as *mut *const _,
+ result.len(),
+ )
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ImportRule_GetHref(rule: &LockedImportRule, result: &mut nsAString) {
+ read_locked_arc(rule, |rule: &ImportRule| {
+ write!(result, "{}", rule.url.as_str()).unwrap();
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ImportRule_GetLayerName(rule: &LockedImportRule, result: &mut nsACString) {
+ // https://w3c.github.io/csswg-drafts/cssom/#dom-cssimportrule-layername
+ read_locked_arc(rule, |rule: &ImportRule| match rule.layer {
+ ImportLayer::Named(ref name) => name.to_css(&mut CssWriter::new(result)).unwrap(), // "return the layer name declared in the at-rule itself"
+ ImportLayer::Anonymous => {}, // "or an empty string if the layer is anonymous"
+ ImportLayer::None => result.set_is_void(true), // "or null if the at-rule does not declare a layer"
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ImportRule_GetSupportsText(
+ rule: &LockedImportRule,
+ result: &mut nsACString,
+) {
+ read_locked_arc(rule, |rule: &ImportRule| match rule.supports {
+ Some(ref supports) => supports
+ .condition
+ .to_css(&mut CssWriter::new(result))
+ .unwrap(),
+ None => result.set_is_void(true),
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ImportRule_GetSheet(rule: &LockedImportRule) -> *const DomStyleSheet {
+ read_locked_arc(rule, |rule: &ImportRule| {
+ rule.stylesheet
+ .as_sheet()
+ .map_or(ptr::null(), |s| s.raw() as *const DomStyleSheet)
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ImportRule_SetSheet(
+ rule: &LockedImportRule,
+ sheet: *mut DomStyleSheet,
+) {
+ write_locked_arc(rule, |rule: &mut ImportRule| {
+ rule.stylesheet = ImportSheet::new(GeckoStyleSheet::new(sheet));
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Keyframe_GetKeyText(keyframe: &LockedKeyframe, result: &mut nsACString) {
+ read_locked_arc(keyframe, |keyframe: &Keyframe| {
+ keyframe
+ .selector
+ .to_css(&mut CssWriter::new(result))
+ .unwrap()
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Keyframe_SetKeyText(keyframe: &LockedKeyframe, text: &nsACString) -> bool {
+ let text = unsafe { text.as_str_unchecked() };
+ let mut input = ParserInput::new(&text);
+ if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) {
+ write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
+ keyframe.selector = selector;
+ });
+ true
+ } else {
+ false
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Keyframe_GetStyle(
+ keyframe: &LockedKeyframe,
+) -> Strong<LockedDeclarationBlock> {
+ read_locked_arc(keyframe, |keyframe: &Keyframe| {
+ keyframe.block.clone().into()
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_Keyframe_SetStyle(
+ keyframe: &LockedKeyframe,
+ declarations: &LockedDeclarationBlock,
+) {
+ write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
+ keyframe.block = unsafe { Arc::from_raw_addrefed(declarations) };
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_GetName(rule: &LockedKeyframesRule) -> *mut nsAtom {
+ read_locked_arc(rule, |rule: &KeyframesRule| rule.name.as_atom().as_ptr())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_KeyframesRule_SetName(
+ rule: &LockedKeyframesRule,
+ name: *mut nsAtom,
+) {
+ write_locked_arc(rule, |rule: &mut KeyframesRule| {
+ rule.name = KeyframesName::from_atom(Atom::from_addrefed(name));
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_GetCount(rule: &LockedKeyframesRule) -> u32 {
+ read_locked_arc(rule, |rule: &KeyframesRule| rule.keyframes.len() as u32)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_GetKeyframeAt(
+ rule: &LockedKeyframesRule,
+ index: u32,
+ line: &mut u32,
+ column: &mut u32,
+) -> Strong<LockedKeyframe> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let key = rule.read_with(&guard).keyframes[index as usize].clone();
+ let location = key.read_with(&guard).source_location;
+ *line = location.line as u32;
+ *column = location.column as u32;
+ key.into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_FindRule(
+ rule: &LockedKeyframesRule,
+ key: &nsACString,
+) -> u32 {
+ let key = unsafe { key.as_str_unchecked() };
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ rule.read_with(&guard)
+ .find_rule(&guard, key)
+ .map(|index| index as u32)
+ .unwrap_or(u32::max_value())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_AppendRule(
+ rule: &LockedKeyframesRule,
+ contents: &StylesheetContents,
+ css: &nsACString,
+) -> bool {
+ let css = unsafe { css.as_str_unchecked() };
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+
+ match Keyframe::parse(css, &contents, &global_style_data.shared_lock) {
+ Ok(keyframe) => {
+ write_locked_arc(rule, |rule: &mut KeyframesRule| {
+ rule.keyframes.push(keyframe);
+ });
+ true
+ },
+ Err(..) => false,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_KeyframesRule_DeleteRule(rule: &LockedKeyframesRule, index: u32) {
+ write_locked_arc(rule, |rule: &mut KeyframesRule| {
+ rule.keyframes.remove(index as usize);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaRule_GetMedia(rule: &MediaRule) -> Strong<LockedMediaList> {
+ rule.media_queries.clone().into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_NamespaceRule_GetPrefix(rule: &NamespaceRule) -> *mut nsAtom {
+ rule.prefix
+ .as_ref()
+ .map_or(atom!("").as_ptr(), |a| a.as_ptr())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_NamespaceRule_GetURI(rule: &NamespaceRule) -> *mut nsAtom {
+ rule.url.0.as_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PageRule_GetStyle(rule: &LockedPageRule) -> Strong<LockedDeclarationBlock> {
+ read_locked_arc(rule, |rule: &PageRule| rule.block.clone().into())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PageRule_SetStyle(
+ rule: &LockedPageRule,
+ declarations: &LockedDeclarationBlock,
+) {
+ write_locked_arc(rule, |rule: &mut PageRule| {
+ rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PageRule_GetSelectorText(rule: &LockedPageRule, result: &mut nsACString) {
+ read_locked_arc(rule, |rule: &PageRule| {
+ rule.selectors.to_css(&mut CssWriter::new(result)).unwrap();
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PageRule_SetSelectorText(
+ contents: &StylesheetContents,
+ rule: &LockedPageRule,
+ text: &nsACString,
+) -> bool {
+ let value_str = unsafe { text.as_str_unchecked() };
+
+ write_locked_arc(rule, |rule: &mut PageRule| {
+ use style::stylesheets::PageSelectors;
+
+ let mut parser_input = ParserInput::new(&value_str);
+ let mut parser = Parser::new(&mut parser_input);
+
+ // Ensure that a blank input results in empty page selectors
+ if parser.is_exhausted() {
+ rule.selectors = PageSelectors::default();
+ return true;
+ }
+
+ let url_data = contents.url_data.read();
+ let context = ParserContext::new(
+ Origin::Author,
+ &url_data,
+ None,
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ match parser.parse_entirely(|i| PageSelectors::parse(&context, i)) {
+ Ok(selectors) => {
+ rule.selectors = selectors;
+ true
+ },
+ Err(_) => false,
+ }
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PropertyRule_GetName(rule: &PropertyRule, result: &mut nsACString) {
+ write!(result, "--{}", rule.name.0).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PropertyRule_GetSyntax(rule: &PropertyRule, result: &mut nsACString) {
+ if let Some(syntax) = rule.data.syntax.specified_string() {
+ result.assign(syntax);
+ } else {
+ debug_assert!(false, "Rule without specified syntax?");
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PropertyRule_GetInherits(rule: &PropertyRule) -> bool {
+ rule.inherits()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_PropertyRule_GetInitialValue(
+ rule: &PropertyRule,
+ result: &mut nsACString,
+) -> bool {
+ rule.data
+ .initial_value
+ .to_css(&mut CssWriter::new(result))
+ .unwrap();
+ rule.data.initial_value.is_some()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_SupportsRule_GetConditionText(
+ rule: &SupportsRule,
+ result: &mut nsACString,
+) {
+ rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ContainerRule_GetConditionText(
+ rule: &ContainerRule,
+ result: &mut nsACString,
+) {
+ rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ContainerRule_GetContainerQuery(
+ rule: &ContainerRule,
+ result: &mut nsACString,
+) {
+ rule.query_condition()
+ .to_css(&mut CssWriter::new(result))
+ .unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ContainerRule_QueryContainerFor(
+ rule: &ContainerRule,
+ element: &RawGeckoElement,
+) -> *const RawGeckoElement {
+ rule.condition
+ .find_container(GeckoElement(element), None)
+ .map_or(ptr::null(), |result| result.element.0)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ContainerRule_GetContainerName(
+ rule: &ContainerRule,
+ result: &mut nsACString,
+) {
+ let name = rule.container_name();
+ if !name.is_none() {
+ name.to_css(&mut CssWriter::new(result)).unwrap();
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DocumentRule_GetConditionText(
+ rule: &DocumentRule,
+ result: &mut nsACString,
+) {
+ rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFeatureValuesRule_GetFontFamily(
+ rule: &FontFeatureValuesRule,
+ result: &mut nsACString,
+) {
+ rule.family_names
+ .to_css(&mut CssWriter::new(result))
+ .unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFeatureValuesRule_GetValueText(
+ rule: &FontFeatureValuesRule,
+ result: &mut nsACString,
+) {
+ rule.value_to_css(&mut CssWriter::new(result)).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontPaletteValuesRule_GetName(
+ rule: &FontPaletteValuesRule,
+ result: &mut nsACString,
+) {
+ rule.name.to_css(&mut CssWriter::new(result)).unwrap()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontPaletteValuesRule_GetFontFamily(
+ rule: &FontPaletteValuesRule,
+ result: &mut nsACString,
+) {
+ if !rule.family_names.is_empty() {
+ rule.family_names
+ .to_css(&mut CssWriter::new(result))
+ .unwrap()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontPaletteValuesRule_GetBasePalette(
+ rule: &FontPaletteValuesRule,
+ result: &mut nsACString,
+) {
+ rule.base_palette
+ .to_css(&mut CssWriter::new(result))
+ .unwrap()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontPaletteValuesRule_GetOverrideColors(
+ rule: &FontPaletteValuesRule,
+ result: &mut nsACString,
+) {
+ if !rule.override_colors.is_empty() {
+ rule.override_colors
+ .to_css(&mut CssWriter::new(result))
+ .unwrap()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_CreateEmpty() -> Strong<LockedFontFaceRule> {
+ // XXX This is not great. We should split FontFace descriptor data
+ // from the rule, so that we don't need to create the rule like this
+ // and the descriptor data itself can be hold in UniquePtr from the
+ // Gecko side. See bug 1450904.
+ with_maybe_worker_shared_lock(|lock| {
+ Arc::new(lock.wrap(FontFaceRule::empty(SourceLocation { line: 0, column: 0 }))).into()
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_Clone(
+ rule: &LockedFontFaceRule,
+) -> Strong<LockedFontFaceRule> {
+ let clone = read_locked_arc_worker(rule, |rule: &FontFaceRule| rule.clone());
+ with_maybe_worker_shared_lock(|lock| Arc::new(lock.wrap(clone)).into())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetSourceLocation(
+ rule: &LockedFontFaceRule,
+ line: *mut u32,
+ column: *mut u32,
+) {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let location = rule.source_location;
+ *line.as_mut().unwrap() = location.line as u32;
+ *column.as_mut().unwrap() = location.column as u32;
+ });
+}
+
+macro_rules! apply_font_desc_list {
+ ($apply_macro:ident) => {
+ $apply_macro! {
+ valid: [
+ eCSSFontDesc_Family => family,
+ eCSSFontDesc_Style => style,
+ eCSSFontDesc_Weight => weight,
+ eCSSFontDesc_Stretch => stretch,
+ eCSSFontDesc_Src => sources,
+ eCSSFontDesc_UnicodeRange => unicode_range,
+ eCSSFontDesc_FontFeatureSettings => feature_settings,
+ eCSSFontDesc_FontVariationSettings => variation_settings,
+ eCSSFontDesc_FontLanguageOverride => language_override,
+ eCSSFontDesc_Display => display,
+ eCSSFontDesc_AscentOverride => ascent_override,
+ eCSSFontDesc_DescentOverride => descent_override,
+ eCSSFontDesc_LineGapOverride => line_gap_override,
+ eCSSFontDesc_SizeAdjust => size_adjust,
+ ]
+ invalid: [
+ eCSSFontDesc_UNKNOWN,
+ eCSSFontDesc_COUNT,
+ ]
+ }
+ };
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_Length(rule: &LockedFontFaceRule) -> u32 {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let mut result = 0;
+ macro_rules! count_values {
+ (
+ valid: [$($v_enum_name:ident => $field:ident,)*]
+ invalid: [$($i_enum_name:ident,)*]
+ ) => {
+ $(if rule.$field.is_some() {
+ result += 1;
+ })*
+ }
+ }
+ apply_font_desc_list!(count_values);
+ result
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_IndexGetter(
+ rule: &LockedFontFaceRule,
+ index: u32,
+) -> nsCSSFontDesc {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let mut count = 0;
+ macro_rules! lookup_index {
+ (
+ valid: [$($v_enum_name:ident => $field:ident,)*]
+ invalid: [$($i_enum_name:ident,)*]
+ ) => {
+ $(if rule.$field.is_some() {
+ count += 1;
+ if count - 1 == index {
+ return nsCSSFontDesc::$v_enum_name;
+ }
+ })*
+ }
+ }
+ apply_font_desc_list!(lookup_index);
+ return nsCSSFontDesc::eCSSFontDesc_UNKNOWN;
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetDeclCssText(
+ rule: &LockedFontFaceRule,
+ result: &mut nsACString,
+) {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ rule.decl_to_css(result).unwrap();
+ })
+}
+
+macro_rules! simple_font_descriptor_getter_impl {
+ ($rule:ident, $out:ident, $field:ident, $compute:ident) => {
+ read_locked_arc_worker($rule, |rule: &FontFaceRule| {
+ match rule.$field {
+ None => return false,
+ Some(ref f) => *$out = f.$compute(),
+ }
+ true
+ })
+ };
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetFontWeight(
+ rule: &LockedFontFaceRule,
+ out: &mut font_face::ComputedFontWeightRange,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, weight, compute)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetFontStretch(
+ rule: &LockedFontFaceRule,
+ out: &mut font_face::ComputedFontStretchRange,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, stretch, compute)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetFontStyle(
+ rule: &LockedFontFaceRule,
+ out: &mut font_face::ComputedFontStyleDescriptor,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, style, compute)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetFontDisplay(
+ rule: &LockedFontFaceRule,
+ out: &mut font_face::FontDisplay,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, display, clone)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetFontLanguageOverride(
+ rule: &LockedFontFaceRule,
+ out: &mut computed::FontLanguageOverride,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, language_override, clone)
+}
+
+// Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
+// rather than an actual percentage value.
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetAscentOverride(
+ rule: &LockedFontFaceRule,
+ out: &mut computed::Percentage,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, ascent_override, compute)
+}
+
+// Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
+// rather than an actual percentage value.
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetDescentOverride(
+ rule: &LockedFontFaceRule,
+ out: &mut computed::Percentage,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, descent_override, compute)
+}
+
+// Returns a Percentage of -1.0 if the override descriptor is present but 'normal'
+// rather than an actual percentage value.
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetLineGapOverride(
+ rule: &LockedFontFaceRule,
+ out: &mut computed::Percentage,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, line_gap_override, compute)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetSizeAdjust(
+ rule: &LockedFontFaceRule,
+ out: &mut computed::Percentage,
+) -> bool {
+ simple_font_descriptor_getter_impl!(rule, out, size_adjust, compute)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetFamilyName(
+ rule: &LockedFontFaceRule,
+) -> *mut nsAtom {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ // TODO(emilio): font-family is a mandatory descriptor, can't we unwrap
+ // here, and remove the null-checks in Gecko?
+ rule.family
+ .as_ref()
+ .map_or(ptr::null_mut(), |f| f.name.as_ptr())
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetUnicodeRanges(
+ rule: &LockedFontFaceRule,
+ out_len: *mut usize,
+) -> *const UnicodeRange {
+ *out_len = 0;
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let ranges = match rule.unicode_range {
+ Some(ref ranges) => ranges,
+ None => return ptr::null(),
+ };
+ *out_len = ranges.len();
+ ranges.as_ptr() as *const _
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetSources(
+ rule: &LockedFontFaceRule,
+ out: *mut nsTArray<FontFaceSourceListComponent>,
+) {
+ let out = &mut *out;
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let sources = match rule.sources {
+ Some(ref s) => s,
+ None => return,
+ };
+ let len = sources.0.iter().fold(0, |acc, src| {
+ acc + match *src {
+ Source::Url(ref url) => {
+ (if url.format_hint.is_some() { 2 } else { 1 }) +
+ (if url.tech_flags.is_empty() { 0 } else { 1 })
+ },
+ Source::Local(_) => 1,
+ }
+ });
+
+ out.set_len(len as u32);
+
+ let mut iter = out.iter_mut();
+
+ {
+ let mut set_next = |component: FontFaceSourceListComponent| {
+ *iter.next().expect("miscalculated length") = component;
+ };
+
+ for source in sources.0.iter() {
+ match *source {
+ Source::Url(ref url) => {
+ set_next(FontFaceSourceListComponent::Url(&url.url));
+ if let Some(hint) = &url.format_hint {
+ match hint {
+ FontFaceSourceFormat::Keyword(kw) => {
+ set_next(FontFaceSourceListComponent::FormatHintKeyword(*kw))
+ },
+ FontFaceSourceFormat::String(s) => {
+ set_next(FontFaceSourceListComponent::FormatHintString {
+ length: s.len(),
+ utf8_bytes: s.as_ptr(),
+ })
+ },
+ }
+ }
+ if !url.tech_flags.is_empty() {
+ set_next(FontFaceSourceListComponent::TechFlags(url.tech_flags));
+ }
+ },
+ Source::Local(ref name) => {
+ set_next(FontFaceSourceListComponent::Local(name.name.as_ptr()));
+ },
+ }
+ }
+ }
+
+ assert!(iter.next().is_none(), "miscalculated");
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetVariationSettings(
+ rule: &LockedFontFaceRule,
+ variations: *mut nsTArray<structs::gfxFontVariation>,
+) {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let source_variations = match rule.variation_settings {
+ Some(ref v) => v,
+ None => return,
+ };
+
+ (*variations).set_len(source_variations.0.len() as u32);
+ for (target, source) in (*variations).iter_mut().zip(source_variations.0.iter()) {
+ *target = structs::gfxFontVariation {
+ mTag: source.tag.0,
+ mValue: source.value.get(),
+ };
+ }
+ });
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetFeatureSettings(
+ rule: &LockedFontFaceRule,
+ features: *mut nsTArray<structs::gfxFontFeature>,
+) {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let source_features = match rule.feature_settings {
+ Some(ref v) => v,
+ None => return,
+ };
+
+ (*features).set_len(source_features.0.len() as u32);
+ for (target, source) in (*features).iter_mut().zip(source_features.0.iter()) {
+ *target = structs::gfxFontFeature {
+ mTag: source.tag.0,
+ mValue: source.value.value() as u32,
+ };
+ }
+ });
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_GetDescriptorCssText(
+ rule: &LockedFontFaceRule,
+ desc: nsCSSFontDesc,
+ result: &mut nsACString,
+) {
+ read_locked_arc_worker(rule, |rule: &FontFaceRule| {
+ let mut writer = CssWriter::new(result);
+ macro_rules! to_css_text {
+ (
+ valid: [$($v_enum_name:ident => $field:ident,)*]
+ invalid: [$($i_enum_name:ident,)*]
+ ) => {
+ match desc {
+ $(
+ nsCSSFontDesc::$v_enum_name => {
+ if let Some(ref value) = rule.$field {
+ value.to_css(&mut writer).unwrap();
+ }
+ }
+ )*
+ $(
+ nsCSSFontDesc::$i_enum_name => {
+ debug_assert!(false, "not a valid font descriptor");
+ }
+ )*
+ }
+ }
+ }
+ apply_font_desc_list!(to_css_text)
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_SetDescriptor(
+ rule: &LockedFontFaceRule,
+ desc: nsCSSFontDesc,
+ value: &nsACString,
+ data: *mut URLExtraData,
+ out_changed: *mut bool,
+) -> bool {
+ let value = value.as_str_unchecked();
+ let mut input = ParserInput::new(&value);
+ let mut parser = Parser::new(&mut input);
+ let url_data = UrlExtraData::from_ptr_ref(&data);
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ Some(CssRuleType::FontFace),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
+ macro_rules! to_css_text {
+ (
+ valid: [$($v_enum_name:ident => $field:ident,)*]
+ invalid: [$($i_enum_name:ident,)*]
+ ) => {
+ match desc {
+ $(
+ nsCSSFontDesc::$v_enum_name => {
+ if let Ok(value) = parser.parse_entirely(|i| Parse::parse(&context, i)) {
+ let result = Some(value);
+ *out_changed = result != rule.$field;
+ rule.$field = result;
+ true
+ } else {
+ false
+ }
+ }
+ )*
+ $(
+ nsCSSFontDesc::$i_enum_name => {
+ debug_assert!(false, "not a valid font descriptor");
+ false
+ }
+ )*
+ }
+ }
+ }
+ apply_font_desc_list!(to_css_text)
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_ResetDescriptor(
+ rule: &LockedFontFaceRule,
+ desc: nsCSSFontDesc,
+) {
+ write_locked_arc_worker(rule, |rule: &mut FontFaceRule| {
+ macro_rules! reset_desc {
+ (
+ valid: [$($v_enum_name:ident => $field:ident,)*]
+ invalid: [$($i_enum_name:ident,)*]
+ ) => {
+ match desc {
+ $(nsCSSFontDesc::$v_enum_name => rule.$field = None,)*
+ $(nsCSSFontDesc::$i_enum_name => debug_assert!(false, "not a valid font descriptor"),)*
+ }
+ }
+ }
+ apply_font_desc_list!(reset_desc)
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetName(
+ rule: &LockedCounterStyleRule,
+) -> *mut nsAtom {
+ read_locked_arc(rule, |rule: &CounterStyleRule| rule.name().0.as_ptr())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_SetName(
+ rule: &LockedCounterStyleRule,
+ value: &nsACString,
+) -> bool {
+ let value = value.as_str_unchecked();
+ let mut input = ParserInput::new(&value);
+ let mut parser = Parser::new(&mut input);
+ match parser.parse_entirely(counter_style::parse_counter_style_name_definition) {
+ Ok(name) => {
+ write_locked_arc(rule, |rule: &mut CounterStyleRule| rule.set_name(name));
+ true
+ },
+ Err(_) => false,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetGeneration(
+ rule: &LockedCounterStyleRule,
+) -> u32 {
+ read_locked_arc(rule, |rule: &CounterStyleRule| rule.generation())
+}
+
+fn symbol_to_string(s: &counter_style::Symbol) -> nsString {
+ match *s {
+ counter_style::Symbol::String(ref s) => nsString::from(&**s),
+ counter_style::Symbol::Ident(ref i) => nsString::from(i.0.as_slice()),
+ }
+}
+
+// TODO(emilio): Cbindgen could be used to simplify a bunch of code here.
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetPad(
+ rule: &LockedCounterStyleRule,
+ width: &mut i32,
+ symbol: &mut nsString,
+) -> bool {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ let pad = match rule.pad() {
+ Some(pad) => pad,
+ None => return false,
+ };
+ *width = pad.0.value();
+ *symbol = symbol_to_string(&pad.1);
+ true
+ })
+}
+
+fn get_symbol(s: Option<&counter_style::Symbol>, out: &mut nsString) -> bool {
+ let s = match s {
+ Some(s) => s,
+ None => return false,
+ };
+ *out = symbol_to_string(s);
+ true
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetPrefix(
+ rule: &LockedCounterStyleRule,
+ out: &mut nsString,
+) -> bool {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ get_symbol(rule.prefix(), out)
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetSuffix(
+ rule: &LockedCounterStyleRule,
+ out: &mut nsString,
+) -> bool {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ get_symbol(rule.suffix(), out)
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetNegative(
+ rule: &LockedCounterStyleRule,
+ prefix: &mut nsString,
+ suffix: &mut nsString,
+) -> bool {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ let negative = match rule.negative() {
+ Some(n) => n,
+ None => return false,
+ };
+ *prefix = symbol_to_string(&negative.0);
+ *suffix = match negative.1 {
+ Some(ref s) => symbol_to_string(s),
+ None => nsString::new(),
+ };
+ true
+ })
+}
+
+#[repr(u8)]
+pub enum IsOrdinalInRange {
+ Auto,
+ InRange,
+ NotInRange,
+ NoOrdinalSpecified,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_IsInRange(
+ rule: &LockedCounterStyleRule,
+ ordinal: i32,
+) -> IsOrdinalInRange {
+ use style::counter_style::CounterBound;
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ let range = match rule.range() {
+ Some(r) => r,
+ None => return IsOrdinalInRange::NoOrdinalSpecified,
+ };
+
+ if range.0.is_empty() {
+ return IsOrdinalInRange::Auto;
+ }
+
+ let in_range = range.0.iter().any(|r| {
+ if let CounterBound::Integer(start) = r.start {
+ if start.value() > ordinal {
+ return false;
+ }
+ }
+
+ if let CounterBound::Integer(end) = r.end {
+ if end.value() < ordinal {
+ return false;
+ }
+ }
+
+ true
+ });
+
+ if in_range {
+ IsOrdinalInRange::InRange
+ } else {
+ IsOrdinalInRange::NotInRange
+ }
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetSymbols(
+ rule: &LockedCounterStyleRule,
+ symbols: &mut style::OwnedSlice<nsString>,
+) {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ *symbols = match rule.symbols() {
+ Some(s) => s.0.iter().map(symbol_to_string).collect(),
+ None => style::OwnedSlice::default(),
+ };
+ })
+}
+
+#[repr(C)]
+pub struct AdditiveSymbol {
+ pub weight: i32,
+ pub symbol: nsString,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetAdditiveSymbols(
+ rule: &LockedCounterStyleRule,
+ symbols: &mut style::OwnedSlice<AdditiveSymbol>,
+) {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ *symbols = match rule.additive_symbols() {
+ Some(s) => {
+ s.0.iter()
+ .map(|s| AdditiveSymbol {
+ weight: s.weight.value(),
+ symbol: symbol_to_string(&s.symbol),
+ })
+ .collect()
+ },
+ None => style::OwnedSlice::default(),
+ };
+ })
+}
+
+#[repr(C, u8)]
+pub enum CounterSpeakAs {
+ None,
+ Auto,
+ Bullets,
+ Numbers,
+ Words,
+ Ident(*mut nsAtom),
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetSpeakAs(
+ rule: &LockedCounterStyleRule,
+ out: &mut CounterSpeakAs,
+) {
+ use style::counter_style::SpeakAs;
+ *out = read_locked_arc(rule, |rule: &CounterStyleRule| {
+ let speak_as = match rule.speak_as() {
+ Some(s) => s,
+ None => return CounterSpeakAs::None,
+ };
+ match *speak_as {
+ SpeakAs::Auto => CounterSpeakAs::Auto,
+ SpeakAs::Bullets => CounterSpeakAs::Bullets,
+ SpeakAs::Numbers => CounterSpeakAs::Numbers,
+ SpeakAs::Words => CounterSpeakAs::Words,
+ SpeakAs::Other(ref other) => CounterSpeakAs::Ident(other.0.as_ptr()),
+ }
+ });
+}
+
+#[repr(u8)]
+pub enum CounterSystem {
+ Cyclic = 0,
+ Numeric,
+ Alphabetic,
+ Symbolic,
+ Additive,
+ Fixed,
+ Extends,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetSystem(
+ rule: &LockedCounterStyleRule,
+) -> CounterSystem {
+ use style::counter_style::System;
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ match *rule.resolved_system() {
+ System::Cyclic => CounterSystem::Cyclic,
+ System::Numeric => CounterSystem::Numeric,
+ System::Alphabetic => CounterSystem::Alphabetic,
+ System::Symbolic => CounterSystem::Symbolic,
+ System::Additive => CounterSystem::Additive,
+ System::Fixed { .. } => CounterSystem::Fixed,
+ System::Extends(_) => CounterSystem::Extends,
+ }
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetExtended(
+ rule: &LockedCounterStyleRule,
+) -> *mut nsAtom {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ match *rule.resolved_system() {
+ counter_style::System::Extends(ref name) => name.0.as_ptr(),
+ _ => {
+ debug_assert!(false, "Not extends system");
+ ptr::null_mut()
+ },
+ }
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetFixedFirstValue(
+ rule: &LockedCounterStyleRule,
+) -> i32 {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ match *rule.resolved_system() {
+ counter_style::System::Fixed { first_symbol_value } => {
+ first_symbol_value.map_or(1, |v| v.value())
+ },
+ _ => {
+ debug_assert!(false, "Not fixed system");
+ 0
+ },
+ }
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CounterStyleRule_GetFallback(
+ rule: &LockedCounterStyleRule,
+) -> *mut nsAtom {
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ rule.fallback().map_or(ptr::null_mut(), |i| i.0 .0.as_ptr())
+ })
+}
+
+macro_rules! counter_style_descriptors {
+ {
+ valid: [
+ $($desc:ident => $getter:ident / $setter:ident,)+
+ ]
+ invalid: [
+ $($i_desc:ident,)+
+ ]
+ } => {
+ #[no_mangle]
+ pub unsafe extern "C" fn Servo_CounterStyleRule_GetDescriptorCssText(
+ rule: &LockedCounterStyleRule,
+ desc: nsCSSCounterDesc,
+ result: &mut nsACString,
+ ) {
+ let mut writer = CssWriter::new(result);
+ read_locked_arc(rule, |rule: &CounterStyleRule| {
+ match desc {
+ $(nsCSSCounterDesc::$desc => {
+ if let Some(value) = rule.$getter() {
+ value.to_css(&mut writer).unwrap();
+ }
+ })+
+ $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
+ }
+ });
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn Servo_CounterStyleRule_SetDescriptor(
+ rule: &LockedCounterStyleRule,
+ desc: nsCSSCounterDesc,
+ value: &nsACString,
+ ) -> bool {
+ let value = value.as_str_unchecked();
+ let mut input = ParserInput::new(&value);
+ let mut parser = Parser::new(&mut input);
+ let url_data = dummy_url_data();
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ Some(CssRuleType::CounterStyle),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ write_locked_arc(rule, |rule: &mut CounterStyleRule| {
+ match desc {
+ $(nsCSSCounterDesc::$desc => {
+ match parser.parse_entirely(|i| Parse::parse(&context, i)) {
+ Ok(value) => rule.$setter(value),
+ Err(_) => false,
+ }
+ })+
+ $(nsCSSCounterDesc::$i_desc => unreachable!(),)+
+ }
+ })
+ }
+ }
+}
+
+counter_style_descriptors! {
+ valid: [
+ eCSSCounterDesc_System => system / set_system,
+ eCSSCounterDesc_Symbols => symbols / set_symbols,
+ eCSSCounterDesc_AdditiveSymbols => additive_symbols / set_additive_symbols,
+ eCSSCounterDesc_Negative => negative / set_negative,
+ eCSSCounterDesc_Prefix => prefix / set_prefix,
+ eCSSCounterDesc_Suffix => suffix / set_suffix,
+ eCSSCounterDesc_Range => range / set_range,
+ eCSSCounterDesc_Pad => pad / set_pad,
+ eCSSCounterDesc_Fallback => fallback / set_fallback,
+ eCSSCounterDesc_SpeakAs => speak_as / set_speak_as,
+ ]
+ invalid: [
+ eCSSCounterDesc_UNKNOWN,
+ eCSSCounterDesc_COUNT,
+ ]
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ComputedValues_GetForPageContent(
+ raw_data: &PerDocumentStyleData,
+ page_name: *const nsAtom,
+ pseudos: PagePseudoClassFlags,
+) -> Strong<ComputedValues> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let guards = StylesheetGuards::same(&guard);
+ let data = raw_data.borrow_mut();
+ let cascade_data = data.stylist.cascade_data();
+
+ let mut extra_declarations = vec![];
+ let iter = data.stylist.iter_extra_data_origins_rev();
+ let name = if !page_name.is_null() {
+ Some(Atom::from_raw(page_name as *mut nsAtom))
+ } else {
+ None
+ };
+ for (data, origin) in iter {
+ data.pages.match_and_append_rules(
+ &mut extra_declarations,
+ origin,
+ &guards,
+ cascade_data,
+ &name,
+ pseudos,
+ );
+ }
+
+ let rule_node = data.stylist.rule_node_for_precomputed_pseudo(
+ &guards,
+ &PseudoElement::PageContent,
+ extra_declarations,
+ );
+
+ data.stylist
+ .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
+ &guards,
+ &PseudoElement::PageContent,
+ None,
+ rule_node,
+ )
+ .into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ComputedValues_GetForAnonymousBox(
+ parent_style_or_null: Option<&ComputedValues>,
+ pseudo: PseudoStyleType,
+ raw_data: &PerDocumentStyleData,
+) -> Strong<ComputedValues> {
+ let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
+ debug_assert!(pseudo.is_anon_box());
+ debug_assert_ne!(pseudo, PseudoElement::PageContent);
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let guards = StylesheetGuards::same(&guard);
+ let data = raw_data.borrow_mut();
+ let rule_node = data
+ .stylist
+ .rule_node_for_precomputed_pseudo(&guards, &pseudo, vec![]);
+
+ data.stylist
+ .precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
+ &guards,
+ &pseudo,
+ parent_style_or_null.map(|x| &*x),
+ rule_node,
+ )
+ .into()
+}
+
+fn get_functional_pseudo_parameter_atom(
+ functional_pseudo_parameter: *mut nsAtom,
+) -> Option<AtomIdent> {
+ if functional_pseudo_parameter.is_null() {
+ None
+ } else {
+ Some(AtomIdent::new(unsafe {
+ Atom::from_raw(functional_pseudo_parameter)
+ }))
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ResolvePseudoStyle(
+ element: &RawGeckoElement,
+ pseudo_type: PseudoStyleType,
+ functional_pseudo_parameter: *mut nsAtom,
+ is_probe: bool,
+ inherited_style: Option<&ComputedValues>,
+ raw_data: &PerDocumentStyleData,
+) -> Strong<ComputedValues> {
+ let element = GeckoElement(element);
+ let doc_data = raw_data.borrow();
+
+ debug!(
+ "Servo_ResolvePseudoStyle: {:?} {:?}, is_probe: {}",
+ element,
+ PseudoElement::from_pseudo_type(
+ pseudo_type,
+ get_functional_pseudo_parameter_atom(functional_pseudo_parameter)
+ ),
+ is_probe
+ );
+
+ let data = element.borrow_data();
+
+ let data = match data.as_ref() {
+ Some(data) if data.has_styles() => data,
+ _ => {
+ // FIXME(bholley, emilio): Assert against this.
+ //
+ // Known offender is nsMathMLmoFrame::MarkIntrinsicISizesDirty,
+ // which goes and does a bunch of work involving style resolution.
+ //
+ // Bug 1403865 tracks fixing it, and potentially adding an assert
+ // here instead.
+ warn!("Calling Servo_ResolvePseudoStyle on unstyled element");
+ return if is_probe {
+ Strong::null()
+ } else {
+ doc_data.default_computed_values().clone().into()
+ };
+ },
+ };
+
+ let pseudo_element = PseudoElement::from_pseudo_type(
+ pseudo_type,
+ get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
+ )
+ .expect("ResolvePseudoStyle with a non-pseudo?");
+
+ let matching_fn = |pseudo: &PseudoElement| *pseudo == pseudo_element;
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let style = get_pseudo_style(
+ &guard,
+ element,
+ &pseudo_element,
+ RuleInclusion::All,
+ &data.styles,
+ inherited_style,
+ &doc_data.stylist,
+ is_probe,
+ /* matching_func = */
+ if pseudo_element.is_highlight() {
+ Some(&matching_fn)
+ } else {
+ None
+ },
+ );
+
+ match style {
+ Some(s) => s.into(),
+ None => {
+ debug_assert!(is_probe);
+ Strong::null()
+ },
+ }
+}
+
+fn debug_atom_array(atoms: &nsTArray<structs::RefPtr<nsAtom>>) -> String {
+ let mut result = String::from("[");
+ for atom in atoms.iter() {
+ if atom.mRawPtr.is_null() {
+ result += "(null), ";
+ } else {
+ let atom = unsafe { WeakAtom::new(atom.mRawPtr) };
+ write!(result, "{}, ", atom).unwrap();
+ }
+ }
+ result.push(']');
+ result
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_ResolveXULTreePseudoStyle(
+ element: &RawGeckoElement,
+ pseudo_tag: *mut nsAtom,
+ inherited_style: &ComputedValues,
+ input_word: &nsTArray<structs::RefPtr<nsAtom>>,
+ raw_data: &PerDocumentStyleData,
+) -> Strong<ComputedValues> {
+ let element = GeckoElement(element);
+ let data = element
+ .borrow_data()
+ .expect("Calling ResolveXULTreePseudoStyle on unstyled element?");
+
+ let pseudo = unsafe {
+ Atom::with(pseudo_tag, |atom| {
+ PseudoElement::from_tree_pseudo_atom(atom, Box::new([]))
+ })
+ .expect("ResolveXULTreePseudoStyle with a non-tree pseudo?")
+ };
+ let doc_data = raw_data.borrow();
+
+ debug!(
+ "ResolveXULTreePseudoStyle: {:?} {:?} {}",
+ element,
+ pseudo,
+ debug_atom_array(input_word)
+ );
+
+ let matching_fn = |pseudo: &PseudoElement| {
+ let args = pseudo
+ .tree_pseudo_args()
+ .expect("Not a tree pseudo-element?");
+ args.iter()
+ .all(|atom| input_word.iter().any(|item| atom.as_ptr() == item.mRawPtr))
+ };
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ get_pseudo_style(
+ &guard,
+ element,
+ &pseudo,
+ RuleInclusion::All,
+ &data.styles,
+ Some(inherited_style),
+ &doc_data.stylist,
+ /* is_probe = */ false,
+ Some(&matching_fn),
+ )
+ .unwrap()
+ .into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_SetExplicitStyle(element: &RawGeckoElement, style: &ComputedValues) {
+ let element = GeckoElement(element);
+ debug!("Servo_SetExplicitStyle: {:?}", element);
+ // We only support this API for initial styling. There's no reason it couldn't
+ // work for other things, we just haven't had a reason to do so.
+ debug_assert!(!element.has_data());
+ let mut data = unsafe { element.ensure_data() };
+ data.styles.primary = Some(unsafe { Arc::from_raw_addrefed(style) });
+}
+
+fn get_pseudo_style(
+ guard: &SharedRwLockReadGuard,
+ element: GeckoElement,
+ pseudo: &PseudoElement,
+ rule_inclusion: RuleInclusion,
+ styles: &ElementStyles,
+ inherited_styles: Option<&ComputedValues>,
+ stylist: &Stylist,
+ is_probe: bool,
+ matching_func: Option<&dyn Fn(&PseudoElement) -> bool>,
+) -> Option<Arc<ComputedValues>> {
+ let style = match pseudo.cascade_type() {
+ PseudoElementCascadeType::Eager => {
+ match *pseudo {
+ PseudoElement::FirstLetter => {
+ styles.pseudos.get(&pseudo).map(|pseudo_styles| {
+ // inherited_styles can be None when doing lazy resolution
+ // (e.g. for computed style) or when probing. In that case
+ // we just inherit from our element, which is what Gecko
+ // does in that situation. What should actually happen in
+ // the computed style case is a bit unclear.
+ let inherited_styles = inherited_styles.unwrap_or(styles.primary());
+ let guards = StylesheetGuards::same(guard);
+ let inputs = CascadeInputs::new_from_style(pseudo_styles);
+ stylist.compute_pseudo_element_style_with_inputs(
+ inputs,
+ pseudo,
+ &guards,
+ Some(inherited_styles),
+ Some(element),
+ )
+ })
+ },
+ _ => {
+ // Unfortunately, we can't assert that inherited_styles, if
+ // present, is pointer-equal to styles.primary(), or even
+ // equal in any meaningful way. The way it can fail is as
+ // follows. Say we append an element with a ::before,
+ // ::after, or ::first-line to a parent with a ::first-line,
+ // such that the element ends up on the first line of the
+ // parent (e.g. it's an inline-block in the case it has a
+ // ::first-line, or any container in the ::before/::after
+ // cases). Then gecko will update its frame's style to
+ // inherit from the parent's ::first-line. The next time we
+ // try to get the ::before/::after/::first-line style for
+ // the kid, we'll likely pass in the frame's style as
+ // inherited_styles, but that's not pointer-identical to
+ // styles.primary(), because it got reparented.
+ //
+ // Now in practice this turns out to be OK, because all the
+ // cases in which there's a mismatch go ahead and reparent
+ // styles again as needed to make sure the ::first-line
+ // affects all the things it should affect. But it makes it
+ // impossible to assert anything about the two styles
+ // matching here, unfortunately.
+ styles.pseudos.get(&pseudo).cloned()
+ },
+ }
+ },
+ PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
+ PseudoElementCascadeType::Lazy => {
+ debug_assert!(
+ inherited_styles.is_none() ||
+ ptr::eq(inherited_styles.unwrap(), &**styles.primary())
+ );
+ let originating_element_style = styles.primary();
+ let guards = StylesheetGuards::same(guard);
+ stylist.lazily_compute_pseudo_element_style(
+ &guards,
+ element,
+ &pseudo,
+ rule_inclusion,
+ originating_element_style,
+ is_probe,
+ matching_func,
+ )
+ },
+ };
+
+ if is_probe {
+ return style;
+ }
+
+ Some(style.unwrap_or_else(|| {
+ StyleBuilder::for_inheritance(
+ stylist.device(),
+ Some(stylist),
+ Some(styles.primary()),
+ Some(pseudo),
+ )
+ .build()
+ }))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ComputedValues_Inherit(
+ raw_data: &PerDocumentStyleData,
+ pseudo: PseudoStyleType,
+ parent_style_context: Option<&ComputedValues>,
+ target: structs::InheritTarget,
+) -> Strong<ComputedValues> {
+ let data = raw_data.borrow();
+
+ let for_text = target == structs::InheritTarget::Text;
+ let pseudo = PseudoElement::from_pseudo_type(pseudo, None).unwrap();
+ debug_assert!(pseudo.is_anon_box());
+
+ let mut style = StyleBuilder::for_inheritance(
+ data.stylist.device(),
+ Some(&data.stylist),
+ parent_style_context,
+ Some(&pseudo),
+ );
+
+ if for_text {
+ StyleAdjuster::new(&mut style).adjust_for_text();
+ }
+
+ style.build().into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(
+ values: &ComputedValues,
+) -> bool {
+ let ui = values.get_ui();
+ ui.specifies_animations() || ui.specifies_transitions()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_GetStyleRuleList(
+ values: &ComputedValues,
+ rules: &mut ThinVec<*const LockedStyleRule>,
+) {
+ let rule_node = match values.rules {
+ Some(ref r) => r,
+ None => return,
+ };
+
+ for node in rule_node.self_and_ancestors() {
+ let style_rule = match node.style_source().and_then(|x| x.as_rule()) {
+ Some(rule) => rule,
+ _ => continue,
+ };
+
+ // For the rules with any important declaration, we insert them into
+ // rule tree twice, one for normal level and another for important
+ // level. So, we skip the important one to keep the specificity order of
+ // rules.
+ if node.importance().important() {
+ continue;
+ }
+
+ rules.push(&*style_rule);
+ }
+}
+
+/// println_stderr!() calls Gecko's printf_stderr(), which, unlike eprintln!(),
+/// will funnel output to Android logcat.
+#[cfg(feature = "gecko_debug")]
+macro_rules! println_stderr {
+ ($($e:expr),+) => {
+ {
+ let mut s = nsCString::new();
+ write!(s, $($e),+).unwrap();
+ s.write_char('\n').unwrap();
+ unsafe { bindings::Gecko_PrintfStderr(&s); }
+ }
+ }
+}
+
+#[cfg(feature = "gecko_debug")]
+fn dump_properties_and_rules(cv: &ComputedValues, properties: &LonghandIdSet) {
+ println_stderr!(" Properties:");
+ for p in properties.iter() {
+ let mut v = nsCString::new();
+ cv.computed_or_resolved_value(p, None, &mut v).unwrap();
+ println_stderr!(" {:?}: {}", p, v);
+ }
+ dump_rules(cv);
+}
+
+#[cfg(feature = "gecko_debug")]
+fn dump_rules(cv: &ComputedValues) {
+ println_stderr!(" Rules({:?}):", cv.pseudo());
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ if let Some(rules) = cv.rules.as_ref() {
+ for rn in rules.self_and_ancestors() {
+ if rn.importance().important() {
+ continue;
+ }
+ if let Some(d) = rn.style_source().and_then(|s| s.as_declarations()) {
+ println_stderr!(" [DeclarationBlock: {:?}]", d);
+ }
+ if let Some(r) = rn.style_source().and_then(|s| s.as_rule()) {
+ let mut s = nsCString::new();
+ r.read_with(&guard).to_css(&guard, &mut s).unwrap();
+ println_stderr!(" {}", s);
+ }
+ }
+ }
+}
+
+#[cfg(feature = "gecko_debug")]
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_EqualForCachedAnonymousContentStyle(
+ a: &ComputedValues,
+ b: &ComputedValues,
+) -> bool {
+ let mut differing_properties = a.differing_properties(b);
+
+ // Ignore any difference in -x-lang, which we can't override in the rules in scrollbars.css,
+ // but which makes no difference for the anonymous content subtrees we cache style for.
+ differing_properties.remove(LonghandId::XLang);
+ // Similarly, -x-lang can influence the font-family fallback we have for the initial
+ // font-family so remove it as well.
+ differing_properties.remove(LonghandId::FontFamily);
+ // We reset font-size to an explicit pixel value, and thus it can get affected by our inherited
+ // effective zoom. But we don't care about it for the same reason as above.
+ differing_properties.remove(LonghandId::FontSize);
+
+ // Ignore any difference in pref-controlled, inherited properties. These properties may or may
+ // not be set by the 'all' declaration in scrollbars.css, depending on whether the pref was
+ // enabled at the time the UA sheets were parsed.
+ //
+ // If you add a new pref-controlled, inherited property, it must be defined with
+ // `has_effect_on_gecko_scrollbars=False` to declare that different values of this property on
+ // a <scrollbar> element or its descendant scrollbar part elements should have no effect on
+ // their rendering and behavior.
+ //
+ // If you do need a pref-controlled, inherited property to have an effect on these elements,
+ // then you will need to add some checks to the
+ // nsIAnonymousContentCreator::CreateAnonymousContent implementations of nsHTMLScrollFrame and
+ // nsScrollbarFrame to clear the AnonymousContentKey if a non-initial value is used.
+ differing_properties.remove_all(&LonghandIdSet::has_no_effect_on_gecko_scrollbars());
+
+ if !differing_properties.is_empty() {
+ println_stderr!("Actual style:");
+ dump_properties_and_rules(a, &differing_properties);
+ println_stderr!("Expected style:");
+ dump_properties_and_rules(b, &differing_properties);
+ }
+
+ differing_properties.is_empty()
+}
+
+#[cfg(feature = "gecko_debug")]
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_DumpMatchedRules(s: &ComputedValues) {
+ dump_rules(s);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ComputedValues_BlockifiedDisplay(
+ style: &ComputedValues,
+ is_root_element: bool,
+) -> u16 {
+ let display = style.get_box().clone_display();
+ let blockified_display = display.equivalent_block_display(is_root_element);
+ blockified_display.to_u16()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_Init(doc: &structs::Document) -> *mut PerDocumentStyleData {
+ let data = Box::new(PerDocumentStyleData::new(doc));
+
+ // Do this here rather than in Servo_Initialize since we need a document to
+ // get the default computed values from.
+ style::properties::generated::gecko::assert_initial_values_match(&data);
+
+ Box::into_raw(data) as *mut PerDocumentStyleData
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_Drop(data: *mut PerDocumentStyleData) {
+ let _ = Box::from_raw(data);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_RebuildCachedData(raw_data: &PerDocumentStyleData) {
+ let mut data = raw_data.borrow_mut();
+ data.stylist.device_mut().rebuild_cached_data();
+ data.undisplayed_style_cache.clear();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_CompatModeChanged(raw_data: &PerDocumentStyleData) {
+ let mut data = raw_data.borrow_mut();
+ let quirks_mode = data.stylist.device().document().mCompatMode;
+ data.stylist.set_quirks_mode(quirks_mode.into());
+}
+
+fn parse_property_into(
+ declarations: &mut SourcePropertyDeclaration,
+ property_id: PropertyId,
+ value: &nsACString,
+ origin: Origin,
+ url_data: &UrlExtraData,
+ parsing_mode: ParsingMode,
+ quirks_mode: QuirksMode,
+ rule_type: CssRuleType,
+ reporter: Option<&dyn ParseErrorReporter>,
+) -> Result<(), ()> {
+ let value = unsafe { value.as_str_unchecked() };
+
+ if let Some(non_custom) = property_id.non_custom_id() {
+ if !non_custom.allowed_in_rule(rule_type.into()) {
+ return Err(());
+ }
+ }
+
+ parse_one_declaration_into(
+ declarations,
+ property_id,
+ value,
+ origin,
+ url_data,
+ reporter,
+ parsing_mode,
+ quirks_mode,
+ rule_type,
+ )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ParseProperty(
+ property: &structs::AnimatedPropertyID,
+ value: &nsACString,
+ data: *mut URLExtraData,
+ parsing_mode: ParsingMode,
+ quirks_mode: nsCompatibility,
+ loader: *mut Loader,
+ rule_type: CssRuleType,
+) -> Strong<LockedDeclarationBlock> {
+ let id = get_property_id_from_animatedpropertyid!(property, Strong::null());
+ let mut declarations = SourcePropertyDeclaration::default();
+ let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
+ let data = UrlExtraData::from_ptr_ref(&data);
+ let result = parse_property_into(
+ &mut declarations,
+ id,
+ value,
+ Origin::Author,
+ data,
+ parsing_mode,
+ quirks_mode.into(),
+ rule_type,
+ reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
+ );
+
+ match result {
+ Ok(()) => {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let mut block = PropertyDeclarationBlock::new();
+ block.extend(declarations.drain(), Importance::Normal);
+ Arc::new(global_style_data.shared_lock.wrap(block)).into()
+ },
+ Err(_) => Strong::null(),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ParseEasing(
+ easing: &nsACString,
+ output: &mut ComputedTimingFunction,
+) -> bool {
+ use style::properties::longhands::transition_timing_function;
+
+ let context = ParserContext::new(
+ Origin::Author,
+ unsafe { dummy_url_data() },
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ let easing = easing.to_string();
+ let mut input = ParserInput::new(&easing);
+ let mut parser = Parser::new(&mut input);
+ let result =
+ parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
+ match result {
+ Ok(parsed_easing) => {
+ *output = parsed_easing.to_computed_value_without_context();
+ true
+ },
+ Err(_) => false,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_SerializeEasing(easing: &ComputedTimingFunction, output: &mut nsACString) {
+ easing.to_css(&mut CssWriter::new(output)).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GetProperties_Overriding_Animation(
+ element: &RawGeckoElement,
+ list: &nsTArray<nsCSSPropertyID>,
+ set: &mut structs::nsCSSPropertyIDSet,
+) {
+ let element = GeckoElement(element);
+ let element_data = match element.borrow_data() {
+ Some(data) => data,
+ None => return,
+ };
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let guards = StylesheetGuards::same(&guard);
+ let (overridden, custom) = element_data
+ .styles
+ .primary()
+ .rules()
+ .get_properties_overriding_animations(&guards);
+ for p in list.iter() {
+ match NonCustomPropertyId::from_nscsspropertyid(*p) {
+ Some(property) => {
+ if let Some(id) = property.as_longhand() {
+ if overridden.contains(id) {
+ unsafe { Gecko_AddPropertyToSet(set, *p) };
+ }
+ }
+ },
+ None => {
+ if *p == nsCSSPropertyID::eCSSPropertyExtra_variable && custom {
+ unsafe { Gecko_AddPropertyToSet(set, *p) };
+ }
+ },
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MatrixTransform_Operate(
+ matrix_operator: MatrixTransformOperator,
+ from: *const structs::Matrix4x4Components,
+ to: *const structs::Matrix4x4Components,
+ progress: f64,
+ output: *mut structs::Matrix4x4Components,
+) {
+ use self::MatrixTransformOperator::{Accumulate, Interpolate};
+ use style::values::computed::transform::Matrix3D;
+
+ let from = Matrix3D::from(unsafe { from.as_ref() }.expect("not a valid 'from' matrix"));
+ let to = Matrix3D::from(unsafe { to.as_ref() }.expect("not a valid 'to' matrix"));
+ let result = match matrix_operator {
+ Interpolate => from.animate(&to, Procedure::Interpolate { progress }),
+ Accumulate => from.animate(
+ &to,
+ Procedure::Accumulate {
+ count: progress as u64,
+ },
+ ),
+ };
+
+ let output = unsafe { output.as_mut() }.expect("not a valid 'output' matrix");
+ if let Ok(result) = result {
+ *output = result.into();
+ } else if progress < 0.5 {
+ *output = from.clone().into();
+ } else {
+ *output = to.clone().into();
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ParseStyleAttribute(
+ data: &nsACString,
+ raw_extra_data: *mut URLExtraData,
+ quirks_mode: nsCompatibility,
+ loader: *mut Loader,
+ rule_type: CssRuleType,
+) -> Strong<LockedDeclarationBlock> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let value = data.as_str_unchecked();
+ let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
+ let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
+ Arc::new(global_style_data.shared_lock.wrap(parse_style_attribute(
+ value,
+ url_data,
+ reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
+ quirks_mode.into(),
+ rule_type,
+ )))
+ .into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> Strong<LockedDeclarationBlock> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ Arc::new(
+ global_style_data
+ .shared_lock
+ .wrap(PropertyDeclarationBlock::new()),
+ )
+ .into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_Clear(declarations: &LockedDeclarationBlock) {
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.clear();
+ });
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_Clone(
+ declarations: &LockedDeclarationBlock,
+) -> Strong<LockedDeclarationBlock> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ Arc::new(
+ global_style_data
+ .shared_lock
+ .wrap(declarations.read_with(&guard).clone()),
+ )
+ .into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_Equals(
+ a: &LockedDeclarationBlock,
+ b: &LockedDeclarationBlock,
+) -> bool {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ a.read_with(&guard).declarations() == b.read_with(&guard).declarations()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_GetCssText(
+ declarations: &LockedDeclarationBlock,
+ result: &mut nsACString,
+) {
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ decls.to_css(result).unwrap()
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue(
+ decls: &LockedDeclarationBlock,
+ property_id: &structs::AnimatedPropertyID,
+ buffer: &mut nsACString,
+ computed_values: Option<&ComputedValues>,
+ data: &PerDocumentStyleData,
+) {
+ let property_id = get_property_id_from_animatedpropertyid!(property_id, ());
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let data = data.borrow();
+ let rv = decls.read_with(&guard).single_value_to_css(
+ &property_id,
+ buffer,
+ computed_values,
+ &data.stylist,
+ );
+ debug_assert!(rv.is_ok());
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SerializeFontValueForCanvas(
+ declarations: &LockedDeclarationBlock,
+ buffer: &mut nsACString,
+) {
+ use style::properties::shorthands::font;
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ let longhands = match font::LonghandsToSerialize::from_iter(decls.declarations().iter()) {
+ Ok(l) => l,
+ Err(()) => {
+ warn!("Unexpected property!");
+ return;
+ },
+ };
+
+ let rv = longhands.to_css(&mut CssWriter::new(buffer));
+ debug_assert!(rv.is_ok());
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_Count(declarations: &LockedDeclarationBlock) -> u32 {
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ decls.declarations().len() as u32
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(
+ declarations: &LockedDeclarationBlock,
+ index: u32,
+ result: &mut nsACString,
+) -> bool {
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ if let Some(decl) = decls.declarations().get(index as usize) {
+ result.assign(&decl.id().name());
+ true
+ } else {
+ false
+ }
+ })
+}
+
+macro_rules! get_property_id_from_property {
+ ($property: ident, $ret: expr) => {{
+ let property = $property.as_str_unchecked();
+ match PropertyId::parse_enabled_for_all_content(property) {
+ Ok(property_id) => property_id,
+ Err(_) => return $ret,
+ }
+ }};
+}
+
+unsafe fn get_property_value(
+ declarations: &LockedDeclarationBlock,
+ property_id: PropertyId,
+ value: &mut nsACString,
+) {
+ // This callsite is hot enough that the lock acquisition shows up in profiles.
+ // Using an unchecked read here improves our performance by ~10% on the
+ // microbenchmark in bug 1355599.
+ read_locked_arc_unchecked(declarations, |decls: &PropertyDeclarationBlock| {
+ decls.property_value_to_css(&property_id, value).unwrap();
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValue(
+ declarations: &LockedDeclarationBlock,
+ property: &nsACString,
+ value: &mut nsACString,
+) {
+ get_property_value(
+ declarations,
+ get_property_id_from_property!(property, ()),
+ value,
+ )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyValueById(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: &mut nsACString,
+) {
+ get_property_value(
+ declarations,
+ get_property_id_from_nscsspropertyid!(property, ()),
+ value,
+ )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(
+ declarations: &LockedDeclarationBlock,
+ property: &nsACString,
+) -> bool {
+ let property_id = get_property_id_from_property!(property, false);
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ decls.property_priority(&property_id).important()
+ })
+}
+
+#[inline(always)]
+fn set_property_to_declarations(
+ non_custom_property_id: Option<NonCustomPropertyId>,
+ block: &LockedDeclarationBlock,
+ parsed_declarations: &mut SourcePropertyDeclaration,
+ before_change_closure: DeclarationBlockMutationClosure,
+ importance: Importance,
+) -> bool {
+ let mut updates = Default::default();
+ let will_change = read_locked_arc(block, |decls: &PropertyDeclarationBlock| {
+ decls.prepare_for_update(&parsed_declarations, importance, &mut updates)
+ });
+ if !will_change {
+ return false;
+ }
+
+ before_change_closure.invoke(non_custom_property_id);
+ write_locked_arc(block, |decls: &mut PropertyDeclarationBlock| {
+ decls.update(parsed_declarations.drain(), importance, &mut updates)
+ });
+ true
+}
+
+fn set_property(
+ declarations: &LockedDeclarationBlock,
+ property_id: PropertyId,
+ value: &nsACString,
+ is_important: bool,
+ data: &UrlExtraData,
+ parsing_mode: ParsingMode,
+ quirks_mode: QuirksMode,
+ loader: *mut Loader,
+ rule_type: CssRuleType,
+ before_change_closure: DeclarationBlockMutationClosure,
+) -> bool {
+ let mut source_declarations = SourcePropertyDeclaration::default();
+ let reporter = ErrorReporter::new(ptr::null_mut(), loader, data.ptr());
+ let non_custom_property_id = property_id.non_custom_id();
+ let result = parse_property_into(
+ &mut source_declarations,
+ property_id,
+ value,
+ Origin::Author,
+ data,
+ parsing_mode,
+ quirks_mode,
+ rule_type,
+ reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
+ );
+
+ if result.is_err() {
+ return false;
+ }
+
+ let importance = if is_important {
+ Importance::Important
+ } else {
+ Importance::Normal
+ };
+
+ set_property_to_declarations(
+ non_custom_property_id,
+ declarations,
+ &mut source_declarations,
+ before_change_closure,
+ importance,
+ )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SetProperty(
+ declarations: &LockedDeclarationBlock,
+ property: &nsACString,
+ value: &nsACString,
+ is_important: bool,
+ data: *mut URLExtraData,
+ parsing_mode: ParsingMode,
+ quirks_mode: nsCompatibility,
+ loader: *mut Loader,
+ rule_type: CssRuleType,
+ before_change_closure: DeclarationBlockMutationClosure,
+) -> bool {
+ set_property(
+ declarations,
+ get_property_id_from_property!(property, false),
+ value,
+ is_important,
+ UrlExtraData::from_ptr_ref(&data),
+ parsing_mode,
+ quirks_mode.into(),
+ loader,
+ rule_type,
+ before_change_closure,
+ )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue(
+ declarations: &LockedDeclarationBlock,
+ animation_value: &AnimationValue,
+ before_change_closure: DeclarationBlockMutationClosure,
+) -> bool {
+ let non_custom_property_id = match animation_value.id() {
+ PropertyDeclarationId::Longhand(id) => Some(id.into()),
+ PropertyDeclarationId::Custom(_) => None,
+ };
+ let mut source_declarations = SourcePropertyDeclaration::with_one(animation_value.uncompute());
+
+ set_property_to_declarations(
+ non_custom_property_id,
+ declarations,
+ &mut source_declarations,
+ before_change_closure,
+ Importance::Normal,
+ )
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyById(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: &nsACString,
+ is_important: bool,
+ data: *mut URLExtraData,
+ parsing_mode: ParsingMode,
+ quirks_mode: nsCompatibility,
+ loader: *mut Loader,
+ rule_type: CssRuleType,
+ before_change_closure: DeclarationBlockMutationClosure,
+) -> bool {
+ set_property(
+ declarations,
+ get_property_id_from_nscsspropertyid!(property, false),
+ value,
+ is_important,
+ UrlExtraData::from_ptr_ref(&data),
+ parsing_mode,
+ quirks_mode.into(),
+ loader,
+ rule_type,
+ before_change_closure,
+ )
+}
+
+fn remove_property(
+ declarations: &LockedDeclarationBlock,
+ property_id: PropertyId,
+ before_change_closure: DeclarationBlockMutationClosure,
+) -> bool {
+ let first_declaration = read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ decls.first_declaration_to_remove(&property_id)
+ });
+
+ let first_declaration = match first_declaration {
+ Some(i) => i,
+ None => return false,
+ };
+
+ before_change_closure.invoke(property_id.non_custom_id());
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.remove_property(&property_id, first_declaration)
+ });
+
+ true
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_RemoveProperty(
+ declarations: &LockedDeclarationBlock,
+ property: &nsACString,
+ before_change_closure: DeclarationBlockMutationClosure,
+) -> bool {
+ remove_property(
+ declarations,
+ get_property_id_from_property!(property, false),
+ before_change_closure,
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ before_change_closure: DeclarationBlockMutationClosure,
+) -> bool {
+ remove_property(
+ declarations,
+ get_property_id_from_nscsspropertyid!(property, false),
+ before_change_closure,
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_Create() -> Strong<LockedMediaList> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ Arc::new(global_style_data.shared_lock.wrap(MediaList::empty())).into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_DeepClone(list: &LockedMediaList) -> Strong<LockedMediaList> {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ read_locked_arc(list, |list: &MediaList| {
+ Arc::new(global_style_data.shared_lock.wrap(list.clone())).into()
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_Matches(
+ list: &LockedMediaList,
+ raw_data: &PerDocumentStyleData,
+) -> bool {
+ let per_doc_data = raw_data.borrow();
+ read_locked_arc(list, |list: &MediaList| {
+ list.evaluate(
+ per_doc_data.stylist.device(),
+ per_doc_data.stylist.quirks_mode(),
+ )
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_HasCSSWideKeyword(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+) -> bool {
+ let property_id = get_property_id_from_nscsspropertyid!(property, false);
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ decls.has_css_wide_keyword(&property_id)
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_GetText(list: &LockedMediaList, result: &mut nsACString) {
+ read_locked_arc(list, |list: &MediaList| {
+ list.to_css(&mut CssWriter::new(result)).unwrap();
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_MediaList_SetText(
+ list: &LockedMediaList,
+ text: &nsACString,
+ caller_type: CallerType,
+) {
+ let text = text.as_str_unchecked();
+
+ let mut input = ParserInput::new(&text);
+ let mut parser = Parser::new(&mut input);
+ let url_data = dummy_url_data();
+
+ // TODO(emilio): If the need for `CallerType` appears in more places,
+ // consider adding an explicit member in `ParserContext` instead of doing
+ // this (or adding a dummy "chrome://" url data).
+ //
+ // For media query parsing it's effectively the same, so for now...
+ let origin = match caller_type {
+ CallerType::System => Origin::UserAgent,
+ CallerType::NonSystem => Origin::Author,
+ };
+
+ let context = ParserContext::new(
+ origin,
+ url_data,
+ Some(CssRuleType::Media),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ // TODO(emilio): Looks like error reporting could be useful here?
+ None,
+ None,
+ );
+
+ write_locked_arc(list, |list: &mut MediaList| {
+ *list = MediaList::parse(&context, &mut parser);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_IsViewportDependent(list: &LockedMediaList) -> bool {
+ read_locked_arc(list, |list: &MediaList| list.is_viewport_dependent())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_GetLength(list: &LockedMediaList) -> u32 {
+ read_locked_arc(list, |list: &MediaList| list.media_queries.len() as u32)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_GetMediumAt(
+ list: &LockedMediaList,
+ index: u32,
+ result: &mut nsACString,
+) -> bool {
+ read_locked_arc(list, |list: &MediaList| {
+ let media_query = match list.media_queries.get(index as usize) {
+ Some(mq) => mq,
+ None => return false,
+ };
+ media_query.to_css(&mut CssWriter::new(result)).unwrap();
+ true
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_AppendMedium(list: &LockedMediaList, new_medium: &nsACString) {
+ let new_medium = unsafe { new_medium.as_str_unchecked() };
+ let url_data = unsafe { dummy_url_data() };
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ Some(CssRuleType::Media),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ write_locked_arc(list, |list: &mut MediaList| {
+ list.append_medium(&context, new_medium);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_DeleteMedium(
+ list: &LockedMediaList,
+ old_medium: &nsACString,
+) -> bool {
+ let old_medium = unsafe { old_medium.as_str_unchecked() };
+ let url_data = unsafe { dummy_url_data() };
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ Some(CssRuleType::Media),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ write_locked_arc(list, |list: &mut MediaList| {
+ list.delete_medium(&context, old_medium)
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_MediaList_SizeOfIncludingThis(
+ malloc_size_of: GeckoMallocSizeOf,
+ malloc_enclosing_size_of: GeckoMallocSizeOf,
+ list: &LockedMediaList,
+) -> usize {
+ use malloc_size_of::MallocSizeOf;
+ use malloc_size_of::MallocUnconditionalShallowSizeOf;
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+
+ let mut ops = MallocSizeOfOps::new(
+ malloc_size_of.unwrap(),
+ Some(malloc_enclosing_size_of.unwrap()),
+ None,
+ );
+
+ unsafe { ArcBorrow::from_ref(list) }.with_arc(|list| {
+ let mut n = 0;
+ n += list.unconditional_shallow_size_of(&mut ops);
+ n += list.read_with(&guard).size_of(&mut ops);
+ n
+ })
+}
+
+macro_rules! get_longhand_from_id {
+ ($id:expr) => {
+ match LonghandId::from_nscsspropertyid($id) {
+ Some(lh) => lh,
+ _ => panic!("stylo: unknown presentation property with id"),
+ }
+ };
+}
+
+macro_rules! match_wrap_declared {
+ ($longhand:ident, $($property:ident => $inner:expr,)*) => (
+ match $longhand {
+ $(
+ LonghandId::$property => PropertyDeclaration::$property($inner),
+ )*
+ _ => {
+ panic!("stylo: Don't know how to handle presentation property");
+ }
+ }
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_PropertyIsSet(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+) -> bool {
+ read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
+ decls.contains(PropertyDeclarationId::Longhand(get_longhand_from_id!(
+ property
+ )))
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SetIdentStringValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: *mut nsAtom,
+) {
+ use style::properties::longhands::_x_lang::computed_value::T as Lang;
+ use style::properties::PropertyDeclaration;
+
+ let long = get_longhand_from_id!(property);
+ let prop = match_wrap_declared! { long,
+ XLang => Lang(Atom::from_raw(value)),
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+#[allow(unreachable_code)]
+pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: i32,
+) {
+ use num_traits::FromPrimitive;
+ use style::properties::longhands;
+ use style::properties::PropertyDeclaration;
+ use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword};
+ use style::values::generics::font::FontStyle;
+ use style::values::specified::{
+ table::CaptionSide, BorderStyle, Clear, Display, Float, TextAlign, TextEmphasisPosition,
+ TextTransform,
+ };
+
+ fn get_from_computed<T>(value: u32) -> T
+ where
+ T: ToComputedValue,
+ T::ComputedValue: FromPrimitive,
+ {
+ T::from_computed_value(&T::ComputedValue::from_u32(value).unwrap())
+ }
+
+ let long = get_longhand_from_id!(property);
+ let value = value as u32;
+
+ let prop = match_wrap_declared! { long,
+ MozUserModify => longhands::_moz_user_modify::SpecifiedValue::from_gecko_keyword(value),
+ Direction => get_from_computed::<longhands::direction::SpecifiedValue>(value),
+ Display => get_from_computed::<Display>(value),
+ Float => get_from_computed::<Float>(value),
+ Clear => get_from_computed::<Clear>(value),
+ VerticalAlign => VerticalAlign::Keyword(VerticalAlignKeyword::from_u32(value).unwrap()),
+ TextAlign => get_from_computed::<TextAlign>(value),
+ TextEmphasisPosition => TextEmphasisPosition::from_bits_retain(value as u8),
+ FontSize => {
+ // We rely on Gecko passing in font-size values (0...7) here.
+ longhands::font_size::SpecifiedValue::from_html_size(value as u8)
+ },
+ FontStyle => {
+ style::values::specified::FontStyle::Specified(if value == structs::NS_FONT_STYLE_ITALIC {
+ FontStyle::Italic
+ } else {
+ debug_assert_eq!(value, structs::NS_FONT_STYLE_NORMAL);
+ FontStyle::Normal
+ })
+ },
+ FontWeight => longhands::font_weight::SpecifiedValue::from_gecko_keyword(value),
+ ListStyleType => Box::new(longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value)),
+ MathStyle => longhands::math_style::SpecifiedValue::from_gecko_keyword(value),
+ MozMathVariant => longhands::_moz_math_variant::SpecifiedValue::from_gecko_keyword(value),
+ WhiteSpaceCollapse => get_from_computed::<longhands::white_space_collapse::SpecifiedValue>(value),
+ TextWrapMode => get_from_computed::<longhands::text_wrap_mode::SpecifiedValue>(value),
+ CaptionSide => get_from_computed::<CaptionSide>(value),
+ BorderTopStyle => get_from_computed::<BorderStyle>(value),
+ BorderRightStyle => get_from_computed::<BorderStyle>(value),
+ BorderBottomStyle => get_from_computed::<BorderStyle>(value),
+ BorderLeftStyle => get_from_computed::<BorderStyle>(value),
+ TextTransform => {
+ debug_assert_eq!(value, structs::StyleTextTransformCase_None as u32);
+ TextTransform::none()
+ },
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetIntValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: i32,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::specified::Integer;
+
+ let long = get_longhand_from_id!(property);
+ let prop = match_wrap_declared! { long,
+ XSpan => Integer::new(value),
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetMathDepthValue(
+ declarations: &LockedDeclarationBlock,
+ value: i32,
+ is_relative: bool,
+) {
+ use style::properties::longhands::math_depth::SpecifiedValue as MathDepth;
+ use style::properties::PropertyDeclaration;
+
+ let integer_value = style::values::specified::Integer::new(value);
+ let prop = PropertyDeclaration::MathDepth(if is_relative {
+ MathDepth::Add(integer_value)
+ } else {
+ MathDepth::Absolute(integer_value)
+ });
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetCounterResetListItem(
+ declarations: &LockedDeclarationBlock,
+ counter_value: i32,
+ is_reversed: bool,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::generics::counters::{CounterPair, CounterReset};
+
+ let prop = PropertyDeclaration::CounterReset(CounterReset::new(vec![CounterPair {
+ name: CustomIdent(atom!("list-item")),
+ value: style::values::specified::Integer::new(counter_value),
+ is_reversed,
+ }]));
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetCounterSetListItem(
+ declarations: &LockedDeclarationBlock,
+ counter_value: i32,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::generics::counters::{CounterPair, CounterSet};
+
+ let prop = PropertyDeclaration::CounterSet(CounterSet::new(vec![CounterPair {
+ name: CustomIdent(atom!("list-item")),
+ value: style::values::specified::Integer::new(counter_value),
+ is_reversed: false,
+ }]));
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetPixelValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: f32,
+) {
+ use style::properties::longhands::border_spacing::SpecifiedValue as BorderSpacing;
+ use style::properties::PropertyDeclaration;
+ use style::values::generics::length::{LengthPercentageOrAuto, Size};
+ use style::values::generics::NonNegative;
+ use style::values::specified::length::{
+ LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
+ };
+ use style::values::specified::{BorderCornerRadius, BorderSideWidth};
+
+ let long = get_longhand_from_id!(property);
+ let nocalc = NoCalcLength::from_px(value);
+ let lp = LengthPercentage::Length(nocalc);
+ let lp_or_auto = LengthPercentageOrAuto::LengthPercentage(lp.clone());
+ let prop = match_wrap_declared! { long,
+ Height => Size::LengthPercentage(NonNegative(lp)),
+ Width => Size::LengthPercentage(NonNegative(lp)),
+ BorderTopWidth => BorderSideWidth::from_px(value),
+ BorderRightWidth => BorderSideWidth::from_px(value),
+ BorderBottomWidth => BorderSideWidth::from_px(value),
+ BorderLeftWidth => BorderSideWidth::from_px(value),
+ MarginTop => lp_or_auto,
+ MarginRight => lp_or_auto,
+ MarginBottom => lp_or_auto,
+ MarginLeft => lp_or_auto,
+ PaddingTop => NonNegative(lp),
+ PaddingRight => NonNegative(lp),
+ PaddingBottom => NonNegative(lp),
+ PaddingLeft => NonNegative(lp),
+ BorderSpacing => {
+ let v = NonNegativeLength::from(nocalc);
+ Box::new(BorderSpacing::new(v.clone(), v))
+ },
+ BorderTopLeftRadius => {
+ let length = NonNegativeLengthPercentage::from(nocalc);
+ Box::new(BorderCornerRadius::new(length.clone(), length))
+ },
+ BorderTopRightRadius => {
+ let length = NonNegativeLengthPercentage::from(nocalc);
+ Box::new(BorderCornerRadius::new(length.clone(), length))
+ },
+ BorderBottomLeftRadius => {
+ let length = NonNegativeLengthPercentage::from(nocalc);
+ Box::new(BorderCornerRadius::new(length.clone(), length))
+ },
+ BorderBottomRightRadius => {
+ let length = NonNegativeLengthPercentage::from(nocalc);
+ Box::new(BorderCornerRadius::new(length.clone(), length))
+ },
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetLengthValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: f32,
+ unit: structs::nsCSSUnit,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::generics::length::{LengthPercentageOrAuto, Size};
+ use style::values::generics::NonNegative;
+ use style::values::specified::length::{
+ FontRelativeLength, LengthPercentage, ViewportPercentageLength,
+ };
+ use style::values::specified::FontSize;
+
+ let long = get_longhand_from_id!(property);
+ let nocalc = match unit {
+ structs::nsCSSUnit::eCSSUnit_EM => {
+ NoCalcLength::FontRelative(FontRelativeLength::Em(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_XHeight => {
+ NoCalcLength::FontRelative(FontRelativeLength::Ex(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_RootEM => {
+ NoCalcLength::FontRelative(FontRelativeLength::Rem(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_Char => {
+ NoCalcLength::FontRelative(FontRelativeLength::Ch(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_Ideographic => {
+ NoCalcLength::FontRelative(FontRelativeLength::Ic(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_CapHeight => {
+ NoCalcLength::FontRelative(FontRelativeLength::Cap(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_Pixel => NoCalcLength::Absolute(AbsoluteLength::Px(value)),
+ structs::nsCSSUnit::eCSSUnit_Inch => NoCalcLength::Absolute(AbsoluteLength::In(value)),
+ structs::nsCSSUnit::eCSSUnit_Centimeter => {
+ NoCalcLength::Absolute(AbsoluteLength::Cm(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_Millimeter => {
+ NoCalcLength::Absolute(AbsoluteLength::Mm(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_Point => NoCalcLength::Absolute(AbsoluteLength::Pt(value)),
+ structs::nsCSSUnit::eCSSUnit_Pica => NoCalcLength::Absolute(AbsoluteLength::Pc(value)),
+ structs::nsCSSUnit::eCSSUnit_Quarter => NoCalcLength::Absolute(AbsoluteLength::Q(value)),
+ structs::nsCSSUnit::eCSSUnit_VW => {
+ NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_VH => {
+ NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_VMin => {
+ NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value))
+ },
+ structs::nsCSSUnit::eCSSUnit_VMax => {
+ NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value))
+ },
+ _ => unreachable!("Unknown unit passed to SetLengthValue"),
+ };
+
+ let prop = match_wrap_declared! { long,
+ Width => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
+ Height => Size::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
+ X => LengthPercentage::Length(nocalc),
+ Y => LengthPercentage::Length(nocalc),
+ Cx => LengthPercentage::Length(nocalc),
+ Cy => LengthPercentage::Length(nocalc),
+ R => NonNegative(LengthPercentage::Length(nocalc)),
+ Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
+ Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(LengthPercentage::Length(nocalc))),
+ FontSize => FontSize::Length(LengthPercentage::Length(nocalc)),
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetPathValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ path: &nsTArray<f32>,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::specified::DProperty;
+
+ // 1. Decode the path data from SVG.
+ let path = match specified::SVGPathData::decode_from_f32_array(path) {
+ Ok(p) => p,
+ Err(()) => return,
+ };
+
+ // 2. Set decoded path into style.
+ let long = get_longhand_from_id!(property);
+ let prop = match_wrap_declared! { long,
+ D => if path.0.is_empty() { DProperty::None } else { DProperty::Path(path) },
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetPercentValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: f32,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::computed::Percentage;
+ use style::values::generics::length::{LengthPercentageOrAuto, Size};
+ use style::values::generics::NonNegative;
+ use style::values::specified::length::LengthPercentage;
+ use style::values::specified::FontSize;
+
+ let long = get_longhand_from_id!(property);
+ let pc = Percentage(value);
+ let lp = LengthPercentage::Percentage(pc);
+ let lp_or_auto = LengthPercentageOrAuto::LengthPercentage(lp.clone());
+
+ let prop = match_wrap_declared! { long,
+ Height => Size::LengthPercentage(NonNegative(lp)),
+ Width => Size::LengthPercentage(NonNegative(lp)),
+ X => lp,
+ Y => lp,
+ Cx => lp,
+ Cy => lp,
+ R => NonNegative(lp),
+ Rx => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
+ Ry => LengthPercentageOrAuto::LengthPercentage(NonNegative(lp)),
+ MarginTop => lp_or_auto,
+ MarginRight => lp_or_auto,
+ MarginBottom => lp_or_auto,
+ MarginLeft => lp_or_auto,
+ FontSize => FontSize::Length(LengthPercentage::Percentage(pc)),
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetAutoValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::generics::length::{LengthPercentageOrAuto, Size};
+
+ let long = get_longhand_from_id!(property);
+ let auto = LengthPercentageOrAuto::Auto;
+
+ let prop = match_wrap_declared! { long,
+ Height => Size::auto(),
+ Width => Size::auto(),
+ MarginTop => auto,
+ MarginRight => auto,
+ MarginBottom => auto,
+ MarginLeft => auto,
+ AspectRatio => specified::AspectRatio::auto(),
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetCurrentColor(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::specified::Color;
+
+ let long = get_longhand_from_id!(property);
+ let cc = Color::currentcolor();
+
+ let prop = match_wrap_declared! { long,
+ BorderTopColor => cc,
+ BorderRightColor => cc,
+ BorderBottomColor => cc,
+ BorderLeftColor => cc,
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetColorValue(
+ declarations: &LockedDeclarationBlock,
+ property: nsCSSPropertyID,
+ value: structs::nscolor,
+) {
+ use style::gecko::values::convert_nscolor_to_absolute_color;
+ use style::properties::longhands;
+ use style::properties::PropertyDeclaration;
+ use style::values::specified::Color;
+
+ let long = get_longhand_from_id!(property);
+ let rgba = convert_nscolor_to_absolute_color(value);
+ let color = Color::from_absolute_color(rgba);
+
+ let prop = match_wrap_declared! { long,
+ BorderTopColor => color,
+ BorderRightColor => color,
+ BorderBottomColor => color,
+ BorderLeftColor => color,
+ Color => longhands::color::SpecifiedValue(color),
+ BackgroundColor => color,
+ };
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(prop, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SetFontFamily(
+ declarations: &LockedDeclarationBlock,
+ value: &nsACString,
+) {
+ use style::properties::longhands::font_family::SpecifiedValue as FontFamily;
+ use style::properties::PropertyDeclaration;
+
+ let string = value.as_str_unchecked();
+ let mut input = ParserInput::new(&string);
+ let mut parser = Parser::new(&mut input);
+ let context = ParserContext::new(
+ Origin::Author,
+ dummy_url_data(),
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ let result = FontFamily::parse(&context, &mut parser);
+ if let Ok(family) = result {
+ if parser.is_exhausted() {
+ let decl = PropertyDeclaration::FontFamily(family);
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(decl, Importance::Normal);
+ })
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_DeclarationBlock_SetBackgroundImage(
+ declarations: &LockedDeclarationBlock,
+ value: &nsACString,
+ raw_extra_data: *mut URLExtraData,
+) {
+ use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage;
+ use style::properties::PropertyDeclaration;
+ use style::stylesheets::CorsMode;
+ use style::values::generics::image::Image;
+ use style::values::specified::url::SpecifiedImageUrl;
+
+ let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
+ let string = value.as_str_unchecked();
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ let url = SpecifiedImageUrl::parse_from_string(string.into(), &context, CorsMode::None);
+ let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(vec![Image::Url(url)].into()));
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(decl, Importance::Normal);
+ });
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(
+ declarations: &LockedDeclarationBlock,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::specified::text::TextDecorationLine;
+
+ let decoration = TextDecorationLine::COLOR_OVERRIDE;
+ let decl = PropertyDeclaration::TextDecorationLine(decoration);
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(decl, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_DeclarationBlock_SetAspectRatio(
+ declarations: &LockedDeclarationBlock,
+ width: f32,
+ height: f32,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::values::generics::position::AspectRatio;
+
+ let decl = PropertyDeclaration::AspectRatio(AspectRatio::from_mapped_ratio(width, height));
+ write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
+ decls.push(decl, Importance::Normal);
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_CSSSupports2(property: &nsACString, value: &nsACString) -> bool {
+ let id = unsafe { get_property_id_from_property!(property, false) };
+
+ let mut declarations = SourcePropertyDeclaration::default();
+ parse_property_into(
+ &mut declarations,
+ id,
+ value,
+ Origin::Author,
+ unsafe { dummy_url_data() },
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ CssRuleType::Style,
+ None,
+ )
+ .is_ok()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_CSSSupports(
+ cond: &nsACString,
+ ua_origin: bool,
+ chrome_sheet: bool,
+ quirks: bool,
+) -> bool {
+ let condition = unsafe { cond.as_str_unchecked() };
+ let mut input = ParserInput::new(&condition);
+ let mut input = Parser::new(&mut input);
+ let cond = match input.parse_entirely(parse_condition_or_declaration) {
+ Ok(c) => c,
+ Err(..) => return false,
+ };
+
+ let origin = if ua_origin {
+ Origin::UserAgent
+ } else {
+ Origin::Author
+ };
+ let url_data = unsafe {
+ UrlExtraData::from_ptr_ref(if chrome_sheet {
+ &DUMMY_CHROME_URL_DATA
+ } else {
+ &DUMMY_URL_DATA
+ })
+ };
+ let quirks_mode = if quirks {
+ QuirksMode::Quirks
+ } else {
+ QuirksMode::NoQuirks
+ };
+
+ // NOTE(emilio): The supports API is not associated to any stylesheet,
+ // so the fact that there is no namespace map here is fine.
+ let context = ParserContext::new(
+ origin,
+ url_data,
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ quirks_mode,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ cond.eval(&context)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_CSSSupportsForImport(after_rule: &nsACString) -> bool {
+ let condition = unsafe { after_rule.as_str_unchecked() };
+ let mut input = ParserInput::new(&condition);
+ let mut input = Parser::new(&mut input);
+
+ // NOTE(emilio): The supports API is not associated to any stylesheet,
+ // so the fact that there is no namespace map here is fine.
+ let mut context = ParserContext::new(
+ Origin::Author,
+ unsafe { dummy_url_data() },
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ let (_layer, supports) = ImportRule::parse_layer_and_supports(&mut input, &mut context);
+
+ supports.map_or(true, |s| s.enabled)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_NoteExplicitHints(
+ element: &RawGeckoElement,
+ restyle_hint: RestyleHint,
+ change_hint: nsChangeHint,
+) {
+ GeckoElement(element).note_explicit_hints(restyle_hint, change_hint);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_TakeChangeHint(element: &RawGeckoElement, was_restyled: &mut bool) -> u32 {
+ let element = GeckoElement(element);
+
+ let damage = match element.mutate_data() {
+ Some(mut data) => {
+ *was_restyled = data.is_restyle();
+
+ let damage = data.damage;
+ data.clear_restyle_state();
+ damage
+ },
+ None => {
+ warn!("Trying to get change hint from unstyled element");
+ *was_restyled = false;
+ GeckoRestyleDamage::empty()
+ },
+ };
+
+ debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage);
+ // We'd like to return `nsChangeHint` here, but bindgen bitfield enums don't
+ // work as return values with the Linux 32-bit ABI at the moment because
+ // they wrap the value in a struct, so for now just unwrap it.
+ damage.as_change_hint().0
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ResolveStyle(element: &RawGeckoElement) -> Strong<ComputedValues> {
+ let element = GeckoElement(element);
+ debug!("Servo_ResolveStyle: {:?}", element);
+ let data = element
+ .borrow_data()
+ .expect("Resolving style on unstyled element");
+
+ debug_assert!(
+ element.has_current_styles(&*data),
+ "Resolving style on {:?} without current styles: {:?}",
+ element,
+ data
+ );
+ data.styles.primary().clone().into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ResolveStyleLazily(
+ element: &RawGeckoElement,
+ pseudo_type: PseudoStyleType,
+ functional_pseudo_parameter: *mut nsAtom,
+ rule_inclusion: StyleRuleInclusion,
+ snapshots: *const ServoElementSnapshotTable,
+ cache_generation: u64,
+ can_use_cache: bool,
+ raw_data: &PerDocumentStyleData,
+) -> Strong<ComputedValues> {
+ debug_assert!(!snapshots.is_null());
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let element = GeckoElement(element);
+ let mut data = raw_data.borrow_mut();
+ let data = &mut *data;
+ let rule_inclusion = RuleInclusion::from(rule_inclusion);
+ let pseudo_element = PseudoElement::from_pseudo_type(
+ pseudo_type,
+ get_functional_pseudo_parameter_atom(functional_pseudo_parameter),
+ );
+
+ let matching_fn = |pseudo: &PseudoElement| match pseudo_element {
+ Some(ref p) => *pseudo == *p,
+ _ => false,
+ };
+
+ if cache_generation != data.undisplayed_style_cache_generation {
+ data.undisplayed_style_cache.clear();
+ data.undisplayed_style_cache_generation = cache_generation;
+ }
+
+ let stylist = &data.stylist;
+ let finish = |styles: &ElementStyles, is_probe: bool| -> Option<Arc<ComputedValues>> {
+ match pseudo_element {
+ Some(ref pseudo) => {
+ get_pseudo_style(
+ &guard,
+ element,
+ pseudo,
+ rule_inclusion,
+ styles,
+ /* inherited_styles = */ None,
+ &stylist,
+ is_probe,
+ if pseudo.is_highlight() {
+ Some(&matching_fn)
+ } else {
+ None
+ },
+ )
+ },
+ None => Some(styles.primary().clone()),
+ }
+ };
+
+ let is_before_or_after = pseudo_element
+ .as_ref()
+ .map_or(false, |p| p.is_before_or_after());
+
+ // In the common case we already have the style. Check that before setting
+ // up all the computation machinery.
+ //
+ // Also, only probe in the ::before or ::after case, since their styles may
+ // not be in the `ElementData`, given they may exist but not be applicable
+ // to generate an actual pseudo-element (like, having a `content: none`).
+ if rule_inclusion == RuleInclusion::All {
+ let styles = element.borrow_data().and_then(|d| {
+ if d.has_styles() {
+ finish(&d.styles, is_before_or_after)
+ } else {
+ None
+ }
+ });
+ if let Some(result) = styles {
+ return result.into();
+ }
+ if pseudo_element.is_none() && can_use_cache {
+ if let Some(style) = data.undisplayed_style_cache.get(&element.opaque()) {
+ return style.clone().into();
+ }
+ }
+ }
+
+ // We don't have the style ready. Go ahead and compute it as necessary.
+ let shared = create_shared_context(
+ &global_style_data,
+ &guard,
+ &stylist,
+ TraversalFlags::empty(),
+ unsafe { &*snapshots },
+ );
+ let mut tlc = ThreadLocalStyleContext::new();
+ let mut context = StyleContext {
+ shared: &shared,
+ thread_local: &mut tlc,
+ };
+
+ let styles = resolve_style(
+ &mut context,
+ element,
+ rule_inclusion,
+ pseudo_element.as_ref(),
+ if can_use_cache {
+ Some(&mut data.undisplayed_style_cache)
+ } else {
+ None
+ },
+ );
+
+ finish(&styles, /* is_probe = */ false)
+ .expect("We're not probing, so we should always get a style back")
+ .into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ReparentStyle(
+ style_to_reparent: &ComputedValues,
+ parent_style: &ComputedValues,
+ layout_parent_style: &ComputedValues,
+ element: Option<&RawGeckoElement>,
+ raw_data: &PerDocumentStyleData,
+) -> Strong<ComputedValues> {
+ use style::properties::FirstLineReparenting;
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let doc_data = raw_data.borrow();
+ let inputs = CascadeInputs::new_from_style(style_to_reparent);
+ let pseudo = style_to_reparent.pseudo();
+ let element = element.map(GeckoElement);
+
+ doc_data
+ .stylist
+ .cascade_style_and_visited(
+ element,
+ pseudo.as_ref(),
+ inputs,
+ &StylesheetGuards::same(&guard),
+ Some(parent_style),
+ Some(layout_parent_style),
+ FirstLineReparenting::Yes { style_to_reparent },
+ /* rule_cache = */ None,
+ &mut RuleCacheConditions::default(),
+ )
+ .into()
+}
+
+#[cfg(feature = "gecko_debug")]
+fn simulate_compute_values_failure(property: &PropertyValuePair) -> bool {
+ let p = &property.mProperty;
+ let id = get_property_id_from_animatedpropertyid!(p, false);
+ id.as_shorthand().is_ok() && property.mSimulateComputeValuesFailure
+}
+
+#[cfg(not(feature = "gecko_debug"))]
+fn simulate_compute_values_failure(_: &PropertyValuePair) -> bool {
+ false
+}
+
+fn create_context_for_animation<'a>(
+ per_doc_data: &'a PerDocumentStyleDataImpl,
+ style: &'a ComputedValues,
+ parent_style: Option<&'a ComputedValues>,
+ for_smil_animation: bool,
+ rule_cache_conditions: &'a mut RuleCacheConditions,
+ container_size_query: ContainerSizeQuery<'a>,
+) -> Context<'a> {
+ Context::new_for_animation(
+ StyleBuilder::for_animation(
+ per_doc_data.stylist.device(),
+ Some(&per_doc_data.stylist),
+ style,
+ parent_style,
+ ),
+ for_smil_animation,
+ per_doc_data.stylist.quirks_mode(),
+ rule_cache_conditions,
+ container_size_query,
+ )
+}
+
+struct PropertyAndIndex {
+ property: PropertyId,
+ index: usize,
+}
+
+struct PrioritizedPropertyIter<'a> {
+ properties: &'a [PropertyValuePair],
+ sorted_property_indices: Vec<PropertyAndIndex>,
+ curr: usize,
+}
+
+impl<'a> PrioritizedPropertyIter<'a> {
+ fn new(properties: &'a [PropertyValuePair]) -> PrioritizedPropertyIter {
+ use style::values::animated::compare_property_priority;
+
+ // If we fail to convert a nsCSSPropertyID into a PropertyId we
+ // shouldn't fail outright but instead by treating that property as the
+ // 'all' property we make it sort last.
+ let mut sorted_property_indices: Vec<PropertyAndIndex> = properties
+ .iter()
+ .enumerate()
+ .map(|(index, pair)| {
+ let property = PropertyId::from_gecko_animated_property_id(&pair.mProperty)
+ .unwrap_or(PropertyId::NonCustom(ShorthandId::All.into()));
+ PropertyAndIndex { property, index }
+ })
+ .collect();
+ sorted_property_indices.sort_by(|a, b| compare_property_priority(&a.property, &b.property));
+
+ PrioritizedPropertyIter {
+ properties,
+ sorted_property_indices,
+ curr: 0,
+ }
+ }
+
+ fn reset(&mut self) {
+ self.curr = 0;
+ }
+}
+
+impl<'a> Iterator for PrioritizedPropertyIter<'a> {
+ type Item = &'a PropertyValuePair;
+
+ fn next(&mut self) -> Option<&'a PropertyValuePair> {
+ if self.curr >= self.sorted_property_indices.len() {
+ return None;
+ }
+ self.curr += 1;
+ Some(&self.properties[self.sorted_property_indices[self.curr - 1].index])
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GetComputedKeyframeValues(
+ keyframes: &nsTArray<structs::Keyframe>,
+ element: &RawGeckoElement,
+ pseudo_type: PseudoStyleType,
+ style: &ComputedValues,
+ raw_data: &PerDocumentStyleData,
+ computed_keyframes: &mut nsTArray<structs::ComputedKeyframeValues>,
+) {
+ use style::properties::PropertyDeclaration;
+ use style::custom_properties::CustomPropertiesBuilder;
+ use style::applicable_declarations::CascadePriority;
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+ let pseudo = PseudoElement::from_pseudo_type(pseudo_type, None);
+ let parent_element = if pseudo.is_none() {
+ element.inheritance_parent()
+ } else {
+ Some(element)
+ };
+ let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
+ let parent_style = parent_data
+ .as_ref()
+ .map(|d| d.styles.primary())
+ .map(|x| &**x);
+
+ let container_size_query =
+ ContainerSizeQuery::for_element(element, parent_style, pseudo.is_some());
+ let mut conditions = Default::default();
+ let mut context = create_context_for_animation(
+ &data,
+ &style,
+ parent_style,
+ /* for_smil_animation = */ false,
+ &mut conditions,
+ container_size_query,
+ );
+
+ let restriction = pseudo.and_then(|p| p.property_restriction());
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let default_values = data.default_computed_values();
+
+ for (index, keyframe) in keyframes.iter().enumerate() {
+ let ref mut animation_values = computed_keyframes[index];
+
+ let mut seen = PropertyDeclarationIdSet::default();
+ let mut iter = PrioritizedPropertyIter::new(&keyframe.mPropertyValues);
+
+ // FIXME: This is pretty much a hack. Instead, the AnimatedValue should be better
+ // integrated in the cascade. This would allow us to fix revert() too.
+ {
+ let mut builder = CustomPropertiesBuilder::new_with_properties(
+ &data.stylist,
+ style.custom_properties().clone(),
+ &mut context,
+ );
+ let priority = CascadePriority::same_tree_author_normal_at_root_layer();
+ for property in &mut iter {
+ let is_custom = match PropertyId::from_gecko_animated_property_id(&property.mProperty) {
+ Some(PropertyId::Custom(..)) => true,
+ _ => false,
+ };
+ if !is_custom {
+ break; // Custom props are guaranteed to sort earlier.
+ }
+ if property.mServoDeclarationBlock.mRawPtr.is_null() {
+ continue;
+ }
+ let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
+ let guard = declarations.read_with(&guard);
+ for decl in guard.normal_declaration_iter() {
+ if let PropertyDeclaration::Custom(ref declaration) = *decl {
+ builder.cascade(declaration, priority);
+ }
+ }
+ }
+ iter.reset();
+ let _deferred= builder.build(DeferFontRelativeCustomPropertyResolution::No);
+ debug_assert!(
+ _deferred.is_none(),
+ "Custom property processing deferred despite specifying otherwise?"
+ );
+ };
+
+ let mut property_index = 0;
+ for property in iter {
+ if simulate_compute_values_failure(property) {
+ continue;
+ }
+
+ let mut maybe_append_animation_value =
+ |property: PropertyDeclarationId, value: Option<AnimationValue>| {
+ debug_assert!(!property.is_logical());
+ debug_assert!(property.is_animatable());
+
+ // 'display' is only animatable from SMIL
+ if property == PropertyDeclarationId::Longhand(LonghandId::Display) {
+ return;
+ }
+
+ // Skip restricted properties
+ if restriction.map_or(false, |r| !property.flags().contains(r)) {
+ return;
+ }
+
+ if seen.contains(property) {
+ return;
+ }
+ seen.insert(property);
+
+ // This is safe since we immediately write to the uninitialized values.
+ unsafe {
+ animation_values.set_len((property_index + 1) as u32);
+ ptr::write(
+ &mut animation_values[property_index],
+ structs::PropertyStyleAnimationValuePair {
+ mProperty: property.to_gecko_animated_property_id(/* owned = */ true),
+ mValue: structs::AnimationValue {
+ mServo: value.map_or(structs::RefPtr::null(), |v| {
+ structs::RefPtr::from_arc(Arc::new(v))
+ }),
+ },
+ },
+ );
+ }
+ property_index += 1;
+ };
+
+ if property.mServoDeclarationBlock.mRawPtr.is_null() {
+ if let Some(prop) =
+ OwnedPropertyDeclarationId::from_gecko_animated_property_id(&property.mProperty)
+ {
+ maybe_append_animation_value(prop.as_borrowed(), None);
+ }
+ continue;
+ }
+
+ let declarations = unsafe { &*property.mServoDeclarationBlock.mRawPtr };
+ let guard = declarations.read_with(&guard);
+ let iter = guard.to_animation_value_iter(&mut context, &default_values);
+
+ for value in iter {
+ maybe_append_animation_value(value.id(), Some(value.clone()));
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GetAnimationValues(
+ declarations: &LockedDeclarationBlock,
+ element: &RawGeckoElement,
+ style: &ComputedValues,
+ raw_data: &PerDocumentStyleData,
+ animation_values: &mut ThinVec<structs::RefPtr<AnimationValue>>,
+) {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+ let parent_element = element.inheritance_parent();
+ let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
+ let parent_style = parent_data
+ .as_ref()
+ .map(|d| d.styles.primary())
+ .map(|x| &**x);
+
+ let container_size_query =
+ ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
+ let mut conditions = Default::default();
+ let mut context = create_context_for_animation(
+ &data,
+ &style,
+ parent_style,
+ /* for_smil_animation = */ true,
+ &mut conditions,
+ container_size_query,
+ );
+
+ let default_values = data.default_computed_values();
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+
+ let guard = declarations.read_with(&guard);
+ let iter = guard.to_animation_value_iter(
+ &mut context,
+ &default_values,
+ );
+ animation_values.extend(iter.map(|v| structs::RefPtr::from_arc(Arc::new(v))));
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_GetPropertyId(
+ value: &AnimationValue,
+ property_id: &mut structs::AnimatedPropertyID,
+) {
+ *property_id = value.id().to_gecko_animated_property_id(/* owned = */ true);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Compute(
+ element: &RawGeckoElement,
+ declarations: &LockedDeclarationBlock,
+ style: &ComputedValues,
+ raw_data: &PerDocumentStyleData,
+) -> Strong<AnimationValue> {
+ let data = raw_data.borrow();
+
+ let element = GeckoElement(element);
+ let parent_element = element.inheritance_parent();
+ let parent_data = parent_element.as_ref().and_then(|e| e.borrow_data());
+ let parent_style = parent_data
+ .as_ref()
+ .map(|d| d.styles.primary())
+ .map(|x| &**x);
+
+ let container_size_query =
+ ContainerSizeQuery::for_element(element, None, /* is_pseudo = */ false);
+ let mut conditions = Default::default();
+ let mut context = create_context_for_animation(
+ &data,
+ style,
+ parent_style,
+ /* for_smil_animation = */ false,
+ &mut conditions,
+ container_size_query,
+ );
+
+ let default_values = data.default_computed_values();
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ // We only compute the first element in declarations.
+ match declarations
+ .read_with(&guard)
+ .declaration_importance_iter()
+ .next()
+ {
+ Some((decl, imp)) if imp == Importance::Normal => {
+ let animation = AnimationValue::from_declaration(
+ decl,
+ &mut context,
+ default_values,
+ );
+ animation.map_or(Strong::null(), |value| Arc::new(value).into())
+ },
+ _ => Strong::null(),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_AssertTreeIsClean(root: &RawGeckoElement) {
+ if !cfg!(feature = "gecko_debug") {
+ panic!("Calling Servo_AssertTreeIsClean in release build");
+ }
+
+ let root = GeckoElement(root);
+ debug!("Servo_AssertTreeIsClean: ");
+ debug!("{:?}", ShowSubtreeData(root.as_node()));
+
+ fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) {
+ debug_assert!(
+ !el.has_dirty_descendants() && !el.has_animation_only_dirty_descendants(),
+ "{:?} has still dirty bit {:?} or animation-only dirty bit {:?}",
+ el,
+ el.has_dirty_descendants(),
+ el.has_animation_only_dirty_descendants()
+ );
+ for child in el.traversal_children() {
+ if let Some(child) = child.as_element() {
+ assert_subtree_is_clean(child);
+ }
+ }
+ }
+
+ assert_subtree_is_clean(root);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_IsWorkerThread() -> bool {
+ thread_state::get().is_worker()
+}
+
+enum Offset {
+ Zero,
+ One,
+}
+
+fn fill_in_missing_keyframe_values(
+ all_properties: &PropertyDeclarationIdSet,
+ timing_function: &ComputedTimingFunction,
+ properties_at_offset: &PropertyDeclarationIdSet,
+ offset: Offset,
+ keyframes: &mut nsTArray<structs::Keyframe>,
+) {
+ // Return early if all animated properties are already set.
+ if properties_at_offset.contains_all(all_properties) {
+ return;
+ }
+
+ // Use auto for missing keyframes.
+ // FIXME: This may be a spec issue in css-animations-2 because the spec says the default
+ // keyframe-specific composite is replace, but web-animations-1 uses auto. Use auto now so we
+ // use the value of animation-composition of the element, for missing keyframes.
+ // https://github.com/w3c/csswg-drafts/issues/7476
+ let composition = structs::CompositeOperationOrAuto::Auto;
+ let keyframe = match offset {
+ Offset::Zero => unsafe {
+ Gecko_GetOrCreateInitialKeyframe(keyframes, timing_function, composition)
+ },
+ Offset::One => unsafe {
+ Gecko_GetOrCreateFinalKeyframe(keyframes, timing_function, composition)
+ },
+ };
+
+ // Append properties that have not been set at this offset.
+ for property in all_properties.iter() {
+ if !properties_at_offset.contains(property) {
+ unsafe {
+ Gecko_AppendPropertyValuePair(
+ &mut *(*keyframe).mPropertyValues,
+ &property.to_gecko_animated_property_id(/* owned = */ false),
+ );
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_GetKeyframesForName(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ style: &ComputedValues,
+ name: *mut nsAtom,
+ inherited_timing_function: &ComputedTimingFunction,
+ keyframes: &mut nsTArray<structs::Keyframe>,
+) -> bool {
+ use style::gecko_bindings::structs::CompositeOperationOrAuto;
+ use style::values::computed::AnimationComposition;
+
+ debug_assert!(keyframes.len() == 0, "keyframes should be initially empty");
+
+ let element = GeckoElement(element);
+ let data = raw_data.borrow();
+ let name = Atom::from_raw(name);
+
+ let animation = match data.stylist.get_animation(&name, element) {
+ Some(animation) => animation,
+ None => return false,
+ };
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+
+ let mut properties_set_at_current_offset = PropertyDeclarationIdSet::default();
+ let mut properties_set_at_start = PropertyDeclarationIdSet::default();
+ let mut properties_set_at_end = PropertyDeclarationIdSet::default();
+ let mut has_complete_initial_keyframe = false;
+ let mut has_complete_final_keyframe = false;
+ let mut current_offset = -1.;
+
+ let writing_mode = style.writing_mode;
+
+ // Iterate over the keyframe rules backwards so we can drop overridden
+ // properties (since declarations in later rules override those in earlier
+ // ones).
+ for step in animation.steps.iter().rev() {
+ if step.start_percentage.0 != current_offset {
+ properties_set_at_current_offset.clear();
+ current_offset = step.start_percentage.0;
+ }
+
+ // Override timing_function if the keyframe has an animation-timing-function.
+ let timing_function = match step.get_animation_timing_function(&guard) {
+ Some(val) => val.to_computed_value_without_context(),
+ None => (*inherited_timing_function).clone(),
+ };
+
+ // Override composite operation if the keyframe has an animation-composition.
+ let composition =
+ step.get_animation_composition(&guard)
+ .map_or(CompositeOperationOrAuto::Auto, |val| match val {
+ AnimationComposition::Replace => CompositeOperationOrAuto::Replace,
+ AnimationComposition::Add => CompositeOperationOrAuto::Add,
+ AnimationComposition::Accumulate => CompositeOperationOrAuto::Accumulate,
+ });
+
+ // Look for an existing keyframe with the same offset, timing function, and compsition, or
+ // else add a new keyframe at the beginning of the keyframe array.
+ let keyframe = Gecko_GetOrCreateKeyframeAtStart(
+ keyframes,
+ step.start_percentage.0 as f32,
+ &timing_function,
+ composition,
+ );
+
+ match step.value {
+ KeyframesStepValue::ComputedValues => {
+ // In KeyframesAnimation::from_keyframes if there is no 0% or
+ // 100% keyframe at all, we will create a 'ComputedValues' step
+ // to represent that all properties animated by the keyframes
+ // animation should be set to the underlying computed value for
+ // that keyframe.
+ let mut seen = PropertyDeclarationIdSet::default();
+ for property in animation.properties_changed.iter() {
+ let property = property.to_physical(writing_mode);
+ if seen.contains(property) {
+ continue;
+ }
+ seen.insert(property);
+
+ Gecko_AppendPropertyValuePair(
+ &mut *(*keyframe).mPropertyValues,
+ &property.to_gecko_animated_property_id(/* owned = */ false),
+ );
+ }
+ if current_offset == 0.0 {
+ has_complete_initial_keyframe = true;
+ } else if current_offset == 1.0 {
+ has_complete_final_keyframe = true;
+ }
+ },
+ KeyframesStepValue::Declarations { ref block } => {
+ let guard = block.read_with(&guard);
+
+ // Filter out non-animatable properties and properties with
+ // !important.
+ //
+ // Also, iterate in reverse to respect the source order in case
+ // there are logical and physical longhands in the same block.
+ for declaration in guard.normal_declaration_iter().rev() {
+ let id = declaration.id().to_physical(writing_mode);
+
+ // Skip non-animatable properties, including the 'display' property because
+ // although it is animatable from SMIL, it should not be animatable from CSS
+ // Animations.
+ if !id.is_animatable() ||
+ id == PropertyDeclarationId::Longhand(LonghandId::Display)
+ {
+ continue;
+ }
+
+ if properties_set_at_current_offset.contains(id) {
+ continue;
+ }
+
+ let pair = Gecko_AppendPropertyValuePair(
+ &mut *(*keyframe).mPropertyValues,
+ &id.to_gecko_animated_property_id(/* owned = */ false),
+ );
+
+ (*pair).mServoDeclarationBlock.set_arc(Arc::new(
+ global_style_data
+ .shared_lock
+ .wrap(PropertyDeclarationBlock::with_one(
+ declaration.to_physical(writing_mode),
+ Importance::Normal,
+ )),
+ ));
+
+ if current_offset == 0.0 {
+ properties_set_at_start.insert(id);
+ } else if current_offset == 1.0 {
+ properties_set_at_end.insert(id);
+ }
+ properties_set_at_current_offset.insert(id);
+ }
+ },
+ }
+ }
+
+ let mut properties_changed = PropertyDeclarationIdSet::default();
+ for property in animation.properties_changed.iter() {
+ properties_changed.insert(property.to_physical(writing_mode));
+ }
+
+ // Append property values that are missing in the initial or the final keyframes.
+ if !has_complete_initial_keyframe {
+ fill_in_missing_keyframe_values(
+ &properties_changed,
+ inherited_timing_function,
+ &properties_set_at_start,
+ Offset::Zero,
+ keyframes,
+ );
+ }
+ if !has_complete_final_keyframe {
+ fill_in_missing_keyframe_values(
+ &properties_changed,
+ inherited_timing_function,
+ &properties_set_at_end,
+ Offset::One,
+ keyframes,
+ );
+ }
+ true
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_GetFontFaceRules(
+ raw_data: &PerDocumentStyleData,
+ rules: &mut ThinVec<structs::nsFontFaceRuleContainer>,
+) {
+ let data = raw_data.borrow();
+ debug_assert_eq!(rules.len(), 0);
+
+ // Reversed iterator because Gecko expects rules to appear sorted
+ // UserAgent first, Author last.
+ let font_face_iter = data
+ .stylist
+ .iter_extra_data_origins_rev()
+ .flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
+
+ rules.extend(font_face_iter.map(|(&(ref rule, _layer_id), origin)| {
+ structs::nsFontFaceRuleContainer {
+ mRule: structs::RefPtr::from_arc(rule.clone()),
+ mOrigin: origin,
+ }
+ }))
+}
+
+// XXX Ideally this should return a Option<&LockedCounterStyleRule>,
+// but we cannot, because the value from AtomicRefCell::borrow() can only
+// live in this function, and thus anything derived from it cannot get the
+// same lifetime as raw_data in parameter. See bug 1451543.
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleSet_GetCounterStyleRule(
+ raw_data: &PerDocumentStyleData,
+ name: *mut nsAtom,
+) -> *const LockedCounterStyleRule {
+ let data = raw_data.borrow();
+ Atom::with(name, |name| {
+ data.stylist
+ .iter_extra_data_origins()
+ .find_map(|(d, _)| d.counter_styles.get(name))
+ .map_or(ptr::null(), |rule| &**rule as *const _)
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_BuildFontFeatureValueSet(
+ raw_data: &PerDocumentStyleData,
+) -> *mut gfxFontFeatureValueSet {
+ let data = raw_data.borrow();
+
+ let has_rule = data
+ .stylist
+ .iter_extra_data_origins()
+ .any(|(d, _)| !d.font_feature_values.is_empty());
+
+ if !has_rule {
+ return ptr::null_mut();
+ }
+
+ let font_feature_values_iter = data
+ .stylist
+ .iter_extra_data_origins_rev()
+ .flat_map(|(d, _)| d.font_feature_values.iter());
+
+ let set = unsafe { Gecko_ConstructFontFeatureValueSet() };
+ for &(ref rule, _) in font_feature_values_iter {
+ rule.set_at_rules(set);
+ }
+ set
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_BuildFontPaletteValueSet(
+ raw_data: &PerDocumentStyleData,
+) -> *mut FontPaletteValueSet {
+ let data = raw_data.borrow();
+
+ let has_rule = data
+ .stylist
+ .iter_extra_data_origins()
+ .any(|(d, _)| !d.font_palette_values.is_empty());
+
+ if !has_rule {
+ return ptr::null_mut();
+ }
+
+ let font_palette_values_iter = data
+ .stylist
+ .iter_extra_data_origins_rev()
+ .flat_map(|(d, _)| d.font_palette_values.iter());
+
+ let set = unsafe { Gecko_ConstructFontPaletteValueSet() };
+ for &(ref rule, _) in font_palette_values_iter {
+ rule.to_gecko_palette_value_set(set);
+ }
+ set
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_ResolveForDeclarations(
+ raw_data: &PerDocumentStyleData,
+ parent_style_context: Option<&ComputedValues>,
+ declarations: &LockedDeclarationBlock,
+) -> Strong<ComputedValues> {
+ let doc_data = raw_data.borrow();
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let guards = StylesheetGuards::same(&guard);
+
+ let parent_style = match parent_style_context {
+ Some(parent) => &*parent,
+ None => doc_data.default_computed_values(),
+ };
+
+ doc_data
+ .stylist
+ .compute_for_declarations::<GeckoElement>(&guards, parent_style, unsafe {
+ Arc::from_raw_addrefed(declarations)
+ })
+ .into()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_AddSizeOfExcludingThis(
+ malloc_size_of: GeckoMallocSizeOf,
+ malloc_enclosing_size_of: GeckoMallocSizeOf,
+ sizes: *mut ServoStyleSetSizes,
+ raw_data: &PerDocumentStyleData,
+) {
+ let data = raw_data.borrow_mut();
+ let mut ops = MallocSizeOfOps::new(
+ malloc_size_of.unwrap(),
+ Some(malloc_enclosing_size_of.unwrap()),
+ None,
+ );
+ let sizes = unsafe { sizes.as_mut() }.unwrap();
+ data.add_size_of(&mut ops, sizes);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_UACache_AddSizeOf(
+ malloc_size_of: GeckoMallocSizeOf,
+ malloc_enclosing_size_of: GeckoMallocSizeOf,
+ sizes: *mut ServoStyleSetSizes,
+) {
+ let mut ops = MallocSizeOfOps::new(
+ malloc_size_of.unwrap(),
+ Some(malloc_enclosing_size_of.unwrap()),
+ None,
+ );
+ let sizes = unsafe { sizes.as_mut() }.unwrap();
+ add_size_of_ua_cache(&mut ops, sizes);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ local_name: *mut nsAtom,
+) -> bool {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+
+ unsafe {
+ AtomIdent::with(local_name, |atom| {
+ data.stylist.any_applicable_rule_data(element, |data| {
+ data.might_have_attribute_dependency(atom)
+ })
+ })
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MightHaveNthOfIDDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ old_id: *mut nsAtom,
+ new_id: *mut nsAtom,
+) -> bool {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+
+ data.stylist.any_applicable_rule_data(element, |data| {
+ [old_id, new_id]
+ .iter()
+ .filter(|id| !id.is_null())
+ .any(|id| unsafe {
+ AtomIdent::with(*id, |atom| data.might_have_nth_of_id_dependency(atom))
+ }) ||
+ data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("id")))
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MightHaveNthOfClassDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ snapshots: &ServoElementSnapshotTable,
+) -> bool {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+
+ data.stylist.any_applicable_rule_data(element, |data| {
+ classes_changed(&element, snapshots)
+ .iter()
+ .any(|atom| data.might_have_nth_of_class_dependency(atom)) ||
+ data.might_have_nth_of_attribute_dependency(&AtomIdent(atom!("class")))
+ })
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MightHaveNthOfAttributeDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ local_name: *mut nsAtom,
+) -> bool {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+
+ unsafe {
+ AtomIdent::with(local_name, |atom| {
+ data.stylist.any_applicable_rule_data(element, |data| {
+ data.might_have_nth_of_attribute_dependency(atom)
+ })
+ })
+ }
+}
+
+fn on_siblings_invalidated(element: GeckoElement) {
+ let parent = element
+ .traversal_parent()
+ .expect("How could we invalidate siblings without a common parent?");
+ unsafe {
+ parent.set_dirty_descendants();
+ bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
+ }
+}
+
+fn restyle_for_nth_of(element: GeckoElement, flags: ElementSelectorFlags) {
+ debug_assert!(
+ !flags.is_empty(),
+ "Calling restyle for nth but no relevant flag is set."
+ );
+ fn invalidate_siblings_of(
+ element: GeckoElement,
+ get_sibling: fn(GeckoElement) -> Option<GeckoElement>,
+ ) {
+ let mut sibling = get_sibling(element);
+ while let Some(sib) = sibling {
+ if let Some(mut data) = sib.mutate_data() {
+ data.hint.insert(RestyleHint::restyle_subtree());
+ }
+ sibling = get_sibling(sib);
+ }
+ }
+
+ if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
+ invalidate_siblings_of(element, |e| e.prev_sibling_element());
+ }
+ if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
+ invalidate_siblings_of(element, |e| e.next_sibling_element());
+ }
+ on_siblings_invalidated(element);
+}
+
+fn relative_selector_invalidated_at(element: GeckoElement, result: &InvalidationResult) {
+ if result.has_invalidated_siblings() {
+ on_siblings_invalidated(element);
+ } else if result.has_invalidated_descendants() {
+ unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
+ } else if result.has_invalidated_self() {
+ unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
+ let flags = element
+ .parent_element()
+ .map_or(ElementSelectorFlags::empty(), |e| e.slow_selector_flags());
+ // We invalidated up to the anchor, and it has a flag for nth-of invalidation.
+ if !flags.is_empty() {
+ restyle_for_nth_of(element, flags);
+ }
+ }
+}
+
+fn add_relative_selector_attribute_dependency<'a>(
+ element: &GeckoElement<'a>,
+ scope: &Option<OpaqueElement>,
+ invalidation_map: &'a RelativeSelectorInvalidationMap,
+ attribute: &AtomIdent,
+ collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
+) {
+ match invalidation_map
+ .map
+ .other_attribute_affecting_selectors
+ .get(attribute)
+ {
+ Some(v) => {
+ for dependency in v {
+ collector.add_dependency(dependency, *element, *scope);
+ }
+ },
+ None => (),
+ };
+}
+
+fn inherit_relative_selector_search_direction(
+ parent: Option<GeckoElement>,
+ prev_sibling: Option<GeckoElement>,
+) -> ElementSelectorFlags {
+ let mut inherited = ElementSelectorFlags::empty();
+ if let Some(parent) = parent {
+ if let Some(direction) = parent.relative_selector_search_direction() {
+ inherited |= direction
+ .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR);
+ }
+ }
+ if let Some(sibling) = prev_sibling {
+ if let Some(direction) = sibling.relative_selector_search_direction() {
+ // Inherit both, for e.g. a sibling with `:has(~.sibling .descendant)`
+ inherited |= direction.intersection(
+ ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING,
+ );
+ }
+ }
+ inherited
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorIDDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ old_id: *mut nsAtom,
+ new_id: *mut nsAtom,
+ snapshots: &ServoElementSnapshotTable,
+) {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+ let invalidator = RelativeSelectorInvalidator {
+ element,
+ quirks_mode,
+ snapshot_table: Some(snapshots),
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ _marker: std::marker::PhantomData,
+ };
+
+ invalidator.invalidate_relative_selectors_for_this(
+ &data.stylist,
+ |element, scope, data, quirks_mode, collector| {
+ let invalidation_map = data.relative_selector_invalidation_map();
+ relative_selector_dependencies_for_id(
+ old_id,
+ new_id,
+ element,
+ scope,
+ quirks_mode,
+ &invalidation_map,
+ collector,
+ );
+ add_relative_selector_attribute_dependency(
+ element,
+ &scope,
+ invalidation_map,
+ &AtomIdent(atom!("id")),
+ collector,
+ );
+ },
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorClassDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ snapshots: &ServoElementSnapshotTable,
+) {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+ let invalidator = RelativeSelectorInvalidator {
+ element,
+ quirks_mode,
+ snapshot_table: Some(snapshots),
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ _marker: std::marker::PhantomData,
+ };
+
+ invalidator.invalidate_relative_selectors_for_this(
+ &data.stylist,
+ |element, scope, data, quirks_mode, mut collector| {
+ let invalidation_map = data.relative_selector_invalidation_map();
+
+ relative_selector_dependencies_for_class(
+ &classes_changed(element, snapshots),
+ &element,
+ scope,
+ quirks_mode,
+ invalidation_map,
+ collector,
+ );
+ add_relative_selector_attribute_dependency(
+ element,
+ &scope,
+ invalidation_map,
+ &AtomIdent(atom!("class")),
+ &mut collector,
+ );
+ },
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorAttributeDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ local_name: *mut nsAtom,
+ snapshots: &ServoElementSnapshotTable,
+) {
+ let data = raw_data.borrow();
+ let element = GeckoElement(element);
+
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+ unsafe {
+ AtomIdent::with(local_name, |atom| {
+ let invalidator = RelativeSelectorInvalidator {
+ element,
+ quirks_mode,
+ snapshot_table: Some(snapshots),
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ _marker: std::marker::PhantomData,
+ };
+
+ invalidator.invalidate_relative_selectors_for_this(
+ &data.stylist,
+ |element, scope, data, _quirks_mode, mut collector| {
+ add_relative_selector_attribute_dependency(
+ element,
+ &scope,
+ data.relative_selector_invalidation_map(),
+ atom,
+ &mut collector,
+ );
+ },
+ );
+ })
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorStateDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ state: u64,
+ snapshots: &ServoElementSnapshotTable,
+) {
+ let element = GeckoElement(element);
+
+ let state = match ElementState::from_bits(state) {
+ Some(state) => state,
+ None => return,
+ };
+ let data = raw_data.borrow();
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+
+ let invalidator = RelativeSelectorInvalidator {
+ element,
+ quirks_mode,
+ snapshot_table: Some(snapshots),
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ _marker: std::marker::PhantomData,
+ };
+
+ invalidator.invalidate_relative_selectors_for_this(
+ &data.stylist,
+ |element, scope, data, quirks_mode, collector| {
+ let invalidation_map = data.relative_selector_invalidation_map();
+ invalidation_map
+ .map
+ .state_affecting_selectors
+ .lookup_with_additional(*element, quirks_mode, None, &[], state, |dependency| {
+ if !dependency.state.intersects(state) {
+ return true;
+ }
+ collector.add_dependency(&dependency.dep, *element, scope);
+ true
+ });
+ },
+ );
+}
+
+fn invalidate_relative_selector_prev_sibling_side_effect(
+ prev_sibling: GeckoElement,
+ quirks_mode: QuirksMode,
+ sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
+ stylist: &Stylist,
+) {
+ let invalidator = RelativeSelectorInvalidator {
+ element: prev_sibling,
+ quirks_mode,
+ snapshot_table: None,
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map,
+ _marker: std::marker::PhantomData,
+ };
+ invalidator.invalidate_relative_selectors_for_dom_mutation(
+ false,
+ &stylist,
+ ElementSelectorFlags::empty(),
+ DomMutationOperation::SideEffectPrevSibling,
+ );
+}
+
+fn invalidate_relative_selector_next_sibling_side_effect(
+ next_sibling: GeckoElement,
+ quirks_mode: QuirksMode,
+ sibling_traversal_map: SiblingTraversalMap<GeckoElement>,
+ stylist: &Stylist,
+) {
+ let invalidator = RelativeSelectorInvalidator {
+ element: next_sibling,
+ quirks_mode,
+ snapshot_table: None,
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map,
+ _marker: std::marker::PhantomData,
+ };
+ invalidator.invalidate_relative_selectors_for_dom_mutation(
+ false,
+ &stylist,
+ ElementSelectorFlags::empty(),
+ DomMutationOperation::SideEffectNextSibling,
+ );
+}
+
+fn invalidate_relative_selector_ts_dependency(
+ raw_data: &PerDocumentStyleData,
+ element: GeckoElement,
+ state: TSStateForInvalidation,
+) {
+ let data = raw_data.borrow();
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+
+ let invalidator = RelativeSelectorInvalidator {
+ element,
+ quirks_mode,
+ snapshot_table: None,
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ _marker: std::marker::PhantomData,
+ };
+
+ invalidator.invalidate_relative_selectors_for_this(
+ &data.stylist,
+ |element, scope, data, quirks_mode, collector| {
+ let invalidation_map = data.relative_selector_invalidation_map();
+ invalidation_map
+ .ts_state_to_selector
+ .lookup_with_additional(
+ *element,
+ quirks_mode,
+ None,
+ &[],
+ ElementState::empty(),
+ |dependency| {
+ if !dependency.state.intersects(state) {
+ return true;
+ }
+ collector.add_dependency(&dependency.dep, *element, scope);
+ true
+ },
+ );
+ },
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorEmptyDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+) {
+ invalidate_relative_selector_ts_dependency(
+ raw_data,
+ GeckoElement(element),
+ TSStateForInvalidation::EMPTY,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthEdgeDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+) {
+ invalidate_relative_selector_ts_dependency(
+ raw_data,
+ GeckoElement(element),
+ TSStateForInvalidation::NTH_EDGE,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorNthDependencyFromSibling(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+) {
+ let mut element = Some(GeckoElement(element));
+
+ // Short of doing the actual matching, any of the siblings can match the selector, so we
+ // have to try invalidating against all of them.
+ while let Some(sibling) = element {
+ invalidate_relative_selector_ts_dependency(raw_data, sibling, TSStateForInvalidation::NTH);
+ element = sibling.next_sibling_element();
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForInsertion(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+) {
+ let element = GeckoElement(element);
+ let data = raw_data.borrow();
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+
+ let inherited =
+ inherit_relative_selector_search_direction(element.parent_element(), element.prev_sibling_element());
+ // Technically, we're not handling breakouts, where the anchor is a (later-sibling) descendant.
+ // For descendant case, we're ok since it's a descendant of an element yet to be styled.
+ // For later-sibling descendant, `HAS_SLOW_SELECTOR_LATER_SIBLINGS` is set anyway.
+ if inherited.is_empty() {
+ return;
+ }
+
+ // Ok, we could've been inserted between two sibling elements that were connected
+ // through next sibling. This can happen in two ways:
+ // * `.a:has(+ .b)`
+ // * `:has(.. .a + .b ..)`
+ // Note that the previous sibling may be the anchor, and not part of the invalidation chain.
+ // Either way, there must be siblings to both sides of the element being inserted
+ // to consider it.
+ match (
+ element.prev_sibling_element(),
+ element.next_sibling_element(),
+ ) {
+ (Some(prev_sibling), Some(next_sibling)) => 'sibling: {
+ // If the prev sibling is not on the sibling search path, skip.
+ if prev_sibling
+ .relative_selector_search_direction()
+ .map_or(true, |direction| {
+ !direction.intersects(
+ ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
+ )
+ })
+ {
+ break 'sibling;
+ }
+ element.apply_selector_flags(
+ ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
+ );
+ invalidate_relative_selector_prev_sibling_side_effect(
+ prev_sibling,
+ quirks_mode,
+ SiblingTraversalMap::new(
+ prev_sibling,
+ prev_sibling.prev_sibling_element(),
+ element.next_sibling_element(),
+ ), // Pretend this inserted element isn't here.
+ &data.stylist,
+ );
+ invalidate_relative_selector_next_sibling_side_effect(
+ next_sibling,
+ quirks_mode,
+ SiblingTraversalMap::new(
+ next_sibling,
+ Some(prev_sibling),
+ next_sibling.next_sibling_element(),
+ ),
+ &data.stylist,
+ );
+ },
+ _ => (),
+ };
+
+ let invalidator = RelativeSelectorInvalidator {
+ element,
+ quirks_mode,
+ snapshot_table: None,
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ _marker: std::marker::PhantomData,
+ };
+
+ invalidator.invalidate_relative_selectors_for_dom_mutation(
+ true,
+ &data.stylist,
+ inherited,
+ DomMutationOperation::Insert,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForAppend(
+ raw_data: &PerDocumentStyleData,
+ first_node: &RawGeckoNode,
+) {
+ let first_node = GeckoNode(first_node);
+ let inherited = inherit_relative_selector_search_direction(
+ first_node.parent_element(),
+ first_node.prev_sibling_element(),
+ );
+ if inherited.is_empty() {
+ return;
+ }
+ let first_element = if let Some(e) = first_node.as_element() {
+ e
+ } else if let Some(e) = first_node.next_sibling_element() {
+ e
+ } else {
+ return;
+ };
+ let data = raw_data.borrow();
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+
+ let mut element = Some(first_element);
+ while let Some(e) = element {
+ let invalidator = RelativeSelectorInvalidator {
+ element: e,
+ quirks_mode,
+ snapshot_table: None,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ invalidated: relative_selector_invalidated_at,
+ _marker: std::marker::PhantomData,
+ };
+ invalidator.invalidate_relative_selectors_for_dom_mutation(
+ true,
+ &data.stylist,
+ inherited,
+ DomMutationOperation::Append,
+ );
+ element = e.next_sibling_element();
+ }
+}
+
+fn get_siblings_of_element<'e>(
+ element: GeckoElement<'e>,
+ following_node: &'e Option<GeckoNode<'e>>,
+) -> (Option<GeckoElement<'e>>, Option<GeckoElement<'e>>) {
+ let node = match following_node {
+ Some(n) => n,
+ None => return match element.as_node().parent_node() {
+ Some(p) => (p.last_child_element(), None),
+ None => (None, None),
+ },
+ };
+
+ (node.prev_sibling_element(), node.next_sibling_element())
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_MaybeInvalidateRelativeSelectorForRemoval(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ following_node: Option<&RawGeckoNode>,
+) {
+ let element = GeckoElement(element);
+
+ // This element was in-tree, so we can safely say that if it was not on
+ // the relative selector search path, its removal will not invalidate any
+ // relative selector.
+ if element.relative_selector_search_direction().is_none() {
+ return;
+ }
+ let following_node = following_node.map(GeckoNode);
+ let (prev_sibling, next_sibling) =
+ get_siblings_of_element(element, &following_node);
+ let data = raw_data.borrow();
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+
+ let inherited = inherit_relative_selector_search_direction(element.parent_element(), prev_sibling);
+ if inherited.is_empty() {
+ return;
+ }
+
+ // Same comment as insertion applies.
+ match (prev_sibling, next_sibling) {
+ (Some(prev_sibling), Some(next_sibling)) => {
+ invalidate_relative_selector_prev_sibling_side_effect(
+ prev_sibling,
+ quirks_mode,
+ SiblingTraversalMap::default(),
+ &data.stylist,
+ );
+ invalidate_relative_selector_next_sibling_side_effect(
+ next_sibling,
+ quirks_mode,
+ SiblingTraversalMap::default(),
+ &data.stylist,
+ );
+ },
+ _ => (),
+ };
+ let invalidator = RelativeSelectorInvalidator {
+ element,
+ quirks_mode,
+ snapshot_table: None,
+ sibling_traversal_map: SiblingTraversalMap::new(element, prev_sibling, next_sibling),
+ invalidated: relative_selector_invalidated_at,
+ _marker: std::marker::PhantomData,
+ };
+ invalidator.invalidate_relative_selectors_for_dom_mutation(
+ true,
+ &data.stylist,
+ inherited,
+ DomMutationOperation::Remove,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_HasStateDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ state: u64,
+) -> bool {
+ let element = GeckoElement(element);
+
+ let state = ElementState::from_bits_retain(state);
+ let data = raw_data.borrow();
+
+ data.stylist
+ .any_applicable_rule_data(element, |data| data.has_state_dependency(state))
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_HasNthOfStateDependency(
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ state: u64,
+) -> bool {
+ let element = GeckoElement(element);
+
+ let state = ElementState::from_bits_retain(state);
+ let data = raw_data.borrow();
+
+ data.stylist
+ .any_applicable_rule_data(element, |data| data.has_nth_of_state_dependency(state))
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_RestyleSiblingsForNthOf(element: &RawGeckoElement, flags: u32) {
+ let flags = slow_selector_flags_from_node_selector_flags(flags);
+ let element = GeckoElement(element);
+ restyle_for_nth_of(element, flags);
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_StyleSet_HasDocumentStateDependency(
+ raw_data: &PerDocumentStyleData,
+ state: u64,
+) -> bool {
+ let state = DocumentState::from_bits_retain(state);
+ let data = raw_data.borrow();
+
+ data.stylist.has_document_state_dependency(state)
+}
+
+fn computed_or_resolved_value(
+ style: &ComputedValues,
+ prop: nsCSSPropertyID,
+ context: Option<&style::values::resolved::Context>,
+ value: &mut nsACString,
+) {
+ if let Some(longhand) = LonghandId::from_nscsspropertyid(prop) {
+ return style
+ .computed_or_resolved_value(longhand, context, value)
+ .unwrap();
+ }
+
+ let shorthand =
+ ShorthandId::from_nscsspropertyid(prop).expect("Not a shorthand nor a longhand?");
+ let mut block = PropertyDeclarationBlock::new();
+ for longhand in shorthand.longhands() {
+ block.push(
+ style.computed_or_resolved_declaration(longhand, context),
+ Importance::Normal,
+ );
+ }
+ block.shorthand_to_css(shorthand, value).unwrap();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_GetComputedValue(
+ style: &ComputedValues,
+ prop: nsCSSPropertyID,
+ value: &mut nsACString,
+) {
+ computed_or_resolved_value(style, prop, None, value)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_GetResolvedValue(
+ style: &ComputedValues,
+ prop: nsCSSPropertyID,
+ raw_data: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ value: &mut nsACString,
+) {
+ use style::values::resolved;
+
+ let data = raw_data.borrow();
+ let device = data.stylist.device();
+ let context = resolved::Context {
+ style,
+ device,
+ element_info: resolved::ResolvedElementInfo {
+ element: GeckoElement(element),
+ },
+ };
+
+ computed_or_resolved_value(style, prop, Some(&context), value)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_GetCustomPropertyValue(
+ computed_values: &ComputedValues,
+ raw_style_set: &PerDocumentStyleData,
+ name: &nsACString,
+ value: &mut nsACString,
+) -> bool {
+ let doc_data = raw_style_set.borrow();
+ let name = Atom::from(name.as_str_unchecked());
+ let custom_registration = doc_data.stylist.get_custom_property_registration(&name);
+ let computed_value = if custom_registration.inherits() {
+ computed_values.custom_properties.inherited.get(&name)
+ } else {
+ computed_values.custom_properties.non_inherited.get(&name)
+ };
+
+ if let Some(v) = computed_value {
+ v.to_css(&mut CssWriter::new(value)).unwrap();
+ true
+ } else {
+ false
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GetCustomPropertiesCount(computed_values: &ComputedValues) -> u32 {
+ // Just expose the custom property items from custom_properties.inherited
+ // and custom_properties.non_inherited.
+ let properties = computed_values.custom_properties();
+ properties.inherited.len() as u32 + properties.non_inherited.len() as u32
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GetCustomPropertyNameAt(
+ computed_values: &ComputedValues,
+ index: u32,
+) -> *mut nsAtom {
+ match &computed_values
+ .custom_properties
+ .property_at(index as usize)
+ {
+ Some((name, _value)) => name.as_ptr(),
+ None => ptr::null_mut(),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_CssUrl_IsLocalRef(url: &url::CssUrl) -> bool {
+ url.is_fragment()
+}
+
+fn relative_selector_dependencies_for_id<'a>(
+ old_id: *const nsAtom,
+ new_id: *const nsAtom,
+ element: &GeckoElement<'a>,
+ scope: Option<OpaqueElement>,
+ quirks_mode: QuirksMode,
+ invalidation_map: &'a RelativeSelectorInvalidationMap,
+ collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
+) {
+ [old_id, new_id]
+ .iter()
+ .filter(|id| !id.is_null())
+ .for_each(|id| unsafe {
+ AtomIdent::with(*id, |atom| {
+ match invalidation_map.map.id_to_selector.get(atom, quirks_mode) {
+ Some(v) => {
+ for dependency in v {
+ collector.add_dependency(dependency, *element, scope);
+ }
+ },
+ None => (),
+ };
+ })
+ });
+}
+
+fn relative_selector_dependencies_for_class<'a>(
+ classes_changed: &SmallVec<[Atom; 8]>,
+ element: &GeckoElement<'a>,
+ scope: Option<OpaqueElement>,
+ quirks_mode: QuirksMode,
+ invalidation_map: &'a RelativeSelectorInvalidationMap,
+ collector: &mut RelativeSelectorDependencyCollector<'a, GeckoElement<'a>>,
+) {
+ classes_changed.iter().for_each(|atom| {
+ match invalidation_map
+ .map
+ .class_to_selector
+ .get(atom, quirks_mode)
+ {
+ Some(v) => {
+ for dependency in v {
+ collector.add_dependency(dependency, *element, scope);
+ }
+ },
+ None => (),
+ };
+ });
+}
+
+fn process_relative_selector_invalidations(
+ element: &GeckoElement,
+ snapshot_table: &ServoElementSnapshotTable,
+ data: &PerDocumentStyleDataImpl,
+) {
+ let snapshot = match snapshot_table.get(element) {
+ None => return,
+ Some(s) => s,
+ };
+ let mut states = None;
+ let mut classes = None;
+
+ let quirks_mode: QuirksMode = data.stylist.quirks_mode();
+ let invalidator = RelativeSelectorInvalidator {
+ element: *element,
+ quirks_mode,
+ invalidated: relative_selector_invalidated_at,
+ sibling_traversal_map: SiblingTraversalMap::default(),
+ snapshot_table: Some(snapshot_table),
+ _marker: std::marker::PhantomData,
+ };
+
+ invalidator.invalidate_relative_selectors_for_this(
+ &data.stylist,
+ |element, scope, data, quirks_mode, collector| {
+ let invalidation_map = data.relative_selector_invalidation_map();
+ let states = *states.get_or_insert_with(|| {
+ ElementWrapper::new(*element, snapshot_table).state_changes()
+ });
+ let classes = classes.get_or_insert_with(|| classes_changed(element, snapshot_table));
+ if snapshot.id_changed() {
+ relative_selector_dependencies_for_id(
+ element
+ .id()
+ .map(|id| id.as_ptr().cast_const())
+ .unwrap_or(ptr::null()),
+ snapshot
+ .id_attr()
+ .map(|id| id.as_ptr().cast_const())
+ .unwrap_or(ptr::null()),
+ element,
+ scope,
+ quirks_mode,
+ invalidation_map,
+ collector,
+ );
+ }
+ relative_selector_dependencies_for_class(
+ &classes,
+ element,
+ scope,
+ quirks_mode,
+ invalidation_map,
+ collector,
+ );
+ snapshot.each_attr_changed(|attr| {
+ add_relative_selector_attribute_dependency(
+ element,
+ &scope,
+ invalidation_map,
+ attr,
+ collector,
+ )
+ });
+ invalidation_map
+ .map
+ .state_affecting_selectors
+ .lookup_with_additional(*element, quirks_mode, None, &[], states, |dependency| {
+ if !dependency.state.intersects(states) {
+ return true;
+ }
+ collector.add_dependency(&dependency.dep, *element, scope);
+ true
+ });
+ },
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ProcessInvalidations(
+ set: &PerDocumentStyleData,
+ element: &RawGeckoElement,
+ snapshots: *const ServoElementSnapshotTable,
+) {
+ debug_assert!(!snapshots.is_null());
+
+ let element = GeckoElement(element);
+ debug_assert!(element.has_snapshot());
+ debug_assert!(!element.handled_snapshot());
+
+ let snapshot_table = unsafe { &*snapshots };
+ let per_doc_data = set.borrow();
+ process_relative_selector_invalidations(&element, snapshot_table, &per_doc_data);
+
+ let mut data = element.mutate_data();
+ if data.is_none() {
+ // Snapshot for unstyled element is really only meant for relative selector
+ // invalidation, so this is fine.
+ return;
+ }
+
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let guard = global_style_data.shared_lock.read();
+ let per_doc_data = set.borrow();
+ let shared_style_context = create_shared_context(
+ &global_style_data,
+ &guard,
+ &per_doc_data.stylist,
+ TraversalFlags::empty(),
+ snapshot_table,
+ );
+ let mut data = data.as_mut().map(|d| &mut **d);
+
+ let mut selector_caches = SelectorCaches::default();
+ if let Some(ref mut data) = data {
+ // FIXME(emilio): Ideally we could share the nth-index-cache across all
+ // the elements?
+ let result = data.invalidate_style_if_needed(
+ element,
+ &shared_style_context,
+ None,
+ &mut selector_caches,
+ );
+
+ if result.has_invalidated_siblings() {
+ let parent = element
+ .traversal_parent()
+ .expect("How could we invalidate siblings without a common parent?");
+ unsafe {
+ parent.set_dirty_descendants();
+ bindings::Gecko_NoteDirtySubtreeForInvalidation(parent.0);
+ }
+ } else if result.has_invalidated_descendants() {
+ unsafe { bindings::Gecko_NoteDirtySubtreeForInvalidation(element.0) };
+ } else if result.has_invalidated_self() {
+ unsafe { bindings::Gecko_NoteDirtyElement(element.0) };
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_HasPendingRestyleAncestor(
+ element: &RawGeckoElement,
+ may_need_to_flush_layout: bool,
+) -> bool {
+ let mut has_yet_to_be_styled = false;
+ let mut element = Some(GeckoElement(element));
+ while let Some(e) = element {
+ if e.has_any_animation() {
+ return true;
+ }
+
+ // If the element needs a frame, it means that we haven't styled it yet
+ // after it got inserted in the document, and thus we may need to do
+ // that for transitions and animations to trigger.
+ //
+ // This is a fast path in the common case, but `has_yet_to_be_styled` is
+ // the real check for this.
+ if e.needs_frame() {
+ return true;
+ }
+
+ let data = e.borrow_data();
+ if let Some(ref data) = data {
+ if !data.hint.is_empty() {
+ return true;
+ }
+ if has_yet_to_be_styled && !data.styles.is_display_none() {
+ return true;
+ }
+ // Ideally, DOM mutations wouldn't affect layout trees of siblings.
+ //
+ // In practice, this can happen because Gecko deals pretty badly
+ // with some kinds of content insertion and removals.
+ //
+ // If we may need to flush layout, we need frames to accurately
+ // determine whether we'll actually flush, so if we have to
+ // reconstruct we need to flush style, which is what will take care
+ // of ensuring that frames are constructed, even if the style itself
+ // is up-to-date.
+ if may_need_to_flush_layout && data.damage.contains(GeckoRestyleDamage::reconstruct()) {
+ return true;
+ }
+ }
+ has_yet_to_be_styled = data.is_none();
+
+ element = e.traversal_parent();
+ }
+ false
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SelectorList_Parse(
+ selector_list: &nsACString,
+ is_chrome: bool,
+) -> *mut SelectorList {
+ use style::selector_parser::SelectorParser;
+
+ let url_data = UrlExtraData::from_ptr_ref(if is_chrome {
+ &DUMMY_CHROME_URL_DATA
+ } else {
+ &DUMMY_URL_DATA
+ });
+
+ let input = selector_list.as_str_unchecked();
+ let selector_list = match SelectorParser::parse_author_origin_no_namespace(&input, url_data) {
+ Ok(selector_list) => selector_list,
+ Err(..) => return ptr::null_mut(),
+ };
+
+ Box::into_raw(Box::new(selector_list))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SelectorList_Drop(list: *mut SelectorList) {
+ let _ = Box::from_raw(list);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_IsValidCSSColor(value: &nsACString) -> bool {
+ let mut input = ParserInput::new(value.as_str_unchecked());
+ let mut input = Parser::new(&mut input);
+ let context = ParserContext::new(
+ Origin::Author,
+ dummy_url_data(),
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ specified::Color::is_valid(&context, &mut input)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ComputeColor(
+ raw_data: Option<&PerDocumentStyleData>,
+ current_color: structs::nscolor,
+ value: &nsACString,
+ result_color: &mut structs::nscolor,
+ was_current_color: *mut bool,
+ loader: *mut Loader,
+) -> bool {
+ let mut input = ParserInput::new(value.as_str_unchecked());
+ let mut input = Parser::new(&mut input);
+ let reporter = loader.as_mut().and_then(|loader| {
+ // Make an ErrorReporter that will report errors as being "from DOM".
+ ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
+ });
+
+ let context = ParserContext::new(
+ Origin::Author,
+ dummy_url_data(),
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ reporter.as_ref().map(|e| e as &dyn ParseErrorReporter),
+ None,
+ );
+
+ let data;
+ let device = match raw_data {
+ Some(d) => {
+ data = d.borrow();
+ Some(data.stylist.device())
+ },
+ None => None,
+ };
+
+ let computed = match specified::Color::parse_and_compute(&context, &mut input, device) {
+ Some(c) => c,
+ None => return false,
+ };
+
+ let current_color = style::gecko::values::convert_nscolor_to_absolute_color(current_color);
+ if !was_current_color.is_null() {
+ *was_current_color = computed.is_currentcolor();
+ }
+
+ let rgba = computed.resolve_to_absolute(&current_color);
+ *result_color = style::gecko::values::convert_absolute_color_to_nscolor(&rgba);
+ true
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ResolveColor(
+ color: &computed::Color,
+ foreground: &style::color::AbsoluteColor,
+) -> style::color::AbsoluteColor {
+ color.resolve_to_absolute(foreground)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ResolveCalcLengthPercentage(
+ calc: &computed::length_percentage::CalcLengthPercentage,
+ basis: f32,
+) -> f32 {
+ calc.resolve(computed::Length::new(basis)).px()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ConvertColorSpace(
+ color: &AbsoluteColor,
+ color_space: ColorSpace,
+) -> AbsoluteColor {
+ color.to_color_space(color_space)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_IntersectionObserverRootMargin_Parse(
+ value: &nsACString,
+ result: *mut IntersectionObserverRootMargin,
+) -> bool {
+ let value = value.as_str_unchecked();
+ let result = result.as_mut().unwrap();
+
+ let mut input = ParserInput::new(&value);
+ let mut parser = Parser::new(&mut input);
+
+ let url_data = dummy_url_data();
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ let margin = parser.parse_entirely(|p| IntersectionObserverRootMargin::parse(&context, p));
+ match margin {
+ Ok(margin) => {
+ *result = margin;
+ true
+ },
+ Err(..) => false,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_IntersectionObserverRootMargin_ToString(
+ root_margin: &IntersectionObserverRootMargin,
+ result: &mut nsACString,
+) {
+ let mut writer = CssWriter::new(result);
+ root_margin.to_css(&mut writer).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ParseTransformIntoMatrix(
+ value: &nsACString,
+ contain_3d: &mut bool,
+ result: &mut structs::Matrix4x4Components,
+) -> bool {
+ use style::properties::longhands::transform;
+
+ let string = unsafe { value.as_str_unchecked() };
+ let mut input = ParserInput::new(&string);
+ let mut parser = Parser::new(&mut input);
+ let context = ParserContext::new(
+ Origin::Author,
+ unsafe { dummy_url_data() },
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
+ Ok(t) => t,
+ Err(..) => return false,
+ };
+
+ let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
+ Ok(result) => result,
+ Err(..) => return false,
+ };
+
+ *result = m.to_array();
+ *contain_3d = is_3d;
+ true
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ParseFilters(
+ value: &nsACString,
+ ignore_urls: bool,
+ data: *mut URLExtraData,
+ out: &mut style::OwnedSlice<Filter>,
+) -> bool {
+ use style::values::specified::effects::SpecifiedFilter;
+
+ let string = unsafe { value.as_str_unchecked() };
+ let mut input = ParserInput::new(&string);
+ let mut parser = Parser::new(&mut input);
+ let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ None,
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ let mut filters = vec![];
+
+ if parser
+ .try_parse(|i| i.expect_ident_matching("none"))
+ .is_ok()
+ {
+ return parser.expect_exhausted().is_ok();
+ }
+
+ if parser.is_exhausted() {
+ return false;
+ }
+
+ while !parser.is_exhausted() {
+ let specified_filter = match SpecifiedFilter::parse(&context, &mut parser) {
+ Ok(f) => f,
+ Err(..) => return false,
+ };
+
+ let filter = match specified_filter.to_computed_value_without_context() {
+ Ok(f) => f,
+ Err(..) => return false,
+ };
+
+ if ignore_urls && matches!(filter, Filter::Url(_)) {
+ continue;
+ }
+
+ filters.push(filter);
+ }
+
+ *out = style::OwnedSlice::from(filters);
+ true
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_ParseFontShorthandForMatching(
+ value: &nsACString,
+ data: *mut URLExtraData,
+ family: &mut FontFamilyList,
+ style: &mut FontStyle,
+ stretch: &mut FontStretch,
+ weight: &mut FontWeight,
+ size: Option<&mut f32>,
+ small_caps: Option<&mut bool>,
+) -> bool {
+ use style::properties::shorthands::font;
+ use style::values::generics::font::FontStyle as GenericFontStyle;
+ use style::values::specified::font as specified;
+
+ let string = value.as_str_unchecked();
+ let mut input = ParserInput::new(&string);
+ let mut parser = Parser::new(&mut input);
+ let url_data = UrlExtraData::from_ptr_ref(&data);
+ let context = ParserContext::new(
+ Origin::Author,
+ url_data,
+ Some(CssRuleType::FontFace),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ let font = match parser.parse_entirely(|f| font::parse_value(&context, f)) {
+ Ok(f) => f,
+ Err(..) => return false,
+ };
+
+ // The system font is not acceptable, so we return false.
+ match font.font_family {
+ specified::FontFamily::Values(list) => *family = list,
+ specified::FontFamily::System(_) => return false,
+ }
+
+ let specified_font_style = match font.font_style {
+ specified::FontStyle::Specified(ref s) => s,
+ specified::FontStyle::System(_) => return false,
+ };
+
+ *style = match *specified_font_style {
+ GenericFontStyle::Normal => FontStyle::NORMAL,
+ GenericFontStyle::Italic => FontStyle::ITALIC,
+ GenericFontStyle::Oblique(ref angle) => FontStyle::oblique(angle.degrees()),
+ };
+
+ *stretch = match font.font_stretch {
+ specified::FontStretch::Keyword(ref k) => k.compute(),
+ specified::FontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()),
+ specified::FontStretch::System(_) => return false,
+ };
+
+ *weight = match font.font_weight {
+ specified::FontWeight::Absolute(w) => w.compute(),
+ // Resolve relative font weights against the initial of font-weight
+ // (normal, which is equivalent to 400).
+ specified::FontWeight::Bolder => FontWeight::normal().bolder(),
+ specified::FontWeight::Lighter => FontWeight::normal().lighter(),
+ specified::FontWeight::System(_) => return false,
+ };
+
+ // XXX This is unfinished; see values::specified::FontSize::ToComputedValue
+ // for a more complete implementation (but we can't use it as-is).
+ if let Some(size) = size {
+ *size = match font.font_size {
+ specified::FontSize::Length(lp) => {
+ use style::values::generics::transform::ToAbsoluteLength;
+ match lp.to_pixel_length(None) {
+ Ok(len) => len,
+ Err(..) => return false,
+ }
+ },
+ specified::FontSize::Keyword(info) => {
+ let keyword = if info.kw != specified::FontSizeKeyword::Math {
+ info.kw
+ } else {
+ specified::FontSizeKeyword::Medium
+ };
+ // Map absolute-size keywords to sizes.
+ // TODO: Maybe get a meaningful quirks / base size from the caller?
+ let quirks_mode = QuirksMode::NoQuirks;
+ keyword
+ .to_length_without_context(
+ quirks_mode,
+ computed::Length::new(specified::FONT_MEDIUM_PX),
+ )
+ .0
+ .px()
+ },
+ // smaller, larger not currently supported
+ specified::FontSize::Smaller |
+ specified::FontSize::Larger |
+ specified::FontSize::System(_) => {
+ return false;
+ },
+ };
+ }
+
+ if let Some(small_caps) = small_caps {
+ use style::computed_values::font_variant_caps::T::SmallCaps;
+ *small_caps = font.font_variant_caps == SmallCaps;
+ }
+
+ true
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SourceSizeList_Parse(value: &nsACString) -> *mut SourceSizeList {
+ let value = value.as_str_unchecked();
+ let mut input = ParserInput::new(value);
+ let mut parser = Parser::new(&mut input);
+
+ let context = ParserContext::new(
+ Origin::Author,
+ dummy_url_data(),
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+
+ // NB: Intentionally not calling parse_entirely.
+ let list = SourceSizeList::parse(&context, &mut parser);
+ Box::into_raw(Box::new(list))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SourceSizeList_Evaluate(
+ raw_data: &PerDocumentStyleData,
+ list: Option<&SourceSizeList>,
+) -> i32 {
+ let doc_data = raw_data.borrow();
+ let device = doc_data.stylist.device();
+ let quirks_mode = doc_data.stylist.quirks_mode();
+
+ let result = match list {
+ Some(list) => list.evaluate(device, quirks_mode),
+ None => SourceSizeList::empty().evaluate(device, quirks_mode),
+ };
+
+ result.0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SourceSizeList_Drop(list: *mut SourceSizeList) {
+ let _ = Box::from_raw(list);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges(
+ root: &RawGeckoElement,
+ document_style: &PerDocumentStyleData,
+ non_document_styles: &nsTArray<&AuthorStyles>,
+ states_changed: u64,
+) {
+ use style::invalidation::element::document_state::DocumentStateInvalidationProcessor;
+ use style::invalidation::element::invalidator::TreeStyleInvalidator;
+
+ let document_data = document_style.borrow();
+
+ let iter = document_data
+ .stylist
+ .iter_origins()
+ .map(|(data, _origin)| data)
+ .chain(
+ non_document_styles
+ .iter()
+ .map(|author_styles| &*author_styles.data),
+ );
+
+ let mut selector_caches = SelectorCaches::default();
+ let root = GeckoElement(root);
+ let mut processor = DocumentStateInvalidationProcessor::new(
+ iter,
+ DocumentState::from_bits_retain(states_changed),
+ &mut selector_caches,
+ root.as_node().owner_doc().quirks_mode(),
+ );
+
+ let result =
+ TreeStyleInvalidator::new(root, /* stack_limit_checker = */ None, &mut processor)
+ .invalidate();
+
+ debug_assert!(!result.has_invalidated_siblings(), "How in the world?");
+ if result.has_invalidated_descendants() {
+ bindings::Gecko_NoteDirtySubtreeForInvalidation(root.0);
+ } else if result.has_invalidated_self() {
+ bindings::Gecko_NoteDirtyElement(root.0);
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_PseudoClass_GetStates(name: &nsACString) -> u64 {
+ let name = name.as_str_unchecked();
+ match NonTSPseudoClass::parse_non_functional(name) {
+ None => 0,
+ // Ignore :any-link since it contains both visited and unvisited state.
+ Some(NonTSPseudoClass::AnyLink) => 0,
+ Some(pseudo_class) => pseudo_class.state_flag().bits(),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_UseCounters_Create() -> *mut UseCounters {
+ Box::into_raw(Box::<UseCounters>::default())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_UseCounters_Drop(c: *mut UseCounters) {
+ let _ = Box::from_raw(c);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_UseCounters_Merge(
+ doc_counters: &UseCounters,
+ sheet_counters: &UseCounters,
+) {
+ doc_counters.merge(sheet_counters)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_IsPropertyIdRecordedInUseCounter(
+ use_counters: &UseCounters,
+ id: nsCSSPropertyID,
+) -> bool {
+ let id = NonCustomPropertyId::from_nscsspropertyid(id).unwrap();
+ use_counters.non_custom_properties.recorded(id)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_IsUnknownPropertyRecordedInUseCounter(
+ use_counters: &UseCounters,
+ p: CountedUnknownProperty,
+) -> bool {
+ use_counters.counted_unknown_properties.recorded(p)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_IsCssPropertyRecordedInUseCounter(
+ use_counters: &UseCounters,
+ property: &nsACString,
+ known_prop: *mut bool,
+) -> bool {
+ *known_prop = false;
+
+ let prop_name = property.as_str_unchecked();
+ if let Ok(p) = PropertyId::parse_unchecked_for_testing(prop_name) {
+ if let Some(id) = p.non_custom_id() {
+ *known_prop = true;
+ return use_counters.non_custom_properties.recorded(id);
+ }
+ }
+
+ if let Some(p) = CountedUnknownProperty::parse_for_testing(prop_name) {
+ *known_prop = true;
+ return use_counters.counted_unknown_properties.recorded(p);
+ }
+
+ false
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Create(
+ buffer: *mut u8,
+ len: usize,
+) -> *mut SharedMemoryBuilder {
+ Box::into_raw(Box::new(SharedMemoryBuilder::new(buffer, len)))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SharedMemoryBuilder_AddStylesheet(
+ builder: &mut SharedMemoryBuilder,
+ contents: &StylesheetContents,
+ error_message: &mut nsACString,
+) -> *const LockedCssRules {
+ // Assert some things we assume when we create a style sheet from shared
+ // memory.
+ debug_assert_eq!(contents.origin, Origin::UserAgent);
+ debug_assert_eq!(contents.quirks_mode, QuirksMode::NoQuirks);
+ debug_assert!(contents.source_map_url.read().is_none());
+ debug_assert!(contents.source_url.read().is_none());
+
+ match builder.write(&contents.rules) {
+ Ok(rules_ptr) => &**rules_ptr,
+ Err(message) => {
+ error_message.assign(&message);
+ ptr::null()
+ },
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SharedMemoryBuilder_GetLength(
+ builder: &SharedMemoryBuilder,
+) -> usize {
+ builder.len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Drop(builder: *mut SharedMemoryBuilder) {
+ let _ = Box::from_raw(builder);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_StyleArcSlice_EmptyPtr() -> *mut c_void {
+ style_traits::arc_slice::ArcSlice::<u64>::leaked_empty_ptr()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_LoadData_GetLazy(
+ source: &url::LoadDataSource,
+) -> *const url::LoadData {
+ source.get()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_LengthPercentage_ToCss(
+ lp: &computed::LengthPercentage,
+ result: &mut nsACString,
+) {
+ lp.to_css(&mut CssWriter::new(result)).unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontStyle_ToCss(s: &FontStyle, result: &mut nsACString) {
+ s.to_css(&mut CssWriter::new(result)).unwrap()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontWeight_ToCss(w: &FontWeight, result: &mut nsACString) {
+ w.to_css(&mut CssWriter::new(result)).unwrap()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontStretch_ToCss(s: &FontStretch, result: &mut nsACString) {
+ s.to_css(&mut CssWriter::new(result)).unwrap()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontStretch_SerializeKeyword(
+ s: &FontStretch,
+ result: &mut nsACString,
+) -> bool {
+ let kw = match s.as_keyword() {
+ Some(kw) => kw,
+ None => return false,
+ };
+ kw.to_css(&mut CssWriter::new(result)).unwrap();
+ true
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_CursorKind_Parse(
+ cursor: &nsACString,
+ result: &mut computed::ui::CursorKind,
+) -> bool {
+ match computed::ui::CursorKind::from_ident(cursor.as_str_unchecked()) {
+ Ok(c) => {
+ *result = c;
+ true
+ },
+ Err(..) => false,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFamily_Generic(generic: GenericFontFamily) -> &'static FontFamily {
+ FontFamily::generic(generic)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFamily_ForSystemFont(name: &nsACString, out: &mut FontFamily) {
+ *out = FontFamily::for_system_font(&name.to_utf8());
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FontFamilyList_WithNames(
+ names: &nsTArray<computed::font::SingleFontFamily>,
+ out: &mut FontFamilyList,
+) {
+ *out = FontFamilyList {
+ list: style_traits::arc_slice::ArcSlice::from_iter(names.iter().cloned()),
+ };
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_FamilyName_Serialize(name: &FamilyName, result: &mut nsACString) {
+ name.to_css(&mut CssWriter::new(result)).unwrap()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GenericFontFamily_Parse(input: &nsACString) -> GenericFontFamily {
+ let context = ParserContext::new(
+ Origin::Author,
+ unsafe { dummy_url_data() },
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ let value = input.to_utf8();
+ let mut input = ParserInput::new(&value);
+ let mut input = Parser::new(&mut input);
+ GenericFontFamily::parse(&context, &mut input).unwrap_or(GenericFontFamily::None)
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ColorScheme_Parse(input: &nsACString, out: &mut u8) -> bool {
+ use style::values::specified::ColorScheme;
+
+ let context = ParserContext::new(
+ Origin::Author,
+ unsafe { dummy_url_data() },
+ Some(CssRuleType::Style),
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ let input = unsafe { input.as_str_unchecked() };
+ let mut input = ParserInput::new(&input);
+ let mut input = Parser::new(&mut input);
+ let scheme = match input.parse_entirely(|i| ColorScheme::parse(&context, i)) {
+ Ok(scheme) => scheme,
+ Err(..) => return false,
+ };
+ *out = scheme.raw_bits();
+ true
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_LayerBlockRule_GetName(rule: &LayerBlockRule, result: &mut nsACString) {
+ if let Some(ref name) = rule.name {
+ name.to_css(&mut CssWriter::new(result)).unwrap()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_LayerStatementRule_GetNameCount(rule: &LayerStatementRule) -> usize {
+ rule.names.len()
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_LayerStatementRule_GetNameAt(
+ rule: &LayerStatementRule,
+ index: usize,
+ result: &mut nsACString,
+) {
+ if let Some(ref name) = rule.names.get(index) {
+ name.to_css(&mut CssWriter::new(result)).unwrap()
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_InvalidateForViewportUnits(
+ document_style: &PerDocumentStyleData,
+ root: &RawGeckoElement,
+ dynamic_only: bool,
+) {
+ let mut document_data = document_style.borrow_mut();
+ let ref mut stylist = document_data.stylist;
+ let device = stylist.device();
+
+ if !device.used_viewport_size() {
+ return;
+ }
+
+ if dynamic_only && !device.used_dynamic_viewport_size() {
+ return;
+ }
+
+ // If the viewport changed, then initial values containing viewport units need to be recomputed.
+ if stylist
+ .get_custom_property_initial_values_flags()
+ .intersects(ComputedValueFlags::USES_VIEWPORT_UNITS)
+ {
+ stylist.rebuild_initial_values_for_custom_properties();
+ }
+
+ if style::invalidation::viewport_units::invalidate(GeckoElement(root)) {
+ // The invalidation machinery propagates the bits up, but we still need
+ // to tell the Gecko restyle root machinery about it.
+ bindings::Gecko_NoteDirtySubtreeForInvalidation(root);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_InterpolateColor(
+ interpolation: ColorInterpolationMethod,
+ left: &AbsoluteColor,
+ right: &AbsoluteColor,
+ progress: f32,
+) -> AbsoluteColor {
+ style::color::mix::mix(
+ interpolation,
+ left,
+ progress,
+ right,
+ 1.0 - progress,
+ ColorMixFlags::empty(),
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_EasingFunctionAt(
+ easing_function: &ComputedTimingFunction,
+ progress: f64,
+ before_flag: BeforeFlag,
+) -> f64 {
+ easing_function.calculate_output(progress, before_flag, 1e-7)
+}
+
+fn parse_no_context<'i, F, R>(string: &'i str, parse: F) -> Result<R, ()>
+where
+ F: FnOnce(&ParserContext, &mut Parser<'i, '_>) -> Result<R, ParseError<'i>>,
+{
+ let context = ParserContext::new(
+ Origin::Author,
+ unsafe { dummy_url_data() },
+ None,
+ ParsingMode::DEFAULT,
+ QuirksMode::NoQuirks,
+ /* namespaces = */ Default::default(),
+ None,
+ None,
+ );
+ let mut input = ParserInput::new(string);
+ Parser::new(&mut input)
+ .parse_entirely(|i| parse(&context, i))
+ .map_err(|_| ())
+}
+
+#[no_mangle]
+// Parse a length without style context (for canvas2d letterSpacing/wordSpacing attributes).
+// This accepts absolute lengths, and if a font-metrics-getter function is passed, also
+// font-relative ones, but not other units (such as percentages, viewport-relative, etc)
+// that would require a full style context to resolve.
+pub extern "C" fn Servo_ParseLengthWithoutStyleContext(
+ len: &nsACString,
+ out: &mut f32,
+ get_font_metrics: Option<unsafe extern "C" fn(*mut c_void) -> GeckoFontMetrics>,
+ getter_context: *mut c_void,
+) -> bool {
+ let metrics_getter = if let Some(getter) = get_font_metrics {
+ Some(move || -> GeckoFontMetrics { unsafe { getter(getter_context) } })
+ } else {
+ None
+ };
+ let value = parse_no_context(unsafe { len.as_str_unchecked() }, specified::Length::parse)
+ .and_then(|p| p.to_computed_pixel_length_with_font_metrics(metrics_getter));
+ match value {
+ Ok(v) => {
+ *out = v;
+ true
+ },
+ Err(..) => false,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_SlowRgbToColorName(r: u8, g: u8, b: u8, result: &mut nsACString) -> bool {
+ let mut candidates = SmallVec::<[&'static str; 5]>::new();
+ for (name, color) in cssparser::color::all_named_colors() {
+ if color == (r, g, b) {
+ candidates.push(name);
+ }
+ }
+ if candidates.is_empty() {
+ return false;
+ }
+ // DevTools expect the first alphabetically.
+ candidates.sort();
+ result.assign(candidates[0]);
+ true
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_ColorNameToRgb(name: &nsACString, out: &mut structs::nscolor) -> bool {
+ match cssparser::color::parse_named_color(unsafe { name.as_str_unchecked() }) {
+ Ok((r, g, b)) => {
+ *out = style::gecko::values::convert_absolute_color_to_nscolor(&AbsoluteColor::new(
+ ColorSpace::Srgb,
+ r,
+ g,
+ b,
+ 1.0,
+ ));
+ true
+ },
+ _ => false,
+ }
+}
+
+#[repr(u8)]
+pub enum RegisterCustomPropertyResult {
+ SuccessfullyRegistered,
+ InvalidName,
+ AlreadyRegistered,
+ InvalidSyntax,
+ NoInitialValue,
+ InvalidInitialValue,
+ InitialValueNotComputationallyIndependent,
+}
+
+/// https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function
+#[no_mangle]
+pub extern "C" fn Servo_RegisterCustomProperty(
+ per_doc_data: &PerDocumentStyleData,
+ extra_data: *mut URLExtraData,
+ name: &nsACString,
+ syntax: &nsACString,
+ inherits: bool,
+ initial_value: Option<&nsACString>,
+) -> RegisterCustomPropertyResult {
+ use self::RegisterCustomPropertyResult::*;
+ use style::custom_properties::SpecifiedValue;
+ use style::properties_and_values::rule::{PropertyRegistrationError, PropertyRuleName};
+ use style::properties_and_values::syntax::Descriptor;
+
+ let mut per_doc_data = per_doc_data.borrow_mut();
+ let url_data = unsafe { UrlExtraData::from_ptr_ref(&extra_data) };
+ let name = unsafe { name.as_str_unchecked() };
+ let syntax = unsafe { syntax.as_str_unchecked() };
+ let initial_value = initial_value.map(|v| unsafe { v.as_str_unchecked() });
+
+ // If name is not a custom property name string, throw a SyntaxError and exit this algorithm.
+ let name = match style::custom_properties::parse_name(name) {
+ Ok(n) => Atom::from(n),
+ Err(()) => return InvalidName,
+ };
+
+ // If property set already contains an entry with name as its property name (compared
+ // codepoint-wise), throw an InvalidModificationError and exit this algorithm.
+ if per_doc_data
+ .stylist
+ .custom_property_script_registry()
+ .get(&name)
+ .is_some()
+ {
+ return AlreadyRegistered;
+ }
+ // Attempt to consume a syntax definition from syntax. If it returns failure, throw a
+ // SyntaxError. Otherwise, let syntax definition be the returned syntax definition.
+ let Ok(syntax) = Descriptor::from_str(syntax, /* preserve_specified = */ false) else {
+ return InvalidSyntax;
+ };
+
+ let initial_value = match initial_value {
+ Some(v) => {
+ let mut input = ParserInput::new(v);
+ let parsed = Parser::new(&mut input)
+ .parse_entirely(|input| {
+ input.skip_whitespace();
+ SpecifiedValue::parse(input, url_data).map(Arc::new)
+ })
+ .ok();
+ if parsed.is_none() {
+ return InvalidInitialValue;
+ }
+ parsed
+ },
+ None => None,
+ };
+
+ if let Err(error) =
+ PropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())
+ {
+ return match error {
+ PropertyRegistrationError::InitialValueNotComputationallyIndependent => {
+ InitialValueNotComputationallyIndependent
+ },
+ PropertyRegistrationError::InvalidInitialValue => InvalidInitialValue,
+ PropertyRegistrationError::NoInitialValue => NoInitialValue,
+ };
+ }
+
+ per_doc_data
+ .stylist
+ .custom_property_script_registry_mut()
+ .register(PropertyRegistration {
+ name: PropertyRuleName(name),
+ data: PropertyRegistrationData {
+ syntax,
+ inherits: if inherits {
+ PropertyInherits::True
+ } else {
+ PropertyInherits::False
+ },
+ initial_value,
+ },
+ url_data: url_data.clone(),
+ source_location: SourceLocation { line: 0, column: 0 },
+ });
+
+ per_doc_data
+ .stylist
+ .rebuild_initial_values_for_custom_properties();
+
+ SuccessfullyRegistered
+}
+
+#[repr(C)]
+pub struct PropDef {
+ // The name of the property.
+ pub name: Atom,
+ // The syntax of the property.
+ pub syntax: nsCString,
+ // Whether the property inherits.
+ pub inherits: bool,
+ pub has_initial_value: bool,
+ pub initial_value: nsCString,
+ // True if the property was set with CSS.registerProperty
+ pub from_js: bool,
+}
+
+impl PropDef {
+ /// Creates a PropDef from a name and a PropertyRegistration.
+ pub fn new(name: Atom, property_registration: &PropertyRegistration, from_js: bool) -> Self {
+ let mut syntax = nsCString::new();
+ if let Some(spec) = property_registration.data.syntax.specified_string() {
+ syntax.assign(spec);
+ } else {
+ // FIXME: Descriptor::to_css should behave consistently (probably this shouldn't use
+ // the ToCss trait).
+ property_registration
+ .data
+ .syntax
+ .to_css(&mut CssWriter::new(&mut syntax))
+ .unwrap();
+ };
+ let initial_value = property_registration.data.initial_value.to_css_nscstring();
+ PropDef {
+ name,
+ syntax,
+ inherits: property_registration.data.inherits(),
+ has_initial_value: property_registration.data.initial_value.is_some(),
+ initial_value,
+ from_js,
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GetRegisteredCustomProperties(
+ per_doc_data: &PerDocumentStyleData,
+ custom_properties: &mut ThinVec<PropDef>,
+) {
+ let stylist = &per_doc_data.borrow().stylist;
+
+ custom_properties.extend(
+ stylist
+ .custom_property_script_registry()
+ .get_all()
+ .iter()
+ .map(|(name, property_registration)| {
+ PropDef::new(name.clone(), property_registration, /* from_js */ true)
+ }),
+ );
+
+ for (cascade_data, _origin) in stylist.iter_origins() {
+ custom_properties.extend(cascade_data.custom_property_registrations().iter().map(
+ |(name, value)| {
+ let property_registration = &value.last().unwrap().0;
+ PropDef::new(
+ name.clone(),
+ property_registration,
+ /* from_js */
+ false,
+ )
+ },
+ ))
+ }
+}
+
+#[repr(C)]
+pub struct SelectorWarningData {
+ /// Index to the selector generating the warning.
+ pub index: usize,
+ /// Kind of the warning.
+ pub kind: SelectorWarningKind,
+}
+
+#[no_mangle]
+pub extern "C" fn Servo_GetSelectorWarnings(
+ rule: &LockedStyleRule,
+ warnings: &mut ThinVec<SelectorWarningData>,
+) {
+ read_locked_arc(rule, |r| {
+ for (i, selector) in r.selectors.slice().iter().enumerate() {
+ for k in SelectorWarningKind::from_selector(selector) {
+ warnings.push(SelectorWarningData { index: i, kind: k });
+ }
+ }
+ });
+}
diff --git a/servo/ports/geckolib/lib.rs b/servo/ports/geckolib/lib.rs
new file mode 100644
index 0000000000..c11f6ab36e
--- /dev/null
+++ b/servo/ports/geckolib/lib.rs
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+extern crate bincode;
+extern crate cssparser;
+#[macro_use]
+extern crate cstr;
+extern crate dom;
+#[macro_use]
+extern crate gecko_profiler;
+extern crate libc;
+#[macro_use]
+extern crate log;
+extern crate malloc_size_of;
+extern crate nsstring;
+extern crate num_traits;
+extern crate selectors;
+extern crate servo_arc;
+extern crate smallvec;
+#[macro_use]
+extern crate style;
+extern crate style_traits;
+extern crate thin_vec;
+extern crate to_shmem;
+
+mod error_reporter;
+#[allow(non_snake_case)]
+pub mod glue;
+mod stylesheet_loader;
+
+// FIXME(bholley): This should probably go away once we harmonize the allocators.
+#[no_mangle]
+pub extern "C" fn je_malloc_usable_size(_: *const ::libc::c_void) -> ::libc::size_t {
+ 0
+}
diff --git a/servo/ports/geckolib/stylesheet_loader.rs b/servo/ports/geckolib/stylesheet_loader.rs
new file mode 100644
index 0000000000..08706fa6a8
--- /dev/null
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -0,0 +1,200 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use cssparser::SourceLocation;
+use nsstring::nsCString;
+use servo_arc::Arc;
+use style::context::QuirksMode;
+use style::gecko::data::GeckoStyleSheet;
+use style::gecko_bindings::bindings;
+use style::gecko_bindings::bindings::Gecko_LoadStyleSheet;
+use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
+use style::gecko_bindings::structs::{
+ SheetLoadData, SheetLoadDataHolder, StyleSheet as DomStyleSheet,
+};
+use style::gecko_bindings::sugar::refptr::RefPtr;
+use style::global_style_data::GLOBAL_STYLE_DATA;
+use style::media_queries::MediaList;
+use style::parser::ParserContext;
+use style::shared_lock::{Locked, SharedRwLock};
+use style::stylesheets::import_rule::{ImportLayer, ImportSheet, ImportSupportsCondition};
+use style::stylesheets::AllowImportRules;
+use style::stylesheets::{ImportRule, Origin, StylesheetLoader as StyleStylesheetLoader};
+use style::stylesheets::{StylesheetContents, UrlExtraData};
+use style::use_counters::UseCounters;
+use style::values::CssUrl;
+
+pub struct StylesheetLoader(
+ *mut Loader,
+ *mut DomStyleSheet,
+ *mut SheetLoadData,
+ *mut LoaderReusableStyleSheets,
+);
+
+impl StylesheetLoader {
+ pub fn new(
+ loader: *mut Loader,
+ parent: *mut DomStyleSheet,
+ parent_load_data: *mut SheetLoadData,
+ reusable_sheets: *mut LoaderReusableStyleSheets,
+ ) -> Self {
+ StylesheetLoader(loader, parent, parent_load_data, reusable_sheets)
+ }
+}
+
+impl StyleStylesheetLoader for StylesheetLoader {
+ fn request_stylesheet(
+ &self,
+ url: CssUrl,
+ source_location: SourceLocation,
+ _context: &ParserContext,
+ lock: &SharedRwLock,
+ media: Arc<Locked<MediaList>>,
+ supports: Option<ImportSupportsCondition>,
+ layer: ImportLayer,
+ ) -> Arc<Locked<ImportRule>> {
+ // Ensure the supports conditions for this @import are true, if not, refuse to load
+ if !supports.as_ref().map_or(true, |s| s.enabled) {
+ return Arc::new(lock.wrap(ImportRule {
+ url,
+ stylesheet: ImportSheet::new_refused(),
+ supports,
+ layer,
+ source_location,
+ }));
+ }
+
+ // After we get this raw pointer ImportRule will be moved into a lock and Arc
+ // and so the Arc<Url> pointer inside will also move,
+ // but the Url it points to or the allocating backing the String inside that Url won’t,
+ // so this raw pointer will still be valid.
+
+ let child_sheet =
+ unsafe { Gecko_LoadStyleSheet(self.0, self.1, self.2, self.3, &url, media.into()) };
+
+ debug_assert!(
+ !child_sheet.is_null(),
+ "Import rules should always have a strong sheet"
+ );
+ let sheet = unsafe { GeckoStyleSheet::from_addrefed(child_sheet) };
+ let stylesheet = ImportSheet::new(sheet);
+ Arc::new(lock.wrap(ImportRule {
+ url,
+ stylesheet,
+ supports,
+ layer,
+ source_location,
+ }))
+ }
+}
+
+pub struct AsyncStylesheetParser {
+ load_data: RefPtr<SheetLoadDataHolder>,
+ extra_data: UrlExtraData,
+ bytes: nsCString,
+ origin: Origin,
+ quirks_mode: QuirksMode,
+ should_record_use_counters: bool,
+ allow_import_rules: AllowImportRules,
+}
+
+impl AsyncStylesheetParser {
+ pub fn new(
+ load_data: RefPtr<SheetLoadDataHolder>,
+ extra_data: UrlExtraData,
+ bytes: nsCString,
+ origin: Origin,
+ quirks_mode: QuirksMode,
+ should_record_use_counters: bool,
+ allow_import_rules: AllowImportRules,
+ ) -> Self {
+ AsyncStylesheetParser {
+ load_data,
+ extra_data,
+ bytes,
+ origin,
+ quirks_mode,
+ should_record_use_counters,
+ allow_import_rules,
+ }
+ }
+
+ pub fn parse(self) {
+ let global_style_data = &*GLOBAL_STYLE_DATA;
+ let input: &str = unsafe { (*self.bytes).as_str_unchecked() };
+
+ let use_counters = if self.should_record_use_counters {
+ Some(Box::new(UseCounters::default()))
+ } else {
+ None
+ };
+
+ // Note: Parallel CSS parsing doesn't report CSS errors. When errors are
+ // being logged, Gecko prevents the parallel parsing path from running.
+ let sheet = StylesheetContents::from_str(
+ input,
+ self.extra_data.clone(),
+ self.origin,
+ &global_style_data.shared_lock,
+ Some(&self),
+ None,
+ self.quirks_mode.into(),
+ use_counters.as_deref(),
+ self.allow_import_rules,
+ /* sanitized_output = */ None,
+ );
+
+ unsafe {
+ bindings::Gecko_StyleSheet_FinishAsyncParse(
+ self.load_data.get(),
+ sheet.into(),
+ use_counters.map_or(std::ptr::null_mut(), Box::into_raw),
+ );
+ }
+ }
+}
+
+impl StyleStylesheetLoader for AsyncStylesheetParser {
+ fn request_stylesheet(
+ &self,
+ url: CssUrl,
+ source_location: SourceLocation,
+ _context: &ParserContext,
+ lock: &SharedRwLock,
+ media: Arc<Locked<MediaList>>,
+ supports: Option<ImportSupportsCondition>,
+ layer: ImportLayer,
+ ) -> Arc<Locked<ImportRule>> {
+ // Ensure the supports conditions for this @import are true, if not, refuse to load
+ if !supports.as_ref().map_or(true, |s| s.enabled) {
+ return Arc::new(lock.wrap(ImportRule {
+ url: url.clone(),
+ stylesheet: ImportSheet::new_refused(),
+ supports,
+ layer,
+ source_location,
+ }));
+ }
+
+ let stylesheet = ImportSheet::new_pending();
+ let rule = Arc::new(lock.wrap(ImportRule {
+ url: url.clone(),
+ stylesheet,
+ supports,
+ layer,
+ source_location,
+ }));
+
+ unsafe {
+ bindings::Gecko_LoadStyleSheetAsync(
+ self.load_data.get(),
+ &url,
+ media.into(),
+ rule.clone().into(),
+ );
+ }
+
+ rule
+ }
+}
diff --git a/servo/ports/geckolib/tests/Cargo.toml b/servo/ports/geckolib/tests/Cargo.toml
new file mode 100644
index 0000000000..124ba91d96
--- /dev/null
+++ b/servo/ports/geckolib/tests/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "stylo_tests"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+license = "MPL-2.0"
+
+[lib]
+name = "stylo_tests"
+path = "lib.rs"
+doctest = false
+
+[dependencies]
+style = {path = "../../../components/style", features = ["gecko"]}
+euclid = "0.22"
diff --git a/servo/ports/geckolib/tests/lib.rs b/servo/ports/geckolib/tests/lib.rs
new file mode 100644
index 0000000000..40e361a2d5
--- /dev/null
+++ b/servo/ports/geckolib/tests/lib.rs
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+// Disable this entire crate on Windows when Gecko symbols are not available
+// as linking would fail:
+// https://github.com/rust-lang/rust/pull/44603#issuecomment-338807312
+//
+// On Linux and OS X linking succeeds anyway.
+// Presumably these symbol declarations don’t need to be resolved
+// as they’re not used in any code called from this crate.
+#![cfg(any(linking_with_gecko, not(windows)))]
+
+extern crate euclid;
+extern crate style;
+
+mod piecewise_linear;
diff --git a/servo/ports/geckolib/tests/piecewise_linear.rs b/servo/ports/geckolib/tests/piecewise_linear.rs
new file mode 100644
index 0000000000..728c76638a
--- /dev/null
+++ b/servo/ports/geckolib/tests/piecewise_linear.rs
@@ -0,0 +1,345 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use euclid::approxeq::ApproxEq;
+use style::piecewise_linear::{PiecewiseLinearFunction, PiecewiseLinearFunctionBuilder};
+
+fn get_linear_keyword_equivalent() -> PiecewiseLinearFunction {
+ PiecewiseLinearFunctionBuilder::default().build()
+}
+#[test]
+fn linear_keyword_equivalent_in_bound() {
+ let function = get_linear_keyword_equivalent();
+ assert!(function.at(0.).approx_eq(&0.));
+ assert!(function.at(0.5).approx_eq(&0.5));
+ assert!(function.at(1.0).approx_eq(&1.0));
+}
+
+#[test]
+fn linear_keyword_equivalent_out_of_bounds() {
+ let function = get_linear_keyword_equivalent();
+ assert!(function.at(-0.1).approx_eq(&-0.1));
+ assert!(function.at(1.1).approx_eq(&1.1));
+}
+
+fn get_const_function() -> PiecewiseLinearFunction {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(CONST_VALUE as f32, None);
+ builder.build()
+}
+
+const CONST_VALUE: f32 = 0.2;
+
+#[test]
+fn const_function() {
+ let function = get_const_function();
+ assert!(function.at(0.).approx_eq(&CONST_VALUE));
+ assert!(function.at(0.5).approx_eq(&CONST_VALUE));
+ assert!(function.at(1.0).approx_eq(&CONST_VALUE));
+}
+
+#[test]
+fn const_function_out_of_bounds() {
+ let function = get_const_function();
+ assert!(function.at(-0.1).approx_eq(&CONST_VALUE));
+ assert!(function.at(1.1).approx_eq(&CONST_VALUE));
+}
+
+#[test]
+fn implied_input_spacing() {
+ let explicit_spacing = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ let implied_spacing = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, None);
+ builder.push(1.0, None);
+ builder.build()
+ };
+
+ assert!(implied_spacing.at(0.).approx_eq(&explicit_spacing.at(0.)));
+ assert!(implied_spacing.at(0.5).approx_eq(&explicit_spacing.at(0.5)));
+ assert!(implied_spacing.at(1.0).approx_eq(&explicit_spacing.at(1.0)));
+}
+
+#[test]
+fn interpolation() {
+ let interpolate = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, None);
+ builder.push(0.7, None);
+ builder.push(1.0, None);
+ builder.build()
+ };
+ assert!(interpolate.at(0.1).approx_eq(&0.14));
+ assert!(interpolate.at(0.25).approx_eq(&0.35));
+ assert!(interpolate.at(0.45).approx_eq(&0.63));
+ assert!(interpolate.at(0.7).approx_eq(&0.82));
+ assert!(interpolate.at(0.75).approx_eq(&0.85));
+ assert!(interpolate.at(0.95).approx_eq(&0.97));
+}
+
+#[test]
+fn implied_multiple_input_spacing() {
+ let multiple_implied = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, None);
+ builder.push(0.8, None);
+ builder.push(0.6, None);
+ builder.push(0.4, None);
+ builder.push(0.5, Some(0.4));
+ builder.push(0.1, None);
+ builder.push(0.9, None);
+ builder.push(1.0, None);
+ builder.build()
+ };
+ assert!(multiple_implied.at(0.1).approx_eq(&0.8));
+ assert!(multiple_implied.at(0.2).approx_eq(&0.6));
+ assert!(multiple_implied.at(0.3).approx_eq(&0.4));
+ assert!(multiple_implied.at(0.4).approx_eq(&0.5));
+ assert!(multiple_implied.at(0.6).approx_eq(&0.1));
+ assert!(multiple_implied.at(0.8).approx_eq(&0.9));
+ assert!(multiple_implied.at(1.).approx_eq(&1.));
+}
+
+#[test]
+fn nonzero_edge_values() {
+ let nonzero_edges = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.1, Some(0.0));
+ builder.push(0.7, Some(1.0));
+ builder.build()
+ };
+ assert!(nonzero_edges.at(0.).approx_eq(&0.1));
+ assert!(nonzero_edges.at(0.5).approx_eq(&0.4));
+ assert!(nonzero_edges.at(1.0).approx_eq(&0.7));
+}
+
+#[test]
+fn out_of_bounds_extrapolate() {
+ // General case: extrapolate from the edges' slope
+ let oob_extend = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, None);
+ builder.push(0.7, None);
+ builder.push(1.0, None);
+ builder.build()
+ };
+ assert!(oob_extend.at(-0.25).approx_eq(&-0.35));
+ assert!(oob_extend.at(1.25).approx_eq(&1.15));
+}
+
+#[test]
+fn out_of_bounds_flat() {
+ // Repeated endpoints: flat extrapolation out-of-bounds
+ let oob_flat = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.0, Some(0.0));
+ builder.push(0.7, None);
+ builder.push(1.0, Some(1.0));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(oob_flat.at(0.0).approx_eq(&oob_flat.at(-0.25)));
+ assert!(oob_flat.at(1.0).approx_eq(&oob_flat.at(1.25)));
+}
+
+#[test]
+fn flat_region() {
+ let flat = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.5, Some(0.25));
+ builder.push(0.5, Some(0.7));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(flat.at(0.125).approx_eq(&0.25));
+ assert!(flat.at(0.5).approx_eq(&0.5));
+ assert!(flat.at(0.85).approx_eq(&0.75));
+}
+
+#[test]
+fn step() {
+ let step = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.0, Some(0.5));
+ builder.push(1.0, Some(0.5));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(step.at(0.25).approx_eq(&0.0));
+ // At the discontinuity, take the right hand side value
+ assert!(step.at(0.5).approx_eq(&1.0));
+ assert!(step.at(0.75).approx_eq(&1.0));
+}
+
+#[test]
+fn step_multiple_conflicting() {
+ let step = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.0, Some(0.5));
+ builder.push(0.75, Some(0.5));
+ builder.push(0.75, Some(0.5));
+ builder.push(1.0, Some(0.5));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(step.at(0.25).approx_eq(&0.0));
+ assert!(step.at(0.5).approx_eq(&1.0));
+ assert!(step.at(0.75).approx_eq(&1.0));
+}
+
+#[test]
+fn always_monotonic() {
+ let monotonic = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.3, Some(0.5));
+ builder.push(0.4, Some(0.4));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(monotonic.at(0.25).approx_eq(&0.15));
+ // A discontinuity at x = 0.5 from y = 0.3 to 0.4
+ assert!(monotonic.at(0.5).approx_eq(&0.4));
+ assert!(monotonic.at(0.6).approx_eq(&0.52));
+}
+
+#[test]
+fn always_monotonic_flat() {
+ let monotonic = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.2, Some(0.2));
+ builder.push(0.4, Some(0.1));
+ builder.push(0.4, Some(0.15));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(monotonic.at(0.2).approx_eq(&0.4));
+ // A discontinuity at x = 0.2 from y = 0.2 to 0.4
+ assert!(monotonic.at(0.3).approx_eq(&0.475));
+}
+
+#[test]
+fn always_monotonic_flat_backwards() {
+ let monotonic = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.2, Some(0.2));
+ builder.push(0.3, Some(0.3));
+ builder.push(0.3, Some(0.2));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(monotonic.at(0.2).approx_eq(&0.2));
+ assert!(monotonic.at(0.3).approx_eq(&0.3));
+ assert!(monotonic.at(0.4).approx_eq(&0.4));
+}
+
+#[test]
+fn input_out_of_bounds() {
+ let oob = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(-0.5));
+ builder.push(1.0, Some(1.5));
+ builder.build()
+ };
+ assert!(oob.at(-0.5).approx_eq(&0.0));
+ assert!(oob.at(0.0).approx_eq(&0.25));
+ assert!(oob.at(0.5).approx_eq(&0.5));
+ assert!(oob.at(1.0).approx_eq(&0.75));
+ assert!(oob.at(1.5).approx_eq(&1.0));
+}
+
+#[test]
+fn invalid_builder_input() {
+ let built_from_invalid = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(f32::NEG_INFINITY));
+ builder.push(0.7, Some(f32::NAN));
+ builder.push(1.0, Some(f32::INFINITY));
+ builder.build()
+ };
+ let equivalent = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, None);
+ builder.push(0.7, None);
+ builder.push(1.0, None);
+ builder.build()
+ };
+
+ assert!(built_from_invalid.at(0.0).approx_eq(&equivalent.at(0.0)));
+ assert!(built_from_invalid.at(0.25).approx_eq(&equivalent.at(0.25)));
+ assert!(built_from_invalid.at(0.5).approx_eq(&equivalent.at(0.5)));
+ assert!(built_from_invalid.at(0.75).approx_eq(&equivalent.at(0.75)));
+ assert!(built_from_invalid.at(1.0).approx_eq(&equivalent.at(1.0)));
+}
+
+#[test]
+fn input_domain_not_complete() {
+ let not_covered = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.2, Some(0.2));
+ builder.push(0.8, Some(0.8));
+ builder.build()
+ };
+ assert!(not_covered.at(0.0).approx_eq(&0.0));
+ assert!(not_covered.at(0.5).approx_eq(&0.5));
+ assert!(not_covered.at(1.0).approx_eq(&1.0));
+}
+
+#[test]
+fn input_second_negative() {
+ let function = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, None);
+ builder.push(0.0, Some(-0.1));
+ builder.push(0.3, Some(-0.05));
+ builder.push(0.5, None);
+ builder.push(0.2, Some(0.6));
+ builder.push(1.0, None);
+ builder.build()
+ };
+ let equivalent = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(0.0, Some(0.0));
+ builder.push(0.3, Some(0.0));
+ builder.push(0.5, Some(0.3));
+ builder.push(0.2, Some(0.6));
+ builder.push(1.0, Some(1.0));
+ builder.build()
+ };
+ assert!(function.at(-0.1).approx_eq(&equivalent.at(-0.1)));
+ assert!(function.at(0.0).approx_eq(&equivalent.at(0.0)));
+ assert!(function.at(0.3).approx_eq(&equivalent.at(0.3)));
+ assert!(function.at(0.6).approx_eq(&equivalent.at(0.6)));
+ assert!(function.at(1.0).approx_eq(&equivalent.at(1.0)));
+}
+
+#[test]
+fn input_second_last_above_1() {
+ let function = {
+ let mut builder = PiecewiseLinearFunctionBuilder::default();
+ builder.push(0.0, Some(0.0));
+ builder.push(1.0, Some(2.0));
+ builder.push(1.0, None);
+ builder.build()
+ };
+ assert!(function.at(-0.5).approx_eq(&-0.25));
+ assert!(function.at(0.0).approx_eq(&0.0));
+ assert!(function.at(0.5).approx_eq(&0.25));
+ assert!(function.at(1.0).approx_eq(&0.5));
+ assert!(function.at(1.5).approx_eq(&0.75));
+ assert!(function.at(2.0).approx_eq(&1.0));
+ assert!(function.at(3.0).approx_eq(&1.0));
+}