Auto merge of #13134 - servo:archery, r=emilio

Add lots of Arc’s in style, and prepare for using DOMRefCell

<!-- Please describe your changes on the following line: -->

`DOMRefCell` usage is not there year because of thread-safety questions, but I have this much already that I’d like to land before it bitrots.

r? @emilio

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [ ] There are tests for these changes OR
- [x] These changes do not require new tests because refactor

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- 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/13134)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-08-31 16:30:56 -05:00 committed by GitHub
commit bbfe38e35f
29 changed files with 487 additions and 246 deletions

View file

@ -195,7 +195,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
fn as_node(&self) -> Self::ConcreteNode;
fn style_attribute(&self) -> &Option<PropertyDeclarationBlock>;
fn style_attribute(&self) -> Option<&Arc<PropertyDeclarationBlock>>;
fn get_state(&self) -> ElementState;

View file

@ -0,0 +1,129 @@
/* 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/. */
//! A shareable mutable container for the DOM.
use refcell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
use thread_state;
/// A mutable field in the DOM.
///
/// This extends the API of `core::cell::RefCell` to allow unsafe access in
/// certain situations, with dynamic checking in debug builds.
#[derive(Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct DOMRefCell<T> {
value: RefCell<T>,
}
// Functionality specific to Servo's `DOMRefCell` type
// ===================================================
impl<T> DOMRefCell<T> {
/// Return a reference to the contents.
///
/// For use in the layout thread only.
#[allow(unsafe_code)]
pub unsafe fn borrow_for_layout(&self) -> &T {
debug_assert!(thread_state::get().is_layout());
&*self.value.as_ptr()
}
/// Borrow the contents for the purpose of GC tracing.
///
/// This succeeds even if the object is mutably borrowed,
/// so you have to be careful in trace code!
#[allow(unsafe_code)]
pub unsafe fn borrow_for_gc_trace(&self) -> &T {
// FIXME: IN_GC isn't reliable enough - doesn't catch minor GCs
// https://github.com/servo/servo/issues/6389
// debug_assert!(thread_state::get().contains(SCRIPT | IN_GC));
&*self.value.as_ptr()
}
/// Borrow the contents for the purpose of script deallocation.
///
#[allow(unsafe_code)]
pub unsafe fn borrow_for_script_deallocation(&self) -> &mut T {
debug_assert!(thread_state::get().contains(thread_state::SCRIPT));
&mut *self.value.as_ptr()
}
/// Version of the above that we use during restyle while the script thread
/// is blocked.
pub fn borrow_mut_for_layout(&self) -> RefMut<T> {
debug_assert!(thread_state::get().is_layout());
self.value.borrow_mut()
}
}
// Functionality duplicated with `core::cell::RefCell`
// ===================================================
impl<T> DOMRefCell<T> {
/// Create a new `DOMRefCell` containing `value`.
pub fn new(value: T) -> DOMRefCell<T> {
DOMRefCell {
value: RefCell::new(value),
}
}
/// Immutably borrows the wrapped value.
///
/// The borrow lasts until the returned `Ref` exits scope. Multiple
/// immutable borrows can be taken out at the same time.
///
/// # Panics
///
/// Panics if this is called off the script thread.
///
/// Panics if the value is currently mutably borrowed.
pub fn borrow(&self) -> Ref<T> {
self.try_borrow().expect("DOMRefCell<T> already mutably borrowed")
}
/// Mutably borrows the wrapped value.
///
/// The borrow lasts until the returned `RefMut` exits scope. The value
/// cannot be borrowed while this borrow is active.
///
/// # Panics
///
/// Panics if this is called off the script thread.
///
/// Panics if the value is currently borrowed.
pub fn borrow_mut(&self) -> RefMut<T> {
self.try_borrow_mut().expect("DOMRefCell<T> already borrowed")
}
/// Attempts to immutably borrow the wrapped value.
///
/// The borrow lasts until the returned `Ref` exits scope. Multiple
/// immutable borrows can be taken out at the same time.
///
/// Returns `None` if the value is currently mutably borrowed.
///
/// # Panics
///
/// Panics if this is called off the script thread.
pub fn try_borrow(&self) -> Result<Ref<T>, BorrowError<T>> {
debug_assert!(thread_state::get().is_script());
self.value.try_borrow()
}
/// Mutably borrows the wrapped value.
///
/// The borrow lasts until the returned `RefMut` exits scope. The value
/// cannot be borrowed while this borrow is active.
///
/// Returns `None` if the value is currently borrowed.
///
/// # Panics
///
/// Panics if this is called off the script thread.
pub fn try_borrow_mut(&self) -> Result<RefMut<T>, BorrowMutError<T>> {
debug_assert!(thread_state::get().is_script());
self.value.try_borrow_mut()
}
}

