mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #21735 - emilio:gecko-sync, r=emilio
style: Sync changes from mozilla-central. See each individual commit for details. <!-- 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/21735) <!-- Reviewable:end -->
This commit is contained in:
commit
bdf450336e
16 changed files with 1212 additions and 923 deletions
|
@ -685,7 +685,9 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
|||
type Impl = SelectorImpl;
|
||||
|
||||
fn opaque(&self) -> ::selectors::OpaqueElement {
|
||||
::selectors::OpaqueElement::new(self.as_node().opaque().0 as *const ())
|
||||
::selectors::OpaqueElement::new(unsafe {
|
||||
&*(self.as_node().opaque().0 as *const ())
|
||||
})
|
||||
}
|
||||
|
||||
fn parent_element(&self) -> Option<ServoLayoutElement<'le>> {
|
||||
|
@ -1258,7 +1260,9 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
|||
type Impl = SelectorImpl;
|
||||
|
||||
fn opaque(&self) -> ::selectors::OpaqueElement {
|
||||
::selectors::OpaqueElement::new(self.as_node().opaque().0 as *const ())
|
||||
::selectors::OpaqueElement::new(unsafe {
|
||||
&*(self.as_node().opaque().0 as *const ())
|
||||
})
|
||||
}
|
||||
|
||||
fn parent_element(&self) -> Option<Self> {
|
||||
|
|
|
@ -2616,8 +2616,11 @@ impl VirtualMethods for Element {
|
|||
impl<'a> SelectorsElement for DomRoot<Element> {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn opaque(&self) -> ::selectors::OpaqueElement {
|
||||
::selectors::OpaqueElement::new(self.reflector().get_jsobject().get())
|
||||
::selectors::OpaqueElement::new(unsafe {
|
||||
&*self.reflector().get_jsobject().get()
|
||||
})
|
||||
}
|
||||
|
||||
fn parent_element(&self) -> Option<DomRoot<Element>> {
|
||||
|
|
|
@ -8,18 +8,22 @@
|
|||
use attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
|
||||
use matching::{ElementSelectorFlags, MatchingContext};
|
||||
use parser::SelectorImpl;
|
||||
use servo_arc::NonZeroPtrMut;
|
||||
use std::fmt::Debug;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
/// Opaque representation of an Element, for identity comparisons. We use
|
||||
/// NonZeroPtrMut to get the NonZero optimization.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct OpaqueElement(NonZeroPtrMut<()>);
|
||||
pub struct OpaqueElement(NonNull<()>);
|
||||
|
||||
unsafe impl Send for OpaqueElement {}
|
||||
|
||||
impl OpaqueElement {
|
||||
/// Creates a new OpaqueElement from an arbitrarily-typed pointer.
|
||||
pub fn new<T>(ptr: *const T) -> Self {
|
||||
OpaqueElement(NonZeroPtrMut::new(ptr as *const () as *mut ()))
|
||||
pub fn new<T>(ptr: &T) -> Self {
|
||||
unsafe {
|
||||
OpaqueElement(NonNull::new_unchecked(ptr as *const T as *const () as *mut ()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
//!
|
||||
//! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883
|
||||
|
||||
// The semantics of Arc are alread documented in the Rust docs, so we don't
|
||||
// The semantics of `Arc` are alread documented in the Rust docs, so we don't
|
||||
// duplicate those here.
|
||||
#![allow(missing_docs)]
|
||||
|
||||
|
@ -74,72 +74,41 @@ macro_rules! offset_of {
|
|||
/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
|
||||
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
|
||||
|
||||
/// Wrapper type for pointers to get the non-zero optimization. When
|
||||
/// NonZero/Shared/Unique are stabilized, we should just use Shared
|
||||
/// here to get the same effect. Gankro is working on this in [1].
|
||||
/// An atomically reference counted shared pointer
|
||||
///
|
||||
/// It's unfortunate that this needs to infect all the caller types
|
||||
/// with 'static. It would be nice to just use a &() and a PhantomData<T>
|
||||
/// instead, but then the compiler can't determine whether the &() should
|
||||
/// be thin or fat (which depends on whether or not T is sized). Given
|
||||
/// that this is all a temporary hack, this restriction is fine for now.
|
||||
/// See the documentation for [`Arc`] in the standard library. Unlike the
|
||||
/// standard library `Arc`, this `Arc` does not support weak reference counting.
|
||||
///
|
||||
/// [1]: https://github.com/rust-lang/rust/issues/27730
|
||||
// FIXME: remove this and use std::ptr::NonNull when Firefox requires Rust 1.25+
|
||||
pub struct NonZeroPtrMut<T: ?Sized + 'static>(&'static mut T);
|
||||
impl<T: ?Sized> NonZeroPtrMut<T> {
|
||||
pub fn new(ptr: *mut T) -> Self {
|
||||
assert!(!(ptr as *mut u8).is_null());
|
||||
NonZeroPtrMut(unsafe { mem::transmute(ptr) })
|
||||
}
|
||||
|
||||
pub fn ptr(&self) -> *mut T {
|
||||
self.0 as *const T as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + 'static> Clone for NonZeroPtrMut<T> {
|
||||
fn clone(&self) -> Self {
|
||||
NonZeroPtrMut::new(self.ptr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + 'static> fmt::Pointer for NonZeroPtrMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&self.ptr(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + 'static> fmt::Debug for NonZeroPtrMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Pointer>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + 'static> PartialEq for NonZeroPtrMut<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ptr() == other.ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + 'static> Eq for NonZeroPtrMut<T> {}
|
||||
|
||||
impl<T: Sized + 'static> Hash for NonZeroPtrMut<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.ptr().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
|
||||
#[repr(C)]
|
||||
pub struct Arc<T: ?Sized + 'static> {
|
||||
p: NonZeroPtrMut<ArcInner<T>>,
|
||||
pub struct Arc<T: ?Sized> {
|
||||
p: ptr::NonNull<ArcInner<T>>,
|
||||
}
|
||||
|
||||
/// An Arc that is known to be uniquely owned
|
||||
/// An `Arc` that is known to be uniquely owned
|
||||
///
|
||||
/// This lets us build arcs that we can mutate before
|
||||
/// freezing, without needing to change the allocation
|
||||
pub struct UniqueArc<T: ?Sized + 'static>(Arc<T>);
|
||||
/// When `Arc`s are constructed, they are known to be
|
||||
/// uniquely owned. In such a case it is safe to mutate
|
||||
/// the contents of the `Arc`. Normally, one would just handle
|
||||
/// this by mutating the data on the stack before allocating the
|
||||
/// `Arc`, however it's possible the data is large or unsized
|
||||
/// and you need to heap-allocate it earlier in such a way
|
||||
/// that it can be freely converted into a regular `Arc` once you're
|
||||
/// done.
|
||||
///
|
||||
/// `UniqueArc` exists for this purpose, when constructed it performs
|
||||
/// the same allocations necessary for an `Arc`, however it allows mutable access.
|
||||
/// Once the mutation is finished, you can call `.shareable()` and get a regular `Arc`
|
||||
/// out of it.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servo_arc::UniqueArc;
|
||||
/// let data = [1, 2, 3, 4, 5];
|
||||
/// let mut x = UniqueArc::new(data);
|
||||
/// x[4] = 7; // mutate!
|
||||
/// let y = x.shareable(); // y is an Arc<T>
|
||||
/// ```
|
||||
pub struct UniqueArc<T: ?Sized>(Arc<T>);
|
||||
|
||||
impl<T> UniqueArc<T> {
|
||||
#[inline]
|
||||
|
@ -149,7 +118,7 @@ impl<T> UniqueArc<T> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
/// Convert to a shareable Arc<T> once we're done using it
|
||||
/// Convert to a shareable Arc<T> once we're done mutating it
|
||||
pub fn shareable(self) -> Arc<T> {
|
||||
self.0
|
||||
}
|
||||
|
@ -172,6 +141,7 @@ impl<T> DerefMut for UniqueArc<T> {
|
|||
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
|
||||
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
|
||||
|
||||
/// The object allocated by an Arc<T>
|
||||
#[repr(C)]
|
||||
struct ArcInner<T: ?Sized> {
|
||||
count: atomic::AtomicUsize,
|
||||
|
@ -182,36 +152,52 @@ unsafe impl<T: ?Sized + Sync + Send> Send for ArcInner<T> {}
|
|||
unsafe impl<T: ?Sized + Sync + Send> Sync for ArcInner<T> {}
|
||||
|
||||
impl<T> Arc<T> {
|
||||
/// Construct an `Arc<T>`
|
||||
#[inline]
|
||||
pub fn new(data: T) -> Self {
|
||||
let x = Box::new(ArcInner {
|
||||
count: atomic::AtomicUsize::new(1),
|
||||
data: data,
|
||||
});
|
||||
Arc {
|
||||
p: NonZeroPtrMut::new(Box::into_raw(x)),
|
||||
unsafe {
|
||||
Arc {
|
||||
p: ptr::NonNull::new_unchecked(Box::into_raw(x)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// It is recommended to use RawOffsetArc for this.
|
||||
#[inline]
|
||||
pub fn into_raw(this: Self) -> *const T {
|
||||
fn into_raw(this: Self) -> *const T {
|
||||
let ptr = unsafe { &((*this.ptr()).data) as *const _ };
|
||||
mem::forget(this);
|
||||
ptr
|
||||
}
|
||||
|
||||
/// Reconstruct the Arc<T> from a raw pointer obtained from into_raw()
|
||||
///
|
||||
/// Note: This raw pointer will be offset in the allocation and must be preceded
|
||||
/// by the atomic count.
|
||||
///
|
||||
/// It is recommended to use RawOffsetArc for this
|
||||
#[inline]
|
||||
unsafe fn from_raw(ptr: *const T) -> Self {
|
||||
// To find the corresponding pointer to the `ArcInner` we need
|
||||
// to subtract the offset of the `data` field from the pointer.
|
||||
let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner<T>, data));
|
||||
Arc {
|
||||
p: NonZeroPtrMut::new(ptr as *mut ArcInner<T>),
|
||||
p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>),
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce a pointer to the data that can be converted back
|
||||
/// to an arc
|
||||
/// to an Arc. This is basically an `&Arc<T>`, without the extra indirection.
|
||||
/// It has the benefits of an `&T` but also knows about the underlying refcount
|
||||
/// and can be converted into more `Arc<T>`s if necessary.
|
||||
#[inline]
|
||||
pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> {
|
||||
ArcBorrow(&**self)
|
||||
|
@ -240,7 +226,7 @@ impl<T> Arc<T> {
|
|||
/// Returns the address on the heap of the Arc itself -- not the T within it -- for memory
|
||||
/// reporting.
|
||||
pub fn heap_ptr(&self) -> *const c_void {
|
||||
self.p.ptr() as *const ArcInner<T> as *const c_void
|
||||
self.p.as_ptr() as *const ArcInner<T> as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,13 +247,15 @@ impl<T: ?Sized> Arc<T> {
|
|||
let _ = Box::from_raw(self.ptr());
|
||||
}
|
||||
|
||||
/// Test pointer equality between the two Arcs, i.e. they must be the _same_
|
||||
/// allocation
|
||||
#[inline]
|
||||
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
|
||||
this.ptr() == other.ptr()
|
||||
}
|
||||
|
||||
fn ptr(&self) -> *mut ArcInner<T> {
|
||||
self.p.ptr()
|
||||
self.p.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,8 +288,10 @@ impl<T: ?Sized> Clone for Arc<T> {
|
|||
process::abort();
|
||||
}
|
||||
|
||||
Arc {
|
||||
p: NonZeroPtrMut::new(self.ptr()),
|
||||
unsafe {
|
||||
Arc {
|
||||
p: ptr::NonNull::new_unchecked(self.ptr()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -316,6 +306,19 @@ impl<T: ?Sized> Deref for Arc<T> {
|
|||
}
|
||||
|
||||
impl<T: Clone> Arc<T> {
|
||||
/// Makes a mutable reference to the `Arc`, cloning if necessary
|
||||
///
|
||||
/// This is functionally equivalent to [`Arc::make_mut`][mm] from the standard library.
|
||||
///
|
||||
/// If this `Arc` is uniquely owned, `make_mut()` will provide a mutable
|
||||
/// reference to the contents. If not, `make_mut()` will create a _new_ `Arc`
|
||||
/// with a copy of the contents, update `this` to point to it, and provide
|
||||
/// a mutable reference to its contents.
|
||||
///
|
||||
/// This is useful for implementing copy-on-write schemes where you wish to
|
||||
/// avoid copying things if your `Arc` is not shared.
|
||||
///
|
||||
/// [mm]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.make_mut
|
||||
#[inline]
|
||||
pub fn make_mut(this: &mut Self) -> &mut T {
|
||||
if !this.is_unique() {
|
||||
|
@ -335,6 +338,7 @@ impl<T: Clone> Arc<T> {
|
|||
}
|
||||
|
||||
impl<T: ?Sized> Arc<T> {
|
||||
/// Provides mutable access to the contents _if_ the `Arc` is uniquely owned.
|
||||
#[inline]
|
||||
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
|
||||
if this.is_unique() {
|
||||
|
@ -347,6 +351,7 @@ impl<T: ?Sized> Arc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether or not the `Arc` is uniquely owned (is the refcount 1?)
|
||||
#[inline]
|
||||
pub fn is_unique(&self) -> bool {
|
||||
// See the extensive discussion in [1] for why this needs to be Acquire.
|
||||
|
@ -402,6 +407,7 @@ impl<T: ?Sized + PartialEq> PartialEq for Arc<T> {
|
|||
!Self::ptr_eq(self, other) && *(*self) != *(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PartialOrd> PartialOrd for Arc<T> {
|
||||
fn partial_cmp(&self, other: &Arc<T>) -> Option<Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
|
@ -615,8 +621,10 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
|
|||
size_of::<usize>() * 2,
|
||||
"The Arc will be fat"
|
||||
);
|
||||
Arc {
|
||||
p: NonZeroPtrMut::new(ptr),
|
||||
unsafe {
|
||||
Arc {
|
||||
p: ptr::NonNull::new_unchecked(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,7 +659,22 @@ impl<H> HeaderWithLength<H> {
|
|||
}
|
||||
|
||||
type HeaderSliceWithLength<H, T> = HeaderSlice<HeaderWithLength<H>, T>;
|
||||
pub struct ThinArc<H: 'static, T: 'static> {
|
||||
|
||||
/// A "thin" `Arc` containing dynamically sized data
|
||||
///
|
||||
/// This is functionally equivalent to Arc<(H, [T])>
|
||||
///
|
||||
/// When you create an `Arc` containing a dynamically sized type
|
||||
/// like `HeaderSlice<H, [T]>`, the `Arc` is represented on the stack
|
||||
/// as a "fat pointer", where the length of the slice is stored
|
||||
/// alongside the `Arc`'s pointer. In some situations you may wish to
|
||||
/// have a thin pointer instead, perhaps for FFI compatibility
|
||||
/// or space efficiency.
|
||||
///
|
||||
/// `ThinArc` solves this by storing the length in the allocation itself,
|
||||
/// via `HeaderSliceWithLength`.
|
||||
#[repr(C)]
|
||||
pub struct ThinArc<H, T> {
|
||||
ptr: *mut ArcInner<HeaderSliceWithLength<H, [T; 1]>>,
|
||||
}
|
||||
|
||||
|
@ -670,7 +693,7 @@ fn thin_to_thick<H, T>(
|
|||
fake_slice as *mut ArcInner<HeaderSliceWithLength<H, [T]>>
|
||||
}
|
||||
|
||||
impl<H: 'static, T: 'static> ThinArc<H, T> {
|
||||
impl<H, T> ThinArc<H, T> {
|
||||
/// Temporarily converts |self| into a bonafide Arc and exposes it to the
|
||||
/// provided callback. The refcount is not modified.
|
||||
#[inline]
|
||||
|
@ -679,9 +702,11 @@ impl<H: 'static, T: 'static> ThinArc<H, T> {
|
|||
F: FnOnce(&Arc<HeaderSliceWithLength<H, [T]>>) -> U,
|
||||
{
|
||||
// Synthesize transient Arc, which never touches the refcount of the ArcInner.
|
||||
let transient = NoDrop::new(Arc {
|
||||
p: NonZeroPtrMut::new(thin_to_thick(self.ptr)),
|
||||
});
|
||||
let transient = unsafe {
|
||||
NoDrop::new(Arc {
|
||||
p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr)),
|
||||
})
|
||||
};
|
||||
|
||||
// Expose the transient Arc to the callback, which may clone it if it wants.
|
||||
let result = f(&transient);
|
||||
|
@ -695,6 +720,16 @@ impl<H: 'static, T: 'static> ThinArc<H, T> {
|
|||
result
|
||||
}
|
||||
|
||||
/// Creates a `ThinArc` for a HeaderSlice using the given header struct and
|
||||
/// iterator to generate the slice.
|
||||
pub fn from_header_and_iter<I>(header: H, items: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = T> + ExactSizeIterator,
|
||||
{
|
||||
let header = HeaderWithLength::new(header, items.len());
|
||||
Arc::into_thin(Arc::from_header_and_iter(header, items))
|
||||
}
|
||||
|
||||
/// Returns the address on the heap of the ThinArc itself -- not the T
|
||||
/// within it -- for memory reporting.
|
||||
#[inline]
|
||||
|
@ -712,22 +747,22 @@ impl<H, T> Deref for ThinArc<H, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<H: 'static, T: 'static> Clone for ThinArc<H, T> {
|
||||
impl<H, T> Clone for ThinArc<H, T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
ThinArc::with_arc(self, |a| Arc::into_thin(a.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: 'static, T: 'static> Drop for ThinArc<H, T> {
|
||||
impl<H, T> Drop for ThinArc<H, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
let _ = Arc::from_thin(ThinArc { ptr: self.ptr });
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: 'static, T: 'static> Arc<HeaderSliceWithLength<H, [T]>> {
|
||||
/// Converts an Arc into a ThinArc. This consumes the Arc, so the refcount
|
||||
impl<H, T> Arc<HeaderSliceWithLength<H, [T]>> {
|
||||
/// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
|
||||
/// is not modified.
|
||||
#[inline]
|
||||
pub fn into_thin(a: Self) -> ThinArc<H, T> {
|
||||
|
@ -744,29 +779,31 @@ impl<H: 'static, T: 'static> Arc<HeaderSliceWithLength<H, [T]>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts a ThinArc into an Arc. This consumes the ThinArc, so the refcount
|
||||
/// Converts a `ThinArc` into an `Arc`. This consumes the `ThinArc`, so the refcount
|
||||
/// is not modified.
|
||||
#[inline]
|
||||
pub fn from_thin(a: ThinArc<H, T>) -> Self {
|
||||
let ptr = thin_to_thick(a.ptr);
|
||||
mem::forget(a);
|
||||
Arc {
|
||||
p: NonZeroPtrMut::new(ptr),
|
||||
unsafe {
|
||||
Arc {
|
||||
p: ptr::NonNull::new_unchecked(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: PartialEq + 'static, T: PartialEq + 'static> PartialEq for ThinArc<H, T> {
|
||||
impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &ThinArc<H, T>) -> bool {
|
||||
ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| *a == *b))
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Eq + 'static, T: Eq + 'static> Eq for ThinArc<H, T> {}
|
||||
impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {}
|
||||
|
||||
/// An Arc, except it holds a pointer to the T instead of to the
|
||||
/// entire ArcInner.
|
||||
/// An `Arc`, except it holds a pointer to the T instead of to the
|
||||
/// entire ArcInner. This struct is FFI-compatible.
|
||||
///
|
||||
/// ```text
|
||||
/// Arc<T> RawOffsetArc<T>
|
||||
|
@ -779,31 +816,35 @@ impl<H: Eq + 'static, T: Eq + 'static> Eq for ThinArc<H, T> {}
|
|||
///
|
||||
/// This means that this is a direct pointer to
|
||||
/// its contained data (and can be read from by both C++ and Rust),
|
||||
/// but we can also convert it to a "regular" Arc<T> by removing the offset
|
||||
/// but we can also convert it to a "regular" Arc<T> by removing the offset.
|
||||
///
|
||||
/// This is very useful if you have an Arc-containing struct shared between Rust and C++,
|
||||
/// and wish for C++ to be able to read the data behind the `Arc` without incurring
|
||||
/// an FFI call overhead.
|
||||
#[derive(Eq)]
|
||||
#[repr(C)]
|
||||
pub struct RawOffsetArc<T: 'static> {
|
||||
ptr: NonZeroPtrMut<T>,
|
||||
pub struct RawOffsetArc<T> {
|
||||
ptr: ptr::NonNull<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T: 'static + Sync + Send> Send for RawOffsetArc<T> {}
|
||||
unsafe impl<T: 'static + Sync + Send> Sync for RawOffsetArc<T> {}
|
||||
unsafe impl<T: Sync + Send> Send for RawOffsetArc<T> {}
|
||||
unsafe impl<T: Sync + Send> Sync for RawOffsetArc<T> {}
|
||||
|
||||
impl<T: 'static> Deref for RawOffsetArc<T> {
|
||||
impl<T> Deref for RawOffsetArc<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr.ptr() }
|
||||
unsafe { &*self.ptr.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for RawOffsetArc<T> {
|
||||
impl<T> Clone for RawOffsetArc<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Arc::into_raw_offset(self.clone_arc())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Drop for RawOffsetArc<T> {
|
||||
impl<T> Drop for RawOffsetArc<T> {
|
||||
fn drop(&mut self) {
|
||||
let _ = Arc::from_raw_offset(RawOffsetArc {
|
||||
ptr: self.ptr.clone(),
|
||||
|
@ -811,7 +852,7 @@ impl<T: 'static> Drop for RawOffsetArc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + 'static> fmt::Debug for RawOffsetArc<T> {
|
||||
impl<T: fmt::Debug> fmt::Debug for RawOffsetArc<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
|
@ -827,7 +868,7 @@ impl<T: PartialEq> PartialEq for RawOffsetArc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> RawOffsetArc<T> {
|
||||
impl<T> RawOffsetArc<T> {
|
||||
/// Temporarily converts |self| into a bonafide Arc and exposes it to the
|
||||
/// provided callback. The refcount is not modified.
|
||||
#[inline]
|
||||
|
@ -836,7 +877,7 @@ impl<T: 'static> RawOffsetArc<T> {
|
|||
F: FnOnce(&Arc<T>) -> U,
|
||||
{
|
||||
// Synthesize transient Arc, which never touches the refcount of the ArcInner.
|
||||
let transient = unsafe { NoDrop::new(Arc::from_raw(self.ptr.ptr())) };
|
||||
let transient = unsafe { NoDrop::new(Arc::from_raw(self.ptr.as_ptr())) };
|
||||
|
||||
// Expose the transient Arc to the callback, which may clone it if it wants.
|
||||
let result = f(&transient);
|
||||
|
@ -852,6 +893,8 @@ impl<T: 'static> RawOffsetArc<T> {
|
|||
|
||||
/// If uniquely owned, provide a mutable reference
|
||||
/// Else create a copy, and mutate that
|
||||
///
|
||||
/// This is functionally the same thing as `Arc::make_mut`
|
||||
#[inline]
|
||||
pub fn make_mut(&mut self) -> &mut T
|
||||
where
|
||||
|
@ -872,54 +915,57 @@ impl<T: 'static> RawOffsetArc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Clone it as an Arc
|
||||
/// Clone it as an `Arc`
|
||||
#[inline]
|
||||
pub fn clone_arc(&self) -> Arc<T> {
|
||||
RawOffsetArc::with_arc(self, |a| a.clone())
|
||||
}
|
||||
|
||||
/// Produce a pointer to the data that can be converted back
|
||||
/// to an arc
|
||||
/// to an `Arc`
|
||||
#[inline]
|
||||
pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> {
|
||||
ArcBorrow(&**self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Arc<T> {
|
||||
/// Converts an Arc into a RawOffsetArc. This consumes the Arc, so the refcount
|
||||
impl<T> Arc<T> {
|
||||
/// Converts an `Arc` into a `RawOffsetArc`. This consumes the `Arc`, so the refcount
|
||||
/// is not modified.
|
||||
#[inline]
|
||||
pub fn into_raw_offset(a: Self) -> RawOffsetArc<T> {
|
||||
RawOffsetArc {
|
||||
ptr: NonZeroPtrMut::new(Arc::into_raw(a) as *mut T),
|
||||
unsafe {
|
||||
RawOffsetArc {
|
||||
ptr: ptr::NonNull::new_unchecked(Arc::into_raw(a) as *mut T),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a RawOffsetArc into an Arc. This consumes the RawOffsetArc, so the refcount
|
||||
/// Converts a `RawOffsetArc` into an `Arc`. This consumes the `RawOffsetArc`, so the refcount
|
||||
/// is not modified.
|
||||
#[inline]
|
||||
pub fn from_raw_offset(a: RawOffsetArc<T>) -> Self {
|
||||
let ptr = a.ptr.ptr();
|
||||
let ptr = a.ptr.as_ptr();
|
||||
mem::forget(a);
|
||||
unsafe { Arc::from_raw(ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A "borrowed Arc". This is a pointer to
|
||||
/// A "borrowed `Arc`". This is a pointer to
|
||||
/// a T that is known to have been allocated within an
|
||||
/// Arc.
|
||||
/// `Arc`.
|
||||
///
|
||||
/// This is equivalent in guarantees to `&Arc<T>`, however it is
|
||||
/// a bit more flexible. To obtain an `&Arc<T>` you must have
|
||||
/// an Arc<T> instance somewhere pinned down until we're done with it.
|
||||
/// an `Arc<T>` instance somewhere pinned down until we're done with it.
|
||||
/// It's also a direct pointer to `T`, so using this involves less pointer-chasing
|
||||
///
|
||||
/// However, Gecko hands us refcounted things as pointers to T directly,
|
||||
/// so we have to conjure up a temporary Arc on the stack each time. The
|
||||
/// same happens for when the object is managed by a RawOffsetArc.
|
||||
/// However, C++ code may hand us refcounted things as pointers to T directly,
|
||||
/// so we have to conjure up a temporary `Arc` on the stack each time. The
|
||||
/// same happens for when the object is managed by a `RawOffsetArc`.
|
||||
///
|
||||
/// ArcBorrow lets us deal with borrows of known-refcounted objects
|
||||
/// without needing to worry about how they're actually stored.
|
||||
/// `ArcBorrow` lets us deal with borrows of known-refcounted objects
|
||||
/// without needing to worry about where the `Arc<T>` is.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct ArcBorrow<'a, T: 'a>(&'a T);
|
||||
|
||||
|
@ -932,6 +978,7 @@ impl<'a, T> Clone for ArcBorrow<'a, T> {
|
|||
}
|
||||
|
||||
impl<'a, T> ArcBorrow<'a, T> {
|
||||
/// Clone this as an `Arc<T>`. This bumps the refcount.
|
||||
#[inline]
|
||||
pub fn clone_arc(&self) -> Arc<T> {
|
||||
let arc = unsafe { Arc::from_raw(self.0) };
|
||||
|
@ -947,10 +994,14 @@ impl<'a, T> ArcBorrow<'a, T> {
|
|||
ArcBorrow(r)
|
||||
}
|
||||
|
||||
/// Compare two `ArcBorrow`s via pointer equality. Will only return
|
||||
/// true if they come from the same allocation
|
||||
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
|
||||
this.0 as *const T == other.0 as *const T
|
||||
}
|
||||
|
||||
/// Temporarily converts |self| into a bonafide Arc and exposes it to the
|
||||
/// provided callback. The refcount is not modified.
|
||||
#[inline]
|
||||
pub fn with_arc<F, U>(&self, f: F) -> U
|
||||
where
|
||||
|
@ -989,18 +1040,25 @@ impl<'a, T> Deref for ArcBorrow<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A tagged union that can represent Arc<A> or Arc<B> while only consuming a
|
||||
/// single word. The type is also NonZero, and thus can be stored in an Option
|
||||
/// A tagged union that can represent `Arc<A>` or `Arc<B>` while only consuming a
|
||||
/// single word. The type is also `NonNull`, and thus can be stored in an Option
|
||||
/// without increasing size.
|
||||
///
|
||||
/// This is functionally equivalent to
|
||||
/// `enum ArcUnion<A, B> { First(Arc<A>), Second(Arc<B>)` but only takes up
|
||||
/// up a single word of stack space.
|
||||
///
|
||||
/// This could probably be extended to support four types if necessary.
|
||||
pub struct ArcUnion<A: 'static, B: 'static> {
|
||||
p: NonZeroPtrMut<()>,
|
||||
phantom_a: PhantomData<&'static A>,
|
||||
phantom_b: PhantomData<&'static B>,
|
||||
pub struct ArcUnion<A, B> {
|
||||
p: ptr::NonNull<()>,
|
||||
phantom_a: PhantomData<A>,
|
||||
phantom_b: PhantomData<B>,
|
||||
}
|
||||
|
||||
impl<A: PartialEq + 'static, B: PartialEq + 'static> PartialEq for ArcUnion<A, B> {
|
||||
unsafe impl<A: Sync + Send, B: Send + Sync> Send for ArcUnion<A, B> {}
|
||||
unsafe impl<A: Sync + Send, B: Send + Sync> Sync for ArcUnion<A, B> {}
|
||||
|
||||
impl<A: PartialEq, B: PartialEq> PartialEq for ArcUnion<A, B> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use ArcUnionBorrow::*;
|
||||
match (self.borrow(), other.borrow()) {
|
||||
|
@ -1011,16 +1069,17 @@ impl<A: PartialEq + 'static, B: PartialEq + 'static> PartialEq for ArcUnion<A, B
|
|||
}
|
||||
}
|
||||
|
||||
/// This represents a borrow of an `ArcUnion`.
|
||||
#[derive(Debug)]
|
||||
pub enum ArcUnionBorrow<'a, A: 'static, B: 'static> {
|
||||
pub enum ArcUnionBorrow<'a, A: 'a, B: 'a> {
|
||||
First(ArcBorrow<'a, A>),
|
||||
Second(ArcBorrow<'a, B>),
|
||||
}
|
||||
|
||||
impl<A: 'static, B: 'static> ArcUnion<A, B> {
|
||||
fn new(ptr: *mut ()) -> Self {
|
||||
impl<A, B> ArcUnion<A, B> {
|
||||
unsafe fn new(ptr: *mut ()) -> Self {
|
||||
ArcUnion {
|
||||
p: NonZeroPtrMut::new(ptr),
|
||||
p: ptr::NonNull::new_unchecked(ptr),
|
||||
phantom_a: PhantomData,
|
||||
phantom_b: PhantomData,
|
||||
}
|
||||
|
@ -1034,37 +1093,37 @@ impl<A: 'static, B: 'static> ArcUnion<A, B> {
|
|||
/// Returns an enum representing a borrow of either A or B.
|
||||
pub fn borrow(&self) -> ArcUnionBorrow<A, B> {
|
||||
if self.is_first() {
|
||||
let ptr = self.p.ptr() as *const A;
|
||||
let ptr = self.p.as_ptr() as *const A;
|
||||
let borrow = unsafe { ArcBorrow::from_ref(&*ptr) };
|
||||
ArcUnionBorrow::First(borrow)
|
||||
} else {
|
||||
let ptr = ((self.p.ptr() as usize) & !0x1) as *const B;
|
||||
let ptr = ((self.p.as_ptr() as usize) & !0x1) as *const B;
|
||||
let borrow = unsafe { ArcBorrow::from_ref(&*ptr) };
|
||||
ArcUnionBorrow::Second(borrow)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an ArcUnion from an instance of the first type.
|
||||
/// Creates an `ArcUnion` from an instance of the first type.
|
||||
pub fn from_first(other: Arc<A>) -> Self {
|
||||
Self::new(Arc::into_raw(other) as *mut _)
|
||||
unsafe { Self::new(Arc::into_raw(other) as *mut _) }
|
||||
}
|
||||
|
||||
/// Creates an ArcUnion from an instance of the second type.
|
||||
/// Creates an `ArcUnion` from an instance of the second type.
|
||||
pub fn from_second(other: Arc<B>) -> Self {
|
||||
Self::new(((Arc::into_raw(other) as usize) | 0x1) as *mut _)
|
||||
unsafe { Self::new(((Arc::into_raw(other) as usize) | 0x1) as *mut _) }
|
||||
}
|
||||
|
||||
/// Returns true if this ArcUnion contains the first type.
|
||||
/// Returns true if this `ArcUnion` contains the first type.
|
||||
pub fn is_first(&self) -> bool {
|
||||
self.p.ptr() as usize & 0x1 == 0
|
||||
self.p.as_ptr() as usize & 0x1 == 0
|
||||
}
|
||||
|
||||
/// Returns true if this ArcUnion contains the second type.
|
||||
/// Returns true if this `ArcUnion` contains the second type.
|
||||
pub fn is_second(&self) -> bool {
|
||||
!self.is_first()
|
||||
}
|
||||
|
||||
/// Returns a borrow of the first type if applicable, otherwise None.
|
||||
/// Returns a borrow of the first type if applicable, otherwise `None`.
|
||||
pub fn as_first(&self) -> Option<ArcBorrow<A>> {
|
||||
match self.borrow() {
|
||||
ArcUnionBorrow::First(x) => Some(x),
|
||||
|
@ -1081,7 +1140,7 @@ impl<A: 'static, B: 'static> ArcUnion<A, B> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: 'static, B: 'static> Clone for ArcUnion<A, B> {
|
||||
impl<A, B> Clone for ArcUnion<A, B> {
|
||||
fn clone(&self) -> Self {
|
||||
match self.borrow() {
|
||||
ArcUnionBorrow::First(x) => ArcUnion::from_first(x.clone_arc()),
|
||||
|
@ -1090,7 +1149,7 @@ impl<A: 'static, B: 'static> Clone for ArcUnion<A, B> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: 'static, B: 'static> Drop for ArcUnion<A, B> {
|
||||
impl<A, B> Drop for ArcUnion<A, B> {
|
||||
fn drop(&mut self) {
|
||||
match self.borrow() {
|
||||
ArcUnionBorrow::First(x) => unsafe {
|
||||
|
|
|
@ -10,7 +10,7 @@ use Atom;
|
|||
use cssparser::{Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType};
|
||||
use hash::map::Entry;
|
||||
use precomputed_hash::PrecomputedHash;
|
||||
use properties::{CSSWideKeyword, DeclaredValue};
|
||||
use properties::{CSSWideKeyword, CustomDeclarationValue};
|
||||
use selector_map::{PrecomputedHashMap, PrecomputedHashSet};
|
||||
use selectors::parser::SelectorParseErrorKind;
|
||||
use servo_arc::Arc;
|
||||
|
@ -519,14 +519,14 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
pub fn cascade(
|
||||
&mut self,
|
||||
name: &'a Name,
|
||||
specified_value: DeclaredValue<'a, Arc<SpecifiedValue>>,
|
||||
specified_value: &CustomDeclarationValue,
|
||||
) {
|
||||
let was_already_present = !self.seen.insert(name);
|
||||
if was_already_present {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.value_may_affect_style(name, &specified_value) {
|
||||
if !self.value_may_affect_style(name, specified_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -538,13 +538,12 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
}
|
||||
|
||||
let map = self.custom_properties.as_mut().unwrap();
|
||||
match specified_value {
|
||||
DeclaredValue::Value(ref specified_value) => {
|
||||
self.may_have_cycles |= !specified_value.references.is_empty();
|
||||
map.insert(name.clone(), (*specified_value).clone());
|
||||
match *specified_value {
|
||||
CustomDeclarationValue::Value(ref unparsed_value) => {
|
||||
self.may_have_cycles |= !unparsed_value.references.is_empty();
|
||||
map.insert(name.clone(), (*unparsed_value).clone());
|
||||
},
|
||||
DeclaredValue::WithVariables(_) => unreachable!(),
|
||||
DeclaredValue::CSSWideKeyword(keyword) => match keyword {
|
||||
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
||||
CSSWideKeyword::Initial => {
|
||||
map.remove(name);
|
||||
},
|
||||
|
@ -557,11 +556,11 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
fn value_may_affect_style(
|
||||
&self,
|
||||
name: &Name,
|
||||
value: &DeclaredValue<Arc<SpecifiedValue>>,
|
||||
value: &CustomDeclarationValue,
|
||||
) -> bool {
|
||||
match *value {
|
||||
DeclaredValue::CSSWideKeyword(CSSWideKeyword::Unset) |
|
||||
DeclaredValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) |
|
||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
||||
// Custom properties are inherited by default. So
|
||||
// explicit 'inherit' or 'unset' means we can just use
|
||||
// any existing value in the inherited CustomPropertiesMap.
|
||||
|
@ -577,12 +576,12 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
.or_else(|| self.inherited.and_then(|m| m.get(name)));
|
||||
|
||||
match (existing_value, value) {
|
||||
(None, &DeclaredValue::CSSWideKeyword(CSSWideKeyword::Initial)) => {
|
||||
(None, &CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)) => {
|
||||
// The initial value of a custom property is the same as it
|
||||
// not existing in the map.
|
||||
return false;
|
||||
},
|
||||
(Some(existing_value), &DeclaredValue::Value(specified_value)) => {
|
||||
(Some(existing_value), &CustomDeclarationValue::Value(ref specified_value)) => {
|
||||
// Don't bother overwriting an existing inherited value with
|
||||
// the same specified value.
|
||||
if existing_value == specified_value {
|
||||
|
|
808
components/style/properties/cascade.rs
Normal file
808
components/style/properties/cascade.rs
Normal file
|
@ -0,0 +1,808 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! The main cascading algorithm of the style system.
|
||||
|
||||
use context::QuirksMode;
|
||||
use custom_properties::CustomPropertiesBuilder;
|
||||
use dom::TElement;
|
||||
use font_metrics::FontMetricsProvider;
|
||||
use logical_geometry::WritingMode;
|
||||
use media_queries::Device;
|
||||
use properties::{ComputedValues, StyleBuilder};
|
||||
use properties::{LonghandId, LonghandIdSet};
|
||||
use properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
|
||||
use properties::CASCADE_PROPERTY;
|
||||
use rule_cache::{RuleCache, RuleCacheConditions};
|
||||
use rule_tree::{CascadeLevel, StrongRuleNode};
|
||||
use selector_parser::PseudoElement;
|
||||
use servo_arc::Arc;
|
||||
use shared_lock::StylesheetGuards;
|
||||
use smallbitvec::SmallBitVec;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use style_adjuster::StyleAdjuster;
|
||||
use values::computed;
|
||||
|
||||
/// We split the cascade in two phases: 'early' properties, and 'late'
|
||||
/// properties.
|
||||
///
|
||||
/// Early properties are the ones that don't have dependencies _and_ other
|
||||
/// properties depend on, for example, writing-mode related properties, color
|
||||
/// (for currentColor), or font-size (for em, etc).
|
||||
///
|
||||
/// Late properties are all the others.
|
||||
trait CascadePhase {
|
||||
fn is_early() -> bool;
|
||||
}
|
||||
|
||||
struct EarlyProperties;
|
||||
impl CascadePhase for EarlyProperties {
|
||||
fn is_early() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct LateProperties;
|
||||
impl CascadePhase for LateProperties {
|
||||
fn is_early() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum ApplyResetProperties {
|
||||
No,
|
||||
Yes,
|
||||
}
|
||||
|
||||
/// Performs the CSS cascade, computing new styles for an element from its parent style.
|
||||
///
|
||||
/// The arguments are:
|
||||
///
|
||||
/// * `device`: Used to get the initial viewport and other external state.
|
||||
///
|
||||
/// * `rule_node`: The rule node in the tree that represent the CSS rules that
|
||||
/// matched.
|
||||
///
|
||||
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
|
||||
///
|
||||
/// Returns the computed values.
|
||||
/// * `flags`: Various flags.
|
||||
///
|
||||
pub fn cascade<E>(
|
||||
device: &Device,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
rule_node: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<&ComputedValues>,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
visited_rules: Option<&StrongRuleNode>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
element: Option<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
cascade_rules(
|
||||
device,
|
||||
pseudo,
|
||||
rule_node,
|
||||
guards,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
font_metrics_provider,
|
||||
CascadeMode::Unvisited { visited_rules },
|
||||
quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
element,
|
||||
)
|
||||
}
|
||||
|
||||
fn cascade_rules<E>(
|
||||
device: &Device,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
rule_node: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<&ComputedValues>,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
cascade_mode: CascadeMode,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
element: Option<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
debug_assert_eq!(
|
||||
parent_style.is_some(),
|
||||
parent_style_ignoring_first_line.is_some()
|
||||
);
|
||||
let empty = SmallBitVec::new();
|
||||
let restriction = pseudo.and_then(|p| p.property_restriction());
|
||||
let iter_declarations = || {
|
||||
rule_node.self_and_ancestors().flat_map(|node| {
|
||||
let cascade_level = node.cascade_level();
|
||||
let node_importance = node.importance();
|
||||
let declarations = match node.style_source() {
|
||||
Some(source) => source
|
||||
.read(cascade_level.guard(guards))
|
||||
.declaration_importance_iter(),
|
||||
None => DeclarationImportanceIterator::new(&[], &empty),
|
||||
};
|
||||
|
||||
declarations
|
||||
// Yield declarations later in source order (with more precedence) first.
|
||||
.rev()
|
||||
.filter_map(move |(declaration, declaration_importance)| {
|
||||
if let Some(restriction) = restriction {
|
||||
// declaration.id() is either a longhand or a custom
|
||||
// property. Custom properties are always allowed, but
|
||||
// longhands are only allowed if they have our
|
||||
// restriction flag set.
|
||||
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
|
||||
if !id.flags().contains(restriction) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if declaration_importance == node_importance {
|
||||
Some((declaration, cascade_level))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
apply_declarations(
|
||||
device,
|
||||
pseudo,
|
||||
rule_node,
|
||||
guards,
|
||||
iter_declarations,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
font_metrics_provider,
|
||||
cascade_mode,
|
||||
quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
element,
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether we're cascading for visited or unvisited styles.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CascadeMode<'a> {
|
||||
/// We're cascading for unvisited styles.
|
||||
Unvisited {
|
||||
/// The visited rules that should match the visited style.
|
||||
visited_rules: Option<&'a StrongRuleNode>,
|
||||
},
|
||||
/// We're cascading for visited styles.
|
||||
Visited {
|
||||
/// The writing mode of our unvisited style, needed to correctly resolve
|
||||
/// logical properties..
|
||||
writing_mode: WritingMode,
|
||||
},
|
||||
}
|
||||
|
||||
/// NOTE: This function expects the declaration with more priority to appear
|
||||
/// first.
|
||||
pub fn apply_declarations<'a, E, F, I>(
|
||||
device: &Device,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
rules: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
iter_declarations: F,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<&ComputedValues>,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
cascade_mode: CascadeMode,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
element: Option<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
F: Fn() -> I,
|
||||
I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>,
|
||||
{
|
||||
debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
|
||||
debug_assert_eq!(
|
||||
parent_style.is_some(),
|
||||
parent_style_ignoring_first_line.is_some()
|
||||
);
|
||||
#[cfg(feature = "gecko")]
|
||||
debug_assert!(
|
||||
parent_style.is_none() ||
|
||||
::std::ptr::eq(
|
||||
parent_style.unwrap(),
|
||||
parent_style_ignoring_first_line.unwrap()
|
||||
) ||
|
||||
parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine)
|
||||
);
|
||||
|
||||
let inherited_style = parent_style.unwrap_or(device.default_computed_values());
|
||||
|
||||
let custom_properties = {
|
||||
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties());
|
||||
|
||||
for (declaration, _cascade_level) in iter_declarations() {
|
||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||
builder.cascade(&declaration.name, &declaration.value);
|
||||
}
|
||||
}
|
||||
|
||||
builder.build()
|
||||
};
|
||||
|
||||
let mut context = computed::Context {
|
||||
is_root_element: pseudo.is_none() && element.map_or(false, |e| e.is_root()),
|
||||
// We'd really like to own the rules here to avoid refcount traffic, but
|
||||
// animation's usage of `apply_declarations` make this tricky. See bug
|
||||
// 1375525.
|
||||
builder: StyleBuilder::new(
|
||||
device,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
pseudo,
|
||||
Some(rules.clone()),
|
||||
custom_properties,
|
||||
),
|
||||
cached_system_font: None,
|
||||
in_media_query: false,
|
||||
for_smil_animation: false,
|
||||
for_non_inherited_property: None,
|
||||
font_metrics_provider,
|
||||
quirks_mode,
|
||||
rule_cache_conditions: RefCell::new(rule_cache_conditions),
|
||||
};
|
||||
|
||||
let using_cached_reset_properties = {
|
||||
let mut cascade = Cascade::new(&mut context, cascade_mode);
|
||||
|
||||
cascade
|
||||
.apply_properties::<EarlyProperties, I>(ApplyResetProperties::Yes, iter_declarations());
|
||||
|
||||
cascade.compute_visited_style_if_needed(
|
||||
element,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
guards,
|
||||
);
|
||||
|
||||
let using_cached_reset_properties =
|
||||
cascade.try_to_use_cached_reset_properties(rule_cache, guards);
|
||||
|
||||
let apply_reset = if using_cached_reset_properties {
|
||||
ApplyResetProperties::No
|
||||
} else {
|
||||
ApplyResetProperties::Yes
|
||||
};
|
||||
|
||||
cascade.apply_properties::<LateProperties, I>(apply_reset, iter_declarations());
|
||||
|
||||
using_cached_reset_properties
|
||||
};
|
||||
|
||||
context.builder.clear_modified_reset();
|
||||
|
||||
if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
|
||||
StyleAdjuster::new(&mut context.builder)
|
||||
.adjust(layout_parent_style.unwrap_or(inherited_style), element);
|
||||
}
|
||||
|
||||
if context.builder.modified_reset() || using_cached_reset_properties {
|
||||
// If we adjusted any reset structs, we can't cache this ComputedValues.
|
||||
//
|
||||
// Also, if we re-used existing reset structs, don't bother caching it
|
||||
// back again. (Aside from being wasted effort, it will be wrong, since
|
||||
// context.rule_cache_conditions won't be set appropriately if we didn't
|
||||
// compute those reset properties.)
|
||||
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
||||
}
|
||||
|
||||
context.builder.build()
|
||||
}
|
||||
|
||||
fn should_ignore_declaration_when_ignoring_document_colors(
|
||||
device: &Device,
|
||||
longhand_id: LonghandId,
|
||||
cascade_level: CascadeLevel,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
declaration: &mut Cow<PropertyDeclaration>,
|
||||
) -> bool {
|
||||
if !longhand_id.ignored_when_document_colors_disabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let is_ua_or_user_rule = matches!(
|
||||
cascade_level,
|
||||
CascadeLevel::UANormal |
|
||||
CascadeLevel::UserNormal |
|
||||
CascadeLevel::UserImportant |
|
||||
CascadeLevel::UAImportant
|
||||
);
|
||||
|
||||
if is_ua_or_user_rule {
|
||||
return false;
|
||||
}
|
||||
|
||||
let is_style_attribute = matches!(
|
||||
cascade_level,
|
||||
CascadeLevel::StyleAttributeNormal | CascadeLevel::StyleAttributeImportant
|
||||
);
|
||||
|
||||
// Don't override colors on pseudo-element's style attributes. The
|
||||
// background-color on ::-moz-color-swatch is an example. Those are set
|
||||
// as an author style (via the style attribute), but it's pretty
|
||||
// important for it to show up for obvious reasons :)
|
||||
if pseudo.is_some() && is_style_attribute {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Treat background-color a bit differently. If the specified color is
|
||||
// anything other than a fully transparent color, convert it into the
|
||||
// Device's default background color.
|
||||
{
|
||||
let background_color = match **declaration {
|
||||
PropertyDeclaration::BackgroundColor(ref color) => color,
|
||||
_ => return true,
|
||||
};
|
||||
|
||||
if background_color.is_transparent() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let color = device.default_background_color();
|
||||
*declaration.to_mut() = PropertyDeclaration::BackgroundColor(color.into());
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
struct Cascade<'a, 'b: 'a> {
|
||||
context: &'a mut computed::Context<'b>,
|
||||
cascade_mode: CascadeMode<'a>,
|
||||
seen: LonghandIdSet,
|
||||
saved_font_size: Option<PropertyDeclaration>,
|
||||
saved_font_family: Option<PropertyDeclaration>,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||
fn new(context: &'a mut computed::Context<'b>, cascade_mode: CascadeMode<'a>) -> Self {
|
||||
Self {
|
||||
context,
|
||||
cascade_mode,
|
||||
seen: LonghandIdSet::default(),
|
||||
saved_font_size: None,
|
||||
saved_font_family: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn substitute_variables_if_needed<'decl>(
|
||||
&mut self,
|
||||
declaration: &'decl PropertyDeclaration,
|
||||
) -> Cow<'decl, PropertyDeclaration> {
|
||||
let declaration = match *declaration {
|
||||
PropertyDeclaration::WithVariables(ref declaration) => declaration,
|
||||
ref d => return Cow::Borrowed(d),
|
||||
};
|
||||
|
||||
if !declaration.id.inherited() {
|
||||
self.context
|
||||
.rule_cache_conditions
|
||||
.borrow_mut()
|
||||
.set_uncacheable();
|
||||
}
|
||||
|
||||
Cow::Owned(declaration.value.substitute_variables(
|
||||
declaration.id,
|
||||
self.context.builder.custom_properties.as_ref(),
|
||||
self.context.quirks_mode,
|
||||
))
|
||||
}
|
||||
|
||||
fn apply_declaration<Phase: CascadePhase>(
|
||||
&mut self,
|
||||
longhand_id: LonghandId,
|
||||
declaration: &PropertyDeclaration,
|
||||
) {
|
||||
// FIXME(emilio): Find a cleaner abstraction for this.
|
||||
//
|
||||
// font-size and font-family are special because in Gecko they're
|
||||
// they're dependent on other early props, like lang and
|
||||
// -moz-min-font-size-ratio. This sucks a bit, we should ideally
|
||||
// move the font-size computation code somewhere else...
|
||||
if Phase::is_early() {
|
||||
if longhand_id == LonghandId::FontSize {
|
||||
self.saved_font_size = Some(declaration.clone());
|
||||
return;
|
||||
}
|
||||
if longhand_id == LonghandId::FontFamily {
|
||||
self.saved_font_family = Some(declaration.clone());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.apply_declaration_ignoring_phase(longhand_id, declaration);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn apply_declaration_ignoring_phase(
|
||||
&mut self,
|
||||
longhand_id: LonghandId,
|
||||
declaration: &PropertyDeclaration,
|
||||
) {
|
||||
// We could (and used to) use a pattern match here, but that bloats this
|
||||
// function to over 100K of compiled code!
|
||||
//
|
||||
// To improve i-cache behavior, we outline the individual functions and
|
||||
// use virtual dispatch instead.
|
||||
let discriminant = longhand_id as usize;
|
||||
(CASCADE_PROPERTY[discriminant])(declaration, &mut self.context);
|
||||
}
|
||||
|
||||
fn apply_properties<'decls, Phase, I>(
|
||||
&mut self,
|
||||
apply_reset: ApplyResetProperties,
|
||||
declarations: I,
|
||||
) where
|
||||
Phase: CascadePhase,
|
||||
I: Iterator<Item = (&'decls PropertyDeclaration, CascadeLevel)>,
|
||||
{
|
||||
let apply_reset = apply_reset == ApplyResetProperties::Yes;
|
||||
|
||||
debug_assert!(
|
||||
!Phase::is_early() || apply_reset,
|
||||
"Should always apply reset properties in the early phase, since we \
|
||||
need to know font-size / writing-mode to decide whether to use the \
|
||||
cached reset properties"
|
||||
);
|
||||
|
||||
let ignore_colors = !self.context.builder.device.use_document_colors();
|
||||
|
||||
for (declaration, cascade_level) in declarations {
|
||||
let declaration_id = declaration.id();
|
||||
let longhand_id = match declaration_id {
|
||||
PropertyDeclarationId::Longhand(id) => id,
|
||||
PropertyDeclarationId::Custom(..) => continue,
|
||||
};
|
||||
|
||||
let inherited = longhand_id.inherited();
|
||||
if !apply_reset && !inherited {
|
||||
continue;
|
||||
}
|
||||
|
||||
if Phase::is_early() != longhand_id.is_early_property() {
|
||||
continue;
|
||||
}
|
||||
|
||||
debug_assert!(!Phase::is_early() || !longhand_id.is_logical());
|
||||
let physical_longhand_id = if Phase::is_early() {
|
||||
longhand_id
|
||||
} else {
|
||||
longhand_id.to_physical(self.context.builder.writing_mode)
|
||||
};
|
||||
|
||||
if self.seen.contains(physical_longhand_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only a few properties are allowed to depend on the visited state
|
||||
// of links. When cascading visited styles, we can save time by
|
||||
// only processing these properties.
|
||||
if matches!(self.cascade_mode, CascadeMode::Visited { .. }) &&
|
||||
!physical_longhand_id.is_visited_dependent()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut declaration = self.substitute_variables_if_needed(declaration);
|
||||
|
||||
// When document colors are disabled, skip properties that are
|
||||
// marked as ignored in that mode, unless they come from a UA or
|
||||
// user style sheet.
|
||||
if ignore_colors {
|
||||
let should_ignore = should_ignore_declaration_when_ignoring_document_colors(
|
||||
self.context.builder.device,
|
||||
longhand_id,
|
||||
cascade_level,
|
||||
self.context.builder.pseudo,
|
||||
&mut declaration,
|
||||
);
|
||||
if should_ignore {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
self.seen.insert(physical_longhand_id);
|
||||
|
||||
// FIXME(emilio): We should avoid generating code for logical
|
||||
// longhands and just use the physical ones, then rename
|
||||
// physical_longhand_id to just longhand_id.
|
||||
self.apply_declaration::<Phase>(longhand_id, &*declaration);
|
||||
}
|
||||
|
||||
if Phase::is_early() {
|
||||
self.fixup_font_and_apply_saved_font_properties();
|
||||
self.compute_writing_mode();
|
||||
} else {
|
||||
self.finished_applying_properties();
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_writing_mode(&mut self) {
|
||||
let writing_mode = match self.cascade_mode {
|
||||
CascadeMode::Unvisited { .. } => {
|
||||
WritingMode::new(self.context.builder.get_inherited_box())
|
||||
},
|
||||
CascadeMode::Visited { writing_mode } => writing_mode,
|
||||
};
|
||||
self.context.builder.writing_mode = writing_mode;
|
||||
}
|
||||
|
||||
fn compute_visited_style_if_needed<E>(
|
||||
&mut self,
|
||||
element: Option<E>,
|
||||
parent_style: Option<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<&ComputedValues>,
|
||||
layout_parent_style: Option<&ComputedValues>,
|
||||
guards: &StylesheetGuards,
|
||||
) where
|
||||
E: TElement,
|
||||
{
|
||||
let visited_rules = match self.cascade_mode {
|
||||
CascadeMode::Unvisited { visited_rules } => visited_rules,
|
||||
CascadeMode::Visited { .. } => return,
|
||||
};
|
||||
|
||||
let visited_rules = match visited_rules {
|
||||
Some(rules) => rules,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let is_link = self.context.builder.pseudo.is_none() && element.unwrap().is_link();
|
||||
|
||||
macro_rules! visited_parent {
|
||||
($parent:expr) => {
|
||||
if is_link {
|
||||
$parent
|
||||
} else {
|
||||
$parent.map(|p| p.visited_style().unwrap_or(p))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let writing_mode = self.context.builder.writing_mode;
|
||||
|
||||
// We could call apply_declarations directly, but that'd cause
|
||||
// another instantiation of this function which is not great.
|
||||
let style = cascade_rules(
|
||||
self.context.builder.device,
|
||||
self.context.builder.pseudo,
|
||||
visited_rules,
|
||||
guards,
|
||||
visited_parent!(parent_style),
|
||||
visited_parent!(parent_style_ignoring_first_line),
|
||||
visited_parent!(layout_parent_style),
|
||||
self.context.font_metrics_provider,
|
||||
CascadeMode::Visited { writing_mode },
|
||||
self.context.quirks_mode,
|
||||
// The rule cache doesn't care about caching :visited
|
||||
// styles, we cache the unvisited style instead. We still do
|
||||
// need to set the caching dependencies properly if present
|
||||
// though, so the cache conditions need to match.
|
||||
/* rule_cache = */ None,
|
||||
&mut *self.context.rule_cache_conditions.borrow_mut(),
|
||||
element,
|
||||
);
|
||||
self.context.builder.visited_style = Some(style);
|
||||
}
|
||||
|
||||
fn finished_applying_properties(&mut self) {
|
||||
let builder = &mut self.context.builder;
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
if let Some(bg) = builder.get_background_if_mutated() {
|
||||
bg.fill_arrays();
|
||||
}
|
||||
|
||||
if let Some(svg) = builder.get_svg_if_mutated() {
|
||||
svg.fill_arrays();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
{
|
||||
// TODO(emilio): Use get_font_if_mutated instead.
|
||||
if self.seen.contains(LonghandId::FontStyle) ||
|
||||
self.seen.contains(LonghandId::FontWeight) ||
|
||||
self.seen.contains(LonghandId::FontStretch) ||
|
||||
self.seen.contains(LonghandId::FontFamily)
|
||||
{
|
||||
builder.mutate_font().compute_font_hash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_to_use_cached_reset_properties(
|
||||
&mut self,
|
||||
cache: Option<&'b RuleCache>,
|
||||
guards: &StylesheetGuards,
|
||||
) -> bool {
|
||||
let cache = match cache {
|
||||
Some(cache) => cache,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let cached_style = match cache.find(guards, &self.context.builder) {
|
||||
Some(style) => style,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
self.context.builder.copy_reset_from(cached_style);
|
||||
true
|
||||
}
|
||||
|
||||
// FIXME(emilio): It'd be really nice to simplify all this, somehow. This is
|
||||
// very annoying code in lots of ways, and there are various bits about it
|
||||
// which I think are broken or could be improved, see the various FIXMEs
|
||||
// below.
|
||||
fn fixup_font_and_apply_saved_font_properties(&mut self) {
|
||||
let font_family = self.saved_font_family.take();
|
||||
let font_size = self.saved_font_size.take();
|
||||
let mut _skip_font_family = false;
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
// <svg:text> is not affected by text zoom, and it uses a preshint
|
||||
// to disable it. We fix up the struct when this happens by
|
||||
// unzooming its contained font values, which will have been zoomed
|
||||
// in the parent.
|
||||
//
|
||||
// FIXME(emilio): Could be cleaner if we just removed this property
|
||||
// and made a style adjustment o something like that.
|
||||
if self.seen.contains(LonghandId::XTextZoom) {
|
||||
let builder = &mut self.context.builder;
|
||||
|
||||
let parent_zoom = builder.get_parent_font().gecko().mAllowZoom;
|
||||
let zoom = builder.get_font().gecko().mAllowZoom;
|
||||
if zoom != parent_zoom {
|
||||
debug_assert!(
|
||||
!zoom,
|
||||
"We only ever disable text zoom (in svg:text), never enable it"
|
||||
);
|
||||
let device = builder.device;
|
||||
builder.mutate_font().unzoom_fonts(device);
|
||||
}
|
||||
}
|
||||
|
||||
// Whenever a single generic value is specified, Gecko used to do a
|
||||
// bunch of recalculation walking up the rule tree, including
|
||||
// handling the font-size stuff.
|
||||
//
|
||||
// It basically repopulated the font struct with the default font
|
||||
// for a given generic and language. We handle the font-size stuff
|
||||
// separately, so this boils down to just copying over the
|
||||
// font-family lists (no other aspect of the default font can be
|
||||
// configured).
|
||||
if self.seen.contains(LonghandId::XLang) || self.seen.contains(LonghandId::FontFamily) {
|
||||
// If just the language changed, the inherited generic is all we
|
||||
// need.
|
||||
let mut generic = self.context.builder.get_parent_font().gecko().mGenericID;
|
||||
|
||||
// FIXME(emilio): Isn't this bogus for CSS wide keywords like
|
||||
// reset or such?
|
||||
if let Some(ref declaration) = font_family {
|
||||
if let PropertyDeclaration::FontFamily(ref fam) = *declaration {
|
||||
if let Some(id) = fam.single_generic() {
|
||||
generic = id;
|
||||
// In case of a specified font family with a single
|
||||
// generic, we will end up setting font family
|
||||
// below, but its value would get overwritten later
|
||||
// in the pipeline when cascading.
|
||||
//
|
||||
// We instead skip cascading font-family in that
|
||||
// case.
|
||||
//
|
||||
// In case of the language changing, we wish for a
|
||||
// specified font-family to override this, so we do
|
||||
// not skip cascading then.
|
||||
_skip_font_family = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(emilio): Why both setting the generic and passing it
|
||||
// down?
|
||||
let pres_context = self.context.builder.device.pres_context();
|
||||
let gecko_font = self.context.builder.mutate_font().gecko_mut();
|
||||
gecko_font.mGenericID = generic;
|
||||
unsafe {
|
||||
::gecko_bindings::bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric(
|
||||
gecko_font,
|
||||
pres_context,
|
||||
generic,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It is important that font-size is computed before the late
|
||||
// properties (for em units), but after font-family (for the
|
||||
// base-font-size dependence for default and keyword font-sizes).
|
||||
//
|
||||
// It's important that font-family comes after the other font properties
|
||||
// to support system fonts.
|
||||
//
|
||||
// NOTE(emilio): I haven't verified that comment, but it was there.
|
||||
// Verify, and if it's false make font-size the only weird property?
|
||||
if !_skip_font_family {
|
||||
if let Some(ref declaration) = font_family {
|
||||
self.apply_declaration_ignoring_phase(LonghandId::FontFamily, declaration);
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
let context = &mut self.context;
|
||||
let device = context.builder.device;
|
||||
if let PropertyDeclaration::FontFamily(ref val) = *declaration {
|
||||
if val.get_system().is_some() {
|
||||
let default = context
|
||||
.cached_system_font
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.default_font_type;
|
||||
context.builder.mutate_font().fixup_system(default);
|
||||
} else {
|
||||
context.builder.mutate_font().fixup_none_generic(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(declaration) = font_size {
|
||||
self.apply_declaration_ignoring_phase(LonghandId::FontSize, &declaration);
|
||||
} else {
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
if self.seen.contains(LonghandId::XLang) ||
|
||||
self.seen.contains(LonghandId::MozScriptLevel) ||
|
||||
self.seen.contains(LonghandId::MozMinFontSizeRatio) ||
|
||||
self.seen.contains(LonghandId::FontFamily)
|
||||
{
|
||||
use properties::{CSSWideKeyword, WideKeywordDeclaration};
|
||||
|
||||
// font-size must be explicitly inherited to handle lang
|
||||
// changes and scriptlevel changes.
|
||||
//
|
||||
// FIXME(emilio): That looks a bit bogus...
|
||||
let inherit = PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
|
||||
id: LonghandId::FontSize,
|
||||
keyword: CSSWideKeyword::Inherit,
|
||||
});
|
||||
|
||||
self.apply_declaration_ignoring_phase(LonghandId::FontSize, &inherit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -849,7 +849,7 @@ impl PropertyDeclarationBlock {
|
|||
|
||||
for declaration in self.normal_declaration_iter() {
|
||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||
builder.cascade(&declaration.name, declaration.value.borrow());
|
||||
builder.cascade(&declaration.name, &declaration.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -289,7 +289,7 @@
|
|||
#[allow(unused_imports)]
|
||||
use properties::longhands;
|
||||
#[allow(unused_imports)]
|
||||
use properties::{DeclaredValue, LonghandId, LonghandIdSet};
|
||||
use properties::{LonghandId, LonghandIdSet};
|
||||
#[allow(unused_imports)]
|
||||
use properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration};
|
||||
#[allow(unused_imports)]
|
||||
|
@ -312,20 +312,6 @@
|
|||
declaration: &PropertyDeclaration,
|
||||
context: &mut computed::Context,
|
||||
) {
|
||||
let value = match *declaration {
|
||||
PropertyDeclaration::${property.camel_case}(ref value) => {
|
||||
DeclaredValue::Value(value)
|
||||
},
|
||||
PropertyDeclaration::CSSWideKeyword(ref declaration) => {
|
||||
debug_assert_eq!(declaration.id, LonghandId::${property.camel_case});
|
||||
DeclaredValue::CSSWideKeyword(declaration.keyword)
|
||||
},
|
||||
PropertyDeclaration::WithVariables(..) => {
|
||||
panic!("variables should already have been substituted")
|
||||
}
|
||||
_ => panic!("entered the wrong cascade_property() implementation"),
|
||||
};
|
||||
|
||||
context.for_non_inherited_property =
|
||||
% if property.style_struct.inherited:
|
||||
None;
|
||||
|
@ -333,79 +319,86 @@
|
|||
Some(LonghandId::${property.camel_case});
|
||||
% endif
|
||||
|
||||
match value {
|
||||
DeclaredValue::Value(specified_value) => {
|
||||
% if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
|
||||
if let Some(sf) = specified_value.get_system() {
|
||||
longhands::system_font::resolve_system_font(sf, context);
|
||||
let specified_value = match *declaration {
|
||||
PropertyDeclaration::${property.camel_case}(ref value) => value,
|
||||
PropertyDeclaration::CSSWideKeyword(ref declaration) => {
|
||||
debug_assert_eq!(declaration.id, LonghandId::${property.camel_case});
|
||||
match declaration.keyword {
|
||||
% if not data.current_style_struct.inherited:
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Initial => {
|
||||
% if property.ident == "font_size":
|
||||
computed::FontSize::cascade_initial_font_size(context);
|
||||
% else:
|
||||
context.builder.reset_${property.ident}();
|
||||
% endif
|
||||
},
|
||||
% if data.current_style_struct.inherited:
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Inherit => {
|
||||
% if not property.style_struct.inherited:
|
||||
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
||||
% endif
|
||||
% if property.ident == "font_size":
|
||||
computed::FontSize::cascade_inherit_font_size(context);
|
||||
% else:
|
||||
context.builder.inherit_${property.ident}();
|
||||
% endif
|
||||
}
|
||||
% endif
|
||||
% if not property.style_struct.inherited and property.logical:
|
||||
context.rule_cache_conditions.borrow_mut()
|
||||
.set_writing_mode_dependency(context.builder.writing_mode);
|
||||
% endif
|
||||
% if property.is_vector:
|
||||
// In the case of a vector property we want to pass
|
||||
// down an iterator so that this can be computed
|
||||
// without allocation
|
||||
//
|
||||
// However, computing requires a context, but the
|
||||
// style struct being mutated is on the context. We
|
||||
// temporarily remove it, mutate it, and then put it
|
||||
// back. Vector longhands cannot touch their own
|
||||
// style struct whilst computing, else this will
|
||||
// panic.
|
||||
let mut s =
|
||||
context.builder.take_${data.current_style_struct.name_lower}();
|
||||
{
|
||||
let iter = specified_value.compute_iter(context);
|
||||
s.set_${property.ident}(iter);
|
||||
}
|
||||
context.builder.put_${data.current_style_struct.name_lower}(s);
|
||||
% else:
|
||||
% if property.boxed:
|
||||
let computed = (**specified_value).to_computed_value(context);
|
||||
% else:
|
||||
let computed = specified_value.to_computed_value(context);
|
||||
% endif
|
||||
% if property.ident == "font_size":
|
||||
specified::FontSize::cascade_specified_font_size(
|
||||
context,
|
||||
&specified_value,
|
||||
computed,
|
||||
);
|
||||
% else:
|
||||
context.builder.set_${property.ident}(computed)
|
||||
% endif
|
||||
% endif
|
||||
}
|
||||
DeclaredValue::WithVariables(_) => unreachable!(),
|
||||
DeclaredValue::CSSWideKeyword(keyword) => match keyword {
|
||||
% if not data.current_style_struct.inherited:
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Initial => {
|
||||
% if property.ident == "font_size":
|
||||
computed::FontSize::cascade_initial_font_size(context);
|
||||
% else:
|
||||
context.builder.reset_${property.ident}();
|
||||
% endif
|
||||
},
|
||||
% if data.current_style_struct.inherited:
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Inherit => {
|
||||
% if not property.style_struct.inherited:
|
||||
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
||||
% endif
|
||||
% if property.ident == "font_size":
|
||||
computed::FontSize::cascade_inherit_font_size(context);
|
||||
% else:
|
||||
context.builder.inherit_${property.ident}();
|
||||
% endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
PropertyDeclaration::WithVariables(..) => {
|
||||
panic!("variables should already have been substituted")
|
||||
}
|
||||
_ => panic!("entered the wrong cascade_property() implementation"),
|
||||
};
|
||||
|
||||
% if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
|
||||
if let Some(sf) = specified_value.get_system() {
|
||||
longhands::system_font::resolve_system_font(sf, context);
|
||||
}
|
||||
% endif
|
||||
|
||||
% if not property.style_struct.inherited and property.logical:
|
||||
context.rule_cache_conditions.borrow_mut()
|
||||
.set_writing_mode_dependency(context.builder.writing_mode);
|
||||
% endif
|
||||
|
||||
% if property.is_vector:
|
||||
// In the case of a vector property we want to pass down an
|
||||
// iterator so that this can be computed without allocation.
|
||||
//
|
||||
// However, computing requires a context, but the style struct
|
||||
// being mutated is on the context. We temporarily remove it,
|
||||
// mutate it, and then put it back. Vector longhands cannot
|
||||
// touch their own style struct whilst computing, else this will
|
||||
// panic.
|
||||
let mut s =
|
||||
context.builder.take_${data.current_style_struct.name_lower}();
|
||||
{
|
||||
let iter = specified_value.compute_iter(context);
|
||||
s.set_${property.ident}(iter);
|
||||
}
|
||||
context.builder.put_${data.current_style_struct.name_lower}(s);
|
||||
% else:
|
||||
% if property.boxed:
|
||||
let computed = (**specified_value).to_computed_value(context);
|
||||
% else:
|
||||
let computed = specified_value.to_computed_value(context);
|
||||
% endif
|
||||
% if property.ident == "font_size":
|
||||
specified::FontSize::cascade_specified_font_size(
|
||||
context,
|
||||
&specified_value,
|
||||
computed,
|
||||
);
|
||||
% else:
|
||||
context.builder.set_${property.ident}(computed)
|
||||
% endif
|
||||
% endif
|
||||
}
|
||||
|
||||
pub fn parse_declared<'i, 't>(
|
||||
|
|
|
@ -13,13 +13,9 @@
|
|||
#[cfg(feature = "servo")]
|
||||
use app_units::Au;
|
||||
use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
|
||||
use dom::TElement;
|
||||
use custom_properties::CustomPropertiesBuilder;
|
||||
use servo_arc::{Arc, UniqueArc};
|
||||
use smallbitvec::SmallBitVec;
|
||||
use std::borrow::Cow;
|
||||
use std::{ops, ptr};
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{self, Write};
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
|
||||
|
@ -27,8 +23,6 @@ use cssparser::{Parser, RGBA, TokenSerializationType};
|
|||
use cssparser::ParserInput;
|
||||
#[cfg(feature = "servo")] use euclid::SideOffsets2D;
|
||||
use context::QuirksMode;
|
||||
use font_metrics::FontMetricsProvider;
|
||||
#[cfg(feature = "gecko")] use gecko_bindings::bindings;
|
||||
#[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
|
||||
#[cfg(feature = "servo")] use logical_geometry::LogicalMargin;
|
||||
#[cfg(feature = "servo")] use computed_values;
|
||||
|
@ -37,11 +31,9 @@ use logical_geometry::WritingMode;
|
|||
use media_queries::Device;
|
||||
use parser::ParserContext;
|
||||
use properties::longhands::system_font::SystemFont;
|
||||
use rule_cache::{RuleCache, RuleCacheConditions};
|
||||
use selector_parser::PseudoElement;
|
||||
use selectors::parser::SelectorParseErrorKind;
|
||||
#[cfg(feature = "servo")] use servo_config::prefs::PREFS;
|
||||
use shared_lock::StylesheetGuards;
|
||||
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
|
||||
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
|
||||
use stylesheets::{CssRuleType, Origin, UrlExtraData};
|
||||
|
@ -49,12 +41,12 @@ use values::generics::text::LineHeight;
|
|||
use values::computed;
|
||||
use values::computed::NonNegativeLength;
|
||||
use values::serialize_atom_name;
|
||||
use rule_tree::{CascadeLevel, StrongRuleNode};
|
||||
use rule_tree::StrongRuleNode;
|
||||
use self::computed_value_flags::*;
|
||||
use str::{CssString, CssStringBorrow, CssStringWriter};
|
||||
use style_adjuster::StyleAdjuster;
|
||||
|
||||
pub use self::declaration_block::*;
|
||||
pub use self::cascade::*;
|
||||
|
||||
<%!
|
||||
from collections import defaultdict
|
||||
|
@ -66,6 +58,8 @@ pub use self::declaration_block::*;
|
|||
pub mod computed_value_flags;
|
||||
#[path="${repr(os.path.join(os.path.dirname(__file__), 'declaration_block.rs'))[1:-1]}"]
|
||||
pub mod declaration_block;
|
||||
#[path="${repr(os.path.join(os.path.dirname(__file__), 'cascade.rs'))[1:-1]}"]
|
||||
pub mod cascade;
|
||||
|
||||
/// Conversion with fewer impls than From/Into
|
||||
pub trait MaybeBoxed<Out> {
|
||||
|
@ -731,7 +725,7 @@ static ${name}: LonghandIdSet = LonghandIdSet {
|
|||
</%def>
|
||||
|
||||
/// A set of longhand properties
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq)]
|
||||
pub struct LonghandIdSet {
|
||||
storage: [u32; (${len(data.longhands)} - 1 + 32) / 32]
|
||||
}
|
||||
|
@ -859,25 +853,22 @@ impl CSSWideKeyword {
|
|||
CSSWideKeyword::Unset => "unset",
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the result of cssparser::Parser::expect_ident() and converts it
|
||||
/// to a CSSWideKeyword.
|
||||
pub fn from_ident<'i>(ident: &str) -> Option<Self> {
|
||||
match_ignore_ascii_case! { ident,
|
||||
// If modifying this set of keyword, also update values::CustomIdent::from_ident
|
||||
"initial" => Some(CSSWideKeyword::Initial),
|
||||
"inherit" => Some(CSSWideKeyword::Inherit),
|
||||
"unset" => Some(CSSWideKeyword::Unset),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CSSWideKeyword {
|
||||
fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||
let ident = input.expect_ident().map_err(|_| ())?.clone();
|
||||
let keyword = {
|
||||
let ident = input.expect_ident().map_err(|_| ())?;
|
||||
match_ignore_ascii_case! { ident,
|
||||
// If modifying this set of keyword, also update values::CustomIdent::from_ident
|
||||
"initial" => CSSWideKeyword::Initial,
|
||||
"inherit" => CSSWideKeyword::Inherit,
|
||||
"unset" => CSSWideKeyword::Unset,
|
||||
_ => return Err(()),
|
||||
}
|
||||
};
|
||||
input.expect_exhausted().map_err(|_| ())?;
|
||||
CSSWideKeyword::from_ident(&ident).ok_or(())
|
||||
Ok(keyword)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -969,6 +960,7 @@ impl LonghandId {
|
|||
}
|
||||
|
||||
/// Returns whether the longhand property is inherited by default.
|
||||
#[inline]
|
||||
pub fn inherited(&self) -> bool {
|
||||
${static_longhand_id_set("INHERITED", lambda p: p.style_struct.inherited)}
|
||||
INHERITED.contains(*self)
|
||||
|
@ -1121,16 +1113,19 @@ impl LonghandId {
|
|||
}
|
||||
|
||||
/// Returns PropertyFlags for given longhand property.
|
||||
pub fn flags(&self) -> PropertyFlags {
|
||||
match *self {
|
||||
#[inline(always)]
|
||||
pub fn flags(self) -> PropertyFlags {
|
||||
// TODO(emilio): This can be simplified further as Rust gains more
|
||||
// constant expression support.
|
||||
const FLAGS: [u8; ${len(data.longhands)}] = [
|
||||
% for property in data.longhands:
|
||||
LonghandId::${property.camel_case} =>
|
||||
% for flag in property.flags:
|
||||
PropertyFlags::${flag} |
|
||||
% endfor
|
||||
PropertyFlags::empty(),
|
||||
% for flag in property.flags:
|
||||
PropertyFlags::${flag}.bits |
|
||||
% endfor
|
||||
0,
|
||||
% endfor
|
||||
}
|
||||
];
|
||||
PropertyFlags::from_bits_truncate(FLAGS[self as usize])
|
||||
}
|
||||
|
||||
/// Only a few properties are allowed to depend on the visited state of
|
||||
|
@ -1160,40 +1155,14 @@ impl LonghandId {
|
|||
|
||||
/// Returns true if the property is one that is ignored when document
|
||||
/// colors are disabled.
|
||||
fn is_ignored_when_document_colors_disabled(
|
||||
&self,
|
||||
cascade_level: CascadeLevel,
|
||||
pseudo: Option<<&PseudoElement>,
|
||||
) -> bool {
|
||||
let is_ua_or_user_rule = matches!(
|
||||
cascade_level,
|
||||
CascadeLevel::UANormal |
|
||||
CascadeLevel::UserNormal |
|
||||
CascadeLevel::UserImportant |
|
||||
CascadeLevel::UAImportant
|
||||
);
|
||||
#[inline]
|
||||
fn ignored_when_document_colors_disabled(self) -> bool {
|
||||
${static_longhand_id_set(
|
||||
"IGNORED_WHEN_COLORS_DISABLED",
|
||||
lambda p: p.ignored_when_colors_disabled
|
||||
)}
|
||||
|
||||
if is_ua_or_user_rule {
|
||||
return false;
|
||||
}
|
||||
|
||||
let is_style_attribute = matches!(
|
||||
cascade_level,
|
||||
CascadeLevel::StyleAttributeNormal |
|
||||
CascadeLevel::StyleAttributeImportant
|
||||
);
|
||||
// Don't override colors on pseudo-element's style attributes. The
|
||||
// background-color on ::-moz-color-swatch is an example. Those are set
|
||||
// as an author style (via the style attribute), but it's pretty
|
||||
// important for it to show up for obvious reasons :)
|
||||
if pseudo.is_some() && is_style_attribute {
|
||||
return false;
|
||||
}
|
||||
|
||||
matches!(*self,
|
||||
${" | ".join([("LonghandId::" + p.camel_case)
|
||||
for p in data.longhands if p.ignored_when_colors_disabled])}
|
||||
)
|
||||
IGNORED_WHEN_COLORS_DISABLED.contains(self)
|
||||
}
|
||||
|
||||
/// The computed value of some properties depends on the (sometimes
|
||||
|
@ -1469,47 +1438,6 @@ impl ShorthandId {
|
|||
}
|
||||
}
|
||||
|
||||
/// Servo's representation of a declared value for a given `T`, which is the
|
||||
/// declared value for that property.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum DeclaredValue<'a, T: 'a> {
|
||||
/// A known specified value from the stylesheet.
|
||||
Value(&'a T),
|
||||
/// An unparsed value that contains `var()` functions.
|
||||
WithVariables(&'a Arc<UnparsedValue>),
|
||||
/// An CSS-wide keyword.
|
||||
CSSWideKeyword(CSSWideKeyword),
|
||||
}
|
||||
|
||||
/// A variant of DeclaredValue that owns its data. This separation exists so
|
||||
/// that PropertyDeclaration can avoid embedding a DeclaredValue (and its
|
||||
/// extra discriminant word) and synthesize dependent DeclaredValues for
|
||||
/// PropertyDeclaration instances as needed.
|
||||
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
|
||||
pub enum DeclaredValueOwned<T> {
|
||||
/// A known specified value from the stylesheet.
|
||||
Value(T),
|
||||
/// An unparsed value that contains `var()` functions.
|
||||
WithVariables(
|
||||
#[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
|
||||
Arc<UnparsedValue>
|
||||
),
|
||||
/// An CSS-wide keyword.
|
||||
CSSWideKeyword(CSSWideKeyword),
|
||||
}
|
||||
|
||||
impl<T> DeclaredValueOwned<T> {
|
||||
/// Creates a dependent DeclaredValue from this DeclaredValueOwned.
|
||||
fn borrow(&self) -> DeclaredValue<T> {
|
||||
match *self {
|
||||
DeclaredValueOwned::Value(ref v) => DeclaredValue::Value(v),
|
||||
DeclaredValueOwned::WithVariables(ref v) => DeclaredValue::WithVariables(v),
|
||||
DeclaredValueOwned::CSSWideKeyword(v) => DeclaredValue::CSSWideKeyword(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An unparsed property value that contains `var()` functions.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct UnparsedValue {
|
||||
|
@ -1968,6 +1896,16 @@ pub struct VariableDeclaration {
|
|||
value: Arc<UnparsedValue>,
|
||||
}
|
||||
|
||||
/// A custom property declaration value is either an unparsed value or a CSS
|
||||
/// wide-keyword.
|
||||
#[derive(Clone, PartialEq, ToCss)]
|
||||
pub enum CustomDeclarationValue {
|
||||
/// A value.
|
||||
Value(Arc<::custom_properties::SpecifiedValue>),
|
||||
/// A wide keyword.
|
||||
CSSWideKeyword(CSSWideKeyword),
|
||||
}
|
||||
|
||||
/// A custom property declaration with the property name and the declared value.
|
||||
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
||||
#[derive(Clone, PartialEq, ToCss)]
|
||||
|
@ -1977,7 +1915,7 @@ pub struct CustomDeclaration {
|
|||
pub name: ::custom_properties::Name,
|
||||
/// The value of the custom property.
|
||||
#[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
|
||||
pub value: DeclaredValueOwned<Arc<::custom_properties::SpecifiedValue>>,
|
||||
pub value: CustomDeclarationValue,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PropertyDeclaration {
|
||||
|
@ -2159,13 +2097,13 @@ impl PropertyDeclaration {
|
|||
/// This is the case of custom properties and values that contain
|
||||
/// unsubstituted variables.
|
||||
pub fn value_is_unparsed(&self) -> bool {
|
||||
match *self {
|
||||
PropertyDeclaration::WithVariables(..) => true,
|
||||
PropertyDeclaration::Custom(ref declaration) => {
|
||||
!matches!(declaration.value.borrow(), DeclaredValue::CSSWideKeyword(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
match *self {
|
||||
PropertyDeclaration::WithVariables(..) => true,
|
||||
PropertyDeclaration::Custom(ref declaration) => {
|
||||
matches!(declaration.value, CustomDeclarationValue::Value(..))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this property declaration is for one of the animatable
|
||||
|
@ -2210,9 +2148,9 @@ impl PropertyDeclaration {
|
|||
// before adding skip_whitespace here.
|
||||
// This probably affects some test results.
|
||||
let value = match input.try(CSSWideKeyword::parse) {
|
||||
Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword),
|
||||
Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
|
||||
Err(()) => match ::custom_properties::SpecifiedValue::parse(input) {
|
||||
Ok(value) => DeclaredValueOwned::Value(value),
|
||||
Ok(value) => CustomDeclarationValue::Value(value),
|
||||
Err(e) => return Err(StyleParseErrorKind::new_invalid(
|
||||
format!("--{}", property_name),
|
||||
e,
|
||||
|
@ -3469,6 +3407,10 @@ impl<'a> StyleBuilder<'a> {
|
|||
self.modified_reset = true;
|
||||
% endif
|
||||
|
||||
// TODO(emilio): There's a maybe-worth it optimization here: We should
|
||||
// avoid allocating a new reset struct if `reset_struct` and our struct
|
||||
// is the same pointer. Would remove a bunch of stupid allocations if
|
||||
// you did something like `* { all: initial }` or what not.
|
||||
self.${property.style_struct.ident}.mutate()
|
||||
.reset_${property.ident}(
|
||||
reset_struct,
|
||||
|
@ -3740,538 +3682,12 @@ pub type CascadePropertyFn =
|
|||
|
||||
/// A per-longhand array of functions to perform the CSS cascade on each of
|
||||
/// them, effectively doing virtual dispatch.
|
||||
static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
|
||||
pub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
|
||||
% for property in data.longhands:
|
||||
longhands::${property.ident}::cascade_property,
|
||||
% endfor
|
||||
];
|
||||
|
||||
/// Performs the CSS cascade, computing new styles for an element from its parent style.
|
||||
///
|
||||
/// The arguments are:
|
||||
///
|
||||
/// * `device`: Used to get the initial viewport and other external state.
|
||||
///
|
||||
/// * `rule_node`: The rule node in the tree that represent the CSS rules that
|
||||
/// matched.
|
||||
///
|
||||
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
|
||||
///
|
||||
/// Returns the computed values.
|
||||
/// * `flags`: Various flags.
|
||||
///
|
||||
pub fn cascade<E>(
|
||||
device: &Device,
|
||||
pseudo: Option<<&PseudoElement>,
|
||||
rule_node: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
parent_style: Option<<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<<&ComputedValues>,
|
||||
layout_parent_style: Option<<&ComputedValues>,
|
||||
visited_rules: Option<<&StrongRuleNode>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
element: Option<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
cascade_rules(
|
||||
device,
|
||||
pseudo,
|
||||
rule_node,
|
||||
guards,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
font_metrics_provider,
|
||||
CascadeMode::Unvisited { visited_rules },
|
||||
quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
element,
|
||||
)
|
||||
}
|
||||
|
||||
fn cascade_rules<E>(
|
||||
device: &Device,
|
||||
pseudo: Option<<&PseudoElement>,
|
||||
rule_node: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
parent_style: Option<<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<<&ComputedValues>,
|
||||
layout_parent_style: Option<<&ComputedValues>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
cascade_mode: CascadeMode,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
element: Option<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
||||
let empty = SmallBitVec::new();
|
||||
let restriction = pseudo.and_then(|p| p.property_restriction());
|
||||
let iter_declarations = || {
|
||||
rule_node.self_and_ancestors().flat_map(|node| {
|
||||
let cascade_level = node.cascade_level();
|
||||
let node_importance = node.importance();
|
||||
let declarations = match node.style_source() {
|
||||
Some(source) => {
|
||||
source.read(cascade_level.guard(guards)).declaration_importance_iter()
|
||||
}
|
||||
None => DeclarationImportanceIterator::new(&[], &empty),
|
||||
};
|
||||
|
||||
declarations
|
||||
// Yield declarations later in source order (with more precedence) first.
|
||||
.rev()
|
||||
.filter_map(move |(declaration, declaration_importance)| {
|
||||
if let Some(restriction) = restriction {
|
||||
// declaration.id() is either a longhand or a custom
|
||||
// property. Custom properties are always allowed, but
|
||||
// longhands are only allowed if they have our
|
||||
// restriction flag set.
|
||||
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
|
||||
if !id.flags().contains(restriction) {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if declaration_importance == node_importance {
|
||||
Some((declaration, cascade_level))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
apply_declarations(
|
||||
device,
|
||||
pseudo,
|
||||
rule_node,
|
||||
guards,
|
||||
iter_declarations,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
layout_parent_style,
|
||||
font_metrics_provider,
|
||||
cascade_mode,
|
||||
quirks_mode,
|
||||
rule_cache,
|
||||
rule_cache_conditions,
|
||||
element,
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether we're cascading for visited or unvisited styles.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CascadeMode<'a> {
|
||||
/// We're cascading for unvisited styles.
|
||||
Unvisited {
|
||||
/// The visited rules that should match the visited style.
|
||||
visited_rules: Option<<&'a StrongRuleNode>,
|
||||
},
|
||||
/// We're cascading for visited styles.
|
||||
Visited {
|
||||
/// The writing mode of our unvisited style, needed to correctly resolve
|
||||
/// logical properties..
|
||||
writing_mode: WritingMode,
|
||||
},
|
||||
}
|
||||
|
||||
/// NOTE: This function expects the declaration with more priority to appear
|
||||
/// first.
|
||||
pub fn apply_declarations<'a, E, F, I>(
|
||||
device: &Device,
|
||||
pseudo: Option<<&PseudoElement>,
|
||||
rules: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
iter_declarations: F,
|
||||
parent_style: Option<<&ComputedValues>,
|
||||
parent_style_ignoring_first_line: Option<<&ComputedValues>,
|
||||
layout_parent_style: Option<<&ComputedValues>,
|
||||
font_metrics_provider: &FontMetricsProvider,
|
||||
cascade_mode: CascadeMode,
|
||||
quirks_mode: QuirksMode,
|
||||
rule_cache: Option<<&RuleCache>,
|
||||
rule_cache_conditions: &mut RuleCacheConditions,
|
||||
element: Option<E>,
|
||||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
F: Fn() -> I,
|
||||
I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>,
|
||||
{
|
||||
debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
|
||||
debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
|
||||
#[cfg(feature = "gecko")]
|
||||
debug_assert!(parent_style.is_none() ||
|
||||
::std::ptr::eq(parent_style.unwrap(),
|
||||
parent_style_ignoring_first_line.unwrap()) ||
|
||||
parent_style.unwrap().pseudo() == Some(PseudoElement::FirstLine));
|
||||
let inherited_style =
|
||||
parent_style.unwrap_or(device.default_computed_values());
|
||||
|
||||
let custom_properties = {
|
||||
let mut builder =
|
||||
CustomPropertiesBuilder::new(inherited_style.custom_properties());
|
||||
|
||||
for (declaration, _cascade_level) in iter_declarations() {
|
||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||
builder.cascade(&declaration.name, declaration.value.borrow());
|
||||
}
|
||||
}
|
||||
|
||||
builder.build()
|
||||
};
|
||||
|
||||
let mut context = computed::Context {
|
||||
is_root_element: pseudo.is_none() && element.map_or(false, |e| e.is_root()),
|
||||
// We'd really like to own the rules here to avoid refcount traffic, but
|
||||
// animation's usage of `apply_declarations` make this tricky. See bug
|
||||
// 1375525.
|
||||
builder: StyleBuilder::new(
|
||||
device,
|
||||
parent_style,
|
||||
parent_style_ignoring_first_line,
|
||||
pseudo,
|
||||
Some(rules.clone()),
|
||||
custom_properties,
|
||||
),
|
||||
cached_system_font: None,
|
||||
in_media_query: false,
|
||||
for_smil_animation: false,
|
||||
for_non_inherited_property: None,
|
||||
font_metrics_provider,
|
||||
quirks_mode,
|
||||
rule_cache_conditions: RefCell::new(rule_cache_conditions),
|
||||
};
|
||||
|
||||
let ignore_colors = !device.use_document_colors();
|
||||
|
||||
// Set computed values, overwriting earlier declarations for the same
|
||||
// property.
|
||||
let mut seen = LonghandIdSet::new();
|
||||
|
||||
// Declaration blocks are stored in increasing precedence order, we want
|
||||
// them in decreasing order here.
|
||||
//
|
||||
// We could (and used to) use a pattern match here, but that bloats this
|
||||
// function to over 100K of compiled code!
|
||||
//
|
||||
// To improve i-cache behavior, we outline the individual functions and use
|
||||
// virtual dispatch instead.
|
||||
let mut apply_reset = true;
|
||||
% for category_to_cascade_now in ["early", "other"]:
|
||||
% if category_to_cascade_now == "early":
|
||||
// Pull these out so that we can compute them in a specific order
|
||||
// without introducing more iterations.
|
||||
let mut font_size = None;
|
||||
let mut font_family = None;
|
||||
% endif
|
||||
for (declaration, cascade_level) in iter_declarations() {
|
||||
let declaration_id = declaration.id();
|
||||
let longhand_id = match declaration_id {
|
||||
PropertyDeclarationId::Longhand(id) => id,
|
||||
PropertyDeclarationId::Custom(..) => continue,
|
||||
};
|
||||
|
||||
if !apply_reset && !longhand_id.inherited() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if
|
||||
% if category_to_cascade_now == "early":
|
||||
!
|
||||
% endif
|
||||
longhand_id.is_early_property()
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
<% maybe_to_physical = ".to_physical(writing_mode)" if category_to_cascade_now != "early" else "" %>
|
||||
let physical_longhand_id = longhand_id ${maybe_to_physical};
|
||||
if seen.contains(physical_longhand_id) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only a few properties are allowed to depend on the visited state
|
||||
// of links. When cascading visited styles, we can save time by
|
||||
// only processing these properties.
|
||||
if matches!(cascade_mode, CascadeMode::Visited { .. }) &&
|
||||
!physical_longhand_id.is_visited_dependent() {
|
||||
continue
|
||||
}
|
||||
|
||||
let mut declaration = match *declaration {
|
||||
PropertyDeclaration::WithVariables(ref declaration) => {
|
||||
if !declaration.id.inherited() {
|
||||
context.rule_cache_conditions.borrow_mut()
|
||||
.set_uncacheable();
|
||||
}
|
||||
Cow::Owned(declaration.value.substitute_variables(
|
||||
declaration.id,
|
||||
context.builder.custom_properties.as_ref(),
|
||||
context.quirks_mode
|
||||
))
|
||||
}
|
||||
ref d => Cow::Borrowed(d)
|
||||
};
|
||||
|
||||
// When document colors are disabled, skip properties that are
|
||||
// marked as ignored in that mode, unless they come from a UA or
|
||||
// user style sheet.
|
||||
if ignore_colors &&
|
||||
longhand_id.is_ignored_when_document_colors_disabled(
|
||||
cascade_level,
|
||||
context.builder.pseudo
|
||||
)
|
||||
{
|
||||
let non_transparent_background = match *declaration {
|
||||
PropertyDeclaration::BackgroundColor(ref color) => {
|
||||
// Treat background-color a bit differently. If the specified
|
||||
// color is anything other than a fully transparent color, convert
|
||||
// it into the Device's default background color.
|
||||
color.is_non_transparent()
|
||||
}
|
||||
_ => continue
|
||||
};
|
||||
|
||||
// FIXME: moving this out of `match` is a work around for
|
||||
// borrows being lexical.
|
||||
if non_transparent_background {
|
||||
let color = device.default_background_color();
|
||||
declaration =
|
||||
Cow::Owned(PropertyDeclaration::BackgroundColor(color.into()));
|
||||
}
|
||||
}
|
||||
|
||||
seen.insert(physical_longhand_id);
|
||||
|
||||
% if category_to_cascade_now == "early":
|
||||
if LonghandId::FontSize == longhand_id {
|
||||
font_size = Some(declaration.clone());
|
||||
continue;
|
||||
}
|
||||
if LonghandId::FontFamily == longhand_id {
|
||||
font_family = Some(declaration.clone());
|
||||
continue;
|
||||
}
|
||||
% endif
|
||||
|
||||
let discriminant = longhand_id as usize;
|
||||
(CASCADE_PROPERTY[discriminant])(&*declaration, &mut context);
|
||||
}
|
||||
% if category_to_cascade_now == "early":
|
||||
let writing_mode = match cascade_mode {
|
||||
CascadeMode::Unvisited { .. } => {
|
||||
WritingMode::new(context.builder.get_inherited_box())
|
||||
}
|
||||
CascadeMode::Visited { writing_mode } => writing_mode,
|
||||
};
|
||||
|
||||
context.builder.writing_mode = writing_mode;
|
||||
if let CascadeMode::Unvisited { visited_rules: Some(visited_rules) } = cascade_mode {
|
||||
let is_link = pseudo.is_none() && element.unwrap().is_link();
|
||||
macro_rules! visited_parent {
|
||||
($parent:expr) => {
|
||||
if is_link {
|
||||
$parent
|
||||
} else {
|
||||
$parent.map(|p| p.visited_style().unwrap_or(p))
|
||||
}
|
||||
}
|
||||
}
|
||||
// We could call apply_declarations directly, but that'd cause
|
||||
// another instantiation of this function which is not great.
|
||||
context.builder.visited_style = Some(cascade_rules(
|
||||
device,
|
||||
pseudo,
|
||||
visited_rules,
|
||||
guards,
|
||||
visited_parent!(parent_style),
|
||||
visited_parent!(parent_style_ignoring_first_line),
|
||||
visited_parent!(layout_parent_style),
|
||||
font_metrics_provider,
|
||||
CascadeMode::Visited { writing_mode },
|
||||
quirks_mode,
|
||||
// The rule cache doesn't care about caching :visited
|
||||
// styles, we cache the unvisited style instead. We still do
|
||||
// need to set the caching dependencies properly if present
|
||||
// though, so the cache conditions need to match.
|
||||
/* rule_cache = */ None,
|
||||
&mut *context.rule_cache_conditions.borrow_mut(),
|
||||
element,
|
||||
));
|
||||
}
|
||||
|
||||
let mut _skip_font_family = false;
|
||||
|
||||
% if product == "gecko":
|
||||
|
||||
// <svg:text> is not affected by text zoom, and it uses a preshint to
|
||||
// disable it. We fix up the struct when this happens by unzooming
|
||||
// its contained font values, which will have been zoomed in the parent
|
||||
if seen.contains(LonghandId::XTextZoom) {
|
||||
let zoom = context.builder.get_font().gecko().mAllowZoom;
|
||||
let parent_zoom = context.style().get_parent_font().gecko().mAllowZoom;
|
||||
if zoom != parent_zoom {
|
||||
debug_assert!(!zoom,
|
||||
"We only ever disable text zoom (in svg:text), never enable it");
|
||||
// can't borrow both device and font, use the take/put machinery
|
||||
let mut font = context.builder.take_font();
|
||||
font.unzoom_fonts(context.device());
|
||||
context.builder.put_font(font);
|
||||
}
|
||||
}
|
||||
|
||||
// Whenever a single generic value is specified, gecko will do a bunch of
|
||||
// recalculation walking up the rule tree, including handling the font-size stuff.
|
||||
// It basically repopulates the font struct with the default font for a given
|
||||
// generic and language. We handle the font-size stuff separately, so this boils
|
||||
// down to just copying over the font-family lists (no other aspect of the default
|
||||
// font can be configured).
|
||||
|
||||
if seen.contains(LonghandId::XLang) || font_family.is_some() {
|
||||
// if just the language changed, the inherited generic is all we need
|
||||
let mut generic = inherited_style.get_font().gecko().mGenericID;
|
||||
if let Some(ref declaration) = font_family {
|
||||
if let PropertyDeclaration::FontFamily(ref fam) = **declaration {
|
||||
if let Some(id) = fam.single_generic() {
|
||||
generic = id;
|
||||
// In case of a specified font family with a single generic, we will
|
||||
// end up setting font family below, but its value would get
|
||||
// overwritten later in the pipeline when cascading.
|
||||
//
|
||||
// We instead skip cascading font-family in that case.
|
||||
//
|
||||
// In case of the language changing, we wish for a specified font-
|
||||
// family to override this, so we do not skip cascading then.
|
||||
_skip_font_family = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pres_context = context.builder.device.pres_context();
|
||||
let gecko_font = context.builder.mutate_font().gecko_mut();
|
||||
gecko_font.mGenericID = generic;
|
||||
unsafe {
|
||||
bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric(
|
||||
gecko_font,
|
||||
pres_context,
|
||||
generic,
|
||||
);
|
||||
}
|
||||
}
|
||||
% endif
|
||||
|
||||
// It is important that font_size is computed before
|
||||
// the late properties (for em units), but after font-family
|
||||
// (for the base-font-size dependence for default and keyword font-sizes)
|
||||
// Additionally, when we support system fonts they will have to be
|
||||
// computed early, and *before* font_family, so I'm including
|
||||
// font_family here preemptively instead of keeping it within
|
||||
// the early properties.
|
||||
//
|
||||
// To avoid an extra iteration, we just pull out the property
|
||||
// during the early iteration and cascade them in order
|
||||
// after it.
|
||||
if !_skip_font_family {
|
||||
if let Some(ref declaration) = font_family {
|
||||
|
||||
let discriminant = LonghandId::FontFamily as usize;
|
||||
(CASCADE_PROPERTY[discriminant])(declaration, &mut context);
|
||||
% if product == "gecko":
|
||||
let device = context.builder.device;
|
||||
if let PropertyDeclaration::FontFamily(ref val) = **declaration {
|
||||
if val.get_system().is_some() {
|
||||
let default = context.cached_system_font
|
||||
.as_ref().unwrap().default_font_type;
|
||||
context.builder.mutate_font().fixup_system(default);
|
||||
} else {
|
||||
context.builder.mutate_font().fixup_none_generic(device);
|
||||
}
|
||||
}
|
||||
% endif
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref declaration) = font_size {
|
||||
let discriminant = LonghandId::FontSize as usize;
|
||||
(CASCADE_PROPERTY[discriminant])(declaration, &mut context);
|
||||
% if product == "gecko":
|
||||
// Font size must be explicitly inherited to handle lang changes and
|
||||
// scriptlevel changes.
|
||||
} else if seen.contains(LonghandId::XLang) ||
|
||||
seen.contains(LonghandId::MozScriptLevel) ||
|
||||
seen.contains(LonghandId::MozMinFontSizeRatio) ||
|
||||
font_family.is_some() {
|
||||
let discriminant = LonghandId::FontSize as usize;
|
||||
let size = PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
|
||||
id: LonghandId::FontSize,
|
||||
keyword: CSSWideKeyword::Inherit,
|
||||
});
|
||||
|
||||
(CASCADE_PROPERTY[discriminant])(&size, &mut context);
|
||||
% endif
|
||||
}
|
||||
|
||||
if let Some(style) = rule_cache.and_then(|c| c.find(guards, &context.builder)) {
|
||||
context.builder.copy_reset_from(style);
|
||||
apply_reset = false;
|
||||
}
|
||||
% endif // category == "early"
|
||||
% endfor
|
||||
|
||||
let mut builder = context.builder;
|
||||
|
||||
% if product == "gecko":
|
||||
if let Some(ref mut bg) = builder.get_background_if_mutated() {
|
||||
bg.fill_arrays();
|
||||
}
|
||||
|
||||
if let Some(ref mut svg) = builder.get_svg_if_mutated() {
|
||||
svg.fill_arrays();
|
||||
}
|
||||
% endif
|
||||
|
||||
% if product == "servo":
|
||||
if seen.contains(LonghandId::FontStyle) ||
|
||||
seen.contains(LonghandId::FontWeight) ||
|
||||
seen.contains(LonghandId::FontStretch) ||
|
||||
seen.contains(LonghandId::FontFamily) {
|
||||
builder.mutate_font().compute_font_hash();
|
||||
}
|
||||
% endif
|
||||
|
||||
builder.clear_modified_reset();
|
||||
|
||||
if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
|
||||
StyleAdjuster::new(&mut builder).adjust(
|
||||
layout_parent_style.unwrap_or(inherited_style),
|
||||
element,
|
||||
);
|
||||
}
|
||||
|
||||
if builder.modified_reset() || !apply_reset {
|
||||
// If we adjusted any reset structs, we can't cache this ComputedValues.
|
||||
//
|
||||
// Also, if we re-used existing reset structs, don't bother caching it
|
||||
// back again. (Aside from being wasted effort, it will be wrong, since
|
||||
// context.rule_cache_conditions won't be set appropriately if we
|
||||
// didn't compute those reset properties.)
|
||||
context.rule_cache_conditions.borrow_mut().set_uncacheable();
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
/// See StyleAdjuster::adjust_for_border_width.
|
||||
pub fn adjust_border_width(style: &mut StyleBuilder) {
|
||||
|
|
|
@ -12,7 +12,7 @@ use gecko::selector_parser::PseudoElement;
|
|||
#[cfg(feature = "gecko")]
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use properties::{Importance, LonghandIdSet, PropertyDeclarationBlock};
|
||||
use servo_arc::{Arc, ArcBorrow, ArcUnion, ArcUnionBorrow, NonZeroPtrMut};
|
||||
use servo_arc::{Arc, ArcBorrow, ArcUnion, ArcUnionBorrow};
|
||||
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
|
||||
use smallvec::SmallVec;
|
||||
use std::io::{self, Write};
|
||||
|
@ -938,19 +938,20 @@ impl MallocSizeOf for RuleNode {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: use std::ptr::NonNull when Firefox requires Rust 1.25+
|
||||
|
||||
#[derive(Clone)]
|
||||
struct WeakRuleNode {
|
||||
p: NonZeroPtrMut<RuleNode>,
|
||||
p: ptr::NonNull<RuleNode>,
|
||||
}
|
||||
|
||||
/// A strong reference to a rule node.
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
pub struct StrongRuleNode {
|
||||
p: NonZeroPtrMut<RuleNode>,
|
||||
p: ptr::NonNull<RuleNode>,
|
||||
}
|
||||
|
||||
unsafe impl Send for StrongRuleNode {}
|
||||
unsafe impl Sync for StrongRuleNode {}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(StrongRuleNode);
|
||||
|
||||
|
@ -968,7 +969,7 @@ impl StrongRuleNode {
|
|||
|
||||
fn from_ptr(ptr: *mut RuleNode) -> Self {
|
||||
StrongRuleNode {
|
||||
p: NonZeroPtrMut::new(ptr),
|
||||
p: ptr::NonNull::new(ptr).expect("Pointer must not be null"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1052,7 +1053,7 @@ impl StrongRuleNode {
|
|||
|
||||
/// Raw pointer to the RuleNode
|
||||
pub fn ptr(&self) -> *mut RuleNode {
|
||||
self.p.ptr()
|
||||
self.p.as_ptr()
|
||||
}
|
||||
|
||||
fn get(&self) -> &RuleNode {
|
||||
|
@ -1665,12 +1666,12 @@ impl WeakRuleNode {
|
|||
|
||||
fn from_ptr(ptr: *mut RuleNode) -> Self {
|
||||
WeakRuleNode {
|
||||
p: NonZeroPtrMut::new(ptr),
|
||||
p: ptr::NonNull::new(ptr).expect("Pointer must not be null"),
|
||||
}
|
||||
}
|
||||
|
||||
fn ptr(&self) -> *mut RuleNode {
|
||||
self.p.ptr()
|
||||
self.p.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,12 +76,13 @@ use properties::ComputedValues;
|
|||
use rule_tree::StrongRuleNode;
|
||||
use selectors::NthIndexCache;
|
||||
use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
|
||||
use servo_arc::{Arc, NonZeroPtrMut};
|
||||
use servo_arc::Arc;
|
||||
use smallbitvec::SmallBitVec;
|
||||
use smallvec::SmallVec;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::NonNull;
|
||||
use style_resolver::{PrimaryStyle, ResolvedElementStyles};
|
||||
use stylist::Stylist;
|
||||
use uluru::{Entry, LRUCache};
|
||||
|
@ -112,10 +113,16 @@ pub enum StyleSharingBehavior {
|
|||
|
||||
/// Opaque pointer type to compare ComputedValues identities.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct OpaqueComputedValues(NonZeroPtrMut<()>);
|
||||
pub struct OpaqueComputedValues(NonNull<()>);
|
||||
|
||||
unsafe impl Send for OpaqueComputedValues {}
|
||||
unsafe impl Sync for OpaqueComputedValues {}
|
||||
|
||||
impl OpaqueComputedValues {
|
||||
fn from(cv: &ComputedValues) -> Self {
|
||||
let p = NonZeroPtrMut::new(cv as *const ComputedValues as *const () as *mut ());
|
||||
let p = unsafe {
|
||||
NonNull::new_unchecked(cv as *const ComputedValues as *const () as *mut ())
|
||||
};
|
||||
OpaqueComputedValues(p)
|
||||
}
|
||||
|
||||
|
|
|
@ -953,7 +953,7 @@ impl ExtremumLength {
|
|||
/// TODO: After these values are supported for both axes (and maybe
|
||||
/// unprefixed, see bug 1322780) all this complexity can go away, and
|
||||
/// everything can be derived (no need for uncacheable stuff).
|
||||
fn valid_for(&self, wm: WritingMode, longhand: LonghandId) -> bool {
|
||||
fn valid_for(wm: WritingMode, longhand: LonghandId) -> bool {
|
||||
// We only make sense on the inline axis.
|
||||
match longhand {
|
||||
// FIXME(emilio): The flex-basis thing is not quite clear...
|
||||
|
@ -1018,7 +1018,7 @@ impl ToComputedValue for specified::MozLength {
|
|||
.rule_cache_conditions
|
||||
.borrow_mut()
|
||||
.set_writing_mode_dependency(context.builder.writing_mode);
|
||||
if !ext.valid_for(
|
||||
if !ExtremumLength::valid_for(
|
||||
context.builder.writing_mode,
|
||||
context.for_non_inherited_property.unwrap(),
|
||||
) {
|
||||
|
@ -1080,7 +1080,7 @@ impl ToComputedValue for specified::MaxLength {
|
|||
.rule_cache_conditions
|
||||
.borrow_mut()
|
||||
.set_writing_mode_dependency(context.builder.writing_mode);
|
||||
if !ext.valid_for(
|
||||
if !ExtremumLength::valid_for(
|
||||
context.builder.writing_mode,
|
||||
context.for_non_inherited_property.unwrap(),
|
||||
) {
|
||||
|
|
|
@ -47,8 +47,6 @@ pub enum CalcNode {
|
|||
pub enum CalcUnit {
|
||||
/// `<number>`
|
||||
Number,
|
||||
/// `<integer>`
|
||||
Integer,
|
||||
/// `<length>`
|
||||
Length,
|
||||
/// `<percentage>`
|
||||
|
@ -281,8 +279,7 @@ impl CalcNode {
|
|||
let new_root = CalcNode::Mul(Box::new(root), Box::new(rhs));
|
||||
root = new_root;
|
||||
},
|
||||
// TODO(emilio): Figure out why the `Integer` check.
|
||||
Ok(&Token::Delim('/')) if expected_unit != CalcUnit::Integer => {
|
||||
Ok(&Token::Delim('/')) => {
|
||||
let rhs = Self::parse_one(context, input, expected_unit)?;
|
||||
let new_root = CalcNode::Div(Box::new(root), Box::new(rhs));
|
||||
root = new_root;
|
||||
|
@ -532,10 +529,8 @@ impl CalcNode {
|
|||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<CSSInteger, ParseError<'i>> {
|
||||
Self::parse(context, input, CalcUnit::Integer)?
|
||||
.to_number()
|
||||
.map(|n| n as CSSInteger)
|
||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
Self::parse_number(context, input)
|
||||
.map(|n| n.round() as CSSInteger)
|
||||
}
|
||||
|
||||
/// Convenience parsing function for `<length> | <percentage>`.
|
||||
|
|
|
@ -317,12 +317,12 @@ impl Color {
|
|||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
/// Returns false if the color is completely transparent, and
|
||||
/// true otherwise.
|
||||
pub fn is_non_transparent(&self) -> bool {
|
||||
/// Returns true if the color is completely transparent, and false
|
||||
/// otherwise.
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
match *self {
|
||||
Color::Numeric { ref parsed, .. } => parsed.alpha != 0,
|
||||
_ => true,
|
||||
Color::Numeric { ref parsed, .. } => parsed.alpha == 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use cssparser::{Parser, ParserInput};
|
||||
use servo_arc::Arc;
|
||||
use style::custom_properties::{Name, SpecifiedValue, CustomPropertiesMap, CustomPropertiesBuilder};
|
||||
use style::properties::DeclaredValue;
|
||||
use style::properties::CustomDeclarationValue;
|
||||
use test::{self, Bencher};
|
||||
|
||||
fn cascade(
|
||||
|
@ -21,7 +21,7 @@ fn cascade(
|
|||
let mut builder = CustomPropertiesBuilder::new(inherited);
|
||||
|
||||
for &(ref name, ref val) in &values {
|
||||
builder.cascade(name, DeclaredValue::Value(val));
|
||||
builder.cascade(name, &CustomDeclarationValue::Value(val.clone()));
|
||||
}
|
||||
|
||||
builder.build()
|
||||
|
|
|
@ -18,7 +18,7 @@ use style::context::QuirksMode;
|
|||
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
|
||||
use style::media_queries::MediaList;
|
||||
use style::properties::{CSSWideKeyword, CustomDeclaration};
|
||||
use style::properties::{DeclaredValueOwned, Importance};
|
||||
use style::properties::{CustomDeclarationValue, Importance};
|
||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
|
||||
use style::properties::longhands::{self, animation_timing_function};
|
||||
use style::shared_lock::SharedRwLock;
|
||||
|
@ -113,7 +113,7 @@ fn test_parse_stylesheet() {
|
|||
(
|
||||
PropertyDeclaration::Custom(CustomDeclaration {
|
||||
name: Atom::from("a"),
|
||||
value: DeclaredValueOwned::CSSWideKeyword(CSSWideKeyword::Inherit),
|
||||
value: CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit),
|
||||
}),
|
||||
Importance::Important,
|
||||
),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue