Auto merge of #23503 - emilio:gecko-sync, r=emilio

style: sync changes from mozilla-central

See each individual commit for details. This also cherry-picks #23463 with a few fixes that were needed in Gecko-only code.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23503)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-06-04 08:30:39 -04:00 committed by GitHub
commit fe8aad7227
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 897 additions and 915 deletions

View file

@ -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.

View file

@ -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

View file

@ -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(

View file

@ -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)),
}

View file

@ -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>(

View file

@ -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))

View file

@ -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())
@ -897,11 +897,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,
@ -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.

View file

@ -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,

View file

@ -12,6 +12,7 @@ path = "lib.rs"
[features]
servo = ["serde"]
gecko_refcount_logging = []
[dependencies]
nodrop = {version = "0.1.8"}

View file

@ -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;

View file

@ -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]

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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 {

View file

@ -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);
struct LoadDataKey(*const LoadDataSource);
unsafe impl Sync for CssUrlDataKey {}
unsafe impl Send for CssUrlDataKey {}
unsafe impl Sync for LoadDataKey {}
unsafe impl Send for LoadDataKey {}
/// 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),
/// 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 {
impl Drop for LoadData {
fn drop(&mut self) {
if self.load_id != 0 {
unsafe {
bindings::Gecko_LoadData_DeregisterLoad(self);
}
}
}
}
impl Default for LoadData {
fn default() -> Self {
Self {
resolved: RefPtr::null(),
load_id: 0,
tried_to_resolve: false,
}
}
}
/// 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,
}
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 => {},
}
let key = LoadDataKey(self);
{
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
}
}
impl ToShmem for LoadDataSource {
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),
LoadDataSource::Owned(..) => LoadDataSource::Lazy,
LoadDataSource::Lazy => LoadDataSource::Lazy,
})
}
}
/// 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>,
}
pub type SpecifiedUrl = CssUrl;
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 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 }
}
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)
}
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)
}
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)
},
}
}
/// 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)
}
}
/// Clears URL_VALUE_TABLE. Entries in this table, which are for specified URL
/// 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,10 +301,19 @@ impl ToComputedValue for SpecifiedImageUrl {
}
}
fn serialize_computed_url<W>(
url_value: &URLValue,
/// 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 ComputedUrl {
fn serialize_with<W>(
&self,
function: unsafe extern "C" fn(*const Self, *mut nsCString),
dest: &mut CssWriter<W>,
get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString),
) -> fmt::Result
where
W: Write,
@ -331,88 +321,48 @@ where
dest.write_str("url(")?;
unsafe {
let mut string = nsCString::new();
get_url(url_value, &mut string);
function(self, &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)]
pub struct ComputedUrl(pub SpecifiedUrl);
}
impl ToCss for ComputedUrl {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.0
.with_url_value(|r| serialize_computed_url(r, dest, bindings::Gecko_GetComputedURLSpec))
}
}
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()
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()
};
}

View file

@ -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>(

View file

@ -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,18 +107,22 @@ impl<T: RefCounted> RefPtr<T> {
/// Addref the inner data, obviously leaky on its own.
pub fn addref(&self) {
if !self.ptr.is_null() {
unsafe {
(*self.ptr).addref();
}
}
}
/// Release the inner data.
///
/// Call only when the data actually needs releasing.
pub unsafe fn release(&self) {
if !self.ptr.is_null() {
(*self.ptr).release();
}
}
}
impl<T: RefCounted> UniqueRefPtr<T> {
/// Create a unique refptr from an already addrefed pointer obtained from
@ -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,

View file

@ -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(),
};
}

View file

@ -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 namespace(&self) -> &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl {
self.element.namespace()
fn has_namespace(
&self,
ns: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl,
) -> bool {
self.element.has_namespace(ns)
}
#[inline]
fn is_same_type(&self, other: &Self) -> bool {
self.element.is_same_type(&other.element)
}
fn attr_matches(

View file

@ -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);
}
}

View file

@ -25,7 +25,6 @@ STYLE_STRUCT_LIST = [
"background",
"border",
"box",
"color",
"column",
"counters",
"effects",

View file

@ -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,
)
}
}

View file

@ -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;

View file

@ -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",

View file

@ -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

View file

@ -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",

View file

@ -6,6 +6,16 @@
<% from data import Keyword %>
<% 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",

View file

@ -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())
}
}

View file

@ -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 key(&self) -> Option<ChildKey> {
Some(ChildKey(self.level, self.source.as_ref()?.key()))
}
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 prev_sibling(&self) -> &AtomicPtr<RuleNode> {
debug_assert!(!self.is_root());
unsafe { self.prev_sibling_or_free_count.as_prev_sibling() }
}
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;
use parking_lot::RwLockUpgradableReadGuard;
// 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 {
let key = ChildKey(level, source.key());
let read_guard = self.get().children.upgradable_read();
if let Some(child) = read_guard.get(&key) {
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;
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,
)));
loop {
let next;
vacant.insert(new_node.downgrade());
{
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,
};
// 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);
}
return StrongRuleNode::new(node);
}
// 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())
StrongRuleNode::from_ptr(self.p)
}
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()
}
}
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 }
}
}

View file

@ -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,
))
}
}

View file

@ -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 {

View file

@ -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.

View file

@ -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 {

View file

@ -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));

View file

@ -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>;

View file

@ -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>;

View file

@ -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(),

View file

@ -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.

View file

@ -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

View file

@ -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,
}
}
}

View file

@ -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,16 +359,14 @@ impl Parse for Color {
Err(e) => {
#[cfg(feature = "gecko")]
{
if let Ok(ident) = input.expect_ident() {
if let Ok(system) = SystemColor::from_ident(ident) {
if let Ok(system) = input.try(|i| SystemColor::parse(context, i)) {
return Ok(Color::System(system));
}
if let Ok(c) = gecko::SpecialColorKeyword::from_ident(ident) {
if let Ok(c) = input.try(gecko::SpecialColorKeyword::parse) {
return Ok(Color::Special(c));
}
}
}
match e.kind {
ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
@ -340,18 +544,16 @@ 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();
let prefs = _context?.device().pref_sheet_prefs();
convert_nscolor_to_computedcolor(match special {
Keyword::MozDefaultColor => prefs.mDefaultColor,
Keyword::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor,
@ -359,13 +561,12 @@ impl Color {
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]

View file

@ -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)]

View file

@ -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()?;

View file

@ -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};

View file

@ -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]

View file

@ -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,
));
}
})