View file

@ -7,7 +7,7 @@ use cssparser::{DeclarationListParser, DeclarationParser};
use parser::{ParserContext, log_css_error};
use properties::PropertyDeclarationParseResult;
use properties::animated_properties::TransitionProperty;
use properties::{PropertyDeclaration, Importance};
use properties::{PropertyDeclaration, PropertyDeclarationBlock, Importance};
use std::sync::Arc;
/// A number from 1 to 100, indicating the percentage of the animation where
@ -77,7 +77,7 @@ pub struct Keyframe {
/// so the second value of these tuples is always `Importance::Normal`.
/// But including them enables `compute_style_for_animation_step` to create a `DeclarationBlock`
/// by cloning an `Arc<_>` (incrementing a reference count) rather than re-creating a `Vec<_>`.
pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
pub block: Arc<PropertyDeclarationBlock>,
}
/// A keyframes step value. This can be a synthetised keyframes animation, that
@ -88,7 +88,7 @@ pub struct Keyframe {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesStepValue {
/// See `Keyframe::declarations`s docs about the presence of `Importance`.
Declarations(Arc<Vec<(PropertyDeclaration, Importance)>>),
Declarations(Arc<PropertyDeclarationBlock>),
ComputedValues,
}
@ -113,8 +113,8 @@ impl KeyframesStep {
fn new(percentage: KeyframePercentage,
value: KeyframesStepValue) -> Self {
let declared_timing_function = match value {
KeyframesStepValue::Declarations(ref declarations) => {
declarations.iter().any(|&(ref prop_decl, _)| {
KeyframesStepValue::Declarations(ref block) => {
block.declarations.iter().any(|&(ref prop_decl, _)| {
match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => true,
_ => false,
@ -154,7 +154,7 @@ fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
let mut ret = vec![];
// NB: declarations are already deduplicated, so we don't have to check for
// it here.
for &(ref declaration, _) in keyframe.declarations.iter() {
for &(ref declaration, _) in keyframe.block.declarations.iter() {
if let Some(property) = TransitionProperty::from_declaration(declaration) {
ret.push(property);
}
@ -164,7 +164,7 @@ fn get_animated_properties(keyframe: &Keyframe) -> Vec<TransitionProperty> {
}
impl KeyframesAnimation {
pub fn from_keyframes(keyframes: &[Keyframe]) -> Option<Self> {
pub fn from_keyframes(keyframes: &[Arc<Keyframe>]) -> Option<Self> {
if keyframes.is_empty() {
return None;
}
@ -179,7 +179,7 @@ impl KeyframesAnimation {
for keyframe in keyframes {
for percentage in keyframe.selector.0.iter() {
steps.push(KeyframesStep::new(*percentage,
KeyframesStepValue::Declarations(keyframe.declarations.clone())));
KeyframesStepValue::Declarations(keyframe.block.clone())));
}
}
@ -216,7 +216,7 @@ struct KeyframeListParser<'a> {
context: &'a ParserContext<'a>,
}
pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Vec<Keyframe> {
pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Vec<Arc<Keyframe>> {
RuleListParser::new_for_nested_rule(input, KeyframeListParser { context: context })
.filter_map(Result::ok)
.collect()
@ -225,12 +225,12 @@ pub fn parse_keyframe_list(context: &ParserContext, input: &mut Parser) -> Vec<K
enum Void {}
impl<'a> AtRuleParser for KeyframeListParser<'a> {
type Prelude = Void;
type AtRule = Keyframe;
type AtRule = Arc<Keyframe>;
}
impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
type Prelude = KeyframeSelector;
type QualifiedRule = Keyframe;
type QualifiedRule = Arc<Keyframe>;
fn parse_prelude(&self, input: &mut Parser) -> Result<Self::Prelude, ()> {
let start = input.position();
@ -263,10 +263,13 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> {
}
// `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
}
Ok(Keyframe {
Ok(Arc::new(Keyframe {
selector: prelude,
declarations: Arc::new(declarations),
})
block: Arc::new(PropertyDeclarationBlock {
declarations: declarations,
important_count: 0,
}),
}))
}
}

View file

@ -81,6 +81,7 @@ pub mod context;
pub mod custom_properties;
pub mod data;
pub mod dom;
pub mod domrefcell;
pub mod element_state;
pub mod error_reporting;
pub mod font_face;

View file

@ -14,7 +14,7 @@ use context::{StyleContext, SharedStyleContext};
use data::PrivateStyleData;
use dom::{TElement, TNode, TRestyleDamage, UnsafeNode};
use properties::longhands::display::computed_value as display;
use properties::{ComputedValues, cascade};
use properties::{ComputedValues, cascade, PropertyDeclarationBlock};
use selector_impl::{TheSelectorImpl, PseudoElement};
use selector_matching::{DeclarationBlock, Stylist};
use selectors::bloom::BloomFilter;
@ -139,7 +139,7 @@ impl<'a> Hash for ApplicableDeclarationsCacheQuery<'a> {
for declaration in self.declarations {
// Each declaration contians an Arc, which is a stable
// pointer; we use that for hashing and equality.
let ptr: *const Vec<_> = &*declaration.mixed_declarations;
let ptr: *const PropertyDeclarationBlock = &*declaration.mixed_declarations;
ptr.hash(state);
declaration.importance.hash(state);
}
@ -651,7 +651,7 @@ pub trait ElementMatchMethods : TElement {
applicable_declarations: &mut ApplicableDeclarations)
-> StyleRelations {
use traversal::relations_are_shareable;
let style_attribute = self.style_attribute().as_ref();
let style_attribute = self.style_attribute();
let mut relations =
stylist.push_applicable_declarations(self,

View file

@ -282,16 +282,36 @@ impl Importance {
/// Overridden declarations are skipped.
// FIXME (https://github.com/servo/servo/issues/3426)
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct PropertyDeclarationBlock {
#[cfg_attr(feature = "servo", ignore_heap_size_of = "#7038")]
pub declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
pub declarations: Vec<(PropertyDeclaration, Importance)>,
/// The number of entries in `self.declaration` with `Importance::Important`
pub important_count: u32,
}
impl PropertyDeclarationBlock {
/// Returns wheather this block contains any declaration with `!important`.
///
/// This is based on the `important_count` counter,
/// which should be maintained whenever `declarations` is changed.
// FIXME: make fields private and maintain it here in methods?
pub fn any_important(&self) -> bool {
self.important_count > 0
}
/// Returns wheather this block contains any declaration without `!important`.
///
/// This is based on the `important_count` counter,
/// which should be maintained whenever `declarations` is changed.
// FIXME: make fields private and maintain it here in methods?
pub fn any_normal(&self) -> bool {
self.declarations.len() > self.important_count as usize
}
}
impl ToCss for PropertyDeclarationBlock {
// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@ -567,7 +587,7 @@ pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Pars
}
}
let mut block = PropertyDeclarationBlock {
declarations: Arc::new(declarations),
declarations: declarations,
important_count: important_count,
};
deduplicate_property_declarations(&mut block);
@ -583,8 +603,7 @@ fn deduplicate_property_declarations(block: &mut PropertyDeclarationBlock) {
let mut seen_custom_normal = Vec::new();
let mut seen_custom_important = Vec::new();
let declarations = Arc::get_mut(&mut block.declarations).unwrap();
for (declaration, importance) in declarations.drain(..).rev() {
for (declaration, importance) in block.declarations.drain(..).rev() {
match declaration {
% for property in data.longhands:
PropertyDeclaration::${property.camel_case}(..) => {
@ -636,7 +655,7 @@ fn deduplicate_property_declarations(block: &mut PropertyDeclarationBlock) {
deduplicated.push((declaration, importance))
}
deduplicated.reverse();
*declarations = deduplicated;
block.declarations = deduplicated;
}
#[inline]

View file

@ -15,8 +15,11 @@
#![allow(unsafe_code)]
#[cfg(feature = "servo")] use heapsize::HeapSizeOf;
use std::cell::{UnsafeCell, Cell};
use std::cmp::Ordering;
use std::fmt::{self, Debug, Display};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
/// A fork of std::cell::RefCell that makes `as_unsafe_cell` usable on stable Rust.
@ -28,7 +31,13 @@ pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>,
value: UnsafeCell<T>,
}
type BorrowFlag = usize;
#[cfg(feature = "servo")]
impl<T: HeapSizeOf> HeapSizeOf for RefCell<T> {
fn heap_size_of_children(&self) -> usize {
self.borrow().heap_size_of_children()
}
}
/// An enumeration of values returned from the `state` method on a `RefCell<T>`.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@ -41,8 +50,43 @@ pub enum BorrowState {
Unused,
}
/// An error returned by [`RefCell::try_borrow`](struct.RefCell.html#method.try_borrow).
pub struct BorrowError<'a, T: 'a + ?Sized> {
marker: PhantomData<&'a RefCell<T>>,
}
impl<'a, T: ?Sized> Debug for BorrowError<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BorrowError").finish()
}
}
impl<'a, T: ?Sized> Display for BorrowError<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("already mutably borrowed", f)
}
}
/// An error returned by [`RefCell::try_borrow_mut`](struct.RefCell.html#method.try_borrow_mut).
pub struct BorrowMutError<'a, T: 'a + ?Sized> {
marker: PhantomData<&'a RefCell<T>>,
}
impl<'a, T: ?Sized> Debug for BorrowMutError<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BorrowMutError").finish()
}
}
impl<'a, T: ?Sized> Display for BorrowMutError<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("already borrowed", f)
}
}
// Values [1, MAX-1] represent the number of `Ref` active
// (will not outgrow its range since `usize` is the size of the address space)
type BorrowFlag = usize;
const UNUSED: BorrowFlag = 0;
const WRITING: BorrowFlag = !0;
@ -90,6 +134,22 @@ impl<T: ?Sized> RefCell<T> {
///
/// The returned value can be dispatched on to determine if a call to
/// `borrow` or `borrow_mut` would succeed.
///
/// # Examples
///
/// ```
/// #![feature(borrow_state)]
///
/// use std::cell::{BorrowState, RefCell};
///
/// let c = RefCell::new(5);
///
/// match c.borrow_state() {
/// BorrowState::Writing => println!("Cannot be borrowed"),
/// BorrowState::Reading => println!("Cannot be borrowed mutably"),
/// BorrowState::Unused => println!("Can be borrowed (mutably as well)"),
/// }
/// ```
#[inline]
pub fn borrow_state(&self) -> BorrowState {
match self.borrow.get() {
@ -106,7 +166,8 @@ impl<T: ?Sized> RefCell<T> {
///
/// # Panics
///
/// Panics if the value is currently mutably borrowed.
/// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
/// [`try_borrow`](#method.try_borrow).
///
/// # Examples
///
@ -136,12 +197,44 @@ impl<T: ?Sized> RefCell<T> {
/// ```
#[inline]
pub fn borrow(&self) -> Ref<T> {
self.try_borrow().expect("already mutably borrowed")
}
/// Immutably borrows the wrapped value, returning an error if the value is currently mutably
/// borrowed.
///
/// The borrow lasts until the returned `Ref` exits scope. Multiple immutable borrows can be
/// taken out at the same time.
///
/// This is the non-panicking variant of [`borrow`](#method.borrow).
///
/// # Examples
///
/// ```
/// #![feature(try_borrow)]
///
/// use std::cell::RefCell;
///
/// let c = RefCell::new(5);
///
/// {
/// let m = c.borrow_mut();
/// assert!(c.try_borrow().is_err());
/// }
///
/// {
/// let m = c.borrow();
/// assert!(c.try_borrow().is_ok());
/// }
/// ```
#[inline]
pub fn try_borrow(&self) -> Result<Ref<T>, BorrowError<T>> {
match BorrowRef::new(&self.borrow) {
Some(b) => Ref {
Some(b) => Ok(Ref {
value: unsafe { &*self.value.get() },
borrow: b,
},
None => panic!("RefCell<T> already mutably borrowed"),
}),
None => Err(BorrowError { marker: PhantomData }),
}
}
@ -152,7 +245,8 @@ impl<T: ?Sized> RefCell<T> {
///
/// # Panics
///
/// Panics if the value is currently borrowed.
/// Panics if the value is currently borrowed. For a non-panicking variant, use
/// [`try_borrow_mut`](#method.try_borrow_mut).
///
/// # Examples
///
@ -183,12 +277,40 @@ impl<T: ?Sized> RefCell<T> {
/// ```
#[inline]
pub fn borrow_mut(&self) -> RefMut<T> {
self.try_borrow_mut().expect("already borrowed")
}
/// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
///
/// The borrow lasts until the returned `RefMut` exits scope. The value cannot be borrowed
/// while this borrow is active.
///
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
///
/// # Examples
///
/// ```
/// #![feature(try_borrow)]
///
/// use std::cell::RefCell;
///
/// let c = RefCell::new(5);
///
/// {
/// let m = c.borrow();
/// assert!(c.try_borrow_mut().is_err());
/// }
///
/// assert!(c.try_borrow_mut().is_ok());
/// ```
#[inline]
pub fn try_borrow_mut(&self) -> Result<RefMut<T>, BorrowMutError<T>> {
match BorrowRefMut::new(&self.borrow) {
Some(b) => RefMut {
Some(b) => Ok(RefMut {
value: unsafe { &mut *self.value.get() },
borrow: b,
},
None => panic!("RefCell<T> already borrowed"),
}),
None => Err(BorrowMutError { marker: PhantomData }),
}
}
@ -197,15 +319,53 @@ impl<T: ?Sized> RefCell<T> {
/// This can be used to circumvent `RefCell`'s safety checks.
///
/// This function is `unsafe` because `UnsafeCell`'s field is public.
///
/// # Examples
///
/// ```
/// #![feature(as_unsafe_cell)]
///
/// use std::cell::RefCell;
///
/// let c = RefCell::new(5);
/// let c = unsafe { c.as_unsafe_cell() };
/// ```
#[inline]
pub unsafe fn as_unsafe_cell(&self) -> &UnsafeCell<T> {
&self.value
}
/// Returns a raw pointer to the underlying data in this cell.
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// let c = RefCell::new(5);
///
/// let ptr = c.as_ptr();
/// ```
#[inline]
pub fn as_ptr(&self) -> *mut T {
self.value.get()
}
/// Returns a mutable reference to the underlying data.
///
/// This call borrows `RefCell` mutably (at compile-time) so there is no
/// need for dynamic checks.
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// let mut c = RefCell::new(5);
/// *c.get_mut() += 1;
///
/// assert_eq!(c, RefCell::new(6));
/// ```
#[inline]
pub fn get_mut(&mut self) -> &mut T {
unsafe {
@ -375,6 +535,18 @@ impl<'b, T: ?Sized> Ref<'b, T> {
borrow: orig.borrow,
}
}
#[inline]
pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Option<Ref<'b, U>>
where F: FnOnce(&T) -> Option<&U>
{
f(orig.value).map(move |new_value| {
Ref {
value: new_value,
borrow: orig.borrow,
}
})
}
}
impl<'b, T: ?Sized> RefMut<'b, T> {
@ -461,3 +633,35 @@ impl<'b, T: ?Sized> DerefMut for RefMut<'b, T> {
self.value
}
}
// Imported from src/libcore/fmt/mod.rs
impl<T: ?Sized + Debug> Debug for RefCell<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.borrow_state() {
BorrowState::Unused | BorrowState::Reading => {
f.debug_struct("RefCell")
.field("value", &self.borrow())
.finish()
}
BorrowState::Writing => {
f.debug_struct("RefCell")
.field("value", &"<borrowed>")
.finish()
}
}
}
}
impl<'b, T: ?Sized + Debug> Debug for Ref<'b, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<'b, T: ?Sized + Debug> Debug for RefMut<'b, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Debug::fmt(&*(self.deref()), f)
}
}

View file

@ -165,28 +165,26 @@ impl Stylist {
// Take apart the StyleRule into individual Rules and insert
// them into the SelectorMap of that priority.
macro_rules! append(
($style_rule: ident, $priority: ident, $importance: expr, $count: expr) => {
if $count > 0 {
for selector in &$style_rule.selectors {
let map = if let Some(ref pseudo) = selector.pseudo_element {
self.pseudos_map
.entry(pseudo.clone())
.or_insert_with(PerPseudoElementSelectorMap::new)
.borrow_for_origin(&stylesheet.origin)
} else {
self.element_map.borrow_for_origin(&stylesheet.origin)
};
($style_rule: ident, $priority: ident, $importance: expr) => {
for selector in &$style_rule.selectors {
let map = if let Some(ref pseudo) = selector.pseudo_element {
self.pseudos_map
.entry(pseudo.clone())
.or_insert_with(PerPseudoElementSelectorMap::new)
.borrow_for_origin(&stylesheet.origin)
} else {
self.element_map.borrow_for_origin(&stylesheet.origin)
};
map.$priority.insert(Rule {
selector: selector.complex_selector.clone(),
declarations: DeclarationBlock {
specificity: selector.specificity,
mixed_declarations: $style_rule.declarations.declarations.clone(),
importance: $importance,
source_order: rules_source_order,
},
});
}
map.$priority.insert(Rule {
selector: selector.complex_selector.clone(),
declarations: DeclarationBlock {
specificity: selector.specificity,
mixed_declarations: $style_rule.declarations.clone(),
importance: $importance,
source_order: rules_source_order,
},
});
}
};
);
@ -194,10 +192,8 @@ impl Stylist {
for rule in stylesheet.effective_rules(&self.device) {
match *rule {
CSSRule::Style(ref style_rule) => {
let important_count = style_rule.declarations.important_count;
let normal_count = style_rule.declarations.declarations.len() as u32 - important_count;
append!(style_rule, normal, Importance::Normal, normal_count);
append!(style_rule, important, Importance::Important, important_count);
append!(style_rule, normal, Importance::Normal);
append!(style_rule, important, Importance::Important);
rules_source_order += 1;
for selector in &style_rule.selectors {
@ -346,7 +342,7 @@ impl Stylist {
&self,
element: &E,
parent_bf: Option<&BloomFilter>,
style_attribute: Option<&PropertyDeclarationBlock>,
style_attribute: Option<&Arc<PropertyDeclarationBlock>>,
pseudo_element: Option<&PseudoElement>,
applicable_declarations: &mut V) -> StyleRelations
where E: Element<Impl=TheSelectorImpl> +
@ -373,7 +369,8 @@ impl Stylist {
map.user_agent.normal.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut relations);
&mut relations,
Importance::Normal);
debug!("UA normal: {:?}", relations);
// Step 2: Presentational hints.
@ -389,23 +386,23 @@ impl Stylist {
map.user.normal.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut relations);
&mut relations,
Importance::Normal);
debug!("user normal: {:?}", relations);
map.author.normal.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut relations);
&mut relations,
Importance::Normal);
debug!("author normal: {:?}", relations);
// Step 4: Normal style attributes.
if let Some(ref sa) = style_attribute {
if sa.declarations.len() as u32 - sa.important_count > 0 {
if let Some(sa) = style_attribute {
if sa.any_normal() {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
applicable_declarations,
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Normal));
DeclarationBlock::from_declarations(sa.clone(), Importance::Normal));
}
}
@ -415,19 +412,18 @@ impl Stylist {
map.author.important.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut relations);
&mut relations,
Importance::Important);
debug!("author important: {:?}", relations);
// Step 6: `!important` style attributes.
if let Some(ref sa) = style_attribute {
if sa.important_count > 0 {
if let Some(sa) = style_attribute {
if sa.any_important() {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
applicable_declarations,
DeclarationBlock::from_declarations(
sa.declarations.clone(),
Importance::Important));
DeclarationBlock::from_declarations(sa.clone(), Importance::Important));
}
}
@ -437,14 +433,16 @@ impl Stylist {
map.user.important.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut relations);
&mut relations,
Importance::Important);
debug!("user important: {:?}", relations);
map.user_agent.important.get_all_matching_rules(element,
parent_bf,
applicable_declarations,
&mut relations);
&mut relations,
Importance::Important);
debug!("UA important: {:?}", relations);
@ -651,7 +649,8 @@ impl SelectorMap {
element: &E,
parent_bf: Option<&BloomFilter>,
matching_rules_list: &mut V,
relations: &mut StyleRelations)
relations: &mut StyleRelations,
importance: Importance)
where E: Element<Impl=TheSelectorImpl>,
V: VecLike<DeclarationBlock>
{
@ -667,7 +666,8 @@ impl SelectorMap {
&self.id_hash,
&id,
matching_rules_list,
relations)
relations,
importance)
}
element.each_class(|class| {
@ -676,7 +676,8 @@ impl SelectorMap {
&self.class_hash,
class,
matching_rules_list,
relations);
relations,
importance);
});
let local_name_hash = if element.is_html_element_in_html_document() {
@ -689,13 +690,15 @@ impl SelectorMap {
local_name_hash,
element.get_local_name(),
matching_rules_list,
relations);
relations,
importance);
SelectorMap::get_matching_rules(element,
parent_bf,
&self.other_rules,
matching_rules_list,
relations);
relations,
importance);
// Sort only the rules we just added.
sort_by_key(&mut matching_rules_list[init_len..],
@ -731,7 +734,8 @@ impl SelectorMap {
hash: &FnvHashMap<Str, Vec<Rule>>,
key: &BorrowedStr,
matching_rules: &mut Vector,
relations: &mut StyleRelations)
relations: &mut StyleRelations,
importance: Importance)
where E: Element<Impl=TheSelectorImpl>,
Str: Borrow<BorrowedStr> + Eq + Hash,
BorrowedStr: Eq + Hash,
@ -742,7 +746,8 @@ impl SelectorMap {
parent_bf,
rules,
matching_rules,
relations)
relations,
importance)
}
}
@ -751,12 +756,20 @@ impl SelectorMap {
parent_bf: Option<&BloomFilter>,
rules: &[Rule],
matching_rules: &mut V,
relations: &mut StyleRelations)
relations: &mut StyleRelations,
importance: Importance)
where E: Element<Impl=TheSelectorImpl>,
V: VecLike<DeclarationBlock>
{
for rule in rules.iter() {
if matches_complex_selector(&*rule.selector,
let block = &rule.declarations.mixed_declarations;
let any_declaration_for_importance = if importance.important() {
block.any_important()
} else {
block.any_normal()
};
if any_declaration_for_importance &&
matches_complex_selector(&*rule.selector,
element, parent_bf, relations) {
matching_rules.push(rule.declarations.clone());
}
@ -845,7 +858,7 @@ pub struct Rule {
pub struct DeclarationBlock {
/// Contains declarations of either importance, but only those of self.importance are relevant.
/// Use DeclarationBlock::iter
pub mixed_declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
pub mixed_declarations: Arc<PropertyDeclarationBlock>,
pub importance: Importance,
pub source_order: usize,
pub specificity: u32,
@ -853,7 +866,7 @@ pub struct DeclarationBlock {
impl DeclarationBlock {
#[inline]
pub fn from_declarations(declarations: Arc<Vec<(PropertyDeclaration, Importance)>>,
pub fn from_declarations(declarations: Arc<PropertyDeclarationBlock>,
importance: Importance)
-> Self {
DeclarationBlock {
@ -866,7 +879,7 @@ impl DeclarationBlock {
pub fn iter(&self) -> DeclarationBlockIter {
DeclarationBlockIter {
iter: self.mixed_declarations.iter(),
iter: self.mixed_declarations.declarations.iter(),
importance: self.importance,
}
}

View file

@ -19,6 +19,7 @@ use smallvec::SmallVec;
use std::cell::Cell;
use std::iter::Iterator;
use std::slice;
use std::sync::Arc;
use string_cache::{Atom, Namespace};
use url::Url;
use viewport::ViewportRule;
@ -64,31 +65,37 @@ pub struct UserAgentStylesheets {
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum CSSRule {
Charset(String),
Namespace {
/// `None` for the default Namespace
prefix: Option<Atom>,
url: Namespace,
},
Style(StyleRule),
Media(MediaRule),
FontFace(FontFaceRule),
Viewport(ViewportRule),
Keyframes(KeyframesRule),
// No Charset here, CSSCharsetRule has been removed from CSSOM
// https://drafts.csswg.org/cssom/#changes-from-5-december-2013
Namespace(Arc<NamespaceRule>),
Style(Arc<StyleRule>),
Media(Arc<MediaRule>),
FontFace(Arc<FontFaceRule>),
Viewport(Arc<ViewportRule>),
Keyframes(Arc<KeyframesRule>),
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct NamespaceRule {
/// `None` for the default Namespace
pub prefix: Option<Atom>,
pub url: Namespace,
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct KeyframesRule {
pub name: Atom,
pub keyframes: Vec<Keyframe>,
pub keyframes: Vec<Arc<Keyframe>>,
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct MediaRule {
pub media_queries: MediaQueryList,
pub media_queries: Arc<MediaQueryList>,
pub rules: Vec<CSSRule>,
}
@ -104,7 +111,7 @@ impl MediaRule {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct StyleRule {
pub selectors: Vec<Selector<TheSelectorImpl>>,
pub declarations: PropertyDeclarationBlock,
pub declarations: Arc<PropertyDeclarationBlock>,
}
@ -154,13 +161,13 @@ impl Stylesheet {
while let Some(result) = iter.next() {
match result {
Ok(rule) => {
if let CSSRule::Namespace { ref prefix, ref url } = rule {
if let Some(prefix) = prefix.as_ref() {
if let CSSRule::Namespace(ref rule) = rule {
if let Some(ref prefix) = rule.prefix {
iter.parser.context.selector_context.namespace_prefixes.insert(
prefix.clone(), url.clone());
prefix.clone(), rule.url.clone());
} else {
iter.parser.context.selector_context.default_namespace =
Some(url.clone());
Some(rule.url.clone());
}
}
@ -408,7 +415,7 @@ enum AtRulePrelude {
/// A @font-face rule prelude.
FontFace,
/// A @media rule prelude, with its media queries.
Media(MediaQueryList),
Media(Arc<MediaQueryList>),
/// A @viewport rule prelude.
Viewport,
/// A @keyframes rule, with its animation name.
@ -423,16 +430,6 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
fn parse_prelude(&self, name: &str, input: &mut Parser)
-> Result<AtRuleType<AtRulePrelude, CSSRule>, ()> {
match_ignore_ascii_case! { name,
"charset" => {
if self.state.get() <= State::Start {
// Valid @charset rules are just ignored
self.state.set(State::Imports);
let charset = try!(input.expect_string()).into_owned();
return Ok(AtRuleType::WithoutBlock(CSSRule::Charset(charset)))
} else {
return Err(()) // "@charset must be the first rule"
}
},
"import" => {
if self.state.get() <= State::Imports {
self.state.set(State::Imports);
@ -448,14 +445,17 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> {
let prefix = input.try(|input| input.expect_ident()).ok().map(|p| p.into());
let url = Namespace(Atom::from(try!(input.expect_url_or_string())));
return Ok(AtRuleType::WithoutBlock(CSSRule::Namespace {
return Ok(AtRuleType::WithoutBlock(CSSRule::Namespace(Arc::new(NamespaceRule {
prefix: prefix,
url: url,
}))
}))))
} else {
return Err(()) // "@namespace must be before any rule but @charset and @import"
}
},
// @charset is removed by rust-cssparser if its the first rule in the stylesheet
// anything left is invalid.
"charset" => return Err(()), // (insert appropriate error message)
_ => {}
}
@ -502,7 +502,7 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
match_ignore_ascii_case! { name,
"media" => {
let media_queries = parse_media_query_list(input);
Ok(AtRuleType::WithBlock(AtRulePrelude::Media(media_queries)))
Ok(AtRuleType::WithBlock(AtRulePrelude::Media(Arc::new(media_queries))))
},
"font-face" => {
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
@ -530,22 +530,22 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
fn parse_block(&self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CSSRule, ()> {
match prelude {
AtRulePrelude::FontFace => {
parse_font_face_block(self.context, input).map(CSSRule::FontFace)
Ok(CSSRule::FontFace(Arc::new(try!(parse_font_face_block(self.context, input)))))
}
AtRulePrelude::Media(media_queries) => {
Ok(CSSRule::Media(MediaRule {
Ok(CSSRule::Media(Arc::new(MediaRule {
media_queries: media_queries,
rules: parse_nested_rules(self.context, input),
}))
})))
}
AtRulePrelude::Viewport => {
ViewportRule::parse(input, self.context).map(CSSRule::Viewport)
Ok(CSSRule::Viewport(Arc::new(try!(ViewportRule::parse(input, self.context)))))
}
AtRulePrelude::Keyframes(name) => {
Ok(CSSRule::Keyframes(KeyframesRule {
Ok(CSSRule::Keyframes(Arc::new(KeyframesRule {
name: name,
keyframes: parse_keyframe_list(&self.context, input),
}))
})))
}
}
}
@ -560,9 +560,9 @@ impl<'a, 'b> QualifiedRuleParser for NestedRuleParser<'a, 'b> {
}
fn parse_block(&self, prelude: Vec<Selector<TheSelectorImpl>>, input: &mut Parser) -> Result<CSSRule, ()> {
Ok(CSSRule::Style(StyleRule {
Ok(CSSRule::Style(Arc::new(StyleRule {
selectors: prelude,
declarations: parse_property_declaration_list(self.context, input)
}))
declarations: Arc::new(parse_property_declaration_list(self.context, input))
})))
}
}