/* 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 http://mozilla.org/MPL/2.0/. */ use app_units::Au; use atomic_refcell::AtomicRefMut; use cssparser::Parser; use cssparser::ToCss as ParserToCss; use env_logger; use euclid::Size2D; use parking_lot::RwLock; use selectors::Element; use servo_url::ServoUrl; use std::borrow::Cow; use std::fmt::Write; use std::mem; use std::ptr; use std::sync::{Arc, Mutex}; use style::arc_ptr_eq; use style::context::{QuirksMode, ReflowGoal, SharedStyleContext, StyleContext}; use style::context::{ThreadLocalStyleContext, ThreadLocalStyleContextCreationInfo}; use style::data::{ElementData, ElementStyles, RestyleData}; use style::dom::{ShowSubtreeData, TElement, TNode}; use style::error_reporting::StdoutErrorReporter; use style::gecko::data::{NUM_THREADS, PerDocumentStyleData, PerDocumentStyleDataImpl}; use style::gecko::restyle_damage::GeckoRestyleDamage; use style::gecko::selector_parser::{SelectorImpl, PseudoElement}; use style::gecko::traversal::RecalcStyleOnly; use style::gecko::wrapper::DUMMY_BASE_URL; use style::gecko::wrapper::GeckoElement; use style::gecko_bindings::bindings; use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong}; use style::gecko_bindings::bindings::{RawServoStyleRuleBorrowed, RawServoStyleRuleStrong}; use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned}; use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed}; use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong}; use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong}; use style::gecko_bindings::bindings::{nsACString, nsCSSValueBorrowedMut, nsAString}; use style::gecko_bindings::bindings::Gecko_AnimationAppendKeyframe; use style::gecko_bindings::bindings::RawGeckoAnimationValueListBorrowedMut; use style::gecko_bindings::bindings::RawGeckoElementBorrowed; use style::gecko_bindings::bindings::RawGeckoKeyframeListBorrowedMut; use style::gecko_bindings::bindings::RawGeckoPresContextBorrowed; use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed; use style::gecko_bindings::bindings::RawServoAnimationValueStrong; use style::gecko_bindings::bindings::RawServoImportRuleBorrowed; use style::gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull; use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t; use style::gecko_bindings::structs; use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom, nsCSSPropertyID}; use style::gecko_bindings::structs::{ThreadSafePrincipalHolder, ThreadSafeURIHolder}; use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint}; use style::gecko_bindings::structs::Loader; use style::gecko_bindings::structs::RawGeckoPresContextOwned; use style::gecko_bindings::structs::RawServoAnimationValueBorrowedListBorrowed; use style::gecko_bindings::structs::ServoStyleSheet; use style::gecko_bindings::structs::nsCSSValueSharedList; use style::gecko_bindings::structs::nsTimingFunction; use style::gecko_bindings::structs::nsresult; use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasBoxFFI}; use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong}; use style::gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI}; use style::gecko_properties::style_structs; use style::keyframes::KeyframesStepValue; use style::parallel; use style::parser::{ParserContext, ParserContextExtraData}; use style::properties::{CascadeFlags, ComputedValues, Importance, PropertyDeclaration}; use style::properties::{PropertyDeclarationParseResult, PropertyDeclarationBlock, PropertyId}; use style::properties::{apply_declarations, parse_one_declaration}; use style::properties::animated_properties::{AnimationValue, Interpolate, TransitionProperty}; use style::restyle_hints::RestyleHint; use style::selector_parser::PseudoElementCascadeType; use style::sequential; use style::string_cache::Atom; use style::stylesheets::{CssRule, CssRules, Origin, Stylesheet, StyleRule, ImportRule}; use style::stylesheets::StylesheetLoader as StyleStylesheetLoader; use style::supports::parse_condition_or_declaration; use style::thread_state; use style::timer::Timer; use style::traversal::{resolve_style, DomTraversal, TraversalDriver}; use style_traits::ToCss; use stylesheet_loader::StylesheetLoader; /* * 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. */ #[no_mangle] pub extern "C" fn Servo_Initialize() -> () { // Enable standard Rust logging. // // See https://doc.rust-lang.org/log/env_logger/index.html for instructions. env_logger::init().unwrap(); // Pretend that we're a Servo Layout thread, to make some assertions happy. thread_state::initialize(thread_state::LAYOUT); } #[no_mangle] pub extern "C" fn Servo_Shutdown() -> () { } fn create_shared_context(per_doc_data: &PerDocumentStyleDataImpl) -> SharedStyleContext { let local_context_data = ThreadLocalStyleContextCreationInfo::new(per_doc_data.new_animations_sender.clone()); SharedStyleContext { // FIXME (bug 1303229): Use the actual viewport size here viewport_size: Size2D::new(Au(0), Au(0)), screen_size_changed: false, goal: ReflowGoal::ForScriptQuery, stylist: per_doc_data.stylist.clone(), running_animations: per_doc_data.running_animations.clone(), expired_animations: per_doc_data.expired_animations.clone(), error_reporter: Box::new(StdoutErrorReporter), local_context_creation_data: Mutex::new(local_context_data), timer: Timer::new(), // FIXME Find the real QuirksMode information for this document quirks_mode: QuirksMode::NoQuirks, default_computed_values: per_doc_data.default_computed_values().clone(), } } fn traverse_subtree(element: GeckoElement, raw_data: RawServoStyleSetBorrowed, unstyled_children_only: bool) { // When new content is inserted in a display:none subtree, we will call into // servo to try to style it. Detect that here and bail out. if let Some(parent) = element.parent_element() { if parent.borrow_data().map_or(true, |d| d.styles().is_display_none()) { debug!("{:?} has unstyled parent - ignoring call to traverse_subtree", parent); return; } } let mut per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let token = RecalcStyleOnly::pre_traverse(element, &per_doc_data.stylist, unstyled_children_only); if !token.should_traverse() { return; } debug!("Traversing subtree:"); debug!("{:?}", ShowSubtreeData(element.as_node())); let shared_style_context = create_shared_context(&per_doc_data); let traversal_driver = if per_doc_data.num_threads == 1 || per_doc_data.work_queue.is_none() { TraversalDriver::Sequential } else { TraversalDriver::Parallel }; let traversal = RecalcStyleOnly::new(shared_style_context, traversal_driver); let known_depth = None; if traversal_driver.is_parallel() { parallel::traverse_dom(&traversal, element, known_depth, token, per_doc_data.work_queue.as_mut().unwrap()); } else { sequential::traverse_dom(&traversal, element, token); } } /// Traverses the subtree rooted at `root` for restyling. Returns whether a /// Gecko post-traversal (to perform lazy frame construction, or consume any /// RestyleData, or drop any ElementData) is required. #[no_mangle] pub extern "C" fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed, raw_data: RawServoStyleSetBorrowed, behavior: structs::TraversalRootBehavior) -> bool { let element = GeckoElement(root); debug!("Servo_TraverseSubtree: {:?}", element); traverse_subtree(element, raw_data, behavior == structs::TraversalRootBehavior::UnstyledChildrenOnly); element.has_dirty_descendants() || element.mutate_data().unwrap().has_restyle() } #[no_mangle] pub extern "C" fn Servo_AnimationValues_Interpolate(from: RawServoAnimationValueBorrowed, to: RawServoAnimationValueBorrowed, progress: f64) -> RawServoAnimationValueStrong { let from_value = AnimationValue::as_arc(&from); let to_value = AnimationValue::as_arc(&to); if let Ok(value) = from_value.interpolate(to_value, progress) { Arc::new(value).into_strong() } else { RawServoAnimationValueStrong::null() } } #[no_mangle] pub extern "C" fn Servo_AnimationValues_Uncompute(value: RawServoAnimationValueBorrowedListBorrowed) -> RawServoDeclarationBlockStrong { let value = unsafe { value.as_ref().unwrap() }; let uncomputed_values = value.into_iter() .map(|v| { let raw_anim = unsafe { v.as_ref().unwrap() }; let anim = AnimationValue::as_arc(&raw_anim); (anim.uncompute(), Importance::Normal) }) .collect(); Arc::new(RwLock::new(PropertyDeclarationBlock { declarations: uncomputed_values, important_count: 0, })).into_strong() } 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: RawServoAnimationValueBorrowed, property: nsCSSPropertyID, buffer: *mut nsAString) { let uncomputed_value = AnimationValue::as_arc(&value).uncompute(); let mut string = String::new(); let rv = PropertyDeclarationBlock { declarations: vec![(uncomputed_value, Importance::Normal)], important_count: 0 }.single_value_to_css(&get_property_id_from_nscsspropertyid!(property, ()), &mut string); debug_assert!(rv.is_ok()); write!(unsafe { &mut *buffer }, "{}", string).expect("Failed to copy string"); } #[no_mangle] pub extern "C" fn Servo_AnimationValue_GetOpacity(value: RawServoAnimationValueBorrowed) -> 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_GetTransform(value: RawServoAnimationValueBorrowed, list: &mut structs::RefPtr) { let value = AnimationValue::as_arc(&value); if let AnimationValue::Transform(ref servo_list) = **value { style_structs::Box::convert_transform(servo_list.0.clone().unwrap(), list); } else { panic!("The AnimationValue should be transform"); } } /// Takes a ServoAnimationValues and populates it with the animation values corresponding /// to a given property declaration block #[no_mangle] pub extern "C" fn Servo_AnimationValues_Populate(anim: RawGeckoAnimationValueListBorrowedMut, declarations: RawServoDeclarationBlockBorrowed, style: ServoComputedValuesBorrowed, parent_style: ServoComputedValuesBorrowedOrNull, pres_context: RawGeckoPresContextBorrowed) { use style::properties::declaration_block::Importance; use style::values::computed::Context; let parent_style = parent_style.as_ref().map(|r| &**ComputedValues::as_arc(&r)); // FIXME this might not be efficient since Populate // is called multiple times. We should precalculate the Context // and share it across Populate calls let style = ComputedValues::as_arc(&style); let declarations = RwLock::::as_arc(&declarations); let guard = declarations.read(); let init = ComputedValues::default_values(pres_context); let context = Context { is_root_element: false, // FIXME (bug 1303229): Use the actual viewport size here viewport_size: Size2D::new(Au(0), Au(0)), inherited_style: parent_style.unwrap_or(&init), style: (**style).clone(), font_metrics_provider: None, }; let mut iter = guard.declarations .iter() .filter_map(|&(ref decl, imp)| { if imp == Importance::Normal { AnimationValue::from_declaration(decl, &context, &init) } else { None } }); let mut geckoiter = anim.iter_mut(); { // we reborrow to scope the consumed mutable borrow // we need to be able to ensure geckoiter is empty later on // and thus can't directly use `geckoiter` let local_geckoiter = &mut geckoiter; for (gecko, servo) in local_geckoiter.zip(&mut iter) { gecko.mValue.mServo.set_arc_leaky(Arc::new(servo)); } } // we should have gone through both iterators if iter.next().is_some() || geckoiter.next().is_some() { error!("stylo: Mismatched sizes of Gecko and Servo \ array during animation value construction"); } } #[no_mangle] pub extern "C" fn Servo_AnimationValue_AddRef(anim: RawServoAnimationValueBorrowed) -> () { unsafe { AnimationValue::addref(anim) }; } #[no_mangle] pub extern "C" fn Servo_AnimationValue_Release(anim: RawServoAnimationValueBorrowed) -> () { unsafe { AnimationValue::release(anim) }; } #[no_mangle] pub extern "C" fn Servo_RestyleWithAddedDeclaration(raw_data: RawServoStyleSetBorrowed, declarations: RawServoDeclarationBlockBorrowed, previous_style: ServoComputedValuesBorrowed) -> ServoComputedValuesStrong { let previous_style = ComputedValues::as_arc(&previous_style); let declarations = RwLock::::as_arc(&declarations); let guard = declarations.read(); let declarations = || { guard.declarations.iter().rev().map(|&(ref decl, _importance)| decl) }; let data = PerDocumentStyleData::from_ffi(raw_data).borrow(); // FIXME (bug 1303229): Use the actual viewport size here let computed = apply_declarations(Size2D::new(Au(0), Au(0)), /* is_root_element = */ false, declarations, previous_style, data.default_computed_values(), None, Box::new(StdoutErrorReporter), None, CascadeFlags::empty()); Arc::new(computed).into_strong() } #[no_mangle] pub extern "C" fn Servo_StyleWorkerThreadCount() -> u32 { *NUM_THREADS as u32 } #[no_mangle] pub extern "C" fn Servo_Element_ClearData(element: RawGeckoElementBorrowed) -> () { GeckoElement(element).clear_data(); } #[no_mangle] pub extern "C" fn Servo_StyleSheet_Empty(mode: SheetParsingMode) -> RawServoStyleSheetStrong { let url = ServoUrl::parse("about:blank").unwrap(); let extra_data = ParserContextExtraData::default(); let origin = match mode { SheetParsingMode::eAuthorSheetFeatures => Origin::Author, SheetParsingMode::eUserSheetFeatures => Origin::User, SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent, }; let sheet = Arc::new(Stylesheet::from_str( "", url, origin, Default::default(), None, Box::new(StdoutErrorReporter), extra_data)); unsafe { mem::transmute(sheet) } } #[no_mangle] pub extern "C" fn Servo_StyleSheet_FromUTF8Bytes(loader: *mut Loader, stylesheet: *mut ServoStyleSheet, data: *const nsACString, mode: SheetParsingMode, base_url: *const nsACString, base: *mut ThreadSafeURIHolder, referrer: *mut ThreadSafeURIHolder, principal: *mut ThreadSafePrincipalHolder) -> RawServoStyleSheetStrong { let input = unsafe { data.as_ref().unwrap().as_str_unchecked() }; let origin = match mode { SheetParsingMode::eAuthorSheetFeatures => Origin::Author, SheetParsingMode::eUserSheetFeatures => Origin::User, SheetParsingMode::eAgentSheetFeatures => Origin::UserAgent, }; let base_str = unsafe { base_url.as_ref().unwrap().as_str_unchecked() }; let url = ServoUrl::parse(base_str).unwrap(); let extra_data = unsafe { ParserContextExtraData { base: Some(GeckoArcURI::new(base)), referrer: Some(GeckoArcURI::new(referrer)), principal: Some(GeckoArcPrincipal::new(principal)), }}; let loader = if loader.is_null() { None } else { Some(StylesheetLoader::new(loader, stylesheet)) }; // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason? let loader: Option<&StyleStylesheetLoader> = match loader { None => None, Some(ref s) => Some(s), }; let sheet = Arc::new(Stylesheet::from_str( input, url, origin, Default::default(), loader, Box::new(StdoutErrorReporter), extra_data)); unsafe { mem::transmute(sheet) } } #[no_mangle] pub extern "C" fn Servo_StyleSheet_ClearAndUpdate(stylesheet: RawServoStyleSheetBorrowed, loader: *mut Loader, gecko_stylesheet: *mut ServoStyleSheet, data: *const nsACString, base: *mut ThreadSafeURIHolder, referrer: *mut ThreadSafeURIHolder, principal: *mut ThreadSafePrincipalHolder) { let input = unsafe { data.as_ref().unwrap().as_str_unchecked() }; let extra_data = unsafe { ParserContextExtraData { base: Some(GeckoArcURI::new(base)), referrer: Some(GeckoArcURI::new(referrer)), principal: Some(GeckoArcPrincipal::new(principal)), }}; let loader = if loader.is_null() { None } else { Some(StylesheetLoader::new(loader, gecko_stylesheet)) }; // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason? let loader: Option<&StyleStylesheetLoader> = match loader { None => None, Some(ref s) => Some(s), }; let sheet = Stylesheet::as_arc(&stylesheet); sheet.rules.write().0.clear(); Stylesheet::update_from_str(&sheet, input, loader, Box::new(StdoutErrorReporter), extra_data); } #[no_mangle] pub extern "C" fn Servo_StyleSet_AppendStyleSheet(raw_data: RawServoStyleSetBorrowed, raw_sheet: RawServoStyleSheetBorrowed, flush: bool) { let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet)); data.stylesheets.push(sheet.clone()); data.stylesheets_changed = true; if flush { data.flush_stylesheets(); } } #[no_mangle] pub extern "C" fn Servo_StyleSet_PrependStyleSheet(raw_data: RawServoStyleSetBorrowed, raw_sheet: RawServoStyleSheetBorrowed, flush: bool) { let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet)); data.stylesheets.insert(0, sheet.clone()); data.stylesheets_changed = true; if flush { data.flush_stylesheets(); } } #[no_mangle] pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(raw_data: RawServoStyleSetBorrowed, raw_sheet: RawServoStyleSheetBorrowed, raw_reference: RawServoStyleSheetBorrowed, flush: bool) { let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); let reference = HasArcFFI::as_arc(&raw_reference); data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet)); let index = data.stylesheets.iter().position(|x| arc_ptr_eq(x, reference)).unwrap(); data.stylesheets.insert(index, sheet.clone()); data.stylesheets_changed = true; if flush { data.flush_stylesheets(); } } #[no_mangle] pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(raw_data: RawServoStyleSetBorrowed, raw_sheet: RawServoStyleSheetBorrowed, flush: bool) { let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let sheet = HasArcFFI::as_arc(&raw_sheet); data.stylesheets.retain(|x| !arc_ptr_eq(x, sheet)); data.stylesheets_changed = true; if flush { data.flush_stylesheets(); } } #[no_mangle] pub extern "C" fn Servo_StyleSet_FlushStyleSheets(raw_data: RawServoStyleSetBorrowed) { let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); data.flush_stylesheets(); } #[no_mangle] pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(raw_data: RawServoStyleSetBorrowed) { let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); data.stylesheets_changed = true; } #[no_mangle] pub extern "C" fn Servo_StyleSheet_HasRules(raw_sheet: RawServoStyleSheetBorrowed) -> bool { !Stylesheet::as_arc(&raw_sheet).rules.read().0.is_empty() } #[no_mangle] pub extern "C" fn Servo_StyleSheet_GetRules(sheet: RawServoStyleSheetBorrowed) -> ServoCssRulesStrong { Stylesheet::as_arc(&sheet).rules.clone().into_strong() } #[no_mangle] pub extern "C" fn Servo_StyleSheet_AddRef(sheet: RawServoStyleSheetBorrowed) -> () { unsafe { Stylesheet::addref(sheet) }; } #[no_mangle] pub extern "C" fn Servo_StyleSheet_Release(sheet: RawServoStyleSheetBorrowed) -> () { unsafe { Stylesheet::release(sheet) }; } #[no_mangle] pub extern "C" fn Servo_CssRules_ListTypes(rules: ServoCssRulesBorrowed, result: nsTArrayBorrowed_uintptr_t) -> () { let rules = RwLock::::as_arc(&rules).read(); let iter = rules.0.iter().map(|rule| rule.rule_type() as usize); let (size, upper) = iter.size_hint(); debug_assert_eq!(size, upper.unwrap()); unsafe { result.set_len(size as u32) }; result.iter_mut().zip(iter).fold((), |_, (r, v)| *r = v); } #[no_mangle] pub extern "C" fn Servo_CssRules_GetStyleRuleAt(rules: ServoCssRulesBorrowed, index: u32) -> RawServoStyleRuleStrong { let rules = RwLock::::as_arc(&rules).read(); match rules.0[index as usize] { CssRule::Style(ref rule) => rule.clone().into_strong(), _ => { unreachable!("GetStyleRuleAt should only be called on a style rule"); } } } #[no_mangle] pub extern "C" fn Servo_CssRules_InsertRule(rules: ServoCssRulesBorrowed, sheet: RawServoStyleSheetBorrowed, rule: *const nsACString, index: u32, nested: bool, rule_type: *mut u16) -> nsresult { let rules = RwLock::::as_arc(&rules); let sheet = Stylesheet::as_arc(&sheet); let rule = unsafe { rule.as_ref().unwrap().as_str_unchecked() }; match rules.write().insert_rule(rule, sheet, index as usize, nested) { Ok(new_rule) => { *unsafe { rule_type.as_mut().unwrap() } = new_rule.rule_type() as u16; nsresult::NS_OK } Err(err) => err.into() } } #[no_mangle] pub extern "C" fn Servo_CssRules_DeleteRule(rules: ServoCssRulesBorrowed, index: u32) -> nsresult { let rules = RwLock::::as_arc(&rules); match rules.write().remove_rule(index as usize) { Ok(_) => nsresult::NS_OK, Err(err) => err.into() } } #[no_mangle] pub extern "C" fn Servo_CssRules_AddRef(rules: ServoCssRulesBorrowed) -> () { unsafe { RwLock::::addref(rules) }; } #[no_mangle] pub extern "C" fn Servo_CssRules_Release(rules: ServoCssRulesBorrowed) -> () { unsafe { RwLock::::release(rules) }; } #[no_mangle] pub extern "C" fn Servo_StyleRule_AddRef(rule: RawServoStyleRuleBorrowed) -> () { unsafe { RwLock::::addref(rule) }; } #[no_mangle] pub extern "C" fn Servo_StyleRule_Release(rule: RawServoStyleRuleBorrowed) -> () { unsafe { RwLock::::release(rule) }; } #[no_mangle] pub extern "C" fn Servo_StyleRule_Debug(rule: RawServoStyleRuleBorrowed, result: *mut nsACString) -> () { let rule = RwLock::::as_arc(&rule); let result = unsafe { result.as_mut().unwrap() }; write!(result, "{:?}", *rule.read()).unwrap(); } #[no_mangle] pub extern "C" fn Servo_StyleRule_GetStyle(rule: RawServoStyleRuleBorrowed) -> RawServoDeclarationBlockStrong { let rule = RwLock::::as_arc(&rule); rule.read().block.clone().into_strong() } #[no_mangle] pub extern "C" fn Servo_StyleRule_SetStyle(rule: RawServoStyleRuleBorrowed, declarations: RawServoDeclarationBlockBorrowed) -> () { let rule = RwLock::::as_arc(&rule); let declarations = RwLock::::as_arc(&declarations); rule.write().block = declarations.clone(); } #[no_mangle] pub extern "C" fn Servo_StyleRule_GetCssText(rule: RawServoStyleRuleBorrowed, result: *mut nsAString) -> () { let rule = RwLock::::as_arc(&rule); rule.read().to_css(unsafe { result.as_mut().unwrap() }).unwrap(); } #[no_mangle] pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowed, result: *mut nsAString) -> () { let rule = RwLock::::as_arc(&rule); rule.read().selectors.to_css(unsafe { result.as_mut().unwrap() }).unwrap(); } #[no_mangle] pub extern "C" fn Servo_ImportRule_AddRef(rule: RawServoImportRuleBorrowed) -> () { unsafe { RwLock::::addref(rule) }; } #[no_mangle] pub extern "C" fn Servo_ImportRule_Release(rule: RawServoImportRuleBorrowed) -> () { unsafe { RwLock::::release(rule) }; } #[no_mangle] pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: ServoComputedValuesBorrowedOrNull, pseudo_tag: *mut nsIAtom, raw_data: RawServoStyleSetBorrowed) -> ServoComputedValuesStrong { let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let atom = Atom::from(pseudo_tag); let pseudo = PseudoElement::from_atom_unchecked(atom, /* anon_box = */ true); let maybe_parent = ComputedValues::arc_from_borrowed(&parent_style_or_null); data.stylist.precomputed_values_for_pseudo(&pseudo, maybe_parent, data.default_computed_values(), false) .values.unwrap() .into_strong() } #[no_mangle] pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed, pseudo_tag: *mut nsIAtom, is_probe: bool, raw_data: RawServoStyleSetBorrowed) -> ServoComputedValuesStrong { let element = GeckoElement(element); let data = unsafe { element.ensure_data() }.borrow_mut(); let doc_data = PerDocumentStyleData::from_ffi(raw_data); // FIXME(bholley): Assert against this. if data.get_styles().is_none() { error!("Calling Servo_ResolvePseudoStyle on unstyled element"); return if is_probe { Strong::null() } else { doc_data.borrow().default_computed_values().clone().into_strong() }; } match get_pseudo_style(element, pseudo_tag, data.styles(), doc_data) { Some(values) => values.into_strong(), None if !is_probe => data.styles().primary.values().clone().into_strong(), None => Strong::null(), } } fn get_pseudo_style(element: GeckoElement, pseudo_tag: *mut nsIAtom, styles: &ElementStyles, doc_data: &PerDocumentStyleData) -> Option> { let pseudo = PseudoElement::from_atom_unchecked(Atom::from(pseudo_tag), false); match SelectorImpl::pseudo_element_cascade_type(&pseudo) { PseudoElementCascadeType::Eager => styles.pseudos.get(&pseudo).map(|s| s.values().clone()), PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"), PseudoElementCascadeType::Lazy => { let d = doc_data.borrow_mut(); let base = styles.primary.values(); d.stylist.lazily_compute_pseudo_element_style(&element, &pseudo, base, &d.default_computed_values()) .map(|s| s.values().clone()) }, } } #[no_mangle] pub extern "C" fn Servo_ComputedValues_Inherit( raw_data: RawServoStyleSetBorrowed, parent_style: ServoComputedValuesBorrowedOrNull) -> ServoComputedValuesStrong { let data = PerDocumentStyleData::from_ffi(raw_data).borrow(); let maybe_arc = ComputedValues::arc_from_borrowed(&parent_style); let style = if let Some(reference) = maybe_arc.as_ref() { ComputedValues::inherit_from(reference, &data.default_computed_values()) } else { data.default_computed_values().clone() }; style.into_strong() } #[no_mangle] pub extern "C" fn Servo_ComputedValues_AddRef(ptr: ServoComputedValuesBorrowed) { unsafe { ComputedValues::addref(ptr) }; } #[no_mangle] pub extern "C" fn Servo_ComputedValues_Release(ptr: ServoComputedValuesBorrowed) { unsafe { ComputedValues::release(ptr) }; } /// See the comment in `Device` to see why it's ok to pass an owned reference to /// the pres context (hint: the context outlives the StyleSet, that holds the /// device alive). #[no_mangle] pub extern "C" fn Servo_StyleSet_Init(pres_context: RawGeckoPresContextOwned) -> RawServoStyleSetOwned { let data = Box::new(PerDocumentStyleData::new(pres_context)); data.into_ffi() } #[no_mangle] pub extern "C" fn Servo_StyleSet_RebuildData(raw_data: RawServoStyleSetBorrowed) { let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); data.reset_device(); } #[no_mangle] pub extern "C" fn Servo_StyleSet_Drop(data: RawServoStyleSetOwned) -> () { let _ = data.into_box::(); } #[no_mangle] pub extern "C" fn Servo_ParseProperty(property: *const nsACString, value: *const nsACString, base_url: *const nsACString, base: *mut ThreadSafeURIHolder, referrer: *mut ThreadSafeURIHolder, principal: *mut ThreadSafePrincipalHolder) -> RawServoDeclarationBlockStrong { let name = unsafe { property.as_ref().unwrap().as_str_unchecked() }; let id = if let Ok(id) = PropertyId::parse(name.into()) { id } else { return RawServoDeclarationBlockStrong::null() }; let value = unsafe { value.as_ref().unwrap().as_str_unchecked() }; let base_str = unsafe { base_url.as_ref().unwrap().as_str_unchecked() }; let base_url = ServoUrl::parse(base_str).unwrap(); let extra_data = unsafe { ParserContextExtraData { base: Some(GeckoArcURI::new(base)), referrer: Some(GeckoArcURI::new(referrer)), principal: Some(GeckoArcPrincipal::new(principal)), }}; let context = ParserContext::new_with_extra_data(Origin::Author, &base_url, Box::new(StdoutErrorReporter), extra_data); let mut results = vec![]; match PropertyDeclaration::parse(id, &context, &mut Parser::new(value), &mut results, false) { PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => {}, _ => return RawServoDeclarationBlockStrong::null(), } Arc::new(RwLock::new(PropertyDeclarationBlock { declarations: results, important_count: 0, })).into_strong() } #[no_mangle] pub extern "C" fn Servo_ParseStyleAttribute(data: *const nsACString) -> RawServoDeclarationBlockStrong { let value = unsafe { data.as_ref().unwrap().as_str_unchecked() }; Arc::new(RwLock::new(GeckoElement::parse_style_attribute(value))).into_strong() } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> RawServoDeclarationBlockStrong { Arc::new(RwLock::new(PropertyDeclarationBlock { declarations: vec![], important_count: 0 })).into_strong() } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_Clone(declarations: RawServoDeclarationBlockBorrowed) -> RawServoDeclarationBlockStrong { let declarations = RwLock::::as_arc(&declarations); Arc::new(RwLock::new(declarations.read().clone())).into_strong() } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_AddRef(declarations: RawServoDeclarationBlockBorrowed) { unsafe { RwLock::::addref(declarations) }; } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_Release(declarations: RawServoDeclarationBlockBorrowed) { unsafe { RwLock::::release(declarations) }; } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_Equals(a: RawServoDeclarationBlockBorrowed, b: RawServoDeclarationBlockBorrowed) -> bool { *RwLock::::as_arc(&a).read() == *RwLock::::as_arc(&b).read() } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_GetCssText(declarations: RawServoDeclarationBlockBorrowed, result: *mut nsAString) { let declarations = RwLock::::as_arc(&declarations); declarations.read().to_css(unsafe { result.as_mut().unwrap() }).unwrap(); } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue( declarations: RawServoDeclarationBlockBorrowed, property_id: nsCSSPropertyID, buffer: *mut nsAString) { let property_id = get_property_id_from_nscsspropertyid!(property_id, ()); let declarations = RwLock::::as_arc(&declarations); let mut string = String::new(); let rv = declarations.read().single_value_to_css(&property_id, &mut string); debug_assert!(rv.is_ok()); write!(unsafe { &mut *buffer }, "{}", string).expect("Failed to copy string"); } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_Count(declarations: RawServoDeclarationBlockBorrowed) -> u32 { let declarations = RwLock::::as_arc(&declarations); declarations.read().declarations.len() as u32 } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(declarations: RawServoDeclarationBlockBorrowed, index: u32, result: *mut nsAString) -> bool { let declarations = RwLock::::as_arc(&declarations); if let Some(&(ref decl, _)) = declarations.read().declarations.get(index as usize) { let result = unsafe { result.as_mut().unwrap() }; decl.id().to_css(result).unwrap(); true } else { false } } macro_rules! get_property_id_from_property { ($property: ident, $ret: expr) => {{ let property = unsafe { $property.as_ref().unwrap().as_str_unchecked() }; match PropertyId::parse(Cow::Borrowed(property)) { Ok(property_id) => property_id, Err(()) => { return $ret; } } }} } fn get_property_value(declarations: RawServoDeclarationBlockBorrowed, property_id: PropertyId, value: *mut nsAString) { let declarations = RwLock::::as_arc(&declarations); declarations.read().property_value_to_css(&property_id, unsafe { value.as_mut().unwrap() }).unwrap(); } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_GetPropertyValue(declarations: RawServoDeclarationBlockBorrowed, property: *const nsACString, value: *mut nsAString) { get_property_value(declarations, get_property_id_from_property!(property, ()), value) } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_GetPropertyValueById(declarations: RawServoDeclarationBlockBorrowed, property: nsCSSPropertyID, value: *mut nsAString) { get_property_value(declarations, get_property_id_from_nscsspropertyid!(property, ()), value) } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(declarations: RawServoDeclarationBlockBorrowed, property: *const nsACString) -> bool { let property_id = get_property_id_from_property!(property, false); let declarations = RwLock::::as_arc(&declarations); declarations.read().property_priority(&property_id).important() } fn set_property(declarations: RawServoDeclarationBlockBorrowed, property_id: PropertyId, value: *mut nsACString, is_important: bool) -> bool { let value = unsafe { value.as_ref().unwrap().as_str_unchecked() }; // FIXME Needs real URL and ParserContextExtraData. let base_url = &*DUMMY_BASE_URL; let extra_data = ParserContextExtraData::default(); if let Ok(decls) = parse_one_declaration(property_id, value, &base_url, Box::new(StdoutErrorReporter), extra_data) { let mut declarations = RwLock::::as_arc(&declarations).write(); let importance = if is_important { Importance::Important } else { Importance::Normal }; for decl in decls.into_iter() { declarations.set_parsed_declaration(decl.0, importance); } true } else { false } } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_SetProperty(declarations: RawServoDeclarationBlockBorrowed, property: *const nsACString, value: *mut nsACString, is_important: bool) -> bool { set_property(declarations, get_property_id_from_property!(property, false), value, is_important) } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_SetPropertyById(declarations: RawServoDeclarationBlockBorrowed, property: nsCSSPropertyID, value: *mut nsACString, is_important: bool) -> bool { set_property(declarations, get_property_id_from_nscsspropertyid!(property, false), value, is_important) } fn remove_property(declarations: RawServoDeclarationBlockBorrowed, property_id: PropertyId) { let declarations = RwLock::::as_arc(&declarations); declarations.write().remove_property(&property_id); } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_RemoveProperty(declarations: RawServoDeclarationBlockBorrowed, property: *const nsACString) { remove_property(declarations, get_property_id_from_property!(property, ())) } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_RemovePropertyById(declarations: RawServoDeclarationBlockBorrowed, property: nsCSSPropertyID) { remove_property(declarations, get_property_id_from_nscsspropertyid!(property, ())) } #[no_mangle] pub extern "C" fn Servo_DeclarationBlock_AddPresValue(declarations: RawServoDeclarationBlockBorrowed, property: nsCSSPropertyID, css_value: nsCSSValueBorrowedMut) { use style::gecko::values::convert_nscolor_to_rgba; use style::properties::{DeclaredValue, LonghandId, PropertyDeclaration, PropertyId, longhands}; use style::values::specified; let declarations = RwLock::::as_arc(&declarations); let prop = PropertyId::from_nscsspropertyid(property); let long = match prop { Ok(PropertyId::Longhand(long)) => long, _ => { error!("stylo: unknown presentation property with id {:?}", property); return } }; let decl = match long { LonghandId::FontSize => { if let Some(int) = css_value.integer() { PropertyDeclaration::FontSize(DeclaredValue::Value( longhands::font_size::SpecifiedValue( specified::LengthOrPercentage::Length( specified::NoCalcLength::from_font_size_int(int as u8) ) ) )) } else { error!("stylo: got unexpected non-integer value for font-size presentation attribute"); return } } LonghandId::Color => { if let Some(color) = css_value.color_value() { PropertyDeclaration::Color(DeclaredValue::Value(Box::new( specified::CSSRGBA { parsed: convert_nscolor_to_rgba(color), authored: None } ))) } else { error!("stylo: got unexpected non-integer value for color presentation attribute"); return } } _ => { error!("stylo: cannot handle longhand {:?} from presentation attribute", long); return } }; declarations.write().declarations.push((decl, Importance::Normal)); } #[no_mangle] pub extern "C" fn Servo_CSSSupports2(property: *const nsACString, value: *const nsACString) -> bool { let property = unsafe { property.as_ref().unwrap().as_str_unchecked() }; let id = if let Ok(id) = PropertyId::parse(property.into()) { id } else { return false }; let value = unsafe { value.as_ref().unwrap().as_str_unchecked() }; let base_url = &*DUMMY_BASE_URL; let extra_data = ParserContextExtraData::default(); match parse_one_declaration(id, &value, &base_url, Box::new(StdoutErrorReporter), extra_data) { Ok(decls) => !decls.is_empty(), Err(()) => false, } } #[no_mangle] pub extern "C" fn Servo_CSSSupports(cond: *const nsACString) -> bool { let condition = unsafe { cond.as_ref().unwrap().as_str_unchecked() }; let mut input = Parser::new(&condition); let cond = parse_condition_or_declaration(&mut input); if let Ok(cond) = cond { let url = ServoUrl::parse("about:blank").unwrap(); let context = ParserContext::new_for_cssom(&url); cond.eval(&context) } else { false } } /// Only safe to call on the main thread, with exclusive access to the element and /// its ancestors. unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut, element: GeckoElement) -> Option<&'a mut RestyleData> { // Don't generate a useless RestyleData if the element hasn't been styled. if !data.has_styles() { return None; } // Propagate the bit up the chain. let mut curr = element; while let Some(parent) = curr.parent_element() { curr = parent; if curr.has_dirty_descendants() { break; } curr.set_dirty_descendants(); } bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0); // Ensure and return the RestyleData. Some(data.ensure_restyle()) } #[no_mangle] pub extern "C" fn Servo_Element_GetSnapshot(element: RawGeckoElementBorrowed) -> *mut structs::ServoElementSnapshot { let element = GeckoElement(element); let snapshot = match element.mutate_data() { None => ptr::null_mut(), Some(mut data) => { if let Some(restyle_data) = unsafe { maybe_restyle(&mut data, element) } { restyle_data.snapshot.ensure(|| element.create_snapshot()).borrow_mut_raw() } else { ptr::null_mut() } }, }; debug!("Servo_Element_GetSnapshot: {:?}: {:?}", element, snapshot); snapshot } #[no_mangle] pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed, restyle_hint: nsRestyleHint, change_hint: nsChangeHint) { let element = GeckoElement(element); let damage = GeckoRestyleDamage::new(change_hint); debug!("Servo_NoteExplicitHints: {:?}, restyle_hint={:?}, change_hint={:?}", element, restyle_hint, change_hint); let mut maybe_data = element.mutate_data(); let maybe_restyle_data = maybe_data.as_mut().and_then(|d| unsafe { maybe_restyle(d, element) }); if let Some(restyle_data) = maybe_restyle_data { let restyle_hint: RestyleHint = restyle_hint.into(); restyle_data.hint.insert(&restyle_hint.into()); restyle_data.damage |= damage; } else { debug!("(Element not styled, discarding hints)"); } } #[no_mangle] pub extern "C" fn Servo_ImportRule_GetSheet(import_rule: RawServoImportRuleBorrowed) -> RawServoStyleSheetStrong { let import_rule = RwLock::::as_arc(&import_rule); unsafe { mem::transmute(import_rule.read().stylesheet.clone()) } } #[no_mangle] pub extern "C" fn Servo_TakeChangeHint(element: RawGeckoElementBorrowed) -> nsChangeHint { let element = GeckoElement(element); let damage = if let Some(mut data) = element.mutate_data() { let d = data.get_restyle().map_or(GeckoRestyleDamage::empty(), |r| r.damage); data.clear_restyle(); d } else { error!("Trying to get change hint from unstyled element"); GeckoRestyleDamage::empty() }; debug!("Servo_TakeChangeHint: {:?}, damage={:?}", element, damage); damage.as_change_hint() } #[no_mangle] pub extern "C" fn Servo_ResolveStyle(element: RawGeckoElementBorrowed, raw_data: RawServoStyleSetBorrowed) -> ServoComputedValuesStrong { let element = GeckoElement(element); debug!("Servo_ResolveStyle: {:?}", element); let data = unsafe { element.ensure_data() }.borrow_mut(); if !data.has_current_styles() { error!("Resolving style on unstyled element with lazy computation forbidden."); let per_doc_data = PerDocumentStyleData::from_ffi(raw_data).borrow(); return per_doc_data.default_computed_values().clone().into_strong(); } data.styles().primary.values().clone().into_strong() } #[no_mangle] pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed, pseudo_tag: *mut nsIAtom, raw_data: RawServoStyleSetBorrowed) -> ServoComputedValuesStrong { let element = GeckoElement(element); let doc_data = PerDocumentStyleData::from_ffi(raw_data); let finish = |styles: &ElementStyles| -> Arc { let maybe_pseudo = if !pseudo_tag.is_null() { get_pseudo_style(element, pseudo_tag, styles, doc_data) } else { None }; maybe_pseudo.unwrap_or_else(|| styles.primary.values().clone()) }; // In the common case we already have the style. Check that before setting // up all the computation machinery. let mut result = element.mutate_data() .and_then(|d| d.get_styles().map(&finish)); if result.is_some() { return result.unwrap().into_strong(); } // We don't have the style ready. Go ahead and compute it as necessary. let shared = create_shared_context(&mut doc_data.borrow_mut()); let mut tlc = ThreadLocalStyleContext::new(&shared); let mut context = StyleContext { shared: &shared, thread_local: &mut tlc, }; let ensure = |el: GeckoElement| { unsafe { el.ensure_data(); } }; let clear = |el: GeckoElement| el.clear_data(); resolve_style(&mut context, element, &ensure, &clear, |styles| result = Some(finish(styles))); result.unwrap().into_strong() } #[no_mangle] pub extern "C" fn Servo_AssertTreeIsClean(root: RawGeckoElementBorrowed) { if !cfg!(debug_assertions) { panic!("Calling Servo_AssertTreeIsClean in release build"); } let root = GeckoElement(root); fn assert_subtree_is_clean<'le>(el: GeckoElement<'le>) { debug_assert!(!el.has_dirty_descendants()); for child in el.as_node().children() { if let Some(child) = child.as_element() { assert_subtree_is_clean(child); } } } assert_subtree_is_clean(root); } #[no_mangle] pub extern "C" fn Servo_StyleSet_FillKeyframesForName(raw_data: RawServoStyleSetBorrowed, name: *const nsACString, timing_function: *const nsTimingFunction, style: ServoComputedValuesBorrowed, keyframes: RawGeckoKeyframeListBorrowedMut) -> bool { let data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut(); let name = unsafe { Atom::from(name.as_ref().unwrap().as_str_unchecked()) }; let style_timing_function = unsafe { timing_function.as_ref().unwrap() }; let style = ComputedValues::as_arc(&style); if let Some(ref animation) = data.stylist.animations().get(&name) { for step in &animation.steps { // Override timing_function if the keyframe has animation-timing-function. let timing_function = if let Some(val) = step.get_animation_timing_function() { val.into() } else { *style_timing_function }; let keyframe = unsafe { Gecko_AnimationAppendKeyframe(keyframes, step.start_percentage.0 as f32, &timing_function) }; match step.value { KeyframesStepValue::ComputedValues => { for (index, property) in animation.properties_changed.iter().enumerate() { let block = style.to_declaration_block(property.clone().into()); unsafe { (*keyframe).mPropertyValues.set_len((index + 1) as u32); (*keyframe).mPropertyValues[index].mProperty = property.clone().into(); (*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky( Arc::new(RwLock::new(block))); } } }, KeyframesStepValue::Declarations { ref block } => { let guard = block.read(); // Filter out non-animatable properties. let animatable = guard.declarations .iter() .filter(|&&(ref declaration, _)| { declaration.is_animatable() }); for (index, &(ref declaration, _)) in animatable.enumerate() { unsafe { (*keyframe).mPropertyValues.set_len((index + 1) as u32); (*keyframe).mPropertyValues[index].mProperty = TransitionProperty::from_declaration(declaration).unwrap().into(); (*keyframe).mPropertyValues[index].mServoDeclarationBlock.set_arc_leaky( Arc::new(RwLock::new( PropertyDeclarationBlock { declarations: vec![ (declaration.clone(), Importance::Normal) ], important_count: 0 }))); } } }, } } return true } false }