/* 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::{NthIndexCache, SelectorList};
use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
use smallvec::SmallVec;
use std::collections::BTreeSet;
use std::fmt::Write;
use std::iter;
use std::os::raw::c_void;
use std::ptr;
use style::applicable_declarations::ApplicableDeclarationBlock;
use style::author_styles::AuthorStyles;
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;
use style::font_face::{self, FontFaceSourceFormat, FontFaceSourceListComponent, Source};
use style::gecko::data::{GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl};
use style::gecko::restyle_damage::GeckoRestyleDamage;
use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
use style::gecko::traversal::RecalcStyleOnly;
use style::gecko::url;
use style::gecko::wrapper::{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::gfxFontFeatureValueSet;
use style::gecko_bindings::structs::gfx::FontPaletteValueSet;
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::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::RawServoSelectorList;
use style::gecko_bindings::structs::RawServoSourceSizeList;
use style::gecko_bindings::structs::RawServoStyleRule;
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::structs::{
RawServoAnimationValue, RawServoAuthorStyles, RawServoContainerRule, RawServoCounterStyleRule,
RawServoDeclarationBlock, RawServoFontFaceRule, RawServoFontFeatureValuesRule,
RawServoFontPaletteValuesRule,
RawServoImportRule, RawServoKeyframe, RawServoKeyframesRule, RawServoLayerBlockRule,
RawServoLayerStatementRule, RawServoMediaList, RawServoMediaRule, RawServoMozDocumentRule,
RawServoNamespaceRule, RawServoPageRule, RawServoSharedMemoryBuilder, RawServoStyleSet,
RawServoStyleSheetContents, RawServoSupportsRule, ServoCssRules,
};
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasFFI};
use style::gecko_bindings::sugar::ownership::{
HasBoxFFI, HasSimpleFFI, Owned, OwnedOrNull, Strong,
};
use style::gecko_bindings::sugar::refptr::RefPtr;
use style::global_style_data::{
GlobalStyleData, StyleThreadPool, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL,
};
use style::invalidation::element::restyle_hints::RestyleHint;
use style::invalidation::stylesheets::RuleChangeKind;
use style::media_queries::MediaList;
use style::parser::{self, Parse, ParserContext};
use style::properties::animated_properties::{AnimationValue, AnimationValueMap};
use style::properties::{parse_one_declaration_into, parse_style_attribute};
use style::properties::{ComputedValues, CountedUnknownProperty, Importance, NonCustomPropertyId};
use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
use style::properties::{PropertyDeclarationId, ShorthandId};
use style::properties::{SourcePropertyDeclaration, StyleBuilder};
use style::rule_cache::RuleCacheConditions;
use style::rule_tree::{CascadeLevel, 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::ImportSheet;
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
use style::stylesheets::layer_rule::LayerOrder;
use style::stylesheets::supports_rule::parse_condition_or_declaration;
use style::stylesheets::{
AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleType, CssRules,
CssRulesHelpers, DocumentRule, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule,
ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule, MediaRule, NamespaceRule,
Origin, OriginSet, PageRule, 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::color::AnimatedRGBA;
use style::values::animated::{Animate, Procedure, ToAnimatedZero};
use style::values::computed::easing::ComputedTimingFunction;
use style::values::computed::font::{
FontFamily, FontFamilyList, FontStretch, FontStyle, FontWeight, GenericFontFamily,
};
use style::values::computed::{self, Context, ToComputedValue};
use style::values::distance::ComputeSquaredDistance;
use style::values::generics::color::ColorInterpolationMethod;
use style::values::generics::easing::BeforeFlag;
use style::values::specified::gecko::IntersectionObserverRootMargin;
use style::values::specified::source_size_list::SourceSizeList;
use style::values::{specified, AtomIdent, CustomIdent, KeyframesName};
use style_traits::{CssWriter, ParsingMode, ToCss};
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();
parser::assert_parsing_mode_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: &RawServoStyleSet,
snapshots: *const ServoElementSnapshotTable,
raw_flags: ServoTraversalFlags,
) -> bool {
let traversal_flags = TraversalFlags::from_bits_truncate(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 = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet) {
let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
per_doc_data.stylist.rule_tree().maybe_gc();
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValues_Interpolate(
from: &RawServoAnimationValue,
to: &RawServoAnimationValue,
progress: f64,
) -> Strong<RawServoAnimationValue> {
let from_value = AnimationValue::as_arc(&from);
let to_value = AnimationValue::as_arc(&to);
if let Ok(value) = from_value.animate(to_value, Procedure::Interpolate { progress }) {
Arc::new(value).into_strong()
} else {
Strong::null()
}
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValues_IsInterpolable(
from: &RawServoAnimationValue,
to: &RawServoAnimationValue,
) -> bool {
let from_value = AnimationValue::as_arc(&from);
let to_value = AnimationValue::as_arc(&to);
from_value
.animate(to_value, Procedure::Interpolate { progress: 0.5 })
.is_ok()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValues_Add(
a: &RawServoAnimationValue,
b: &RawServoAnimationValue,
) -> Strong<RawServoAnimationValue> {
let a_value = AnimationValue::as_arc(&a);
let b_value = AnimationValue::as_arc(&b);
if let Ok(value) = a_value.animate(b_value, Procedure::Add) {
Arc::new(value).into_strong()
} else {
Strong::null()
}
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValues_Accumulate(
a: &RawServoAnimationValue,
b: &RawServoAnimationValue,
count: u64,
) -> Strong<RawServoAnimationValue> {
let a_value = AnimationValue::as_arc(&a);
let b_value = AnimationValue::as_arc(&b);
if let Ok(value) = a_value.animate(b_value, Procedure::Accumulate { count }) {
Arc::new(value).into_strong()
} else {
Strong::null()
}
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValues_GetZeroValue(
value_to_match: &RawServoAnimationValue,
) -> Strong<RawServoAnimationValue> {
let value_to_match = AnimationValue::as_arc(&value_to_match);
if let Ok(zero_value) = value_to_match.to_animated_zero() {
Arc::new(zero_value).into_strong()
} else {
Strong::null()
}
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValues_ComputeDistance(
from: &RawServoAnimationValue,
to: &RawServoAnimationValue,
) -> f64 {
let from_value = AnimationValue::as_arc(&from);
let to_value = AnimationValue::as_arc(&to);
// If compute_squared_distance() failed, this function will return negative value
// in order to check whether we support the specified paced animation values.
from_value
.compute_squared_distance(to_value)
.map(|d| d.sqrt())
.unwrap_or(-1.0)
}
/// 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<&RawOffsetArc<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<&RawOffsetArc<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 raw_from_value;
let keyframe_from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr };
Some(AnimationValue::as_arc(&raw_from_value))
} else {
None
};
let raw_to_value;
let keyframe_to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr };
Some(AnimationValue::as_arc(&raw_to_value))
} else {
None
};
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<&RawServoAnimationValue>,
last_value: Option<&RawServoAnimationValue>,
iteration_composite: IterationCompositeOperation,
progress: f64,
current_iteration: u64,
) -> Strong<RawServoAnimationValue> {
let underlying_value = AnimationValue::arc_from_borrowed(&underlying_value).map(|v| &**v);
let last_value = AnimationValue::arc_from_borrowed(&last_value).map(|v| &**v);
let result = compose_animation_segment(
segment,
underlying_value,
last_value,
iteration_composite,
current_iteration,
progress,
progress,
);
Arc::new(result).into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationCompose(
raw_value_map: &mut structs::RawServoAnimationValueMap,
base_values: &structs::RawServoAnimationValueTable,
css_property: nsCSSPropertyID,
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 LonghandId::from_nscsspropertyid(css_property) {
Ok(longhand) if longhand.is_animatable() => longhand,
_ => return,
};
let value_map = AnimationValueMap::from_ffi_mut(raw_value_map);
// 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).cloned();
previous_composed_value.or_else(|| {
let raw_base_style =
unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property).as_ref() };
AnimationValue::arc_from_borrowed(&raw_base_style)
.map(|v| &**v)
.cloned()
})
} else {
None
};
if need_underlying_value && underlying_value.is_none() {
warn!("Underlying value should be valid when we expect to use it");
return;
}
let raw_last_value;
let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() {
raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr };
Some(&**AnimationValue::as_arc(&raw_last_value))
} else {
None
};
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.as_ref(),
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) {
Ok(property_id) => property_id,
Err(()) => {
return $ret;
},
}
}};
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Serialize(
value: &RawServoAnimationValue,
property: nsCSSPropertyID,
raw_data: &RawServoStyleSet,
buffer: &mut nsACString,
) {
let uncomputed_value = AnimationValue::as_arc(&value).uncompute();
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
let rv = PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal)
.single_value_to_css(
&get_property_id_from_nscsspropertyid!(property, ()),
buffer,
None,
None, /* No extra custom properties */
&data.stylist.device(),
);
debug_assert!(rv.is_ok());
}
/// Debug: MOZ_DBG for AnimationValue.
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Dump(
value: &RawServoAnimationValue,
result: &mut nsACString,
) {
let value = AnimationValue::as_arc(&value);
write!(result, "{:?}", value).unwrap();
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_GetColor(
value: &RawServoAnimationValue,
foreground_color: structs::nscolor,
) -> structs::nscolor {
use style::gecko::values::convert_nscolor_to_rgba;
use style::gecko::values::convert_rgba_to_nscolor;
use style::values::animated::ToAnimatedValue;
use style::values::computed::color::Color as ComputedColor;
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::BackgroundColor(ref color) => {
let computed: ComputedColor = ToAnimatedValue::from_animated_value(color.clone());
let foreground_color = convert_nscolor_to_rgba(foreground_color);
convert_rgba_to_nscolor(&computed.into_rgba(foreground_color))
},
_ => panic!("Other color properties are not supported yet"),
}
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_IsCurrentColor(value: &RawServoAnimationValue) -> bool {
let value = AnimationValue::as_arc(&value);
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: &RawServoAnimationValue) -> f32 {
let value = AnimationValue::as_arc(&value);
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<RawServoAnimationValue> {
Arc::new(AnimationValue::Opacity(opacity)).into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Color(
color_property: nsCSSPropertyID,
color: structs::nscolor,
) -> Strong<RawServoAnimationValue> {
use style::gecko::values::convert_nscolor_to_rgba;
use style::values::animated::color::Color;
let property = LonghandId::from_nscsspropertyid(color_property)
.expect("We don't have shorthand property animation value");
let rgba = convert_nscolor_to_rgba(color);
let animated = AnimatedRGBA::new(
rgba.red_f32(),
rgba.green_f32(),
rgba.blue_f32(),
rgba.alpha_f32(),
);
match property {
LonghandId::BackgroundColor => {
Arc::new(AnimationValue::BackgroundColor(Color::rgba(animated))).into_strong()
},
_ => panic!("Should be background-color property"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetScale(
value: &RawServoAnimationValue,
) -> *const computed::Scale {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::Scale(ref value) => value,
_ => unreachable!("Expected scale"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetTranslate(
value: &RawServoAnimationValue,
) -> *const computed::Translate {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::Translate(ref value) => value,
_ => unreachable!("Expected translate"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetRotate(
value: &RawServoAnimationValue,
) -> *const computed::Rotate {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::Rotate(ref value) => value,
_ => unreachable!("Expected rotate"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetTransform(
value: &RawServoAnimationValue,
) -> *const computed::Transform {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::Transform(ref value) => value,
_ => unreachable!("Unsupported transform animation value"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetPath(
value: &RawServoAnimationValue,
) -> *const computed::motion::OffsetPath {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::OffsetPath(ref value) => value,
_ => unreachable!("Expected offset-path"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetDistance(
value: &RawServoAnimationValue,
) -> *const computed::LengthPercentage {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::OffsetDistance(ref value) => value,
_ => unreachable!("Expected offset-distance"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetRotate(
value: &RawServoAnimationValue,
) -> *const computed::motion::OffsetRotate {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::OffsetRotate(ref value) => value,
_ => unreachable!("Expected offset-rotate"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_GetOffsetAnchor(
value: &RawServoAnimationValue,
) -> *const computed::position::PositionOrAuto {
let value = AnimationValue::as_arc(&value);
match **value {
AnimationValue::OffsetAnchor(ref value) => value,
_ => unreachable!("Expected offset-anchor"),
}
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Rotate(
r: &computed::Rotate,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::Rotate(r.clone())).into_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Translate(
t: &computed::Translate,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::Translate(t.clone())).into_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Scale(
s: &computed::Scale,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::Scale(s.clone())).into_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_Transform(
transform: &computed::Transform,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::Transform(transform.clone())).into_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetPath(
p: &computed::motion::OffsetPath,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::OffsetPath(p.clone())).into_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetDistance(
d: &computed::length::LengthPercentage,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::OffsetDistance(d.clone())).into_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetRotate(
r: &computed::motion::OffsetRotate,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::OffsetRotate(*r)).into_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValue_OffsetAnchor(
p: &computed::position::PositionOrAuto,
) -> Strong<RawServoAnimationValue> {
Arc::new(AnimationValue::OffsetAnchor(p.clone())).into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_DeepEqual(
this: &RawServoAnimationValue,
other: &RawServoAnimationValue,
) -> bool {
let this_value = AnimationValue::as_arc(&this);
let other_value = AnimationValue::as_arc(&other);
this_value == other_value
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Uncompute(
value: &RawServoAnimationValue,
) -> Strong<RawServoDeclarationBlock> {
let value = AnimationValue::as_arc(&value);
let global_style_data = &*GLOBAL_STYLE_DATA;
Arc::new(
global_style_data
.shared_lock
.wrap(PropertyDeclarationBlock::with_one(
value.uncompute(),
Importance::Normal,
)),
)
.into_strong()
}
#[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_StyleComputedTimingFunction_Serialize,
Servo_StyleComputedTimingFunction_Deserialize,
ComputedTimingFunction
);
#[no_mangle]
pub extern "C" fn Servo_SVGPathData_Normalize(
input: &specified::SVGPathData,
output: &mut specified::SVGPathData,
) {
*output = input.normalize();
}
// 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() -> Owned<structs::RawServoAnimationValueMap> {
Box::<AnimationValueMap>::default().into_ffi()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValueMap_Drop(
value_map: *mut structs::RawServoAnimationValueMap,
) {
AnimationValueMap::drop_ffi(value_map)
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValueMap_GetValue(
raw_value_map: &mut structs::RawServoAnimationValueMap,
property_id: nsCSSPropertyID,
) -> Strong<RawServoAnimationValue> {
let property = match LonghandId::from_nscsspropertyid(property_id) {
Ok(longhand) => longhand,
Err(()) => return Strong::null(),
};
let value_map = AnimationValueMap::from_ffi_mut(raw_value_map);
value_map.get(&property).map_or(Strong::null(), |value| {
Arc::new(value.clone()).into_strong()
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
raw_style_set: &RawServoStyleSet,
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 = PerDocumentStyleData::from_ffi(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()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_GetComputedValuesByAddingAnimation(
raw_style_set: &RawServoStyleSet,
element: &RawGeckoElement,
computed_values: &ComputedValues,
snapshots: *const ServoElementSnapshotTable,
animation_value: &RawServoAnimationValue,
) -> Strong<ComputedValues> {
debug_assert!(!snapshots.is_null());
let rules = match computed_values.rules {
None => return Strong::null(),
Some(ref rules) => rules,
};
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let uncomputed_value = AnimationValue::as_arc(&animation_value).uncompute();
let doc_data = PerDocumentStyleData::from_ffi(raw_style_set).borrow();
let with_animations_rules = {
let guards = StylesheetGuards::same(&guard);
let declarations = Arc::new(global_style_data.shared_lock.wrap(
PropertyDeclarationBlock::with_one(uncomputed_value, Importance::Normal),
));
doc_data
.stylist
.rule_tree()
.add_animation_rules_at_transition_level(rules, declarations, &guards)
};
let element = GeckoElement(element);
if element.borrow_data().is_none() {
return Strong::null();
}
let shared = create_shared_context(
&global_style_data,
&guard,
&doc_data.stylist,
TraversalFlags::empty(),
unsafe { &*snapshots },
);
let mut tlc: ThreadLocalStyleContext<GeckoElement> = ThreadLocalStyleContext::new();
let context = StyleContext {
shared: &shared,
thread_local: &mut tlc,
};
resolve_rules_for_element_with_context(element, context, with_animations_rules, &computed_values).into()
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(
computed_values: &ComputedValues,
property_id: nsCSSPropertyID,
) -> Strong<RawServoAnimationValue> {
let property = match LonghandId::from_nscsspropertyid(property_id) {
Ok(longhand) => longhand,
Err(()) => return Strong::null(),
};
match AnimationValue::from_computed_values(property, &computed_values) {
Some(v) => Arc::new(v).into_strong(),
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) {
Ok(p) => {
let name = p.name();
(name.as_bytes().as_ptr(), name.len())
},
Err(..) => (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(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(_) => return true,
PropertyId::Longhand(id) | PropertyId::LonghandAlias(id, _) => id,
PropertyId::Shorthand(id) | PropertyId::ShorthandAlias(id, _) => {
id.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: nsCSSPropertyID) -> bool {
NonCustomPropertyId::from_nscsspropertyid(prop)
.ok()
.map_or(false, |p| p.is_animatable())
}
#[no_mangle]
pub extern "C" fn Servo_Property_IsTransitionable(prop: nsCSSPropertyID) -> bool {
NonCustomPropertyId::from_nscsspropertyid(prop)
.ok()
.map_or(false, |p| p.is_transitionable())
}
#[no_mangle]
pub extern "C" fn Servo_Property_IsDiscreteAnimatable(property: nsCSSPropertyID) -> bool {
match LonghandId::from_nscsspropertyid(property) {
Ok(longhand) => longhand.is_discrete_animatable(),
Err(()) => 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<RawServoStyleSheetContents> {
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,
0,
/* use_counters = */ None,
AllowImportRules::Yes,
/* sanitization_data = */ None,
)
.into_strong()
}
/// 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,
line_number_offset: u32,
quirks_mode: nsCompatibility,
reusable_sheets: *mut LoaderReusableStyleSheets,
use_counters: Option<&UseCounters>,
allow_import_rules: AllowImportRules,
sanitization_kind: SanitizationKind,
sanitized_output: Option<&mut nsAString>,
) -> Strong<RawServoStyleSheetContents> {
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(),
line_number_offset,
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_strong()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
load_data: *mut SheetLoadDataHolder,
extra_data: *mut URLExtraData,
bytes: &nsACString,
mode: SheetParsingMode,
line_number_offset: u32,
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(),
line_number_offset,
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_StyleSheet_FromSharedData(
extra_data: *mut URLExtraData,
shared_rules: &ServoCssRules,
) -> Strong<RawServoStyleSheetContents> {
let shared_rules = Locked::<CssRules>::as_arc(&shared_rules);
StylesheetContents::from_shared_data(
shared_rules.clone_arc(),
Origin::UserAgent,
UrlExtraData::new(extra_data),
QuirksMode::NoQuirks,
)
.into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
raw_data: &RawServoStyleSet,
sheet: *const DomStyleSheet,
) {
let global_style_data = &*GLOBAL_STYLE_DATA;
let mut data = PerDocumentStyleData::from_ffi(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() -> Owned<RawServoAuthorStyles> {
Box::new(AuthorStyles::<GeckoStyleSheet>::new()).into_ffi()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_Drop(styles: *mut RawServoAuthorStyles) {
AuthorStyles::drop_ffi(styles)
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet(
styles: &mut RawServoAuthorStyles,
sheet: *const DomStyleSheet,
) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
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 RawServoAuthorStyles,
sheet: *const DomStyleSheet,
before_sheet: *const DomStyleSheet,
) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
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 RawServoAuthorStyles,
sheet: *const DomStyleSheet,
) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
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 RawServoAuthorStyles) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
styles.stylesheets.force_dirty();
}
#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_IsDirty(styles: &RawServoAuthorStyles) -> bool {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(styles);
styles.stylesheets.dirty()
}
#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_Flush(
styles: &mut RawServoAuthorStyles,
document_set: &RawServoStyleSet,
) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
// 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 = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet,
) {
let mut document_data = PerDocumentStyleData::from_ffi(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: &RawServoDeclarationBlock,
) -> 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,
);
Locked::<PropertyDeclarationBlock>::as_arc(&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: &RawServoAuthorStyles,
) -> 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 RawServoAuthorStyles as *const c_void);
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(styles);
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: &RawServoStyleSet,
non_document_styles: &mut nsTArray<&mut RawServoAuthorStyles>,
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 = PerDocumentStyleData::from_ffi(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 author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(&mut *author_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: &RawServoStyleSet,
sheet: *const DomStyleSheet,
before_sheet: *const DomStyleSheet,
) {
let global_style_data = &*GLOBAL_STYLE_DATA;
let mut data = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet,
sheet: *const DomStyleSheet,
) {
let global_style_data = &*GLOBAL_STYLE_DATA;
let mut data = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet,
origin: Origin,
index: usize,
) -> *const DomStyleSheet {
let data = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet,
origin: Origin,
) -> usize {
let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
data.stylist.sheet_count(origin)
}
#[no_mangle]
pub unsafe extern "C" fn Servo_StyleSet_FlushStyleSheets(
raw_data: &RawServoStyleSet,
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 = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet,
changed_origins: OriginFlags,
) {
let mut data = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet,
author_style_disabled: bool,
) {
let mut data = PerDocumentStyleData::from_ffi(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: &RawServoStyleSet) -> bool {
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
doc_data.borrow().stylist.device().used_font_metrics()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_HasRules(raw_contents: &RawServoStyleSheetContents) -> bool {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
!StylesheetContents::as_arc(&raw_contents)
.rules
.read_with(&guard)
.0
.is_empty()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_GetRules(
sheet: &RawServoStyleSheetContents,
) -> Strong<ServoCssRules> {
StylesheetContents::as_arc(&sheet)
.rules
.clone()
.into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_Clone(
raw_sheet: &RawServoStyleSheetContents,
reference_sheet: *const DomStyleSheet,
) -> Strong<RawServoStyleSheetContents> {
use style::shared_lock::{DeepCloneParams, DeepCloneWithLock};
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let contents = StylesheetContents::as_arc(&raw_sheet);
let params = DeepCloneParams { reference_sheet };
Arc::new(contents.deep_clone_with_lock(&global_style_data.shared_lock, &guard, ¶ms))
.into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_SizeOfIncludingThis(
malloc_size_of: GeckoMallocSizeOf,
malloc_enclosing_size_of: GeckoMallocSizeOf,
sheet: &RawServoStyleSheetContents,
) -> 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.
StylesheetContents::as_arc(&sheet).size_of(&guard, &mut ops)
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_GetOrigin(sheet: &RawServoStyleSheetContents) -> Origin {
StylesheetContents::as_arc(&sheet).origin
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_GetSourceMapURL(
sheet: &RawServoStyleSheetContents,
result: &mut nsAString,
) {
let contents = StylesheetContents::as_arc(&sheet);
let url_opt = contents.source_map_url.read();
if let Some(ref url) = *url_opt {
write!(result, "{}", url).unwrap();
}
}
#[no_mangle]
pub extern "C" fn Servo_StyleSheet_GetSourceURL(
sheet: &RawServoStyleSheetContents,
result: &mut nsAString,
) {
let contents = StylesheetContents::as_arc(&sheet);
let url_opt = contents.source_url.read();
if let Some(ref url) = *url_opt {
write!(result, "{}", url).unwrap();
}
}
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> as HasFFI>::FFIType, func: F) -> R
where
Locked<T>: HasArcFFI,
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(Locked::<T>::as_arc(&raw).read_with(&guard))
}
fn read_locked_arc_worker<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
where
Locked<T>: HasArcFFI,
F: FnOnce(&T) -> R,
{
with_maybe_worker_shared_lock(|lock| {
let guard = lock.read();
func(Locked::<T>::as_arc(&raw).read_with(&guard))
})
}
#[cfg(debug_assertions)]
unsafe fn read_locked_arc_unchecked<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
where
Locked<T>: HasArcFFI,
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> as HasFFI>::FFIType, func: F) -> R
where
Locked<T>: HasArcFFI,
F: FnOnce(&T) -> R,
{
debug_assert!(!is_dom_worker_thread());
func(Locked::<T>::as_arc(&raw).read_unchecked())
}
fn write_locked_arc<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
where
Locked<T>: HasArcFFI,
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(Locked::<T>::as_arc(&raw).write_with(&mut guard))
}
fn write_locked_arc_worker<T, R, F>(raw: &<Locked<T> as HasFFI>::FFIType, func: F) -> R
where
Locked<T>: HasArcFFI,
F: FnOnce(&mut T) -> R,
{
with_maybe_worker_shared_lock(|lock| {
let mut guard = lock.write();
func(Locked::<T>::as_arc(&raw).write_with(&mut guard))
})
}
#[no_mangle]
pub extern "C" fn Servo_CssRules_ListTypes(rules: &ServoCssRules, 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: &ServoCssRules,
contents: &RawServoStyleSheetContents,
rule: &nsACString,
index: u32,
nested: bool,
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 contents = StylesheetContents::as_arc(&contents);
let result = Locked::<CssRules>::as_arc(&rules).insert_rule(
&global_style_data.shared_lock,
rule,
contents,
index as usize,
nested,
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: &ServoCssRules, 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(),
}
})
}
macro_rules! impl_basic_rule_funcs_without_getter {
{ ($rule_type:ty, $raw_type:ty),
debug: $debug:ident,
to_css: $to_css:ident,
} => {
#[cfg(debug_assertions)]
#[no_mangle]
pub extern "C" fn $debug(rule: &$raw_type, result: *mut nsACString) {
read_locked_arc(rule, |rule: &$rule_type| {
write!(unsafe { result.as_mut().unwrap() }, "{:?}", *rule).unwrap();
})
}
#[cfg(not(debug_assertions))]
#[no_mangle]
pub extern "C" fn $debug(_: &$raw_type, _: *mut nsACString) {
unreachable!()
}
#[no_mangle]
pub extern "C" fn $to_css(rule: &$raw_type, result: &mut nsACString) {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let rule = Locked::<$rule_type>::as_arc(&rule);
rule.read_with(&guard).to_css(&guard, result).unwrap();
}
}
}
macro_rules! impl_basic_rule_funcs {
{ ($name:ident, $rule_type:ty, $raw_type:ty),
getter: $getter:ident,
debug: $debug:ident,
to_css: $to_css:ident,
changed: $changed:ident,
} => {
#[no_mangle]
pub extern "C" fn $getter(
rules: &ServoCssRules,
index: u32,
line: *mut u32,
column: *mut u32,
) -> Strong<$raw_type> {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let rules = Locked::<CssRules>::as_arc(&rules).read_with(&guard);
let index = index as usize;
if index >= rules.0.len() {
return Strong::null();
}
match rules.0[index] {
CssRule::$name(ref rule) => {
let location = rule.read_with(&guard).source_location;
*unsafe { line.as_mut().unwrap() } = location.line as u32;
*unsafe { column.as_mut().unwrap() } = location.column as u32;
rule.clone().into_strong()
},
_ => {
Strong::null()
}
}
}
#[no_mangle]
pub extern "C" fn $changed(
styleset: &RawServoStyleSet,
rule: &$raw_type,
sheet: &DomStyleSheet,
change_kind: RuleChangeKind,
) {
let mut data = PerDocumentStyleData::from_ffi(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 = Locked::<$rule_type>::as_arc(&rule);
let rule = CssRule::$name(rule.clone_arc());
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylist.rule_changed(&sheet, &rule, &guard, change_kind);
}
impl_basic_rule_funcs_without_getter! { ($rule_type, $raw_type),
debug: $debug,
to_css: $to_css,
}
}
}
macro_rules! impl_group_rule_funcs {
{ ($name:ident, $rule_type:ty, $raw_type:ty),
get_rules: $get_rules:ident,
$($basic:tt)+
} => {
impl_basic_rule_funcs! { ($name, $rule_type, $raw_type), $($basic)+ }
#[no_mangle]
pub extern "C" fn $get_rules(rule: &$raw_type) -> Strong<ServoCssRules> {
read_locked_arc(rule, |rule: &$rule_type| {
rule.rules.clone().into_strong()
})
}
}
}
impl_basic_rule_funcs! { (Style, StyleRule, RawServoStyleRule),
getter: Servo_CssRules_GetStyleRuleAt,
debug: Servo_StyleRule_Debug,
to_css: Servo_StyleRule_GetCssText,
changed: Servo_StyleSet_StyleRuleChanged,
}
impl_basic_rule_funcs! { (Import, ImportRule, RawServoImportRule),
getter: Servo_CssRules_GetImportRuleAt,
debug: Servo_ImportRule_Debug,
to_css: Servo_ImportRule_GetCssText,
changed: Servo_StyleSet_ImportRuleChanged,
}
impl_basic_rule_funcs_without_getter! { (Keyframe, RawServoKeyframe),
debug: Servo_Keyframe_Debug,
to_css: Servo_Keyframe_GetCssText,
}
impl_basic_rule_funcs! { (Keyframes, KeyframesRule, RawServoKeyframesRule),
getter: Servo_CssRules_GetKeyframesRuleAt,
debug: Servo_KeyframesRule_Debug,
to_css: Servo_KeyframesRule_GetCssText,
changed: Servo_StyleSet_KeyframesRuleChanged,
}
impl_group_rule_funcs! { (Media, MediaRule, RawServoMediaRule),
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, RawServoNamespaceRule),
getter: Servo_CssRules_GetNamespaceRuleAt,
debug: Servo_NamespaceRule_Debug,
to_css: Servo_NamespaceRule_GetCssText,
changed: Servo_StyleSet_NamespaceRuleChanged,
}
impl_basic_rule_funcs! { (Page, PageRule, RawServoPageRule),
getter: Servo_CssRules_GetPageRuleAt,
debug: Servo_PageRule_Debug,
to_css: Servo_PageRule_GetCssText,
changed: Servo_StyleSet_PageRuleChanged,
}
impl_group_rule_funcs! { (Supports, SupportsRule, RawServoSupportsRule),
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, RawServoContainerRule),
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, RawServoLayerBlockRule),
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, RawServoLayerStatementRule),
getter: Servo_CssRules_GetLayerStatementRuleAt,
debug: Servo_LayerStatementRule_Debug,
to_css: Servo_LayerStatementRule_GetCssText,
changed: Servo_StyleSet_LayerStatementRuleChanged,
}
impl_group_rule_funcs! { (Document, DocumentRule, RawServoMozDocumentRule),
get_rules: Servo_MozDocumentRule_GetRules,
getter: Servo_CssRules_GetMozDocumentRuleAt,
debug: Servo_MozDocumentRule_Debug,
to_css: Servo_MozDocumentRule_GetCssText,
changed: Servo_StyleSet_MozDocumentRuleChanged,
}
impl_basic_rule_funcs! { (FontFeatureValues, FontFeatureValuesRule, RawServoFontFeatureValuesRule),
getter: Servo_CssRules_GetFontFeatureValuesRuleAt,
debug: Servo_FontFeatureValuesRule_Debug,
to_css: Servo_FontFeatureValuesRule_GetCssText,
changed: Servo_StyleSet_FontFeatureValuesRuleChanged,
}
impl_basic_rule_funcs! { (FontPaletteValues, FontPaletteValuesRule, RawServoFontPaletteValuesRule),
getter: Servo_CssRules_GetFontPaletteValuesRuleAt,
debug: Servo_FontPaletteValuesRule_Debug,
to_css: Servo_FontPaletteValuesRule_GetCssText,
changed: Servo_StyleSet_FontPaletteValuesRuleChanged,
}
impl_basic_rule_funcs! { (FontFace, FontFaceRule, RawServoFontFaceRule),
getter: Servo_CssRules_GetFontFaceRuleAt,
debug: Servo_FontFaceRule_Debug,
to_css: Servo_FontFaceRule_GetCssText,
changed: Servo_StyleSet_FontFaceRuleChanged,
}
impl_basic_rule_funcs! { (CounterStyle, CounterStyleRule, RawServoCounterStyleRule),
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: &RawServoStyleRule,
) -> Strong<RawServoDeclarationBlock> {
read_locked_arc(rule, |rule: &StyleRule| rule.block.clone().into_strong())
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_SetStyle(
rule: &RawServoStyleRule,
declarations: &RawServoDeclarationBlock,
) {
let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
write_locked_arc(rule, |rule: &mut StyleRule| {
rule.block = declarations.clone_arc();
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_GetSelectorText(
rule: &RawServoStyleRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &StyleRule| {
rule.selectors.to_css(result).unwrap();
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_GetSelectorTextAtIndex(
rule: &RawServoStyleRule,
index: u32,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &StyleRule| {
let index = index as usize;
if index >= rule.selectors.0.len() {
return;
}
rule.selectors.0[index].to_css(result).unwrap();
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_GetSelectorCount(rule: &RawServoStyleRule, count: *mut u32) {
read_locked_arc(rule, |rule: &StyleRule| {
*unsafe { count.as_mut().unwrap() } = rule.selectors.0.len() as u32;
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_GetSpecificityAtIndex(
rule: &RawServoStyleRule,
index: u32,
specificity: *mut u64,
) {
read_locked_arc(rule, |rule: &StyleRule| {
let specificity = unsafe { specificity.as_mut().unwrap() };
let index = index as usize;
if index >= rule.selectors.0.len() {
*specificity = 0;
return;
}
*specificity = rule.selectors.0[index].specificity() as u64;
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_SelectorMatchesElement(
rule: &RawServoStyleRule,
element: &RawGeckoElement,
index: u32,
pseudo_type: PseudoStyleType,
relevant_link_visited: bool,
) -> bool {
use selectors::matching::{
matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,
};
read_locked_arc(rule, |rule: &StyleRule| {
let index = index as usize;
if index >= rule.selectors.0.len() {
return false;
}
let selector = &rule.selectors.0[index];
let mut matching_mode = MatchingMode::Normal;
match PseudoElement::from_pseudo_type(pseudo_type) {
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 quirks_mode = element.as_node().owner_doc().quirks_mode();
let mut nth_index_cache = Default::default();
let visited_mode = if relevant_link_visited {
VisitedHandlingMode::RelevantLinkVisited
} else {
VisitedHandlingMode::AllLinksUnvisited
};
let mut ctx = MatchingContext::new_for_visited(
matching_mode,
None,
&mut nth_index_cache,
visited_mode,
quirks_mode,
NeedsSelectorFlags::No,
);
matches_selector(selector, 0, None, &element, &mut ctx)
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_SetSelectorText(
sheet: &RawServoStyleSheetContents,
rule: &RawServoStyleRule,
text: &nsACString,
) -> bool {
let value_str = unsafe { text.as_str_unchecked() };
write_locked_arc(rule, |rule: &mut StyleRule| {
use style::selector_parser::SelectorParser;
let contents = StylesheetContents::as_arc(&sheet);
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,
};
let mut parser_input = ParserInput::new(&value_str);
match SelectorList::parse(&parser, &mut Parser::new(&mut parser_input)) {
Ok(selectors) => {
rule.selectors = selectors;
true
},
Err(_) => false,
}
})
}
#[no_mangle]
pub unsafe extern "C" fn Servo_SelectorList_Closest(
element: &RawGeckoElement,
selectors: &RawServoSelectorList,
) -> *const RawGeckoElement {
use std::borrow::Borrow;
use style::dom_apis;
let element = GeckoElement(element);
let quirks_mode = element.as_node().owner_doc().quirks_mode();
let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow();
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: &RawServoSelectorList,
) -> bool {
use std::borrow::Borrow;
use style::dom_apis;
let element = GeckoElement(element);
let quirks_mode = element.as_node().owner_doc().quirks_mode();
let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow();
dom_apis::element_matches(&element, &selectors, quirks_mode)
}
#[no_mangle]
pub unsafe extern "C" fn Servo_SelectorList_QueryFirst(
node: &RawGeckoNode,
selectors: &RawServoSelectorList,
may_use_invalidation: bool,
) -> *const RawGeckoElement {
use std::borrow::Borrow;
use style::dom_apis::{self, MayUseInvalidation, QueryFirst};
let node = GeckoNode(node);
let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow();
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: &RawServoSelectorList,
content_list: *mut structs::nsSimpleContentList,
may_use_invalidation: bool,
) {
use std::borrow::Borrow;
use style::dom_apis::{self, MayUseInvalidation, QueryAll};
let node = GeckoNode(node);
let selectors = ::selectors::SelectorList::from_ffi(selectors).borrow();
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: &RawServoImportRule, 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: &RawServoImportRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &ImportRule| match rule.layer {
Some(ref layer) => layer.name.to_css(&mut CssWriter::new(result)).unwrap(),
None => result.set_is_void(true),
})
}
#[no_mangle]
pub extern "C" fn Servo_ImportRule_GetSheet(rule: &RawServoImportRule) -> *const DomStyleSheet {
read_locked_arc(rule, |rule: &ImportRule| {
rule.stylesheet.as_sheet().unwrap().raw() as *const DomStyleSheet
})
}
#[no_mangle]
pub unsafe extern "C" fn Servo_ImportRule_SetSheet(
rule: &RawServoImportRule,
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: &RawServoKeyframe, 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: &RawServoKeyframe,
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: &RawServoKeyframe,
) -> Strong<RawServoDeclarationBlock> {
read_locked_arc(keyframe, |keyframe: &Keyframe| {
keyframe.block.clone().into_strong()
})
}
#[no_mangle]
pub extern "C" fn Servo_Keyframe_SetStyle(
keyframe: &RawServoKeyframe,
declarations: &RawServoDeclarationBlock,
) {
let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
keyframe.block = declarations.clone_arc();
})
}
#[no_mangle]
pub extern "C" fn Servo_KeyframesRule_GetName(rule: &RawServoKeyframesRule) -> *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: &RawServoKeyframesRule,
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: &RawServoKeyframesRule) -> u32 {
read_locked_arc(rule, |rule: &KeyframesRule| rule.keyframes.len() as u32)
}
#[no_mangle]
pub extern "C" fn Servo_KeyframesRule_GetKeyframeAt(
rule: &RawServoKeyframesRule,
index: u32,
line: *mut u32,
column: *mut u32,
) -> Strong<RawServoKeyframe> {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let key = Locked::<KeyframesRule>::as_arc(&rule)
.read_with(&guard)
.keyframes[index as usize]
.clone();
let location = key.read_with(&guard).source_location;
*unsafe { line.as_mut().unwrap() } = location.line as u32;
*unsafe { column.as_mut().unwrap() } = location.column as u32;
key.into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_KeyframesRule_FindRule(
rule: &RawServoKeyframesRule,
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();
Locked::<KeyframesRule>::as_arc(&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: &RawServoKeyframesRule,
contents: &RawServoStyleSheetContents,
css: &nsACString,
) -> bool {
let css = unsafe { css.as_str_unchecked() };
let contents = StylesheetContents::as_arc(&contents);
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: &RawServoKeyframesRule, 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: &RawServoMediaRule) -> Strong<RawServoMediaList> {
read_locked_arc(rule, |rule: &MediaRule| {
rule.media_queries.clone().into_strong()
})
}
#[no_mangle]
pub extern "C" fn Servo_NamespaceRule_GetPrefix(rule: &RawServoNamespaceRule) -> *mut nsAtom {
read_locked_arc(rule, |rule: &NamespaceRule| {
rule.prefix
.as_ref()
.map_or(atom!("").as_ptr(), |a| a.as_ptr())
})
}
#[no_mangle]
pub extern "C" fn Servo_NamespaceRule_GetURI(rule: &RawServoNamespaceRule) -> *mut nsAtom {
read_locked_arc(rule, |rule: &NamespaceRule| rule.url.0.as_ptr())
}
#[no_mangle]
pub extern "C" fn Servo_PageRule_GetStyle(
rule: &RawServoPageRule,
) -> Strong<RawServoDeclarationBlock> {
read_locked_arc(rule, |rule: &PageRule| rule.block.clone().into_strong())
}
#[no_mangle]
pub extern "C" fn Servo_PageRule_SetStyle(
rule: &RawServoPageRule,
declarations: &RawServoDeclarationBlock,
) {
let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
write_locked_arc(rule, |rule: &mut PageRule| {
rule.block = declarations.clone_arc();
})
}
#[no_mangle]
pub extern "C" fn Servo_PageRule_GetSelectorText(rule: &RawServoPageRule, 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(
sheet: &RawServoStyleSheetContents,
rule: &RawServoPageRule,
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 contents = StylesheetContents::as_arc(&sheet);
let url_data = contents.url_data.read();
let context = ParserContext::new(
Origin::Author,
&url_data,
None,
ParsingMode::DEFAULT,
QuirksMode::NoQuirks,
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_SupportsRule_GetConditionText(
rule: &RawServoSupportsRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &SupportsRule| {
rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
})
}
#[no_mangle]
pub extern "C" fn Servo_ContainerRule_GetConditionText(
rule: &RawServoContainerRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &ContainerRule| {
rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
})
}
#[no_mangle]
pub extern "C" fn Servo_ContainerRule_GetContainerQuery(
rule: &RawServoContainerRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &ContainerRule| {
rule.query_condition()
.to_css(&mut CssWriter::new(result))
.unwrap();
})
}
#[no_mangle]
pub extern "C" fn Servo_ContainerRule_QueryContainerFor(
rule: &RawServoContainerRule,
element: &RawGeckoElement,
) -> *const RawGeckoElement {
read_locked_arc(rule, |rule: &ContainerRule| {
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: &RawServoContainerRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &ContainerRule| {
let name = rule.container_name();
if !name.is_none() {
name.to_css(&mut CssWriter::new(result)).unwrap();
}
})
}
#[no_mangle]
pub extern "C" fn Servo_MozDocumentRule_GetConditionText(
rule: &RawServoMozDocumentRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &DocumentRule| {
rule.condition.to_css(&mut CssWriter::new(result)).unwrap();
})
}
#[no_mangle]
pub extern "C" fn Servo_FontFeatureValuesRule_GetFontFamily(
rule: &RawServoFontFeatureValuesRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &FontFeatureValuesRule| {
rule.family_names.to_css(&mut CssWriter::new(result))
.unwrap()
})
}
#[no_mangle]
pub extern "C" fn Servo_FontFeatureValuesRule_GetValueText(
rule: &RawServoFontFeatureValuesRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &FontFeatureValuesRule| {
rule.value_to_css(&mut CssWriter::new(result)).unwrap();
})
}
#[no_mangle]
pub extern "C" fn Servo_FontPaletteValuesRule_GetName(
rule: &RawServoFontPaletteValuesRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &FontPaletteValuesRule| {
rule.name.to_css(&mut CssWriter::new(result))
.unwrap()
})
}
#[no_mangle]
pub extern "C" fn Servo_FontPaletteValuesRule_GetFontFamily(
rule: &RawServoFontPaletteValuesRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &FontPaletteValuesRule| {
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: &RawServoFontPaletteValuesRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &FontPaletteValuesRule| {
rule.base_palette.to_css(&mut CssWriter::new(result))
.unwrap()
})
}
#[no_mangle]
pub extern "C" fn Servo_FontPaletteValuesRule_GetOverrideColors(
rule: &RawServoFontPaletteValuesRule,
result: &mut nsACString,
) {
read_locked_arc(rule, |rule: &FontPaletteValuesRule| {
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<RawServoFontFaceRule> {
// 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_strong()
})
}
#[no_mangle]
pub unsafe extern "C" fn Servo_FontFaceRule_Clone(
rule: &RawServoFontFaceRule,
) -> Strong<RawServoFontFaceRule> {
let clone = read_locked_arc_worker(rule, |rule: &FontFaceRule| rule.clone());
with_maybe_worker_shared_lock(|lock| Arc::new(lock.wrap(clone)).into_strong())
}
#[no_mangle]
pub unsafe extern "C" fn Servo_FontFaceRule_GetSourceLocation(
rule: &RawServoFontFaceRule,
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: &RawServoFontFaceRule) -> 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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
) -> *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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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: &RawServoFontFaceRule,
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,
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: &RawServoFontFaceRule,
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: &RawServoCounterStyleRule,
) -> *mut nsAtom {
read_locked_arc(rule, |rule: &CounterStyleRule| rule.name().0.as_ptr())
}
#[no_mangle]
pub unsafe extern "C" fn Servo_CounterStyleRule_SetName(
rule: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
) -> 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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
) -> 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: &RawServoCounterStyleRule,
) -> *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: &RawServoCounterStyleRule,
) -> 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: &RawServoCounterStyleRule,
) -> *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: &RawServoCounterStyleRule,
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: &RawServoCounterStyleRule,
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,
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_GetForAnonymousBox(
parent_style_or_null: Option<&ComputedValues>,
pseudo: PseudoStyleType,
raw_data: &RawServoStyleSet,
page_name: *const nsAtom,
) -> Strong<ComputedValues> {
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let guards = StylesheetGuards::same(&guard);
let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
let pseudo = PseudoElement::from_pseudo_type(pseudo).unwrap();
debug_assert!(pseudo.is_anon_box());
// If the pseudo element is PageContent, we should append @page rules to the
// precomputed pseudo.
let mut extra_declarations = vec![];
if pseudo == PseudoElement::PageContent {
let iter = data.stylist.iter_extra_data_origins_rev();
for (data, origin) in iter {
let level = match origin {
Origin::UserAgent => CascadeLevel::UANormal,
Origin::User => CascadeLevel::UserNormal,
Origin::Author => CascadeLevel::same_tree_author_normal(),
};
extra_declarations.reserve(data.pages.global.len());
let mut add_rule = |rule: &Arc<Locked<PageRule>>| {
extra_declarations.push(ApplicableDeclarationBlock::from_declarations(
rule.read_with(level.guard(&guards)).block.clone(),
level,
LayerOrder::root(),
));
};
for &(ref rule, _layer_id) in data.pages.global.iter() {
add_rule(&rule.0);
}
if !page_name.is_null() {
Atom::with(page_name, |name| {
if let Some(rules) = data.pages.named.get(name) {
// Rules are already sorted by source order.
rules.iter().for_each(|d| add_rule(&d.rule));
}
});
}
}
} else {
debug_assert!(page_name.is_null());
}
let rule_node =
data.stylist
.rule_node_for_precomputed_pseudo(&guards, &pseudo, extra_declarations);
data.stylist
.precomputed_values_for_pseudo_with_rule_node::<GeckoElement>(
&guards,
&pseudo,
parent_style_or_null.map(|x| &*x),
rule_node,
)
.into()
}
#[no_mangle]
pub extern "C" fn Servo_ResolvePseudoStyle(
element: &RawGeckoElement,
pseudo_type: PseudoStyleType,
is_probe: bool,
inherited_style: Option<&ComputedValues>,
raw_data: &RawServoStyleSet,
) -> Strong<ComputedValues> {
let element = GeckoElement(element);
let doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow();
debug!(
"Servo_ResolvePseudoStyle: {:?} {:?}, is_probe: {}",
element,
PseudoElement::from_pseudo_type(pseudo_type),
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 = PseudoElement::from_pseudo_type(pseudo_type)
.expect("ResolvePseudoStyle with a non-pseudo?");
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let style = get_pseudo_style(
&guard,
element,
&pseudo,
RuleInclusion::All,
&data.styles,
inherited_style,
&doc_data.stylist,
is_probe,
/* matching_func = */ 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_ResolveHighlightPseudoStyle(
element: &RawGeckoElement,
highlight_name: *const nsAtom,
raw_data: &RawServoStyleSet,
) -> Strong<ComputedValues> {
let element = GeckoElement(element);
let data = element