diff options
47 files changed, 892 insertions, 910 deletions
diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index cc2585c3043..08fad71b4ea 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -1489,7 +1489,7 @@ impl Fragment { state.add_display_item(DisplayItem::Rectangle(CommonDisplayItem::new( base, webrender_api::RectangleDisplayItem { - color: self.style().get_color().color.to_layout(), + color: self.style().get_inherited_text().color.to_layout(), }, ))); } @@ -1967,9 +1967,9 @@ impl Fragment { // TODO(emilio): Allow changing more properties by ::selection // Paint the text with the color as described in its styling. let text_color = if text_fragment.selected() { - self.selected_style().get_color().color + self.selected_style().get_inherited_text().color } else { - self.style().get_color().color + self.style().get_inherited_text().color }; // Determine the orientation and cursor to use. diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index d3e9dbb6779..72029fb2484 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -1519,7 +1519,7 @@ impl Fragment { } pub fn color(&self) -> Color { - self.style().get_color().color + self.style().get_inherited_text().color } /// Returns the text decoration line of this fragment, according to the style of the nearest ancestor diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index d738ef1151f..a0c1a8b7a61 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -715,6 +715,14 @@ impl<'le> TElement for ServoLayoutElement<'le> { .map(ServoShadowRoot::from_layout_js) } } + + fn local_name(&self) -> &LocalName { + self.element.local_name() + } + + fn namespace(&self) -> &Namespace { + self.element.namespace() + } } impl<'le> PartialEq for ServoLayoutElement<'le> { @@ -879,13 +887,19 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } #[inline] - fn local_name(&self) -> &LocalName { - self.element.local_name() + fn has_local_name(&self, name: &LocalName) -> bool { + self.element.local_name() == name } #[inline] - fn namespace(&self) -> &Namespace { - self.element.namespace() + fn has_namespace(&self, ns: &Namespace) -> bool { + self.element.namespace() == ns + } + + #[inline] + fn is_same_type(&self, other: &Self) -> bool { + self.element.local_name() == other.element.local_name() && + self.element.namespace() == other.element.namespace() } fn is_pseudo_element(&self) -> bool { @@ -1266,8 +1280,8 @@ where loop { let next_node = if let Some(ref node) = current_node { if let Some(element) = node.as_element() { - if element.local_name() == &local_name!("summary") && - element.namespace() == &ns!(html) + if element.has_local_name(&local_name!("summary")) && + element.has_namespace(&ns!(html)) { self.current_node = None; return Some(node.clone()); @@ -1286,8 +1300,10 @@ where let node = self.current_node.clone(); let node = node.and_then(|node| { if node.is_element() && - node.as_element().unwrap().local_name() == &local_name!("summary") && - node.as_element().unwrap().namespace() == &ns!(html) + node.as_element() + .unwrap() + .has_local_name(&local_name!("summary")) && + node.as_element().unwrap().has_namespace(&ns!(html)) { unsafe { node.dangerous_next_sibling() } } else { @@ -1437,13 +1453,19 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { } #[inline] - fn local_name(&self) -> &LocalName { - self.element.local_name() + fn has_local_name(&self, name: &LocalName) -> bool { + self.element.local_name() == name } #[inline] - fn namespace(&self) -> &Namespace { - self.element.namespace() + fn has_namespace(&self, ns: &Namespace) -> bool { + self.element.namespace() == ns + } + + #[inline] + fn is_same_type(&self, other: &Self) -> bool { + self.element.local_name() == other.element.local_name() && + self.element.namespace() == other.element.namespace() } fn match_pseudo_element( diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 2a26a1c5752..039ff1039f9 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -260,7 +260,7 @@ impl CanvasState { match canvas_element.style() { Some(ref s) if canvas_element.has_css_layout_box() => { - Ok(s.get_color().color) + Ok(s.get_inherited_text().color) }, _ => Ok(RGBA::new(0, 0, 0, 255)), } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 66560172517..764c7ce6051 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -2982,12 +2982,17 @@ impl<'a> SelectorsElement for DomRoot<Element> { }) } - fn local_name(&self) -> &LocalName { - Element::local_name(self) + fn has_local_name(&self, local_name: &LocalName) -> bool { + Element::local_name(self) == local_name } - fn namespace(&self) -> &Namespace { - Element::namespace(self) + fn has_namespace(&self, ns: &Namespace) -> bool { + Element::namespace(self) == ns + } + + fn is_same_type(&self, other: &Self) -> bool { + Element::local_name(self) == Element::local_name(other) && + Element::namespace(self) == Element::namespace(other) } fn match_non_ts_pseudo_class<F>( diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 0e1df2ac4c8..ae559474d06 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -391,7 +391,7 @@ pub trait ThreadSafeLayoutElement: #[inline] fn get_details_summary_pseudo(&self) -> Option<Self> { - if self.local_name() == &local_name!("details") && self.namespace() == &ns!(html) { + if self.has_local_name(&local_name!("details")) && self.has_namespace(&ns!(html)) { Some(self.with_pseudo(PseudoElementType::DetailsSummary)) } else { None @@ -400,8 +400,8 @@ pub trait ThreadSafeLayoutElement: #[inline] fn get_details_content_pseudo(&self) -> Option<Self> { - if self.local_name() == &local_name!("details") && - self.namespace() == &ns!(html) && + if self.has_local_name(&local_name!("details")) && + self.has_namespace(&ns!(html)) && self.get_attr(&ns!(), &local_name!("open")).is_some() { Some(self.with_pseudo(PseudoElementType::DetailsContent)) diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 6554f2f9cc4..45acc13e7e1 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -596,7 +596,7 @@ where &local_name.lower_name, ) .borrow(); - element.local_name() == name + element.has_local_name(name) } /// Determines whether the given element matches the given compound selector. @@ -681,11 +681,11 @@ where Component::LocalName(ref local_name) => matches_local_name(element, local_name), Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true, Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => { - element.namespace() == url.borrow() + element.has_namespace(&url.borrow()) }, Component::ExplicitNoNamespace => { let ns = crate::parser::namespace_empty_string::<E::Impl>(); - element.namespace() == ns.borrow() + element.has_namespace(&ns.borrow()) }, Component::ID(ref id) => { element.has_id(id, context.shared.classes_and_ids_case_sensitivity()) @@ -898,11 +898,6 @@ where } #[inline] -fn same_type<E: Element>(a: &E, b: &E) -> bool { - a.local_name() == b.local_name() && a.namespace() == b.namespace() -} - -#[inline] fn nth_child_index<E>( element: &E, is_of_type: bool, @@ -924,7 +919,7 @@ where let mut curr = element.clone(); while let Some(e) = curr.prev_sibling_element() { curr = e; - if !is_of_type || same_type(element, &curr) { + if !is_of_type || element.is_same_type(&curr) { if let Some(i) = c.lookup(curr.opaque()) { return i - index; } @@ -945,7 +940,7 @@ where }; while let Some(e) = next(curr) { curr = e; - if !is_of_type || same_type(element, &curr) { + if !is_of_type || element.is_same_type(&curr) { // If we're computing indices from the left, check each element in the // cache. We handle the indices-from-the-right case at the top of this // function. diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index 611e40d6027..f892abd4e94 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -62,10 +62,13 @@ pub trait Element: Sized + Clone + Debug { fn is_html_element_in_html_document(&self) -> bool; - fn local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName; + fn has_local_name(&self, local_name: &<Self::Impl as SelectorImpl>::BorrowedLocalName) -> bool; /// Empty string for no namespace - fn namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl; + fn has_namespace(&self, ns: &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl) -> bool; + + /// Whether this element and the `other` element have the same local name and namespace. + fn is_same_type(&self, other: &Self) -> bool; fn attr_matches( &self, diff --git a/components/servo_arc/Cargo.toml b/components/servo_arc/Cargo.toml index 58f1ea8ef0b..3581210c2f1 100644 --- a/components/servo_arc/Cargo.toml +++ b/components/servo_arc/Cargo.toml @@ -12,6 +12,7 @@ path = "lib.rs" [features] servo = ["serde"] +gecko_refcount_logging = [] [dependencies] nodrop = {version = "0.1.8"} diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index 09f381c62df..aa79140e044 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -42,7 +42,7 @@ use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{ExactSizeIterator, Iterator}; use std::marker::PhantomData; -use std::mem; +use std::mem::{self, align_of, size_of}; use std::ops::{Deref, DerefMut}; use std::os::raw::c_void; use std::process; @@ -90,6 +90,9 @@ const STATIC_REFCOUNT: usize = usize::MAX; /// usage of PhantomData. /// /// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html +/// +/// cbindgen:derive-eq=false +/// cbindgen:derive-neq=false #[repr(C)] pub struct Arc<T: ?Sized> { p: ptr::NonNull<ArcInner<T>>, @@ -112,7 +115,10 @@ pub struct Arc<T: ?Sized> { /// Once the mutation is finished, you can call `.shareable()` and get a regular `Arc` /// out of it. /// -/// ```rust +/// Ignore the doctest below there's no way to skip building with refcount +/// logging during doc tests (see rust-lang/rust#45599). +/// +/// ```rust,ignore /// # use servo_arc::UniqueArc; /// let data = [1, 2, 3, 4, 5]; /// let mut x = UniqueArc::new(data); @@ -166,18 +172,35 @@ impl<T> Arc<T> { /// Construct an `Arc<T>` #[inline] pub fn new(data: T) -> Self { - let x = Box::new(ArcInner { + let ptr = Box::into_raw(Box::new(ArcInner { count: atomic::AtomicUsize::new(1), data, - }); + })); + + #[cfg(feature = "gecko_refcount_logging")] + unsafe { + // FIXME(emilio): Would be so amazing to have + // std::intrinsics::type_name() around, so that we could also report + // a real size. + NS_LogCtor(ptr as *const _, b"ServoArc\0".as_ptr() as *const _, 8); + } + unsafe { Arc { - p: ptr::NonNull::new_unchecked(Box::into_raw(x)), + p: ptr::NonNull::new_unchecked(ptr), phantom: PhantomData, } } } + /// Construct an intentionally-leaked arc. + #[inline] + pub fn new_leaked(data: T) -> Self { + let arc = Self::new(data); + arc.mark_as_intentionally_leaked(); + arc + } + /// Convert the Arc<T> to a raw pointer, suitable for use across FFI /// /// Note: This returns a pointer to the data T, which is offset in the allocation. @@ -287,9 +310,33 @@ impl<T: ?Sized> Arc<T> { unsafe { &*self.ptr() } } - // Non-inlined part of `drop`. Just invokes the destructor. + #[inline(always)] + fn record_drop(&self) { + #[cfg(feature = "gecko_refcount_logging")] + unsafe { + NS_LogDtor( + self.ptr() as *const _, + b"ServoArc\0".as_ptr() as *const _, + 8, + ); + } + } + + /// Marks this `Arc` as intentionally leaked for the purposes of refcount + /// logging. + /// + /// It's a logic error to call this more than once, but it's not unsafe, as + /// it'd just report negative leaks. + #[inline(always)] + pub fn mark_as_intentionally_leaked(&self) { + self.record_drop(); + } + + // Non-inlined part of `drop`. Just invokes the destructor and calls the + // refcount logging machinery if enabled. #[inline(never)] unsafe fn drop_slow(&mut self) { + self.record_drop(); let _ = Box::from_raw(self.ptr()); } @@ -305,6 +352,20 @@ impl<T: ?Sized> Arc<T> { } } +#[cfg(feature = "gecko_refcount_logging")] +extern "C" { + fn NS_LogCtor( + aPtr: *const std::os::raw::c_void, + aTypeName: *const std::os::raw::c_char, + aSize: u32, + ); + fn NS_LogDtor( + aPtr: *const std::os::raw::c_void, + aTypeName: *const std::os::raw::c_char, + aSize: u32, + ); +} + impl<T: ?Sized> Clone for Arc<T> { #[inline] fn clone(&self) -> Self { @@ -609,7 +670,6 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> { F: FnOnce(Layout) -> *mut u8, I: Iterator<Item = T> + ExactSizeIterator, { - use std::mem::{align_of, size_of}; assert_ne!(size_of::<T>(), 0, "Need to think about ZST"); let inner_align = align_of::<ArcInner<HeaderSlice<H, [T; 0]>>>(); @@ -697,6 +757,15 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> { ); } + #[cfg(feature = "gecko_refcount_logging")] + unsafe { + if !is_static { + // FIXME(emilio): Would be so amazing to have + // std::intrinsics::type_name() around. + NS_LogCtor(ptr as *const _, b"ServoArc\0".as_ptr() as *const _, 8) + } + } + // Return the fat Arc. assert_eq!( size_of::<Self>(), @@ -1248,11 +1317,18 @@ impl<A, B> ArcUnion<A, B> { } /// Returns true if the two values are pointer-equal. + #[inline] pub fn ptr_eq(this: &Self, other: &Self) -> bool { this.p == other.p } + #[inline] + pub fn ptr(&self) -> ptr::NonNull<()> { + self.p + } + /// Returns an enum representing a borrow of either A or B. + #[inline] pub fn borrow(&self) -> ArcUnionBorrow<A, B> { if self.is_first() { let ptr = self.p.as_ptr() as *const A; diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 4d5f3e2e94d..05798b83bcb 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -22,6 +22,7 @@ servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5eve "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union", "servo_url", "string_cache", "crossbeam-channel", "to_shmem/servo", "servo_arc/servo"] gecko_debug = [] +gecko_refcount_logging = [] gecko_profiler = [] [dependencies] diff --git a/components/style/bloom.rs b/components/style/bloom.rs index d75d548ff71..c730547986b 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -19,7 +19,7 @@ thread_local! { /// such that they can be reused across style traversals. StyleBloom is responsible /// for ensuring that the bloom filter is zeroed when it is dropped. static BLOOM_KEY: Arc<AtomicRefCell<BloomFilter>> = - Arc::new(AtomicRefCell::new(BloomFilter::new())); + Arc::new_leaked(AtomicRefCell::new(BloomFilter::new())); } /// A struct that allows us to fast-reject deep descendant selectors avoiding diff --git a/components/style/dom.rs b/components/style/dom.rs index 5ee8443b91d..04d6a79b384 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -882,6 +882,13 @@ pub trait TElement: hints: &mut V, ) where V: Push<ApplicableDeclarationBlock>; + + /// Returns element's local name. + fn local_name(&self) -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedLocalName; + + /// Returns element's namespace. + fn namespace(&self) + -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedNamespaceUrl; } /// TNode and TElement aren't Send because we want to be careful and explicit diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 655c8846f76..c8e7e65785b 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -55,7 +55,7 @@ impl OneOrMoreSeparated for Source { #[repr(u8)] #[allow(missing_docs)] pub enum FontFaceSourceListComponent { - Url(*const crate::gecko_bindings::structs::mozilla::css::URLValue), + Url(*const crate::gecko::url::CssUrl), Local(*mut crate::gecko_bindings::structs::nsAtom), FormatHint { length: usize, diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 06e201bf2b5..0be8a81e66a 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -122,11 +122,11 @@ impl nsStyleImage { match image { GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient), GenericImage::Url(ref url) => unsafe { - bindings::Gecko_SetLayerImageImageValue(self, url.url_value_ptr()) + bindings::Gecko_SetLayerImageImageValue(self, url); }, GenericImage::Rect(ref image_rect) => { unsafe { - bindings::Gecko_SetLayerImageImageValue(self, image_rect.url.url_value_ptr()); + bindings::Gecko_SetLayerImageImageValue(self, &image_rect.url); bindings::Gecko_InitializeImageCropRect(self); // Set CropRect @@ -584,9 +584,10 @@ pub mod basic_shape { impl<'a> From<&'a StyleShapeSource> for ClippingShape { fn from(other: &'a StyleShapeSource) -> Self { - use crate::values::generics::image::Image as GenericImage; match other.mType { StyleShapeSourceType::Image => unsafe { + use crate::values::generics::image::Image as GenericImage; + let shape_image = &*other.__bindgen_anon_1.mShapeImage.as_ref().mPtr; let image = shape_image.into_image().expect("Cannot convert to Image"); match image { diff --git a/components/style/gecko/url.rs b/components/style/gecko/url.rs index dbbb3399da7..3a53f639afe 100644 --- a/components/style/gecko/url.rs +++ b/components/style/gecko/url.rs @@ -5,13 +5,11 @@ //! Common handling for the specified value CSS url() values. use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::root::mozilla::css::URLValue; -use crate::gecko_bindings::structs::root::mozilla::CORSMode; -use crate::gecko_bindings::structs::root::nsStyleImageRequest; -use crate::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI}; +use crate::gecko_bindings::structs; +use crate::gecko_bindings::structs::nsStyleImageRequest; use crate::gecko_bindings::sugar::refptr::RefPtr; use crate::parser::{Parse, ParserContext}; -use crate::stylesheets::UrlExtraData; +use crate::stylesheets::{CorsMode, UrlExtraData}; use crate::values::computed::{Context, ToComputedValue}; use cssparser::Parser; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; @@ -27,25 +25,63 @@ use to_shmem::{SharedMemoryBuilder, ToShmem}; /// A CSS url() value for gecko. #[css(function = "url")] #[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +#[repr(C)] pub struct CssUrl(pub Arc<CssUrlData>); /// Data shared between CssUrls. -#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] +/// +/// cbindgen:derive-eq=false +/// cbindgen:derive-neq=false +#[derive(Debug, SpecifiedValueInfo, ToCss, ToShmem)] +#[repr(C)] pub struct CssUrlData { /// The URL in unresolved string form. - serialization: String, + serialization: crate::OwnedStr, /// The URL extra data. #[css(skip)] pub extra_data: UrlExtraData, + + /// The CORS mode that will be used for the load. + #[css(skip)] + cors_mode: CorsMode, + + /// Data to trigger a load from Gecko. This is mutable in C++. + /// + /// TODO(emilio): Maybe we can eagerly resolve URLs and make this immutable? + #[css(skip)] + load_data: LoadDataSource, +} + +impl PartialEq for CssUrlData { + fn eq(&self, other: &Self) -> bool { + self.serialization == other.serialization && + self.extra_data == other.extra_data && + self.cors_mode == other.cors_mode + } } impl CssUrl { + fn parse_with_cors_mode<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + cors_mode: CorsMode, + ) -> Result<Self, ParseError<'i>> { + let url = input.expect_url()?; + Ok(Self::parse_from_string( + url.as_ref().to_owned(), + context, + cors_mode, + )) + } + /// Parse a URL from a string value that is a valid CSS token for a URL. - pub fn parse_from_string(url: String, context: &ParserContext) -> Self { + pub fn parse_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self { CssUrl(Arc::new(CssUrlData { - serialization: url, + serialization: url.into(), extra_data: context.url_data.clone(), + cors_mode, + load_data: LoadDataSource::Owned(LoadData::default()), })) } @@ -85,27 +121,12 @@ impl CssUrlData { } } -#[cfg(debug_assertions)] -impl Drop for CssUrlData { - fn drop(&mut self) { - assert!( - !URL_VALUE_TABLE - .read() - .unwrap() - .contains_key(&CssUrlDataKey(self as *mut _ as *const _)), - "All CssUrlData objects used as keys in URL_VALUE_TABLE should be \ - from shared memory style sheets, and so should never be dropped", - ); - } -} - impl Parse for CssUrl { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { - let url = input.expect_url()?; - Ok(Self::parse_from_string(url.as_ref().to_owned(), context)) + Self::parse_with_cors_mode(context, input, CorsMode::None) } } @@ -122,143 +143,101 @@ impl MallocSizeOf for CssUrl { } } -/// A key type for URL_VALUE_TABLE. +/// A key type for LOAD_DATA_TABLE. #[derive(Eq, Hash, PartialEq)] -struct CssUrlDataKey(*const CssUrlData); - -unsafe impl Sync for CssUrlDataKey {} -unsafe impl Send for CssUrlDataKey {} - -/// The source of a Gecko URLValue object for a SpecifiedUrl. -#[derive(Clone, Debug)] -pub enum URLValueSource { - /// A strong reference to a Gecko URLValue object. - URLValue(RefPtr<URLValue>), - /// A CORSMode value used to lazily construct a Gecko URLValue object. - /// - /// The lazily created object will be stored in URL_VALUE_TABLE. - CORSMode(CORSMode), +struct LoadDataKey(*const LoadDataSource); + +unsafe impl Sync for LoadDataKey {} +unsafe impl Send for LoadDataKey {} + +/// The load data for a given URL. This is mutable from C++, for now at least. +#[repr(C)] +#[derive(Debug)] +pub struct LoadData { + resolved: RefPtr<structs::nsIURI>, + load_id: u64, + tried_to_resolve: bool, } -impl ToShmem for URLValueSource { - fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { - ManuallyDrop::new(match self { - URLValueSource::URLValue(r) => URLValueSource::CORSMode(r.mCORSMode), - URLValueSource::CORSMode(c) => URLValueSource::CORSMode(*c), - }) +impl Drop for LoadData { + fn drop(&mut self) { + if self.load_id != 0 { + unsafe { + bindings::Gecko_LoadData_DeregisterLoad(self); + } + } } } -/// A specified non-image `url()` value. -#[derive(Clone, Debug, SpecifiedValueInfo, ToCss, ToShmem)] -pub struct SpecifiedUrl { - /// The specified url value. - pub url: CssUrl, - /// Gecko's URLValue so that we can reuse it while rematching a - /// property with this specified value. - /// - /// Box this to avoid SpecifiedUrl getting bigger than two words, - /// and increasing the size of PropertyDeclaration. - #[css(skip)] - url_value: Box<URLValueSource>, -} - -fn make_url_value(url: &CssUrl, cors_mode: CORSMode) -> RefPtr<URLValue> { - unsafe { - let ptr = bindings::Gecko_URLValue_Create(url.0.clone().into_strong(), cors_mode); - // We do not expect Gecko_URLValue_Create returns null. - debug_assert!(!ptr.is_null()); - RefPtr::from_addrefed(ptr) +impl Default for LoadData { + fn default() -> Self { + Self { + resolved: RefPtr::null(), + load_id: 0, + tried_to_resolve: false, + } } } -impl SpecifiedUrl { - /// Parse a URL from a string value. - pub fn parse_from_string(url: String, context: &ParserContext) -> Self { - Self::from_css_url(CssUrl::parse_from_string(url, context)) - } - - fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self { - let url_value = Box::new(URLValueSource::URLValue(make_url_value(&url, cors))); - Self { url, url_value } - } +/// The data for a load, or a lazy-loaded, static member that will be stored in +/// LOAD_DATA_TABLE, keyed by the memory location of this object, which is +/// always in the heap because it's inside the CssUrlData object. +/// +/// This type is meant not to be used from C++ so we don't derive helper +/// methods. +/// +/// cbindgen:derive-helper-methods=false +#[derive(Debug)] +#[repr(u8, C)] +pub enum LoadDataSource { + /// An owned copy of the load data. + Owned(LoadData), + /// A lazily-resolved copy of it. + Lazy, +} - fn from_css_url(url: CssUrl) -> Self { - use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE; - Self::from_css_url_with_cors(url, CORSMode_CORS_NONE) - } +impl LoadDataSource { + /// Gets the load data associated with the source. + /// + /// This relies on the source on being in a stable location if lazy. + #[inline] + pub unsafe fn get(&self) -> *const LoadData { + match *self { + LoadDataSource::Owned(ref d) => return d, + LoadDataSource::Lazy => {}, + } - fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self { - use crate::gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS; - Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS) - } + let key = LoadDataKey(self); - fn with_url_value<F, T>(&self, f: F) -> T - where - F: FnOnce(&RefPtr<URLValue>) -> T, - { - match *self.url_value { - URLValueSource::URLValue(ref r) => f(r), - URLValueSource::CORSMode(cors_mode) => { - { - let guard = URL_VALUE_TABLE.read().unwrap(); - if let Some(r) = guard.get(&(CssUrlDataKey(&*self.url.0 as *const _))) { - return f(r); - } - } - let mut guard = URL_VALUE_TABLE.write().unwrap(); - let r = guard - .entry(CssUrlDataKey(&*self.url.0 as *const _)) - .or_insert_with(|| make_url_value(&self.url, cors_mode)); - f(r) - }, + { + let guard = LOAD_DATA_TABLE.read().unwrap(); + if let Some(r) = guard.get(&key) { + return &**r; + } } + let mut guard = LOAD_DATA_TABLE.write().unwrap(); + let r = guard.entry(key).or_insert_with(Default::default); + &**r } +} - /// Clone a new, strong reference to the Gecko URLValue. - pub fn clone_url_value(&self) -> RefPtr<URLValue> { - self.with_url_value(RefPtr::clone) - } - - /// Get a raw pointer to the URLValue held by this SpecifiedUrl, for FFI. - pub fn url_value_ptr(&self) -> *mut URLValue { - self.with_url_value(RefPtr::get) +impl ToShmem for LoadDataSource { + fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { + ManuallyDrop::new(match self { + LoadDataSource::Owned(..) => LoadDataSource::Lazy, + LoadDataSource::Lazy => LoadDataSource::Lazy, + }) } } -/// Clears URL_VALUE_TABLE. Entries in this table, which are for specified URL +/// A specified non-image `url()` value. +pub type SpecifiedUrl = CssUrl; + +/// Clears LOAD_DATA_TABLE. Entries in this table, which are for specified URL /// values that come from shared memory style sheets, would otherwise persist /// until the end of the process and be reported as leaks. pub fn shutdown() { - URL_VALUE_TABLE.write().unwrap().clear(); -} - -impl Parse for SpecifiedUrl { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - CssUrl::parse(context, input).map(Self::from_css_url) - } -} - -impl PartialEq for SpecifiedUrl { - fn eq(&self, other: &Self) -> bool { - self.url.eq(&other.url) - } -} - -impl Eq for SpecifiedUrl {} - -impl MallocSizeOf for SpecifiedUrl { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.url.size_of(ops); - // Although this is a RefPtr, this is the primary reference because - // SpecifiedUrl is responsible for creating the url_value. So we - // measure unconditionally here. - n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value_ptr()) }; - n - } + LOAD_DATA_TABLE.write().unwrap().clear(); } impl ToComputedValue for SpecifiedUrl { @@ -281,8 +260,8 @@ pub struct SpecifiedImageUrl(pub SpecifiedUrl); impl SpecifiedImageUrl { /// Parse a URL from a string value that is a valid CSS token for a URL. - pub fn parse_from_string(url: String, context: &ParserContext) -> Self { - SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context)) + pub fn parse_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self { + SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context, cors_mode)) } /// Provides an alternate method for parsing that associates the URL @@ -291,9 +270,11 @@ impl SpecifiedImageUrl { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { - CssUrl::parse(context, input) - .map(SpecifiedUrl::from_css_url_with_cors_anonymous) - .map(SpecifiedImageUrl) + Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode( + context, + input, + CorsMode::Anonymous, + )?)) } } @@ -320,99 +301,68 @@ impl ToComputedValue for SpecifiedImageUrl { } } -fn serialize_computed_url<W>( - url_value: &URLValue, - dest: &mut CssWriter<W>, - get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString), -) -> fmt::Result -where - W: Write, -{ - dest.write_str("url(")?; - unsafe { - let mut string = nsCString::new(); - get_url(url_value, &mut string); - string.as_str_unchecked().to_css(dest)?; - } - dest.write_char(')') -} - /// The computed value of a CSS non-image `url()`. /// /// The only difference between specified and computed URLs is the /// serialization. #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] +#[repr(C)] pub struct ComputedUrl(pub SpecifiedUrl); -impl ToCss for ComputedUrl { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result +impl ComputedUrl { + fn serialize_with<W>( + &self, + function: unsafe extern "C" fn(*const Self, *mut nsCString), + dest: &mut CssWriter<W>, + ) -> fmt::Result where W: Write, { - self.0 - .with_url_value(|r| serialize_computed_url(r, dest, bindings::Gecko_GetComputedURLSpec)) + dest.write_str("url(")?; + unsafe { + let mut string = nsCString::new(); + function(self, &mut string); + string.as_str_unchecked().to_css(dest)?; + } + dest.write_char(')') } } -impl ComputedUrl { - /// Convert from RefPtr<URLValue> to ComputedUrl. - pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self { - let css_url = &*url_value.mCssUrl.mRawPtr; - let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc()); - ComputedUrl(SpecifiedUrl { - url, - url_value: Box::new(URLValueSource::URLValue(url_value)), - }) - } - - /// Clone a new, strong reference to the Gecko URLValue. - pub fn clone_url_value(&self) -> RefPtr<URLValue> { - self.0.clone_url_value() - } - - /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI. - pub fn url_value_ptr(&self) -> *mut URLValue { - self.0.url_value_ptr() +impl ToCss for ComputedUrl { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + self.serialize_with(bindings::Gecko_GetComputedURLSpec, dest) } } /// The computed value of a CSS image `url()`. #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] +#[repr(transparent)] pub struct ComputedImageUrl(pub ComputedUrl); +impl ComputedImageUrl { + /// Convert from nsStyleImageRequest to ComputedImageUrl. + pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self { + image_request.mImageURL.clone() + } +} + impl ToCss for ComputedImageUrl { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write, { - (self.0).0.with_url_value(|r| { - serialize_computed_url(r, dest, bindings::Gecko_GetComputedImageURLSpec) - }) - } -} - -impl ComputedImageUrl { - /// Convert from nsStyleImageReques to ComputedImageUrl. - pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self { - let url_value = image_request.mImageValue.to_safe(); - ComputedImageUrl(ComputedUrl::from_url_value(url_value)) - } - - /// Clone a new, strong reference to the Gecko URLValue. - pub fn clone_url_value(&self) -> RefPtr<URLValue> { - self.0.clone_url_value() - } - - /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI. - pub fn url_value_ptr(&self) -> *mut URLValue { - self.0.url_value_ptr() + self.0 + .serialize_with(bindings::Gecko_GetComputedImageURLSpec, dest) } } lazy_static! { - /// A table mapping CssUrlData objects to their lazily created Gecko - /// URLValue objects. - static ref URL_VALUE_TABLE: RwLock<HashMap<CssUrlDataKey, RefPtr<URLValue>>> = { + /// A table mapping CssUrlData objects to their lazily created LoadData + /// objects. + static ref LOAD_DATA_TABLE: RwLock<HashMap<LoadDataKey, Box<LoadData>>> = { Default::default() }; } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index a1443d1ad8d..3ce9e6fe6ab 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -704,9 +704,8 @@ impl<'le> GeckoElement<'le> { .map(GeckoElement) } } else { - let binding_parent = unsafe { self.non_xul_xbl_binding_parent_raw_content().as_ref() } - .map(GeckoNode::from_content) - .and_then(|n| n.as_element()); + let binding_parent = + unsafe { self.non_xul_xbl_binding_parent().as_ref() }.map(GeckoElement); debug_assert!( binding_parent == @@ -721,11 +720,10 @@ impl<'le> GeckoElement<'le> { } #[inline] - fn non_xul_xbl_binding_parent_raw_content(&self) -> *mut nsIContent { + fn non_xul_xbl_binding_parent(&self) -> *mut RawGeckoElement { debug_assert!(!self.is_xul_element()); - self.extended_slots().map_or(ptr::null_mut(), |slots| { - slots._base.mBindingParent.raw::<nsIContent>() - }) + self.extended_slots() + .map_or(ptr::null_mut(), |slots| slots._base.mBindingParent.mRawPtr) } #[inline] @@ -1168,6 +1166,19 @@ impl<'le> TElement for GeckoElement<'le> { self.namespace_id() == structs::root::kNameSpaceID_XUL as i32 } + #[inline] + fn local_name(&self) -> &WeakAtom { + unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) } + } + + #[inline] + fn namespace(&self) -> &WeakNamespace { + unsafe { + let namespace_manager = structs::nsContentUtils_sNameSpaceManager; + WeakNamespace::new((*namespace_manager).mURIArray[self.namespace_id() as usize].mRawPtr) + } + } + /// Return the list of slotted nodes of this node. #[inline] fn slotted_nodes(&self) -> &[Self::ConcreteNode] { @@ -1736,7 +1747,7 @@ impl<'le> TElement for GeckoElement<'le> { PropertyDeclaration::TextAlign(SpecifiedTextAlign::MozCenterOrInherit), Importance::Normal, ); - let arc = Arc::new(global_style_data.shared_lock.wrap(pdb)); + let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb)); ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints) }; static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = { @@ -1745,7 +1756,7 @@ impl<'le> TElement for GeckoElement<'le> { PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())), Importance::Normal, ); - let arc = Arc::new(global_style_data.shared_lock.wrap(pdb)); + let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb)); ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints) }; static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = { @@ -1754,7 +1765,7 @@ impl<'le> TElement for GeckoElement<'le> { PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))), Importance::Normal, ); - let arc = Arc::new(global_style_data.shared_lock.wrap(pdb)); + let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb)); ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints) }; static ref SVG_TEXT_DISABLE_ZOOM_RULE: ApplicableDeclarationBlock = { @@ -1763,7 +1774,7 @@ impl<'le> TElement for GeckoElement<'le> { PropertyDeclaration::XTextZoom(SpecifiedZoom(false)), Importance::Normal, ); - let arc = Arc::new(global_style_data.shared_lock.wrap(pdb)); + let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb)); ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints) }; }; @@ -2073,16 +2084,18 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { } #[inline] - fn local_name(&self) -> &WeakAtom { - unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) } + fn has_local_name(&self, name: &WeakAtom) -> bool { + self.local_name() == name } #[inline] - fn namespace(&self) -> &WeakNamespace { - unsafe { - let namespace_manager = structs::nsContentUtils_sNameSpaceManager; - WeakNamespace::new((*namespace_manager).mURIArray[self.namespace_id() as usize].mRawPtr) - } + fn has_namespace(&self, ns: &WeakNamespace) -> bool { + self.namespace() == ns + } + + #[inline] + fn is_same_type(&self, other: &Self) -> bool { + self.local_name() == other.local_name() && self.namespace() == other.namespace() } fn match_non_ts_pseudo_class<F>( diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index 90cc5284bc4..c5246733976 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -63,15 +63,25 @@ impl<T: RefCounted> RefPtr<T> { } } + /// Returns whether the current pointer is null. + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } + + /// Returns a null pointer. + pub fn null() -> Self { + Self { + ptr: ptr::null_mut(), + _marker: PhantomData, + } + } + /// Create a new RefPtr from a pointer obtained from FFI. /// - /// The pointer must be valid and non null. - /// /// This method calls addref() internally pub unsafe fn new(ptr: *mut T) -> Self { - debug_assert!(!ptr.is_null()); let ret = RefPtr { - ptr: ptr, + ptr, _marker: PhantomData, }; ret.addref(); @@ -97,8 +107,10 @@ impl<T: RefCounted> RefPtr<T> { /// Addref the inner data, obviously leaky on its own. pub fn addref(&self) { - unsafe { - (*self.ptr).addref(); + if !self.ptr.is_null() { + unsafe { + (*self.ptr).addref(); + } } } @@ -106,7 +118,9 @@ impl<T: RefCounted> RefPtr<T> { /// /// Call only when the data actually needs releasing. pub unsafe fn release(&self) { - (*self.ptr).release(); + if !self.ptr.is_null() { + (*self.ptr).release(); + } } } @@ -130,6 +144,7 @@ impl<T: RefCounted> UniqueRefPtr<T> { impl<T: RefCounted> Deref for RefPtr<T> { type Target = T; fn deref(&self) -> &T { + debug_assert!(!self.ptr.is_null()); unsafe { &*self.ptr } } } @@ -152,7 +167,6 @@ impl<T: RefCounted> structs::RefPtr<T> { /// /// Must be called on a valid, non-null structs::RefPtr<T>. pub unsafe fn to_safe(&self) -> RefPtr<T> { - debug_assert!(!self.mRawPtr.is_null()); let r = RefPtr { ptr: self.mRawPtr, _marker: PhantomData, @@ -290,9 +304,9 @@ impl_threadsafe_refcount!( bindings::Gecko_ReleaseURLExtraDataArbitraryThread ); impl_threadsafe_refcount!( - structs::mozilla::css::URLValue, - bindings::Gecko_AddRefCSSURLValueArbitraryThread, - bindings::Gecko_ReleaseCSSURLValueArbitraryThread + structs::nsIURI, + bindings::Gecko_AddRefnsIURIArbitraryThread, + bindings::Gecko_ReleasensIURIArbitraryThread ); impl_threadsafe_refcount!( structs::mozilla::css::GridTemplateAreasValue, diff --git a/components/style/global_style_data.rs b/components/style/global_style_data.rs index 90936ff7172..e19cab37bfa 100644 --- a/components/style/global_style_data.rs +++ b/components/style/global_style_data.rs @@ -125,7 +125,7 @@ lazy_static! { }; /// Global style data pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData { - shared_lock: SharedRwLock::new(), + shared_lock: SharedRwLock::new_leaked(), options: StyleSystemOptions::default(), }; } diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index 91f0705853c..cb63e611348 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -312,13 +312,24 @@ where } #[inline] - fn local_name(&self) -> &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName { - self.element.local_name() + fn has_local_name( + &self, + local_name: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName, + ) -> bool { + self.element.has_local_name(local_name) + } + + #[inline] + fn has_namespace( + &self, + ns: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl, + ) -> bool { + self.element.has_namespace(ns) } #[inline] - fn namespace(&self) -> &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl { - self.element.namespace() + fn is_same_type(&self, other: &Self) -> bool { + self.element.is_same_type(&other.element) } fn attr_matches( diff --git a/components/style/matching.rs b/components/style/matching.rs index 583e41ad82e..52d12037707 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -718,7 +718,7 @@ pub trait MatchMethods: TElement { let device = context.shared.stylist.device(); // Needed for the "inherit from body" quirk. - let text_color = new_primary_style.get_color().clone_color(); + let text_color = new_primary_style.get_inherited_text().clone_color(); device.set_body_text_color(text_color); } } diff --git a/components/style/properties/build.py b/components/style/properties/build.py index 3cc2a60134e..1967ed3238a 100644 --- a/components/style/properties/build.py +++ b/components/style/properties/build.py @@ -25,7 +25,6 @@ STYLE_STRUCT_LIST = [ "background", "border", "box", - "color", "column", "counters", "effects", diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index dd5bcf2febf..9f56c42cc2b 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -33,13 +33,11 @@ use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; use crate::gecko_bindings::bindings::Gecko_SetListStyleImageNone; use crate::gecko_bindings::bindings::Gecko_SetListStyleImageImageValue; use crate::gecko_bindings::bindings::Gecko_SetNullImageValue; -use crate::gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom}; use crate::gecko_bindings::structs; use crate::gecko_bindings::structs::nsCSSPropertyID; use crate::gecko_bindings::structs::mozilla::PseudoStyleType; -use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut}; +use crate::gecko_bindings::sugar::ns_style_coord::CoordDataMut; use crate::gecko_bindings::sugar::refptr::RefPtr; -use crate::gecko::values::GeckoStyleCoordConvertible; use crate::gecko::values::round_border_to_device_pixels; use crate::logical_geometry::WritingMode; use crate::media_queries::Device; @@ -52,11 +50,10 @@ use std::marker::PhantomData; use std::mem::{forget, uninitialized, zeroed, ManuallyDrop}; use std::{cmp, ops, ptr}; use crate::values::{self, CustomIdent, Either, KeyframesName, None_}; -use crate::values::computed::{NonNegativeLength, Percentage, TransitionProperty}; +use crate::values::computed::{Percentage, TransitionProperty}; use crate::values::computed::url::ComputedImageUrl; use crate::values::computed::BorderStyle; use crate::values::computed::font::FontSize; -use crate::values::computed::effects::Filter; use crate::values::generics::column::ColumnCount; use crate::values::generics::transform::TransformStyle; use crate::values::generics::url::UrlOrNone; @@ -278,7 +275,7 @@ impl ComputedValuesInner { #[allow(non_snake_case)] pub fn has_moz_binding(&self) -> bool { - !self.get_box().gecko.mBinding.mRawPtr.is_null() + !self.get_box().gecko.mBinding.is_none() } } @@ -550,7 +547,7 @@ def set_gecko_property(ffi_name, expr): unsafe { bindings::Gecko_nsStyleSVGPaint_SetURLValue( paint, - url.url_value_ptr(), + &url ) } } @@ -591,7 +588,6 @@ def set_gecko_property(ffi_name, expr): #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use crate::values::computed::url::ComputedUrl; use crate::values::generics::svg::{SVGPaint, SVGPaintKind}; use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGFallbackType; @@ -613,8 +609,7 @@ def set_gecko_property(ffi_name, expr): nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke => SVGPaintKind::ContextStroke, nsStyleSVGPaintType::eStyleSVGPaintType_Server => { SVGPaintKind::PaintServer(unsafe { - let url = RefPtr::new(*paint.mPaint.mPaintServer.as_ref()); - ComputedUrl::from_url_value(url) + paint.mPaint.mPaintServer.as_ref().clone() }) } nsStyleSVGPaintType::eStyleSVGPaintType_Color => { @@ -735,45 +730,6 @@ def set_gecko_property(ffi_name, expr): } </%def> -<%def name="impl_css_url(ident, gecko_ffi_name)"> - #[allow(non_snake_case)] - pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { - match v { - UrlOrNone::Url(ref url) => { - self.gecko.${gecko_ffi_name}.set_move(url.clone_url_value()) - } - UrlOrNone::None => { - unsafe { - self.gecko.${gecko_ffi_name}.clear(); - } - } - } - } - #[allow(non_snake_case)] - pub fn copy_${ident}_from(&mut self, other: &Self) { - unsafe { - self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); - } - } - #[allow(non_snake_case)] - pub fn reset_${ident}(&mut self, other: &Self) { - self.copy_${ident}_from(other) - } - - #[allow(non_snake_case)] - pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use crate::values::computed::url::ComputedUrl; - - if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() { - return UrlOrNone::none() - } - - UrlOrNone::Url(unsafe { - ComputedUrl::from_url_value(self.gecko.${gecko_ffi_name}.to_safe()) - }) - } -</%def> - <%def name="impl_logical(name, **kwargs)"> ${helpers.logical_setter(name)} </%def> @@ -879,7 +835,6 @@ impl Clone for ${style_struct.gecko_struct_name} { "SVGOpacity": impl_svg_opacity, "SVGPaint": impl_svg_paint, "SVGWidth": impl_svg_length, - "url::UrlOrNone": impl_css_url, } def longhand_method(longhand): @@ -2164,8 +2119,7 @@ fn static_assert() { animation-iteration-count animation-timing-function clear transition-duration transition-delay transition-timing-function transition-property - transform-style -moz-binding shape-outside - -webkit-line-clamp""" %> + transform-style shape-outside -webkit-line-clamp""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> #[inline] pub fn set_display(&mut self, v: longhands::display::computed_value::T) { @@ -2205,7 +2159,7 @@ fn static_assert() { gecko_inexhaustive=True, ) %> ${impl_keyword('clear', 'mBreakType', clear_keyword)} - ${impl_css_url('_moz_binding', 'mBinding')} + ${impl_transition_time_value('delay', 'Delay')} ${impl_transition_time_value('duration', 'Duration')} ${impl_transition_timing_function()} @@ -2834,10 +2788,7 @@ fn static_assert() { } UrlOrNone::Url(ref url) => { unsafe { - Gecko_SetListStyleImageImageValue( - &mut *self.gecko, - url.url_value_ptr(), - ); + Gecko_SetListStyleImageImageValue(&mut *self.gecko, url); } } } @@ -2973,8 +2924,7 @@ fn static_assert() { ${impl_simple_copy('_x_span', 'mSpan')} </%self:impl_trait> -<%self:impl_trait style_struct_name="Effects" - skip_longhands="clip filter"> +<%self:impl_trait style_struct_name="Effects" skip_longhands="clip"> pub fn set_clip(&mut self, v: longhands::clip::computed_value::T) { use crate::gecko_bindings::structs::NS_STYLE_CLIP_AUTO; use crate::gecko_bindings::structs::NS_STYLE_CLIP_RECT; @@ -3082,138 +3032,6 @@ fn static_assert() { Either::First(ClipRect { top, right, bottom, left }) } - - <% - # This array is several filter function which has percentage or - # number value for function of clone / set. - # The setting / cloning process of other function(e.g. Blur / HueRotate) is - # different from these function. So this array don't include such function. - FILTER_FUNCTIONS = [ 'Brightness', 'Contrast', 'Grayscale', 'Invert', - 'Opacity', 'Saturate', 'Sepia' ] - %> - - pub fn set_filter<I>(&mut self, v: I) - where - I: IntoIterator<Item = Filter>, - I::IntoIter: ExactSizeIterator, - { - use crate::values::generics::effects::Filter::*; - use crate::gecko_bindings::structs::nsStyleFilter; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_BLUR; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_INVERT; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_OPACITY; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_SATURATE; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_SEPIA; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW; - - fn fill_filter(m_type: u32, value: CoordDataValue, gecko_filter: &mut nsStyleFilter){ - gecko_filter.mType = m_type; - gecko_filter.mFilterParameter.set_value(value); - } - - let v = v.into_iter(); - unsafe { - Gecko_ResetFilters(&mut *self.gecko, v.len()); - } - debug_assert_eq!(v.len(), self.gecko.mFilters.len()); - - for (servo, gecko_filter) in v.zip(self.gecko.mFilters.iter_mut()) { - match servo { - % for func in FILTER_FUNCTIONS: - ${func}(factor) => fill_filter(NS_STYLE_FILTER_${func.upper()}, - CoordDataValue::Factor(factor.0), - gecko_filter), - % endfor - Blur(length) => fill_filter(NS_STYLE_FILTER_BLUR, - CoordDataValue::Coord(length.0.to_i32_au()), - gecko_filter), - - HueRotate(angle) => fill_filter(NS_STYLE_FILTER_HUE_ROTATE, - CoordDataValue::from(angle), - gecko_filter), - - DropShadow(shadow) => { - gecko_filter.mType = NS_STYLE_FILTER_DROP_SHADOW; - unsafe { - let ref mut union = gecko_filter.__bindgen_anon_1; - ptr::write(union.mDropShadow.as_mut(), shadow); - } - }, - Url(ref url) => { - unsafe { - bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.url_value_ptr()); - } - }, - } - } - } - - pub fn copy_filter_from(&mut self, other: &Self) { - unsafe { - Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut *self.gecko); - } - } - - pub fn reset_filter(&mut self, other: &Self) { - self.copy_filter_from(other) - } - - pub fn clone_filter(&self) -> longhands::filter::computed_value::T { - use crate::values::generics::effects::Filter; - use crate::values::computed::url::ComputedUrl; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_BLUR; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_INVERT; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_OPACITY; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_SATURATE; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_SEPIA; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW; - use crate::gecko_bindings::structs::NS_STYLE_FILTER_URL; - - longhands::filter::computed_value::List(self.gecko.mFilters.iter().map(|filter| { - match filter.mType { - % for func in FILTER_FUNCTIONS: - NS_STYLE_FILTER_${func.upper()} => { - Filter::${func}( - GeckoStyleCoordConvertible::from_gecko_style_coord( - &filter.mFilterParameter - ).unwrap() - ) - }, - % endfor - NS_STYLE_FILTER_BLUR => { - Filter::Blur(NonNegativeLength::from_gecko_style_coord( - &filter.mFilterParameter - ).unwrap()) - }, - NS_STYLE_FILTER_HUE_ROTATE => { - Filter::HueRotate(GeckoStyleCoordConvertible::from_gecko_style_coord( - &filter.mFilterParameter, - ).unwrap()) - }, - NS_STYLE_FILTER_DROP_SHADOW => { - Filter::DropShadow(unsafe { - (*filter.__bindgen_anon_1.mDropShadow.as_ref()).clone() - }) - }, - NS_STYLE_FILTER_URL => { - Filter::Url(unsafe { - let url = RefPtr::new(*filter.__bindgen_anon_1.mURL.as_ref()); - ComputedUrl::from_url_value(url) - }) - } - _ => unreachable!("Unknown filter function?"), - } - }).collect()) - } - </%self:impl_trait> <%self:impl_trait style_struct_name="InheritedBox"> @@ -3537,9 +3355,6 @@ clip-path } </%self:impl_trait> -<%self:impl_trait style_struct_name="Color"> -</%self:impl_trait> - <%self:impl_trait style_struct_name="InheritedUI" skip_longhands="cursor"> pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) { self.gecko.mCursor = v.keyword; @@ -3550,7 +3365,7 @@ clip-path unsafe { Gecko_SetCursorImageValue( &mut self.gecko.mCursorImages[i], - v.images[i].url.url_value_ptr(), + &v.images[i].url ); } @@ -3769,7 +3584,7 @@ clip-path unsafe { bindings::Gecko_SetContentDataImageValue( &mut self.gecko.mContents[i], - url.url_value_ptr(), + url, ) } } diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index d3b3ecd11e4..48f499c7ec3 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -22,7 +22,7 @@ use std::mem::{self, ManuallyDrop}; use crate::hash::FxHashMap; use super::ComputedValues; use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; -use crate::values::animated::effects::Filter as AnimatedFilter; +use crate::values::animated::effects::AnimatedFilter; #[cfg(feature = "gecko")] use crate::values::computed::TransitionProperty; use crate::values::computed::{ClipRect, Context}; use crate::values::computed::ToComputedValue; diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index da9e5b2e767..f37d5968b24 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -390,7 +390,7 @@ ${helpers.predefined_type( "OffsetRotate", "computed::OffsetRotate::auto()", products="gecko", - animation_value_type="none", + animation_value_type="ComputedValue", gecko_pref="layout.css.motion-path.enabled", spec="https://drafts.fxtf.org/motion-1/#offset-rotate-property", servo_restyle_damage="reflow_out_of_flow" @@ -644,7 +644,6 @@ ${helpers.predefined_type( "basic_shape::FloatAreaShape", "generics::basic_shape::ShapeSource::None", products="gecko", - boxed=True, animation_value_type="basic_shape::FloatAreaShape", flags="APPLIES_TO_FIRST_LETTER", spec="https://drafts.csswg.org/css-shapes/#shape-outside-property", diff --git a/components/style/properties/longhands/color.mako.rs b/components/style/properties/longhands/color.mako.rs deleted file mode 100644 index a83787dd48e..00000000000 --- a/components/style/properties/longhands/color.mako.rs +++ /dev/null @@ -1,130 +0,0 @@ -/* 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/. */ - -<%namespace name="helpers" file="/helpers.mako.rs" /> - -<% data.new_style_struct("Color", inherited=True) %> - -<% from data import to_rust_ident %> - -${helpers.predefined_type( - "color", - "ColorPropertyValue", - "::cssparser::RGBA::new(0, 0, 0, 255)", - animation_value_type="AnimatedRGBA", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", - ignored_when_colors_disabled="True", - spec="https://drafts.csswg.org/css-color/#color", -)} - -// FIXME(#15973): Add servo support for system colors -// -// FIXME(emilio): Move outside of mako. -% if product == "gecko": -pub mod system_colors { - <% - # These are actually parsed. See nsCSSProps::kColorKTable - system_colors = """activeborder activecaption appworkspace background buttonface - buttonhighlight buttonshadow buttontext captiontext graytext highlight - highlighttext inactiveborder inactivecaption inactivecaptiontext - infobackground infotext menu menutext scrollbar threeddarkshadow - threedface threedhighlight threedlightshadow threedshadow window - windowframe windowtext -moz-buttondefault -moz-buttonhoverface - -moz-buttonhovertext -moz-cellhighlight -moz-cellhighlighttext - -moz-eventreerow -moz-field -moz-fieldtext -moz-dialog -moz-dialogtext - -moz-dragtargetzone -moz-gtk-info-bar-text -moz-html-cellhighlight - -moz-html-cellhighlighttext -moz-mac-buttonactivetext - -moz-gtk-buttonactivetext - -moz-mac-chrome-active -moz-mac-chrome-inactive - -moz-mac-defaultbuttontext -moz-mac-focusring -moz-mac-menuselect - -moz-mac-menushadow -moz-mac-menutextdisable -moz-mac-menutextselect - -moz-mac-disabledtoolbartext -moz-mac-secondaryhighlight - -moz-mac-vibrancy-light -moz-mac-vibrancy-dark - -moz-mac-vibrant-titlebar-light -moz-mac-vibrant-titlebar-dark - -moz-mac-menupopup - -moz-mac-menuitem -moz-mac-active-menuitem -moz-mac-source-list - -moz-mac-source-list-selection -moz-mac-active-source-list-selection - -moz-mac-tooltip - -moz-menuhover -moz-menuhovertext -moz-menubartext -moz-menubarhovertext - -moz-oddtreerow -moz-win-mediatext -moz-win-communicationstext - -moz-win-accentcolor -moz-win-accentcolortext - -moz-nativehyperlinktext -moz-comboboxtext -moz-combobox""".split() - - # These are not parsed but must be serialized - # They are only ever set directly by Gecko - extra_colors = """WindowBackground WindowForeground WidgetBackground WidgetForeground - WidgetSelectBackground WidgetSelectForeground Widget3DHighlight Widget3DShadow - TextBackground TextForeground TextSelectBackground TextSelectForeground - TextSelectForegroundCustom TextSelectBackgroundDisabled TextSelectBackgroundAttention - TextHighlightBackground TextHighlightForeground IMERawInputBackground - IMERawInputForeground IMERawInputUnderline IMESelectedRawTextBackground - IMESelectedRawTextForeground IMESelectedRawTextUnderline - IMEConvertedTextBackground IMEConvertedTextForeground IMEConvertedTextUnderline - IMESelectedConvertedTextBackground IMESelectedConvertedTextForeground - IMESelectedConvertedTextUnderline SpellCheckerUnderline""".split() - %> - use crate::gecko_bindings::bindings::Gecko_GetLookAndFeelSystemColor; - use crate::gecko_bindings::structs::root::mozilla::LookAndFeel_ColorID; - use std::fmt::{self, Write}; - use style_traits::{CssWriter, ToCss}; - use to_shmem::impl_trivial_to_shmem; - use crate::values::computed::{Context, ToComputedValue}; - - pub type SystemColor = LookAndFeel_ColorID; - - // It's hard to implement MallocSizeOf for LookAndFeel_ColorID because it - // is a bindgen type. So we implement it on the typedef instead. - malloc_size_of_is_0!(SystemColor); - - impl_trivial_to_shmem!(SystemColor); - - impl ToCss for SystemColor { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - let s = match *self { - % for color in system_colors + extra_colors: - LookAndFeel_ColorID::eColorID_${to_rust_ident(color)} => "${color}", - % endfor - LookAndFeel_ColorID::eColorID_LAST_COLOR => unreachable!(), - }; - dest.write_str(s) - } - } - - impl ToComputedValue for SystemColor { - type ComputedValue = u32; // nscolor - - #[inline] - fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue { - unsafe { - Gecko_GetLookAndFeelSystemColor( - *self as i32, - cx.device().document(), - ) - } - } - - #[inline] - fn from_computed_value(_: &Self::ComputedValue) -> Self { - unreachable!() - } - } - - impl SystemColor { - pub fn from_ident<'i, 't>(ident: &str) -> Result<Self, ()> { - ascii_case_insensitive_phf_map! { - color_name -> SystemColor = { - % for color in system_colors: - "${color}" => LookAndFeel_ColorID::eColorID_${to_rust_ident(color)}, - % endfor - } - } - - color_name(ident).cloned().ok_or(()) - } - } -} -% endif diff --git a/components/style/properties/longhands/effects.mako.rs b/components/style/properties/longhands/effects.mako.rs index 9bb8adda32e..c50a0d18ca6 100644 --- a/components/style/properties/longhands/effects.mako.rs +++ b/components/style/properties/longhands/effects.mako.rs @@ -47,6 +47,8 @@ ${helpers.predefined_type( "Filter", None, vector=True, + simple_vector_bindings=True, + gecko_ffi_name="mFilters", separator="Space", animation_value_type="AnimatedFilterList", vector_animation_type="with_zero", diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index e8f56cf2cde..58331efa6bd 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -7,6 +7,16 @@ <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %> ${helpers.predefined_type( + "color", + "ColorPropertyValue", + "::cssparser::RGBA::new(0, 0, 0, 255)", + animation_value_type="AnimatedRGBA", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + ignored_when_colors_disabled="True", + spec="https://drafts.csswg.org/css-color/#color", +)} + +${helpers.predefined_type( "line-height", "LineHeight", "computed::LineHeight::normal()", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 520b4a32b51..c2b390c89d9 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2832,7 +2832,7 @@ impl ComputedValues { /// style.resolve_color(style.get_border().clone_border_top_color()); #[inline] pub fn resolve_color(&self, color: computed::Color) -> RGBA { - color.to_rgba(self.get_color().clone_color()) + color.to_rgba(self.get_inherited_text().clone_color()) } } diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index d653b6be99d..34222103241 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -9,12 +9,14 @@ use crate::applicable_declarations::ApplicableDeclarationList; #[cfg(feature = "gecko")] use crate::gecko::selector_parser::PseudoElement; +use crate::hash::{self, FxHashMap}; use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::stylesheets::{Origin, StyleRule}; use crate::thread_state; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use parking_lot::RwLock; use servo_arc::{Arc, ArcBorrow, ArcUnion, ArcUnionBorrow}; use smallvec::SmallVec; use std::io::{self, Write}; @@ -81,13 +83,25 @@ impl MallocSizeOf for RuleTree { while let Some(node) = stack.pop() { n += unsafe { ops.malloc_size_of(node.ptr()) }; - stack.extend(unsafe { (*node.ptr()).iter_children() }); + stack.extend(unsafe { + (*node.ptr()) + .children + .read() + .iter() + .map(|(_k, v)| v.clone()) + }); } n } } +#[derive(Debug, Eq, Hash, PartialEq)] +struct ChildKey(CascadeLevel, ptr::NonNull<()>); + +unsafe impl Send for ChildKey {} +unsafe impl Sync for ChildKey {} + /// A style source for the rule node. It can either be a CSS style rule or a /// declaration block. /// @@ -110,6 +124,11 @@ impl StyleSource { StyleSource(ArcUnion::from_first(rule)) } + #[inline] + fn key(&self) -> ptr::NonNull<()> { + self.0.ptr() + } + /// Creates a StyleSource from a PropertyDeclarationBlock. pub fn from_declarations(decls: Arc<Locked<PropertyDeclarationBlock>>) -> Self { StyleSource(ArcUnion::from_second(decls)) @@ -570,7 +589,7 @@ const RULE_TREE_GC_INTERVAL: usize = 300; /// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints /// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading #[repr(u8)] -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] #[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub enum CascadeLevel { /// Normal User-Agent rules. @@ -697,23 +716,6 @@ impl CascadeLevel { } } -// The root node never has siblings, but needs a free count. We use the same -// storage for both to save memory. -struct PrevSiblingOrFreeCount(AtomicPtr<RuleNode>); -impl PrevSiblingOrFreeCount { - fn new() -> Self { - PrevSiblingOrFreeCount(AtomicPtr::new(ptr::null_mut())) - } - - unsafe fn as_prev_sibling(&self) -> &AtomicPtr<RuleNode> { - &self.0 - } - - unsafe fn as_free_count(&self) -> &AtomicUsize { - mem::transmute(&self.0) - } -} - /// A node in the rule tree. pub struct RuleNode { /// The root node. Only the root has no root pointer, for obvious reasons. @@ -732,15 +734,14 @@ pub struct RuleNode { level: CascadeLevel, refcount: AtomicUsize, - first_child: AtomicPtr<RuleNode>, - next_sibling: AtomicPtr<RuleNode>, - /// Previous sibling pointer for all non-root nodes. - /// - /// For the root, stores the of RuleNodes we have added to the free list - /// since the last GC. (We don't update this if we rescue a RuleNode from - /// the free list. It's just used as a heuristic to decide when to run GC.) - prev_sibling_or_free_count: PrevSiblingOrFreeCount, + /// Only used for the root, stores the number of free rule nodes that are + /// around. + free_count: AtomicUsize, + + /// The children of a given rule node. Children remove themselves from here + /// when they go away. + children: RwLock<FxHashMap<ChildKey, WeakRuleNode>>, /// The next item in the rule tree free list, that starts on the root node. /// @@ -755,8 +756,7 @@ unsafe impl Sync for RuleTree {} unsafe impl Send for RuleTree {} // On Gecko builds, hook into the leak checking machinery. -#[cfg(feature = "gecko")] -#[cfg(debug_assertions)] +#[cfg(feature = "gecko_refcount_logging")] mod gecko_leak_checking { use super::RuleNode; use std::mem::size_of; @@ -789,15 +789,13 @@ mod gecko_leak_checking { #[inline(always)] fn log_new(_ptr: *const RuleNode) { - #[cfg(feature = "gecko")] - #[cfg(debug_assertions)] + #[cfg(feature = "gecko_refcount_logging")] gecko_leak_checking::log_ctor(_ptr); } #[inline(always)] fn log_drop(_ptr: *const RuleNode) { - #[cfg(feature = "gecko")] - #[cfg(debug_assertions)] + #[cfg(feature = "gecko_refcount_logging")] gecko_leak_checking::log_dtor(_ptr); } @@ -815,9 +813,8 @@ impl RuleNode { source: Some(source), level: level, refcount: AtomicUsize::new(1), - first_child: AtomicPtr::new(ptr::null_mut()), - next_sibling: AtomicPtr::new(ptr::null_mut()), - prev_sibling_or_free_count: PrevSiblingOrFreeCount::new(), + children: Default::default(), + free_count: AtomicUsize::new(0), next_free: AtomicPtr::new(ptr::null_mut()), } } @@ -829,75 +826,39 @@ impl RuleNode { source: None, level: CascadeLevel::UANormal, refcount: AtomicUsize::new(1), - first_child: AtomicPtr::new(ptr::null_mut()), - next_sibling: AtomicPtr::new(ptr::null_mut()), - prev_sibling_or_free_count: PrevSiblingOrFreeCount::new(), + free_count: AtomicUsize::new(0), + children: Default::default(), next_free: AtomicPtr::new(FREE_LIST_SENTINEL), } } - fn is_root(&self) -> bool { - self.parent.is_none() - } - - fn next_sibling(&self) -> Option<WeakRuleNode> { - // We use acquire semantics here to ensure proper synchronization while - // inserting in the child list. - let ptr = self.next_sibling.load(Ordering::Acquire); - if ptr.is_null() { - None - } else { - Some(WeakRuleNode::from_ptr(ptr)) - } + fn key(&self) -> Option<ChildKey> { + Some(ChildKey(self.level, self.source.as_ref()?.key())) } - fn prev_sibling(&self) -> &AtomicPtr<RuleNode> { - debug_assert!(!self.is_root()); - unsafe { self.prev_sibling_or_free_count.as_prev_sibling() } + fn is_root(&self) -> bool { + self.parent.is_none() } fn free_count(&self) -> &AtomicUsize { debug_assert!(self.is_root()); - unsafe { self.prev_sibling_or_free_count.as_free_count() } + &self.free_count } /// Remove this rule node from the child list. /// - /// This method doesn't use proper synchronization, and it's expected to be - /// called in a single-threaded fashion, thus the unsafety. - /// /// This is expected to be called before freeing the node from the free - /// list. + /// list, on the main thread. unsafe fn remove_from_child_list(&self) { debug!( "Remove from child list: {:?}, parent: {:?}", self as *const RuleNode, self.parent.as_ref().map(|p| p.ptr()) ); - // NB: The other siblings we use in this function can also be dead, so - // we can't use `get` here, since it asserts. - let prev_sibling = self.prev_sibling().swap(ptr::null_mut(), Ordering::Relaxed); - - let next_sibling = self.next_sibling.swap(ptr::null_mut(), Ordering::Relaxed); - // Store the `next` pointer as appropriate, either in the previous - // sibling, or in the parent otherwise. - if prev_sibling.is_null() { - let parent = self.parent.as_ref().unwrap(); - parent - .get() - .first_child - .store(next_sibling, Ordering::Relaxed); - } else { - let previous = &*prev_sibling; - previous.next_sibling.store(next_sibling, Ordering::Relaxed); - } - - // Store the previous sibling pointer in the next sibling if present, - // otherwise we're done. - if !next_sibling.is_null() { - let next = &*next_sibling; - next.prev_sibling().store(prev_sibling, Ordering::Relaxed); + if let Some(parent) = self.parent.as_ref() { + let weak = parent.get().children.write().remove(&self.key().unwrap()); + assert_eq!(weak.unwrap().ptr() as *const _, self as *const _); } } @@ -933,25 +894,13 @@ impl RuleNode { } let _ = write!(writer, "\n"); - for child in self.iter_children() { + for (_, child) in self.children.read().iter() { child .upgrade() .get() .dump(guards, writer, indent + INDENT_INCREMENT); } } - - fn iter_children(&self) -> RuleChildrenListIter { - // See next_sibling to see why we need Acquire semantics here. - let first_child = self.first_child.load(Ordering::Acquire); - RuleChildrenListIter { - current: if first_child.is_null() { - None - } else { - Some(WeakRuleNode::from_ptr(first_child)) - }, - } - } } #[derive(Clone)] @@ -975,22 +924,21 @@ impl StrongRuleNode { fn new(n: Box<RuleNode>) -> Self { debug_assert_eq!(n.parent.is_none(), !n.source.is_some()); - let ptr = Box::into_raw(n); - log_new(ptr); + // TODO(emilio): Use into_raw_non_null when it's stable. + let ptr = unsafe { ptr::NonNull::new_unchecked(Box::into_raw(n)) }; + log_new(ptr.as_ptr()); debug!("Creating rule node: {:p}", ptr); StrongRuleNode::from_ptr(ptr) } - fn from_ptr(ptr: *mut RuleNode) -> Self { - StrongRuleNode { - p: ptr::NonNull::new(ptr).expect("Pointer must not be null"), - } + fn from_ptr(p: ptr::NonNull<RuleNode>) -> Self { + StrongRuleNode { p } } fn downgrade(&self) -> WeakRuleNode { - WeakRuleNode::from_ptr(self.ptr()) + WeakRuleNode::from_ptr(self.p) } /// Get the parent rule node of this rule node. @@ -1004,80 +952,44 @@ impl StrongRuleNode { source: StyleSource, level: CascadeLevel, ) -> StrongRuleNode { - let mut last = None; - - // NB: This is an iterator over _weak_ nodes. - // - // It's fine though, because nothing can make us GC while this happens, - // and this happens to be hot. - // - // TODO(emilio): We could actually make this even less hot returning a - // WeakRuleNode, and implementing this on WeakRuleNode itself... - for child in self.get().iter_children() { - let child_node = unsafe { &*child.ptr() }; - if child_node.level == level && child_node.source.as_ref().unwrap() == &source { - return child.upgrade(); - } - last = Some(child); - } - - let mut node = Box::new(RuleNode::new(root, self.clone(), source.clone(), level)); - let new_ptr: *mut RuleNode = &mut *node; + use parking_lot::RwLockUpgradableReadGuard; - loop { - let next; + let key = ChildKey(level, source.key()); - { - let last_node = last.as_ref().map(|l| unsafe { &*l.ptr() }); - let next_sibling_ptr = match last_node { - Some(ref l) => &l.next_sibling, - None => &self.get().first_child, - }; + let read_guard = self.get().children.upgradable_read(); + if let Some(child) = read_guard.get(&key) { + return child.upgrade(); + } - // We use `AqcRel` semantics to ensure the initializing writes - // in `node` are visible after the swap succeeds. - let existing = - next_sibling_ptr.compare_and_swap(ptr::null_mut(), new_ptr, Ordering::AcqRel); - - if existing.is_null() { - // Now we know we're in the correct position in the child - // list, we can set the back pointer, knowing that this will - // only be accessed again in a single-threaded manner when - // we're sweeping possibly dead nodes. - if let Some(ref l) = last { - node.prev_sibling().store(l.ptr(), Ordering::Relaxed); - } + match RwLockUpgradableReadGuard::upgrade(read_guard).entry(key) { + hash::map::Entry::Occupied(ref occupied) => occupied.get().upgrade(), + hash::map::Entry::Vacant(vacant) => { + let new_node = StrongRuleNode::new(Box::new(RuleNode::new( + root, + self.clone(), + source.clone(), + level, + ))); - return StrongRuleNode::new(node); - } + vacant.insert(new_node.downgrade()); - // Existing is not null: some thread inserted a child node since - // we accessed `last`. - next = WeakRuleNode::from_ptr(existing); - - if unsafe { &*next.ptr() }.source.as_ref().unwrap() == &source { - // That node happens to be for the same style source, use - // that, and let node fall out of scope. - return next.upgrade(); - } - } - - // Try again inserting after the new last child. - last = Some(next); + new_node + }, } } /// Raw pointer to the RuleNode + #[inline] pub fn ptr(&self) -> *mut RuleNode { self.p.as_ptr() } fn get(&self) -> &RuleNode { if cfg!(debug_assertions) { - let node = unsafe { &*self.ptr() }; + let node = unsafe { &*self.p.as_ptr() }; assert!(node.refcount.load(Ordering::Relaxed) > 0); } - unsafe { &*self.ptr() } + unsafe { &*self.p.as_ptr() } } /// Get the style source corresponding to this rule node. May return `None` @@ -1107,13 +1019,13 @@ impl StrongRuleNode { /// Returns whether this node has any child, only intended for testing /// purposes, and called on a single-threaded fashion only. pub unsafe fn has_children_for_testing(&self) -> bool { - !self.get().first_child.load(Ordering::Relaxed).is_null() + !self.get().children.read().is_empty() } unsafe fn pop_from_free_list(&self) -> Option<WeakRuleNode> { // NB: This can run from the root node destructor, so we can't use // `get()`, since it asserts the refcount is bigger than zero. - let me = &*self.ptr(); + let me = &*self.p.as_ptr(); debug_assert!(me.is_root()); @@ -1139,7 +1051,7 @@ impl StrongRuleNode { same time?" ); debug_assert!( - current != self.ptr(), + current != self.p.as_ptr(), "How did the root end up in the free list?" ); @@ -1159,17 +1071,17 @@ impl StrongRuleNode { current, next ); - Some(WeakRuleNode::from_ptr(current)) + Some(WeakRuleNode::from_ptr(ptr::NonNull::new_unchecked(current))) } unsafe fn assert_free_list_has_no_duplicates_or_null(&self) { assert!(cfg!(debug_assertions), "This is an expensive check!"); use crate::hash::FxHashSet; - let me = &*self.ptr(); + let me = &*self.p.as_ptr(); assert!(me.is_root()); - let mut current = self.ptr(); + let mut current = self.p.as_ptr(); let mut seen = FxHashSet::default(); while current != FREE_LIST_SENTINEL { let next = (*current).next_free.load(Ordering::Relaxed); @@ -1188,21 +1100,21 @@ impl StrongRuleNode { // NB: This can run from the root node destructor, so we can't use // `get()`, since it asserts the refcount is bigger than zero. - let me = &*self.ptr(); + let me = &*self.p.as_ptr(); debug_assert!(me.is_root(), "Can't call GC on a non-root node!"); while let Some(weak) = self.pop_from_free_list() { - let node = &*weak.ptr(); + let node = &*weak.p.as_ptr(); if node.refcount.load(Ordering::Relaxed) != 0 { // Nothing to do, the node is still alive. continue; } - debug!("GC'ing {:?}", weak.ptr()); + debug!("GC'ing {:?}", weak.p.as_ptr()); node.remove_from_child_list(); - log_drop(weak.ptr()); - let _ = Box::from_raw(weak.ptr()); + log_drop(weak.p.as_ptr()); + let _ = Box::from_raw(weak.p.as_ptr()); } me.free_count().store(0, Ordering::Relaxed); @@ -1509,7 +1421,7 @@ impl Clone for StrongRuleNode { ); debug_assert!(self.get().refcount.load(Ordering::Relaxed) > 0); self.get().refcount.fetch_add(1, Ordering::Relaxed); - StrongRuleNode::from_ptr(self.ptr()) + StrongRuleNode::from_ptr(self.p) } } @@ -1537,7 +1449,7 @@ impl Drop for StrongRuleNode { return; } - debug_assert_eq!(node.first_child.load(Ordering::Acquire), ptr::null_mut()); + debug_assert!(node.children.read().is_empty()); if node.parent.is_none() { debug!("Dropping root node!"); // The free list should be null by this point @@ -1655,41 +1567,25 @@ impl Drop for StrongRuleNode { impl<'a> From<&'a StrongRuleNode> for WeakRuleNode { fn from(node: &'a StrongRuleNode) -> Self { - WeakRuleNode::from_ptr(node.ptr()) + WeakRuleNode::from_ptr(node.p) } } impl WeakRuleNode { + #[inline] + fn ptr(&self) -> *mut RuleNode { + self.p.as_ptr() + } + fn upgrade(&self) -> StrongRuleNode { debug!("Upgrading weak node: {:p}", self.ptr()); let node = unsafe { &*self.ptr() }; node.refcount.fetch_add(1, Ordering::Relaxed); - StrongRuleNode::from_ptr(self.ptr()) - } - - fn from_ptr(ptr: *mut RuleNode) -> Self { - WeakRuleNode { - p: ptr::NonNull::new(ptr).expect("Pointer must not be null"), - } - } - - fn ptr(&self) -> *mut RuleNode { - self.p.as_ptr() + StrongRuleNode::from_ptr(self.p) } -} -struct RuleChildrenListIter { - current: Option<WeakRuleNode>, -} - -impl Iterator for RuleChildrenListIter { - type Item = WeakRuleNode; - - fn next(&mut self) -> Option<Self::Item> { - self.current.take().map(|current| { - self.current = unsafe { &*current.ptr() }.next_sibling(); - current - }) + fn from_ptr(p: ptr::NonNull<RuleNode>) -> Self { + WeakRuleNode { p } } } diff --git a/components/style/servo/url.rs b/components/style/servo/url.rs index fd7726289be..8e011796e79 100644 --- a/components/style/servo/url.rs +++ b/components/style/servo/url.rs @@ -5,14 +5,12 @@ //! Common handling for the specified value CSS url() values. use crate::parser::{Parse, ParserContext}; +use crate::stylesheets::CorsMode; +use crate::values::computed::{Context, ToComputedValue}; use cssparser::Parser; +use servo_arc::Arc; use servo_url::ServoUrl; use std::fmt::{self, Write}; -// Note: We use std::sync::Arc rather than servo_arc::Arc here because the -// nonzero optimization is important in keeping the size of SpecifiedUrl below -// the threshold. -use crate::values::computed::{Context, ToComputedValue}; -use servo_arc::Arc; use style_traits::{CssWriter, ParseError, ToCss}; /// A CSS url() value for servo. @@ -44,7 +42,9 @@ pub struct CssUrl { impl CssUrl { /// Try to parse a URL from a string value that is a valid CSS token for a /// URL. - pub fn parse_from_string(url: String, context: &ParserContext) -> Self { + /// + /// FIXME(emilio): Should honor CorsMode. + pub fn parse_from_string(url: String, context: &ParserContext, _: CorsMode) -> Self { let serialization = Arc::new(url); let resolved = context.url_data.join(&serialization).ok(); CssUrl { @@ -121,7 +121,11 @@ impl Parse for CssUrl { input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>> { let url = input.expect_url()?; - Ok(Self::parse_from_string(url.as_ref().to_owned(), context)) + Ok(Self::parse_from_string( + url.as_ref().to_owned(), + context, + CorsMode::None, + )) } } diff --git a/components/style/shared_lock.rs b/components/style/shared_lock.rs index 073c696fa3d..d46370952d3 100644 --- a/components/style/shared_lock.rs +++ b/components/style/shared_lock.rs @@ -71,6 +71,24 @@ impl SharedRwLock { } } + /// Create a new global shared lock (servo). + #[cfg(feature = "servo")] + pub fn new_leaked() -> Self { + SharedRwLock { + arc: Arc::new_leaked(RwLock::new(())), + } + } + + /// Create a new global shared lock (gecko). + #[cfg(feature = "gecko")] + pub fn new_leaked() -> Self { + SharedRwLock { + cell: Some(Arc::new_leaked(AtomicRefCell::new( + SomethingZeroSizedButTyped, + ))), + } + } + /// Create a new read-only shared lock (gecko). #[cfg(feature = "gecko")] pub fn read_only() -> Self { diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs index 3ce2a717dc8..5f5d1c6cab5 100644 --- a/components/style/sharing/mod.rs +++ b/components/style/sharing/mod.rs @@ -485,8 +485,12 @@ type SharingCache<E> = SharingCacheBase<StyleSharingCandidate<E>>; type TypelessSharingCache = SharingCacheBase<FakeCandidate>; type StoredSharingCache = Arc<AtomicRefCell<TypelessSharingCache>>; -thread_local!(static SHARING_CACHE_KEY: StoredSharingCache = - Arc::new(AtomicRefCell::new(TypelessSharingCache::default()))); +thread_local! { + // TODO(emilio): Looks like a few of these should just be Rc<RefCell<>> or + // something. No need for atomics in the thread-local code. + static SHARING_CACHE_KEY: StoredSharingCache = + Arc::new_leaked(AtomicRefCell::new(TypelessSharingCache::default())); +} /// An LRU cache of the last few nodes seen, so that we can aggressively try to /// reuse their styles. diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index 16842021e8d..6f9c03abc5d 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -63,9 +63,15 @@ pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentSt pub use self::supports_rule::SupportsRule; pub use self::viewport_rule::ViewportRule; -/// Extra data that the backend may need to resolve url values. -#[cfg(not(feature = "gecko"))] -pub type UrlExtraData = ::servo_url::ServoUrl; +/// The CORS mode used for a CSS load. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] +pub enum CorsMode { + /// No CORS mode, so cross-origin loads can be done. + None, + /// Anonymous CORS request. + Anonymous, +} /// Extra data that the backend may need to resolve url values. /// @@ -82,8 +88,13 @@ pub type UrlExtraData = ::servo_url::ServoUrl; /// `from_ptr_ref` can work. #[cfg(feature = "gecko")] #[derive(PartialEq)] +#[repr(C)] pub struct UrlExtraData(usize); +/// Extra data that the backend may need to resolve url values. +#[cfg(not(feature = "gecko"))] +pub type UrlExtraData = ::servo_url::ServoUrl; + #[cfg(feature = "gecko")] impl Clone for UrlExtraData { fn clone(&self) -> UrlExtraData { diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index a3ec24174ed..e7f3fb15f80 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -19,8 +19,8 @@ use crate::stylesheets::keyframes_rule::parse_keyframe_list; use crate::stylesheets::stylesheet::Namespaces; use crate::stylesheets::supports_rule::SupportsCondition; use crate::stylesheets::viewport_rule; +use crate::stylesheets::{CorsMode, DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule}; use crate::stylesheets::{CssRule, CssRuleType, CssRules, RulesMutateError, StylesheetLoader}; -use crate::stylesheets::{DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule}; use crate::stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule}; use crate::values::computed::font::FamilyName; use crate::values::{CssUrl, CustomIdent, KeyframesName}; @@ -197,7 +197,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { } let url_string = input.expect_url_or_string()?.as_ref().to_owned(); - let url = CssUrl::parse_from_string(url_string, &self.context); + let url = CssUrl::parse_from_string(url_string, &self.context, CorsMode::None); let media = MediaList::parse(&self.context, input); let media = Arc::new(self.shared_lock.wrap(media)); diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs index 4c559cd4c30..49e751fec55 100644 --- a/components/style/values/animated/effects.rs +++ b/components/style/values/animated/effects.rs @@ -19,8 +19,8 @@ pub type AnimatedSimpleShadow = GenericSimpleShadow<Color, Length, Length>; /// An animated value for a single `filter`. #[cfg(feature = "gecko")] -pub type Filter = GenericFilter<Angle, Number, Length, AnimatedSimpleShadow, ComputedUrl>; +pub type AnimatedFilter = GenericFilter<Angle, Number, Length, AnimatedSimpleShadow, ComputedUrl>; /// An animated value for a single `filter`. #[cfg(not(feature = "gecko"))] -pub type Filter = GenericFilter<Angle, Number, Length, Impossible, Impossible>; +pub type AnimatedFilter = GenericFilter<Angle, Number, Length, Impossible, Impossible>; diff --git a/components/style/values/computed/effects.rs b/components/style/values/computed/effects.rs index d103f407a75..24ec689e49c 100644 --- a/components/style/values/computed/effects.rs +++ b/components/style/values/computed/effects.rs @@ -24,7 +24,7 @@ pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow, ComputedUrl>; /// A computed value for a single `filter`. -#[cfg(not(feature = "gecko"))] +#[cfg(feature = "servo")] pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, Impossible>; diff --git a/components/style/values/computed/list.rs b/components/style/values/computed/list.rs index 3536aa656fe..bdcf62ba2f8 100644 --- a/components/style/values/computed/list.rs +++ b/components/style/values/computed/list.rs @@ -10,7 +10,7 @@ pub use crate::values::specified::list::MozListReversed; pub use crate::values::specified::list::{QuotePair, Quotes}; lazy_static! { - static ref INITIAL_QUOTES: crate::ArcSlice<QuotePair> = crate::ArcSlice::from_iter( + static ref INITIAL_QUOTES: crate::ArcSlice<QuotePair> = crate::ArcSlice::from_iter_leaked( vec![ QuotePair { opening: "\u{201c}".to_owned().into(), diff --git a/components/style/values/computed/motion.rs b/components/style/values/computed/motion.rs index 932d8074262..e5f82f46558 100644 --- a/components/style/values/computed/motion.rs +++ b/components/style/values/computed/motion.rs @@ -18,13 +18,25 @@ fn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool { } /// A computed offset-rotate. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + ToAnimatedZero, + ToCss, + ToResolvedValue, +)] #[repr(C)] pub struct OffsetRotate { /// If auto is false, this is a fixed angle which indicates a /// constant clockwise rotation transformation applied to it by this /// specified rotation angle. Otherwise, the angle will be added to /// the angle of the direction in layout. + #[animation(constant)] #[css(represents_keyword)] pub auto: bool, /// The angle value. diff --git a/components/style/values/generics/effects.rs b/components/style/values/generics/effects.rs index a27adc49e52..beca716e997 100644 --- a/components/style/values/generics/effects.rs +++ b/components/style/values/generics/effects.rs @@ -34,8 +34,10 @@ pub struct GenericBoxShadow<Color, SizeLength, BlurShapeLength, ShapeLength> { pub use self::GenericBoxShadow as BoxShadow; /// A generic value for a single `filter`. +/// +/// cbindgen:derive-tagged-enum-copy-constructor=true #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[animation(no_bound(Url))] +#[animation(no_bound(U))] #[derive( Clone, ComputeSquaredDistance, @@ -49,7 +51,8 @@ pub use self::GenericBoxShadow as BoxShadow; ToResolvedValue, ToShmem, )] -pub enum Filter<Angle, Factor, Length, DropShadow, Url> { +#[repr(C, u8)] +pub enum GenericFilter<Angle, Factor, Length, Shadow, U> { /// `blur(<length>)` #[css(function)] Blur(Length), @@ -79,12 +82,14 @@ pub enum Filter<Angle, Factor, Length, DropShadow, Url> { Sepia(Factor), /// `drop-shadow(...)` #[css(function)] - DropShadow(DropShadow), + DropShadow(Shadow), /// `<url>` #[animation(error)] - Url(Url), + Url(U), } +pub use self::GenericFilter as Filter; + /// A generic value for the `drop-shadow()` filter and the `text-shadow` property. /// /// Contrary to the canonical order from the spec, the color is serialised diff --git a/components/style/values/generics/url.rs b/components/style/values/generics/url.rs index 9dbed038630..1f271033036 100644 --- a/components/style/values/generics/url.rs +++ b/components/style/values/generics/url.rs @@ -5,6 +5,8 @@ //! Generic types for url properties. /// An image url or none, used for example in list-style-image +/// +/// cbindgen:derive-tagged-enum-copy-constructor=true #[derive( Animate, Clone, @@ -21,16 +23,27 @@ ToResolvedValue, ToShmem, )] -pub enum UrlOrNone<Url> { +#[repr(C, u8)] +pub enum GenericUrlOrNone<U> { /// `none` None, - /// `A URL` - Url(Url), + /// A URL. + Url(U), } +pub use self::GenericUrlOrNone as UrlOrNone; + impl<Url> UrlOrNone<Url> { /// Initial "none" value for properties such as `list-style-image` pub fn none() -> Self { UrlOrNone::None } + + /// Returns whether the value is `none`. + pub fn is_none(&self) -> bool { + match *self { + UrlOrNone::None => true, + UrlOrNone::Url(..) => false, + } + } } diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 375a7d23d49..acad3383014 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -8,8 +8,6 @@ use super::AllowQuirks; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::nscolor; use crate::parser::{Parse, ParserContext}; -#[cfg(feature = "gecko")] -use crate::properties::longhands::system_colors::SystemColor; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; use crate::values::generics::color::{Color as GenericColor, ColorOrAuto as GenericColorOrAuto}; use crate::values::specified::calc::CalcNode; @@ -35,7 +33,6 @@ pub enum Color { }, /// A complex color value from computed value Complex(ComputedColor), - /// A system color #[cfg(feature = "gecko")] System(SystemColor), @@ -47,6 +44,215 @@ pub enum Color { InheritFromBodyQuirk, } +/// System colors. +#[allow(missing_docs)] +#[cfg(feature = "gecko")] +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] +#[repr(u8)] +pub enum SystemColor { + #[css(skip)] + WindowBackground, + #[css(skip)] + WindowForeground, + #[css(skip)] + WidgetBackground, + #[css(skip)] + WidgetForeground, + #[css(skip)] + WidgetSelectBackground, + #[css(skip)] + WidgetSelectForeground, + #[css(skip)] + Widget3DHighlight, + #[css(skip)] + Widget3DShadow, + #[css(skip)] + TextBackground, + #[css(skip)] + TextForeground, + #[css(skip)] + TextSelectBackground, + #[css(skip)] + TextSelectForeground, + #[css(skip)] + TextSelectForegroundCustom, + #[css(skip)] + TextSelectBackgroundDisabled, + #[css(skip)] + TextSelectBackgroundAttention, + #[css(skip)] + TextHighlightBackground, + #[css(skip)] + TextHighlightForeground, + #[css(skip)] + IMERawInputBackground, + #[css(skip)] + IMERawInputForeground, + #[css(skip)] + IMERawInputUnderline, + #[css(skip)] + IMESelectedRawTextBackground, + #[css(skip)] + IMESelectedRawTextForeground, + #[css(skip)] + IMESelectedRawTextUnderline, + #[css(skip)] + IMEConvertedTextBackground, + #[css(skip)] + IMEConvertedTextForeground, + #[css(skip)] + IMEConvertedTextUnderline, + #[css(skip)] + IMESelectedConvertedTextBackground, + #[css(skip)] + IMESelectedConvertedTextForeground, + #[css(skip)] + IMESelectedConvertedTextUnderline, + #[css(skip)] + SpellCheckerUnderline, + Activeborder, + Activecaption, + Appworkspace, + Background, + Buttonface, + Buttonhighlight, + Buttonshadow, + Buttontext, + Captiontext, + Graytext, + Highlight, + Highlighttext, + Inactiveborder, + Inactivecaption, + Inactivecaptiontext, + Infobackground, + Infotext, + Menu, + Menutext, + Scrollbar, + Threeddarkshadow, + Threedface, + Threedhighlight, + Threedlightshadow, + Threedshadow, + Window, + Windowframe, + Windowtext, + MozButtondefault, + MozField, + MozFieldtext, + MozDialog, + MozDialogtext, + /// Used to highlight valid regions to drop something onto. + MozDragtargetzone, + /// Used for selected but not focused cell backgrounds. + MozCellhighlight, + /// Used for selected but not focused cell text. + MozCellhighlighttext, + /// Used for selected but not focused html cell backgrounds. + MozHtmlCellhighlight, + /// Used for selected but not focused html cell text. + MozHtmlCellhighlighttext, + /// Used to button text background when hovered. + MozButtonhoverface, + /// Used to button text color when hovered. + MozButtonhovertext, + /// Used for menu item backgrounds when hovered. + MozMenuhover, + /// Used for menu item text when hovered. + MozMenuhovertext, + /// Used for menubar item text. + MozMenubartext, + /// Used for menubar item text when hovered. + MozMenubarhovertext, + + /// On platforms where these colors are the same as -moz-field, use + /// -moz-fieldtext as foreground color + MozEventreerow, + MozOddtreerow, + + /// Used for button text when pressed. + #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] + MozGtkButtonactivetext, + + /// Used for button text when pressed. + MozMacButtonactivetext, + /// Background color of chrome toolbars in active windows. + MozMacChromeActive, + /// Background color of chrome toolbars in inactive windows. + MozMacChromeInactive, + /// Foreground color of default buttons. + MozMacDefaultbuttontext, + /// Ring color around text fields and lists. + MozMacFocusring, + /// Color used when mouse is over a menu item. + MozMacMenuselect, + /// Color used to do shadows on menu items. + MozMacMenushadow, + /// Color used to display text for disabled menu items. + MozMacMenutextdisable, + /// Color used to display text while mouse is over a menu item. + MozMacMenutextselect, + /// Text color of disabled text on toolbars. + MozMacDisabledtoolbartext, + /// Inactive light hightlight + MozMacSecondaryhighlight, + + /// Font smoothing background colors needed by the Mac OS X theme, based on + /// -moz-appearance names. + MozMacVibrancyLight, + MozMacVibrancyDark, + MozMacVibrantTitlebarLight, + MozMacVibrantTitlebarDark, + MozMacMenupopup, + MozMacMenuitem, + MozMacActiveMenuitem, + MozMacSourceList, + MozMacSourceListSelection, + MozMacActiveSourceListSelection, + MozMacTooltip, + + /// Accent color for title bar. + MozWinAccentcolor, + /// Color from drawing text over the accent color. + MozWinAccentcolortext, + /// Media rebar text. + MozWinMediatext, + /// Communications rebar text. + MozWinCommunicationstext, + + /// Hyperlink color extracted from the system, not affected by the + /// browser.anchor_color user pref. + /// + /// There is no OS-specified safe background color for this text, but it is + /// used regularly within Windows and the Gnome DE on Dialog and Window + /// colors. + MozNativehyperlinktext, + + /// Combobox widgets + MozComboboxtext, + MozCombobox, + + MozGtkInfoBarText, + + #[css(skip)] + End, // Just for array-indexing purposes. +} + +#[cfg(feature = "gecko")] +impl SystemColor { + #[inline] + fn compute(&self, cx: &Context) -> ComputedColor { + use crate::gecko_bindings::bindings; + unsafe { + convert_nscolor_to_computedcolor(bindings::Gecko_GetLookAndFeelSystemColor( + *self as i32, + cx.device().document(), + )) + } + } +} + #[cfg(feature = "gecko")] mod gecko { #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] @@ -153,14 +359,12 @@ impl Parse for Color { Err(e) => { #[cfg(feature = "gecko")] { - if let Ok(ident) = input.expect_ident() { - if let Ok(system) = SystemColor::from_ident(ident) { - return Ok(Color::System(system)); - } - - if let Ok(c) = gecko::SpecialColorKeyword::from_ident(ident) { - return Ok(Color::Special(c)); - } + if let Ok(system) = input.try(|i| SystemColor::parse(context, i)) { + return Ok(Color::System(system)); + } + + if let Ok(c) = input.try(gecko::SpecialColorKeyword::parse) { + return Ok(Color::Special(c)); } } @@ -340,32 +544,29 @@ impl Color { /// If `context` is `None`, and the specified color requires data from /// the context to resolve, then `None` is returned. pub fn to_computed_color(&self, _context: Option<&Context>) -> Option<ComputedColor> { - match *self { - Color::CurrentColor => Some(ComputedColor::currentcolor()), - Color::Numeric { ref parsed, .. } => Some(ComputedColor::rgba(*parsed)), - Color::Complex(ref complex) => Some(*complex), + Some(match *self { + Color::CurrentColor => ComputedColor::currentcolor(), + Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed), + Color::Complex(ref complex) => *complex, #[cfg(feature = "gecko")] - Color::System(system) => _context - .map(|context| convert_nscolor_to_computedcolor(system.to_computed_value(context))), + Color::System(system) => system.compute(_context?), #[cfg(feature = "gecko")] Color::Special(special) => { use self::gecko::SpecialColorKeyword as Keyword; - _context.map(|context| { - let prefs = context.device().pref_sheet_prefs(); - convert_nscolor_to_computedcolor(match special { - Keyword::MozDefaultColor => prefs.mDefaultColor, - Keyword::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor, - Keyword::MozHyperlinktext => prefs.mLinkColor, - Keyword::MozActivehyperlinktext => prefs.mActiveLinkColor, - Keyword::MozVisitedhyperlinktext => prefs.mVisitedLinkColor, - }) + let prefs = _context?.device().pref_sheet_prefs(); + convert_nscolor_to_computedcolor(match special { + Keyword::MozDefaultColor => prefs.mDefaultColor, + Keyword::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor, + Keyword::MozHyperlinktext => prefs.mLinkColor, + Keyword::MozActivehyperlinktext => prefs.mActiveLinkColor, + Keyword::MozVisitedhyperlinktext => prefs.mVisitedLinkColor, }) }, #[cfg(feature = "gecko")] Color::InheritFromBodyQuirk => { - _context.map(|context| ComputedColor::rgba(context.device().body_text_color())) + ComputedColor::rgba(_context?.device().body_text_color()) }, - } + }) } } @@ -443,7 +644,7 @@ impl ToComputedValue for ColorPropertyValue { fn to_computed_value(&self, context: &Context) -> RGBA { self.0 .to_computed_value(context) - .to_rgba(context.builder.get_parent_color().clone_color()) + .to_rgba(context.builder.get_parent_inherited_text().clone_color()) } #[inline] diff --git a/components/style/values/specified/effects.rs b/components/style/values/specified/effects.rs index 3ffad2fa89c..25ef99a362e 100644 --- a/components/style/values/specified/effects.rs +++ b/components/style/values/specified/effects.rs @@ -18,7 +18,7 @@ use crate::values::specified::length::{Length, NonNegativeLength}; #[cfg(feature = "gecko")] use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::{Angle, Number, NumberOrPercentage}; -#[cfg(not(feature = "gecko"))] +#[cfg(feature = "servo")] use crate::values::Impossible; use crate::Zero; use cssparser::{self, BasicParseErrorKind, Parser, Token}; @@ -30,11 +30,14 @@ pub type BoxShadow = /// A specified value for a single `filter`. #[cfg(feature = "gecko")] -pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow, SpecifiedUrl>; +pub type SpecifiedFilter = + GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow, SpecifiedUrl>; /// A specified value for a single `filter`. -#[cfg(not(feature = "gecko"))] -pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible, Impossible>; +#[cfg(feature = "servo")] +pub type SpecifiedFilter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible, Impossible>; + +pub use self::SpecifiedFilter as Filter; /// A value for the `<factor>` parts in `Filter`. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index ad2ecb18086..ca7f687bfea 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -11,6 +11,7 @@ use crate::custom_properties::SpecifiedValue; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs; use crate::parser::{Parse, ParserContext}; +use crate::stylesheets::CorsMode; #[cfg(feature = "gecko")] use crate::values::computed::{Context, Position as ComputedPosition, ToComputedValue}; use crate::values::generics::image::PaintWorklet; @@ -1032,7 +1033,11 @@ impl Parse for MozImageRect { input.try(|i| i.expect_function_matching("-moz-image-rect"))?; input.parse_nested_block(|i| { let string = i.expect_url_or_string()?; - let url = SpecifiedImageUrl::parse_from_string(string.as_ref().to_owned(), context); + let url = SpecifiedImageUrl::parse_from_string( + string.as_ref().to_owned(), + context, + CorsMode::None, + ); i.expect_comma()?; let top = NumberOrPercentage::parse_non_negative(context, i)?; i.expect_comma()?; diff --git a/components/style/values/specified/url.rs b/components/style/values/specified/url.rs index ecf0eaae00c..b0a69b362d8 100644 --- a/components/style/values/specified/url.rs +++ b/components/style/values/specified/url.rs @@ -4,7 +4,7 @@ //! Common handling for the specified value CSS url() values. -use crate::values::generics::url::UrlOrNone as GenericUrlOrNone; +use crate::values::generics::url::GenericUrlOrNone; #[cfg(feature = "gecko")] pub use crate::gecko::url::{SpecifiedImageUrl, SpecifiedUrl}; diff --git a/components/style_traits/arc_slice.rs b/components/style_traits/arc_slice.rs index 6a151e3dc5a..bbbac1a0757 100644 --- a/components/style_traits/arc_slice.rs +++ b/components/style_traits/arc_slice.rs @@ -40,7 +40,7 @@ impl<T> Deref for ArcSlice<T> { lazy_static! { // ThinArc doesn't support alignments greater than align_of::<u64>. static ref EMPTY_ARC_SLICE: ArcSlice<u64> = { - ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, iter::empty())) + ArcSlice::from_iter_leaked(iter::empty()) }; } @@ -74,6 +74,19 @@ impl<T> ArcSlice<T> { ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items)) } + /// Creates an Arc for a slice using the given iterator to generate the + /// slice, and marks the arc as intentionally leaked from the refcount + /// logging point of view. + #[inline] + pub fn from_iter_leaked<I>(items: I) -> Self + where + I: Iterator<Item = T> + ExactSizeIterator, + { + let thin_arc = ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items); + thin_arc.with_arc(|a| a.mark_as_intentionally_leaked()); + ArcSlice(thin_arc) + } + /// Creates a value that can be passed via FFI, and forgets this value /// altogether. #[inline] diff --git a/tests/unit/style/rule_tree/bench.rs b/tests/unit/style/rule_tree/bench.rs index 52ebf2c1e2b..1c561a1f215 100644 --- a/tests/unit/style/rule_tree/bench.rs +++ b/tests/unit/style/rule_tree/bench.rs @@ -11,7 +11,7 @@ use style::error_reporting::{ContextualParseError, ParseErrorReporter}; use style::media_queries::MediaList; use style::properties::{longhands, Importance, PropertyDeclaration, PropertyDeclarationBlock}; use style::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; -use style::shared_lock::SharedRwLock; +use style::shared_lock::{SharedRwLock, StylesheetGuards}; use style::stylesheets::{CssRule, Origin, Stylesheet}; use style::thread_state::{self, ThreadState}; use test::{self, Bencher}; @@ -29,18 +29,23 @@ impl ParseErrorReporter for ErrorringErrorReporter { } } -struct AutoGCRuleTree<'a>(&'a RuleTree); +struct AutoGCRuleTree<'a>(&'a RuleTree, &'a SharedRwLock); impl<'a> AutoGCRuleTree<'a> { - fn new(r: &'a RuleTree) -> Self { - AutoGCRuleTree(r) + fn new(r: &'a RuleTree, lock: &'a SharedRwLock) -> Self { + AutoGCRuleTree(r, lock) } } impl<'a> Drop for AutoGCRuleTree<'a> { fn drop(&mut self) { + const DEBUG: bool = false; unsafe { self.0.gc(); + if DEBUG { + let guard = self.1.read(); + self.0.dump_stdout(&StylesheetGuards::same(&guard)); + } assert!( ::std::thread::panicking() || !self.0.root().has_children_for_testing(), "No rule nodes other than the root shall remain!" @@ -49,8 +54,7 @@ impl<'a> Drop for AutoGCRuleTree<'a> { } } -fn parse_rules(css: &str) -> Vec<(StyleSource, CascadeLevel)> { - let lock = SharedRwLock::new(); +fn parse_rules(lock: &SharedRwLock, css: &str) -> Vec<(StyleSource, CascadeLevel)> { let media = Arc::new(lock.wrap(MediaList::empty())); let s = Stylesheet::from_str( @@ -58,7 +62,7 @@ fn parse_rules(css: &str) -> Vec<(StyleSource, CascadeLevel)> { ServoUrl::parse("http://localhost").unwrap(), Origin::Author, media, - lock, + lock.clone(), None, Some(&ErrorringErrorReporter), QuirksMode::NoQuirks, @@ -105,15 +109,16 @@ fn test_insertion_style_attribute( fn bench_insertion_basic(b: &mut Bencher) { let r = RuleTree::new(); thread_state::initialize(ThreadState::SCRIPT); - + let lock = SharedRwLock::new(); let rules_matched = parse_rules( + &lock, ".foo { width: 200px; } \ .bar { height: 500px; } \ .baz { display: block; }", ); b.iter(|| { - let _gc = AutoGCRuleTree::new(&r); + let _gc = AutoGCRuleTree::new(&r, &lock); for _ in 0..(4000 + 400) { test::black_box(test_insertion(&r, rules_matched.clone())); @@ -126,14 +131,16 @@ fn bench_insertion_basic_per_element(b: &mut Bencher) { let r = RuleTree::new(); thread_state::initialize(ThreadState::SCRIPT); + let lock = SharedRwLock::new(); let rules_matched = parse_rules( + &lock, ".foo { width: 200px; } \ .bar { height: 500px; } \ .baz { display: block; }", ); b.iter(|| { - let _gc = AutoGCRuleTree::new(&r); + let _gc = AutoGCRuleTree::new(&r, &lock); test::black_box(test_insertion(&r, rules_matched.clone())); }); @@ -147,22 +154,19 @@ fn bench_expensive_insertion(b: &mut Bencher) { // This test case tests a case where you style a bunch of siblings // matching the same rules, with a different style attribute each // one. + let lock = SharedRwLock::new(); let rules_matched = parse_rules( + &lock, ".foo { width: 200px; } \ .bar { height: 500px; } \ .baz { display: block; }", ); - let shared_lock = SharedRwLock::new(); b.iter(|| { - let _gc = AutoGCRuleTree::new(&r); + let _gc = AutoGCRuleTree::new(&r, &lock); for _ in 0..(4000 + 400) { - test::black_box(test_insertion_style_attribute( - &r, - &rules_matched, - &shared_lock, - )); + test::black_box(test_insertion_style_attribute(&r, &rules_matched, &lock)); } }); } @@ -172,14 +176,16 @@ fn bench_insertion_basic_parallel(b: &mut Bencher) { let r = RuleTree::new(); thread_state::initialize(ThreadState::SCRIPT); + let lock = SharedRwLock::new(); let rules_matched = parse_rules( + &lock, ".foo { width: 200px; } \ .bar { height: 500px; } \ .baz { display: block; }", ); b.iter(|| { - let _gc = AutoGCRuleTree::new(&r); + let _gc = AutoGCRuleTree::new(&r, &lock); rayon::scope(|s| { for _ in 0..4 { @@ -203,32 +209,29 @@ fn bench_expensive_insertion_parallel(b: &mut Bencher) { let r = RuleTree::new(); thread_state::initialize(ThreadState::SCRIPT); + let lock = SharedRwLock::new(); let rules_matched = parse_rules( + &lock, ".foo { width: 200px; } \ .bar { height: 500px; } \ .baz { display: block; }", ); - let shared_lock = SharedRwLock::new(); b.iter(|| { - let _gc = AutoGCRuleTree::new(&r); + let _gc = AutoGCRuleTree::new(&r, &lock); rayon::scope(|s| { for _ in 0..4 { s.spawn(|s| { for _ in 0..1000 { - test::black_box(test_insertion_style_attribute( - &r, - &rules_matched, - &shared_lock, - )); + test::black_box(test_insertion_style_attribute(&r, &rules_matched, &lock)); } s.spawn(|_| { for _ in 0..100 { test::black_box(test_insertion_style_attribute( &r, &rules_matched, - &shared_lock, + &lock, )); } }) |