servo: Merge #1644 - Implement style sharing and related optimizations (from pcwalton:style-sharing); r=larsbergstrom

This series of patches implements style struct sharing as found in existing browser engines, as well as a bunch of related important optimizations. With them, we are faster than Blink, WebKit, and Gecko on the rainbow page for style recalc in sequential mode by at least 15%. Parallel gains are mixed—the rainbow page turns out to be a degenerate sequential case for the LRU cache used to track candidates for style sharing and so there is no improvement. For cases in which the cache is not hit, such as `perf-rainbow-hard.html`, we are around 25% slower than Blink sequentially, but have very large parallel wins so that we are around 2x faster at style recalc. (Note that parallel flow tree construction will improve this further.)

This patch series also fixes, as near as I can tell, some bugs related to ordering of properties that other properties depend on in selector matching.

r? @larsbergstrom
feedback? @SimonSapin (for selector matching changes)
feedback? @bzbarsky (for style sharing heuristics)

Source-Repo: https://github.com/servo/servo
Source-Revision: 425210b9b404ca769037879f9c2a01c63a0375c8
This commit is contained in:
Patrick Walton 2014-02-14 20:47:01 -05:00
parent c7477ef70c
commit f96de358d8
21 changed files with 18935 additions and 539 deletions

View File

@ -6,30 +6,414 @@
use css::node_style::StyledNode;
use layout::extra::LayoutAuxMethods;
use layout::incremental;
use layout::util::LayoutDataAccess;
use layout::wrapper::LayoutNode;
use layout::util::{LayoutDataAccess, LayoutDataWrapper};
use layout::wrapper::{LayoutElement, LayoutNode};
use extra::arc::Arc;
use script::layout_interface::LayoutChan;
use servo_util::smallvec::SmallVec;
use style::{TNode, Stylist, cascade};
use style::{Before, After};
use servo_util::cache::{Cache, LRUCache, SimpleHashCache};
use servo_util::namespace::Null;
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec16};
use std::cast;
use std::to_bytes;
use std::vec::VecIterator;
use style::{After, Before, ComputedValues, MatchedProperty, Stylist, TElement, TNode, cascade};
pub struct ApplicableDeclarations {
normal: SmallVec16<MatchedProperty>,
before: SmallVec0<MatchedProperty>,
after: SmallVec0<MatchedProperty>,
/// Whether the `normal` declarations are shareable with other nodes.
normal_shareable: bool,
}
impl ApplicableDeclarations {
pub fn new() -> ApplicableDeclarations {
ApplicableDeclarations {
normal: SmallVec16::new(),
before: SmallVec0::new(),
after: SmallVec0::new(),
normal_shareable: false,
}
}
pub fn clear(&mut self) {
self.normal = SmallVec16::new();
self.before = SmallVec0::new();
self.after = SmallVec0::new();
self.normal_shareable = false;
}
}
#[deriving(Clone)]
struct ApplicableDeclarationsCacheEntry {
declarations: SmallVec16<MatchedProperty>,
}
impl ApplicableDeclarationsCacheEntry {
fn new(slice: &[MatchedProperty]) -> ApplicableDeclarationsCacheEntry {
let mut entry_declarations = SmallVec16::new();
for declarations in slice.iter() {
entry_declarations.push(declarations.clone());
}
ApplicableDeclarationsCacheEntry {
declarations: entry_declarations,
}
}
}
impl Eq for ApplicableDeclarationsCacheEntry {
fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
let this_as_query = ApplicableDeclarationsCacheQuery::new(self.declarations.as_slice());
this_as_query.equiv(other)
}
}
impl IterBytes for ApplicableDeclarationsCacheEntry {
fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool {
ApplicableDeclarationsCacheQuery::new(self.declarations.as_slice()).iter_bytes(lsb0, f)
}
}
struct ApplicableDeclarationsCacheQuery<'a> {
declarations: &'a [MatchedProperty],
}
impl<'a> ApplicableDeclarationsCacheQuery<'a> {
fn new(declarations: &'a [MatchedProperty]) -> ApplicableDeclarationsCacheQuery<'a> {
ApplicableDeclarationsCacheQuery {
declarations: declarations,
}
}
}
// Workaround for lack of `ptr_eq` on Arcs...
#[inline]
fn arc_ptr_eq<T>(a: &Arc<T>, b: &Arc<T>) -> bool {
unsafe {
let a: uint = cast::transmute_copy(a);
let b: uint = cast::transmute_copy(b);
a == b
}
}
impl<'a> Equiv<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
if self.declarations.len() != other.declarations.len() {
return false
}
for (this, other) in self.declarations.iter().zip(other.declarations.iter()) {
if !arc_ptr_eq(&this.declarations, &other.declarations) {
return false
}
}
return true
}
}
impl<'a> IterBytes for ApplicableDeclarationsCacheQuery<'a> {
fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) -> bool {
let mut result = true;
for declaration in self.declarations.iter() {
let ptr: uint = unsafe {
cast::transmute_copy(declaration)
};
result = ptr.iter_bytes(lsb0, |x| f(x));
}
result
}
}
static APPLICABLE_DECLARATIONS_CACHE_SIZE: uint = 32;
pub struct ApplicableDeclarationsCache {
cache: SimpleHashCache<ApplicableDeclarationsCacheEntry,Arc<ComputedValues>>,
}
impl ApplicableDeclarationsCache {
pub fn new() -> ApplicableDeclarationsCache {
ApplicableDeclarationsCache {
cache: SimpleHashCache::new(APPLICABLE_DECLARATIONS_CACHE_SIZE),
}
}
fn find(&self, declarations: &[MatchedProperty]) -> Option<Arc<ComputedValues>> {
match self.cache.find_equiv(&ApplicableDeclarationsCacheQuery::new(declarations)) {
None => None,
Some(ref values) => Some((*values).clone()),
}
}
fn insert(&mut self, declarations: &[MatchedProperty], style: Arc<ComputedValues>) {
drop(self.cache.insert(ApplicableDeclarationsCacheEntry::new(declarations), style))
}
}
/// An LRU cache of the last few nodes seen, so that we can aggressively try to reuse their styles.
pub struct StyleSharingCandidateCache {
priv cache: LRUCache<StyleSharingCandidate,()>,
}
#[deriving(Clone)]
struct StyleSharingCandidate {
priv style: Arc<ComputedValues>,
priv parent_style: Arc<ComputedValues>,
// TODO(pcwalton): Intern.
priv local_name: ~str,
priv class: Option<~str>,
}
impl Eq for StyleSharingCandidate {
fn eq(&self, other: &StyleSharingCandidate) -> bool {
arc_ptr_eq(&self.style, &other.style) &&
arc_ptr_eq(&self.parent_style, &other.parent_style) &&
self.local_name == other.local_name &&
self.class == other.class
}
}
impl StyleSharingCandidate {
/// Attempts to create a style sharing candidate from this node. Returns
/// the style sharing candidate or `None` if this node is ineligible for
/// style sharing.
fn new(node: &LayoutNode) -> Option<StyleSharingCandidate> {
let parent_node = match node.parent_node() {
None => return None,
Some(parent_node) => parent_node,
};
if !parent_node.is_element() {
return None
}
let style = unsafe {
match *node.borrow_layout_data_unchecked() {
None => return None,
Some(ref layout_data_ref) => {
match layout_data_ref.data.style {
None => return None,
Some(ref data) => (*data).clone(),
}
}
}
};
let parent_style = unsafe {
match *parent_node.borrow_layout_data_unchecked() {
None => return None,
Some(ref parent_layout_data_ref) => {
match parent_layout_data_ref.data.style {
None => return None,
Some(ref data) => (*data).clone(),
}
}
}
};
let mut style = Some(style);
let mut parent_style = Some(parent_style);
node.with_element(|element| {
if element.style_attribute().is_some() {
return None
}
Some(StyleSharingCandidate {
style: style.take_unwrap(),
parent_style: parent_style.take_unwrap(),
local_name: element.get_local_name().to_str(),
class: element.get_attr(&Null, "class")
.map(|string| string.to_str()),
})
})
}
fn can_share_style_with(&self, element: &LayoutElement) -> bool {
if element.get_local_name() != self.local_name {
return false
}
match (&self.class, element.get_attr(&Null, "class")) {
(&None, Some(_)) | (&Some(_), None) => return false,
(&Some(ref this_class), Some(element_class)) if element_class != *this_class => {
return false
}
(&Some(_), Some(_)) | (&None, None) => {}
}
true
}
}
static STYLE_SHARING_CANDIDATE_CACHE_SIZE: uint = 40;
impl StyleSharingCandidateCache {
pub fn new() -> StyleSharingCandidateCache {
StyleSharingCandidateCache {
cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE),
}
}
pub fn iter<'a>(&'a self) -> VecIterator<'a,(StyleSharingCandidate,())> {
self.cache.iter()
}
pub fn insert_if_possible(&mut self, node: &LayoutNode) {
match StyleSharingCandidate::new(node) {
None => {}
Some(candidate) => self.cache.insert(candidate, ())
}
}
pub fn touch(&mut self, index: uint) {
self.cache.touch(index)
}
}
/// The results of attempting to share a style.
pub enum StyleSharingResult<'ln> {
/// We didn't find anybody to share the style with. The boolean indicates whether the style
/// is shareable at all.
CannotShare(bool),
/// The node's style can be shared. The integer specifies the index in the LRU cache that was
/// hit.
StyleWasShared(uint),
}
pub trait MatchMethods {
fn match_node(&self, stylist: &Stylist);
/// Performs aux initialization, selector matching, and cascading sequentially.
fn match_and_cascade_subtree(&self,
stylist: &Stylist,
layout_chan: &LayoutChan,
applicable_declarations: &mut ApplicableDeclarations,
initial_values: &ComputedValues,
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
style_sharing_candidate_cache: &mut StyleSharingCandidateCache,
parent: Option<LayoutNode>);
unsafe fn cascade_node(&self, parent: Option<LayoutNode>);
fn match_node(&self,
stylist: &Stylist,
applicable_declarations: &mut ApplicableDeclarations,
shareable: &mut bool);
/// Attempts to share a style with another node. This method is unsafe because it depends on
/// the `style_sharing_candidate_cache` having only live nodes in it, and we have no way to
/// guarantee that at the type system level yet.
unsafe fn share_style_if_possible(&self,
style_sharing_candidate_cache:
&mut StyleSharingCandidateCache,
parent: Option<LayoutNode>)
-> StyleSharingResult;
unsafe fn cascade_node(&self,
parent: Option<LayoutNode>,
initial_values: &ComputedValues,
applicable_declarations: &ApplicableDeclarations,
applicable_declarations_cache: &mut ApplicableDeclarationsCache);
}
trait PrivateMatchMethods {
fn cascade_node_pseudo_element(&self,
parent_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[MatchedProperty],
style: &mut Option<Arc<ComputedValues>>,
initial_values: &ComputedValues,
applicable_declarations_cache: &mut
ApplicableDeclarationsCache,
shareable: bool);
fn share_style_with_candidate_if_possible(&self,
parent_node: Option<LayoutNode>,
candidate: &StyleSharingCandidate)
-> Option<Arc<ComputedValues>>;
}
impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
fn cascade_node_pseudo_element(&self,
parent_style: Option<&Arc<ComputedValues>>,
applicable_declarations: &[MatchedProperty],
style: &mut Option<Arc<ComputedValues>>,
initial_values: &ComputedValues,
applicable_declarations_cache: &mut
ApplicableDeclarationsCache,
shareable: bool) {
let this_style;
let cacheable;
match parent_style {
Some(ref parent_style) => {
let cached_computed_values;
let cache_entry = applicable_declarations_cache.find(applicable_declarations);
match cache_entry {
None => cached_computed_values = None,
Some(ref style) => cached_computed_values = Some(style.get()),
}
let (the_style, is_cacheable) = cascade(applicable_declarations,
shareable,
Some(parent_style.get()),
initial_values,
cached_computed_values);
cacheable = is_cacheable;
this_style = Arc::new(the_style);
}
None => {
let (the_style, is_cacheable) = cascade(applicable_declarations,
shareable,
None,
initial_values,
None);
cacheable = is_cacheable;
this_style = Arc::new(the_style);
}
};
// Cache the resolved style if it was cacheable.
if cacheable {
applicable_declarations_cache.insert(applicable_declarations, this_style.clone());
}
*style = Some(this_style);
}
fn share_style_with_candidate_if_possible(&self,
parent_node: Option<LayoutNode>,
candidate: &StyleSharingCandidate)
-> Option<Arc<ComputedValues>> {
assert!(self.is_element());
let parent_node = match parent_node {
Some(parent_node) if parent_node.is_element() => parent_node,
Some(_) | None => return None,
};
let parent_layout_data: &Option<LayoutDataWrapper> = unsafe {
cast::transmute(parent_node.borrow_layout_data_unchecked())
};
match parent_layout_data {
&Some(ref parent_layout_data_ref) => {
// Check parent style.
let parent_style = parent_layout_data_ref.data.style.as_ref().unwrap();
if !arc_ptr_eq(parent_style, &candidate.parent_style) {
return None
}
// Check tag names, classes, etc.
if !self.with_element(|element| candidate.can_share_style_with(element)) {
return None
}
return Some(candidate.style.clone())
}
_ => {}
}
None
}
}
impl<'ln> MatchMethods for LayoutNode<'ln> {
fn match_node(&self, stylist: &Stylist) {
fn match_node(&self,
stylist: &Stylist,
applicable_declarations: &mut ApplicableDeclarations,
shareable: &mut bool) {
let style_attribute = self.with_element(|element| {
match *element.style_attribute() {
None => None,
@ -37,118 +421,160 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
}
});
let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() {
Some(ref mut layout_data) => {
//FIXME To implement a clear() on SmallVec and use it(init_applicable_declarations).
layout_data.data.init_applicable_declarations();
applicable_declarations.normal_shareable =
stylist.push_applicable_declarations(self,
style_attribute,
None,
&mut applicable_declarations.normal);
stylist.push_applicable_declarations(self,
None,
Some(Before),
&mut applicable_declarations.before);
stylist.push_applicable_declarations(self,
None,
Some(After),
&mut applicable_declarations.after);
stylist.push_applicable_declarations(self,
style_attribute,
None,
&mut layout_data.data.applicable_declarations);
stylist.push_applicable_declarations(self,
None,
Some(Before),
&mut layout_data
.data
.before_applicable_declarations);
stylist.push_applicable_declarations(self,
None,
Some(After),
&mut layout_data
.data
.after_applicable_declarations);
}
None => fail!("no layout data")
*shareable = applicable_declarations.normal_shareable
}
unsafe fn share_style_if_possible(&self,
style_sharing_candidate_cache:
&mut StyleSharingCandidateCache,
parent: Option<LayoutNode>)
-> StyleSharingResult {
if !self.is_element() {
return CannotShare(false)
}
let ok = self.with_element(|element| {
element.style_attribute().is_none() && element.get_attr(&Null, "id").is_none()
});
if !ok {
return CannotShare(false)
}
for (i, &(ref candidate, ())) in style_sharing_candidate_cache.iter().enumerate() {
match self.share_style_with_candidate_if_possible(parent, candidate) {
Some(shared_style) => {
// Yay, cache hit. Share the style.
let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() {
None => fail!(),
Some(ref mut layout_data_ref) => {
layout_data_ref.data.style = Some(shared_style);
return StyleWasShared(i)
}
}
}
None => {}
}
}
CannotShare(true)
}
fn match_and_cascade_subtree(&self,
stylist: &Stylist,
layout_chan: &LayoutChan,
applicable_declarations: &mut ApplicableDeclarations,
initial_values: &ComputedValues,
applicable_declarations_cache: &mut ApplicableDeclarationsCache,
style_sharing_candidate_cache: &mut StyleSharingCandidateCache,
parent: Option<LayoutNode>) {
self.initialize_layout_data((*layout_chan).clone());
if self.is_element() {
self.match_node(stylist);
}
// First, check to see whether we can share a style with someone.
let sharing_result = unsafe {
self.share_style_if_possible(style_sharing_candidate_cache, parent)
};
unsafe {
self.cascade_node(parent)
// Otherwise, match and cascade selectors.
match sharing_result {
CannotShare(mut shareable) => {
if self.is_element() {
self.match_node(stylist, applicable_declarations, &mut shareable)
}
unsafe {
self.cascade_node(parent,
initial_values,
applicable_declarations,
applicable_declarations_cache)
}
applicable_declarations.clear();
// Add ourselves to the LRU cache.
if shareable {
style_sharing_candidate_cache.insert_if_possible(self)
}
}
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
}
for kid in self.children() {
kid.match_and_cascade_subtree(stylist, layout_chan, Some(*self))
kid.match_and_cascade_subtree(stylist,
layout_chan,
applicable_declarations,
initial_values,
applicable_declarations_cache,
style_sharing_candidate_cache,
Some(*self))
}
}
unsafe fn cascade_node(&self, parent: Option<LayoutNode>) {
macro_rules! cascade_node(
($applicable_declarations: ident, $style: ident) => {{
// Get our parent's style. This must be unsafe so that we don't touch the parent's
// borrow flags.
//
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
// enforced safe, race-free access to the parent style.
let parent_style = match parent {
None => None,
Some(parent_node) => {
let parent_layout_data = parent_node.borrow_layout_data_unchecked();
match *parent_layout_data {
None => fail!("no parent data?!"),
Some(ref parent_layout_data) => {
match parent_layout_data.data.style {
None => fail!("parent hasn't been styled yet?!"),
Some(ref style) => Some(style.get()),
}
}
unsafe fn cascade_node(&self,
parent: Option<LayoutNode>,
initial_values: &ComputedValues,
applicable_declarations: &ApplicableDeclarations,
applicable_declarations_cache: &mut ApplicableDeclarationsCache) {
// Get our parent's style. This must be unsafe so that we don't touch the parent's
// borrow flags.
//
// FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
// enforced safe, race-free access to the parent style.
let parent_style = match parent {
None => None,
Some(parent_node) => {
let parent_layout_data = parent_node.borrow_layout_data_unchecked();
match *parent_layout_data {
None => fail!("no parent data?!"),
Some(ref parent_layout_data) => {
match parent_layout_data.data.style {
None => fail!("parent hasn't been styled yet?!"),
Some(ref style) => Some(style),
}
}
};
let computed_values = {
let layout_data_ref = self.borrow_layout_data();
let layout_data = layout_data_ref.get().as_ref().unwrap();
Arc::new(cascade(layout_data.data.$applicable_declarations.as_slice(),
parent_style))
};
let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() {
None => fail!("no layout data"),
Some(ref mut layout_data) => {
let style = &mut layout_data.data.$style;
match *style {
None => (),
Some(ref previous_style) => {
layout_data.data.restyle_damage = Some(incremental::compute_damage(
previous_style.get(), computed_values.get()).to_int())
}
}
*style = Some(computed_values)
}
}
}}
);
{
let before_len = {
let layout_data_ref = self.borrow_layout_data();
layout_data_ref.get().as_ref().unwrap().data.before_applicable_declarations.len()
};
if before_len > 0 {
cascade_node!(before_applicable_declarations, before_style);
}
}
cascade_node!(applicable_declarations, style);
{
let after_len = {
let layout_data_ref = self.borrow_layout_data();
layout_data_ref.get().as_ref().unwrap().data.after_applicable_declarations.len()
};
if after_len > 0 {
cascade_node!(after_applicable_declarations, after_style);
};
let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() {
None => fail!("no layout data"),
Some(ref mut layout_data) => {
self.cascade_node_pseudo_element(parent_style,
applicable_declarations.normal.as_slice(),
&mut layout_data.data.style,
initial_values,
applicable_declarations_cache,
applicable_declarations.normal_shareable);
if applicable_declarations.before.len() > 0 {
self.cascade_node_pseudo_element(parent_style,
applicable_declarations.before.as_slice(),
&mut layout_data.data.before_style,
initial_values,
applicable_declarations_cache,
false);
}
if applicable_declarations.after.len() > 0 {
self.cascade_node_pseudo_element(parent_style,
applicable_declarations.after.as_slice(),
&mut layout_data.data.after_style,
initial_values,
applicable_declarations_cache,
false);
}
}
}
}

View File

@ -88,7 +88,12 @@ u, ins { text-decoration: underline }
br:before { content: "\A"; white-space: pre-line }
center { text-align: center }
:link, :visited { text-decoration: underline }
a:link,
a:visited,
area:link,
area:visited,
link:link,
link:visited { text-decoration: underline }
:focus { outline: thin dotted invert }
/* Begin bidirectionality settings (do not change) */
@ -106,6 +111,8 @@ ul, ol, dl { page-break-before: avoid }
}
/* Servo additions */
:link { color: blue }
a:link,
area:link,
link:link { color: blue }
script { display: none }
style { display: none }
style { display: none }

View File

@ -193,9 +193,9 @@ impl BlockFlow {
let style = box_.style();
let (width, maybe_margin_left, maybe_margin_right) =
(MaybeAuto::from_style(style.Box.width, remaining_width),
MaybeAuto::from_style(style.Margin.margin_left, remaining_width),
MaybeAuto::from_style(style.Margin.margin_right, remaining_width));
(MaybeAuto::from_style(style.Box.get().width, remaining_width),
MaybeAuto::from_style(style.Margin.get().margin_left, remaining_width),
MaybeAuto::from_style(style.Margin.get().margin_right, remaining_width));
let (width, margin_left, margin_right) = self.compute_horiz(width,
maybe_margin_left,
@ -206,7 +206,7 @@ impl BlockFlow {
// If the tentative used width is greater than 'max-width', width should be recalculated,
// but this time using the computed value of 'max-width' as the computed value for 'width'.
let (width, margin_left, margin_right) = {
match specified_or_none(style.Box.max_width, remaining_width) {
match specified_or_none(style.Box.get().max_width, remaining_width) {
Some(value) if value < width => self.compute_horiz(Specified(value),
maybe_margin_left,
maybe_margin_right,
@ -218,7 +218,7 @@ impl BlockFlow {
// If the resulting width is smaller than 'min-width', width should be recalculated,
// but this time using the value of 'min-width' as the computed value for 'width'.
let (width, margin_left, margin_right) = {
let computed_min_width = specified(style.Box.min_width, remaining_width);
let computed_min_width = specified(style.Box.get().min_width, remaining_width);
if computed_min_width > width {
self.compute_horiz(Specified(computed_min_width),
maybe_margin_left,
@ -235,13 +235,13 @@ impl BlockFlow {
// CSS Section 10.3.5
fn compute_float_margins(&self, box_: &Box, remaining_width: Au) -> (Au, Au, Au) {
let style = box_.style();
let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
let margin_left = MaybeAuto::from_style(style.Margin.get().margin_left,
remaining_width).specified_or_zero();
let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
let margin_right = MaybeAuto::from_style(style.Margin.get().margin_right,
remaining_width).specified_or_zero();
let shrink_to_fit = geometry::min(self.base.pref_width,
geometry::max(self.base.min_width, remaining_width));
let width = MaybeAuto::from_style(style.Box.width,
let width = MaybeAuto::from_style(style.Box.get().width,
remaining_width).specified_or_default(shrink_to_fit);
debug!("assign_widths_float -- width: {}", width);
return (width, margin_left, margin_right);
@ -376,7 +376,7 @@ impl BlockFlow {
// block per CSS 2.1 § 10.5.
// TODO: We need to pass in the correct containing block height
// for absolutely positioned elems
height = match MaybeAuto::from_style(style.Box.height, height) {
height = match MaybeAuto::from_style(style.Box.get().height, height) {
Auto => height,
Specified(value) => value
};
@ -516,7 +516,7 @@ impl BlockFlow {
box_.border.get().top + box_.border.get().bottom;
//TODO(eatkinson): compute heights properly using the 'height' property.
let height_prop = MaybeAuto::from_style(box_.style().Box.height,
let height_prop = MaybeAuto::from_style(box_.style().Box.get().height,
Au::new(0)).specified_or_zero();
height = geometry::max(height, height_prop) + noncontent_height;
@ -719,7 +719,7 @@ impl Flow for BlockFlow {
let style = box_.style();
// The text alignment of a block flow is the text alignment of its box's style.
self.base.flags_info.flags.set_text_align(style.Text.text_align);
self.base.flags_info.flags.set_text_align(style.InheritedText.get().text_align);
box_.assign_width(remaining_width);
// Can compute padding here since we know containing block width.
@ -729,9 +729,9 @@ impl Flow for BlockFlow {
let available_width = remaining_width - box_.noncontent_width();
// Top and bottom margins for blocks are 0 if auto.
let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
let margin_top = MaybeAuto::from_style(style.Margin.get().margin_top,
remaining_width).specified_or_zero();
let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
let margin_bottom = MaybeAuto::from_style(style.Margin.get().margin_bottom,
remaining_width).specified_or_zero();
let (width, margin_left, margin_right) = if self.is_float() {

View File

@ -373,7 +373,7 @@ impl Box {
}
(_, _) => {
y = position_offsets.top;
match MaybeAuto::from_style(self.style().Box.height, Au(0)) {
match MaybeAuto::from_style(self.style().Box.get().height, Au(0)) {
Auto => {
height = screen_height - position_offsets.top - position_offsets.bottom;
}
@ -408,7 +408,7 @@ impl Box {
}
(_, _) => {
x = position_offsets.left;
match MaybeAuto::from_style(self.style().Box.width, Au(0)) {
match MaybeAuto::from_style(self.style().Box.get().width, Au(0)) {
Auto => {
width = screen_width - position_offsets.left - position_offsets.right;
}
@ -446,14 +446,14 @@ impl Box {
}
let style = self.style();
let width = MaybeAuto::from_style(style.Box.width, Au::new(0)).specified_or_zero();
let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
let width = MaybeAuto::from_style(style.Box.get().width, Au::new(0)).specified_or_zero();
let margin_left = MaybeAuto::from_style(style.Margin.get().margin_left,
Au::new(0)).specified_or_zero();
let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
let margin_right = MaybeAuto::from_style(style.Margin.get().margin_right,
Au::new(0)).specified_or_zero();
let padding_left = self.compute_padding_length(style.Padding.padding_left, Au::new(0));
let padding_right = self.compute_padding_length(style.Padding.padding_right, Au::new(0));
let padding_left = self.compute_padding_length(style.Padding.get().padding_left, Au(0));
let padding_right = self.compute_padding_length(style.Padding.get().padding_right, Au(0));
width + margin_left + margin_right + padding_left + padding_right +
self.border.get().left + self.border.get().right
@ -480,37 +480,45 @@ impl Box {
}
}
self.border.set(SideOffsets2D::new(width(style.Border.border_top_width,
style.Border.border_top_style),
width(style.Border.border_right_width,
style.Border.border_right_style),
width(style.Border.border_bottom_width,
style.Border.border_bottom_style),
width(style.Border.border_left_width,
style.Border.border_left_style)))
self.border.set(SideOffsets2D::new(width(style.Border.get().border_top_width,
style.Border.get().border_top_style),
width(style.Border.get().border_right_width,
style.Border.get().border_right_style),
width(style.Border.get().border_bottom_width,
style.Border.get().border_bottom_style),
width(style.Border.get().border_left_width,
style.Border.get().border_left_style)))
}
pub fn compute_positioned_offsets(&self, style: &ComputedValues, containing_width: Au, containing_height: Au) {
self.position_offsets.set(SideOffsets2D::new(
MaybeAuto::from_style(style.PositionOffsets.top, containing_height)
MaybeAuto::from_style(style.PositionOffsets.get().top, containing_height)
.specified_or_zero(),
MaybeAuto::from_style(style.PositionOffsets.right, containing_width)
MaybeAuto::from_style(style.PositionOffsets.get().right, containing_width)
.specified_or_zero(),
MaybeAuto::from_style(style.PositionOffsets.bottom, containing_height)
MaybeAuto::from_style(style.PositionOffsets.get().bottom, containing_height)
.specified_or_zero(),
MaybeAuto::from_style(style.PositionOffsets.left, containing_width)
MaybeAuto::from_style(style.PositionOffsets.get().left, containing_width)
.specified_or_zero()));
}
/// Populates the box model padding parameters from the given computed style.
pub fn compute_padding(&self, style: &ComputedValues, containing_block_width: Au) {
let padding = SideOffsets2D::new(self.compute_padding_length(style.Padding.padding_top,
let padding = SideOffsets2D::new(self.compute_padding_length(style.Padding
.get()
.padding_top,
containing_block_width),
self.compute_padding_length(style.Padding.padding_right,
self.compute_padding_length(style.Padding
.get()
.padding_right,
containing_block_width),
self.compute_padding_length(style.Padding.padding_bottom,
self.compute_padding_length(style.Padding
.get()
.padding_bottom,
containing_block_width),
self.compute_padding_length(style.Padding.padding_left,
self.compute_padding_length(style.Padding
.get()
.padding_left,
containing_block_width));
self.padding.set(padding)
}
@ -679,28 +687,28 @@ impl Box {
}
}
pub fn relative_position(&self, container_block_size: &Size2D<Au>) -> Point2D<Au> {
fn left_right(style: &ComputedValues,block_width: Au) -> Au {
fn left_right(style: &ComputedValues, block_width: Au) -> Au {
// TODO(ksh8281) : consider RTL(right-to-left) culture
match (style.PositionOffsets.left, style.PositionOffsets.right) {
match (style.PositionOffsets.get().left, style.PositionOffsets.get().right) {
(LPA_Auto, _) => {
-MaybeAuto::from_style(style.PositionOffsets.right, block_width)
-MaybeAuto::from_style(style.PositionOffsets.get().right, block_width)
.specified_or_zero()
}
(_, _) => {
MaybeAuto::from_style(style.PositionOffsets.left, block_width)
MaybeAuto::from_style(style.PositionOffsets.get().left, block_width)
.specified_or_zero()
}
}
}
fn top_bottom(style: &ComputedValues,block_height: Au) -> Au {
match (style.PositionOffsets.top, style.PositionOffsets.bottom) {
match (style.PositionOffsets.get().top, style.PositionOffsets.get().bottom) {
(LPA_Auto, _) => {
-MaybeAuto::from_style(style.PositionOffsets.bottom, block_height)
-MaybeAuto::from_style(style.PositionOffsets.get().bottom, block_height)
.specified_or_zero()
}
(_, _) => {
MaybeAuto::from_style(style.PositionOffsets.top, block_height)
MaybeAuto::from_style(style.PositionOffsets.get().top, block_height)
.specified_or_zero()
}
}
@ -711,7 +719,7 @@ impl Box {
y: Au::new(0),
};
if self.style().Box.position == position::relative {
if self.style().Box.get().position == position::relative {
rel_pos.x = rel_pos.x + left_right(self.style(), container_block_size.width);
rel_pos.y = rel_pos.y + top_bottom(self.style(), container_block_size.height);
}
@ -720,9 +728,11 @@ impl Box {
match info.get() {
&Some(ref info) => {
for info in info.parent_info.iter() {
if info.style.get().Box.position == position::relative {
rel_pos.x = rel_pos.x + left_right(info.style.get(), container_block_size.width);
rel_pos.y = rel_pos.y + top_bottom(info.style.get(), container_block_size.height);
if info.style.get().Box.get().position == position::relative {
rel_pos.x = rel_pos.x + left_right(info.style.get(),
container_block_size.width);
rel_pos.y = rel_pos.y + top_bottom(info.style.get(),
container_block_size.height);
}
}
},
@ -737,7 +747,7 @@ impl Box {
#[inline(always)]
pub fn clear(&self) -> Option<ClearType> {
let style = self.style();
match style.Box.clear {
match style.Box.get().clear {
clear::none => None,
clear::left => Some(ClearLeft),
clear::right => Some(ClearRight),
@ -755,20 +765,20 @@ impl Box {
debug!("(font style) start");
// FIXME: Too much allocation here.
let font_families = my_style.Font.font_family.map(|family| {
let font_families = my_style.Font.get().font_family.map(|family| {
match *family {
font_family::FamilyName(ref name) => (*name).clone(),
}
});
debug!("(font style) font families: `{:?}`", font_families);
let font_size = my_style.Font.font_size.to_f64().unwrap() / 60.0;
let font_size = my_style.Font.get().font_size.to_f64().unwrap() / 60.0;
debug!("(font style) font size: `{:f}px`", font_size);
FontStyle {
pt_size: font_size,
weight: my_style.Font.font_weight,
style: my_style.Font.font_style,
weight: my_style.Font.get().font_weight,
style: my_style.Font.get().font_style,
families: font_families,
}
}
@ -781,19 +791,19 @@ impl Box {
/// Returns the text alignment of the computed style of the nearest ancestor-or-self `Element`
/// node.
pub fn text_align(&self) -> text_align::T {
self.style().Text.text_align
self.style().InheritedText.get().text_align
}
pub fn line_height(&self) -> line_height::T {
self.style().Box.line_height
self.style().InheritedBox.get().line_height
}
pub fn vertical_align(&self) -> vertical_align::T {
self.style().Box.vertical_align
self.style().Box.get().vertical_align
}
pub fn white_space(&self) -> white_space::T {
self.style().Text.white_space
self.style().InheritedText.get().white_space
}
/// Returns the text decoration of this box, according to the style of the nearest ancestor
@ -804,7 +814,7 @@ impl Box {
/// model. Therefore, this is a best lower bound approximation, but the end result may actually
/// have the various decoration flags turned on afterward.
pub fn text_decoration(&self) -> text_decoration::T {
self.style().Text.text_decoration
self.style().Text.get().text_decoration
}
/// Returns the sum of margin, border, and padding on the left.
@ -862,7 +872,7 @@ impl Box {
bg_rect.origin.y = box_info.baseline + offset.y - info.font_ascent;
bg_rect.size.height = info.font_ascent + info.font_descent;
let background_color = info.style.get().resolve_color(
info.style.get().Background.background_color);
info.style.get().Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) {
lists.with_mut(|lists| {
@ -886,14 +896,14 @@ impl Box {
bg_rect.size.height = bg_rect.size.height + border.top + border.bottom;
let style = info.style.get();
let top_color = style.resolve_color(style.Border.border_top_color);
let right_color = style.resolve_color(style.Border.border_right_color);
let bottom_color = style.resolve_color(style.Border.border_bottom_color);
let left_color = style.resolve_color(style.Border.border_left_color);
let top_style = style.Border.border_top_style;
let right_style = style.Border.border_right_style;
let bottom_style = style.Border.border_bottom_style;
let left_style = style.Border.border_left_style;
let top_color = style.resolve_color(style.Border.get().border_top_color);
let right_color = style.resolve_color(style.Border.get().border_right_color);
let bottom_color = style.resolve_color(style.Border.get().border_bottom_color);
let left_color = style.resolve_color(style.Border.get().border_left_color);
let top_style = style.Border.get().border_top_style;
let right_style = style.Border.get().border_right_style;
let bottom_style = style.Border.get().border_bottom_style;
let left_style = style.Border.get().border_left_style;
lists.with_mut(|lists| {
@ -935,7 +945,7 @@ impl Box {
// inefficient. What we really want is something like "nearest ancestor element that
// doesn't have a box".
let style = self.style();
let background_color = style.resolve_color(style.Background.background_color);
let background_color = style.resolve_color(style.Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) {
lists.with_mut(|lists| {
let solid_color_display_item = ~SolidColorDisplayItem {
@ -965,14 +975,14 @@ impl Box {
}
let style = self.style();
let top_color = style.resolve_color(style.Border.border_top_color);
let right_color = style.resolve_color(style.Border.border_right_color);
let bottom_color = style.resolve_color(style.Border.border_bottom_color);
let left_color = style.resolve_color(style.Border.border_left_color);
let top_style = style.Border.border_top_style;
let right_style = style.Border.border_right_style;
let bottom_style = style.Border.border_bottom_style;
let left_style = style.Border.border_left_style;
let top_color = style.resolve_color(style.Border.get().border_top_color);
let right_color = style.resolve_color(style.Border.get().border_right_color);
let bottom_color = style.resolve_color(style.Border.get().border_bottom_color);
let left_color = style.resolve_color(style.Border.get().border_left_color);
let top_style = style.Border.get().border_top_style;
let right_style = style.Border.get().border_right_style;
let bottom_style = style.Border.get().border_bottom_style;
let left_style = style.Border.get().border_left_style;
let mut abs_bounds = abs_bounds.clone();
abs_bounds.origin.x = abs_bounds.origin.x + self.noncontent_inline_left();
@ -1029,7 +1039,7 @@ impl Box {
box_bounds, absolute_box_bounds, self.debug_str());
debug!("Box::build_display_list: dirty={}, offset={}", *dirty, offset);
if self.style().Box.visibility != visibility::visible {
if self.style().InheritedBox.get().visibility != visibility::visible {
return;
}
@ -1047,7 +1057,7 @@ impl Box {
match self.specific {
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
ScannedTextBox(ref text_box) => {
let text_color = self.style().Color.color.to_gfx_color();
let text_color = self.style().Color.get().color.to_gfx_color();
// Set the various text display item flags.
let mut flow_flags = flow::base(flow).flags_info.clone();
@ -1472,13 +1482,13 @@ impl Box {
}
ImageBox(ref image_box_info) => {
// TODO(ksh8281): compute border,margin,padding
let width = ImageBoxInfo::style_length(self.style().Box.width,
let width = ImageBoxInfo::style_length(self.style().Box.get().width,
image_box_info.dom_width,
container_width);
// FIXME(ksh8281): we shouldn't figure height this way
// now, we don't know about size of parent's height
let height = ImageBoxInfo::style_length(self.style().Box.height,
let height = ImageBoxInfo::style_length(self.style().Box.get().height,
image_box_info.dom_height,
Au::new(0));
@ -1520,11 +1530,11 @@ impl Box {
let width = image_box_info.computed_width();
// FIXME(ksh8281): we shouldn't assign height this way
// we don't know about size of parent's height
let height = ImageBoxInfo::style_length(self.style().Box.height,
let height = ImageBoxInfo::style_length(self.style().Box.get().height,
image_box_info.dom_height,
Au::new(0));
let height = match (self.style().Box.width,
let height = match (self.style().Box.get().width,
image_box_info.dom_width,
height) {
(LPA_Auto, None, Auto) => {
@ -1575,7 +1585,7 @@ impl Box {
/// Returns true if the contents should be clipped (i.e. if `overflow` is `hidden`).
pub fn needs_clip(&self) -> bool {
self.style().Box.overflow == overflow::hidden
self.style().Box.get().overflow == overflow::hidden
}
/// Returns a debugging string describing this box.

View File

@ -628,7 +628,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
let (display, float, position) = match node.type_id() {
ElementNodeTypeId(_) => {
let style = node.style().get();
(style.Box.display, style.Box.float, style.Box.position)
(style.Box.get().display, style.Box.get().float, style.Box.get().position)
}
TextNodeTypeId => (display::inline, float::none, position::static_),
CommentNodeTypeId |
@ -730,7 +730,7 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
//
// If you implement other values for this property, you will almost certainly
// want to update this check.
match self.style().get().Text.white_space {
match self.style().get().InheritedText.get().white_space {
white_space::normal => true,
_ => false,
}

View File

@ -4,28 +4,37 @@
//! Data needed by the layout task.
use extra::arc::{Arc, MutexArc};
use green::task::GreenTask;
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use layout::flow::FlowLeafSet;
use layout::util::OpaqueNode;
use layout::wrapper::DomLeafSet;
use extra::arc::{Arc, MutexArc};
use geom::size::Size2D;
use gfx::font_context::{FontContext, FontContextInfo};
use green::task::GreenTask;
use script::layout_interface::LayoutChan;
use servo_msg::constellation_msg::ConstellationChan;
use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
use std::cast;
use std::ptr;
use std::rt::Runtime;
use std::rt::local::Local;
use std::rt::task::Task;
use geom::size::Size2D;
use gfx::font_context::{FontContext, FontContextInfo};
use script::layout_interface::LayoutChan;
use servo_msg::constellation_msg::ConstellationChan;
use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
use style::Stylist;
use style::{ComputedValues, Stylist};
#[thread_local]
static mut FONT_CONTEXT: *mut FontContext = 0 as *mut FontContext;
#[thread_local]
static mut APPLICABLE_DECLARATIONS_CACHE: *mut ApplicableDeclarationsCache =
0 as *mut ApplicableDeclarationsCache;
#[thread_local]
static mut STYLE_SHARING_CANDIDATE_CACHE: *mut StyleSharingCandidateCache =
0 as *mut StyleSharingCandidateCache;
/// Data shared by all layout workers.
#[deriving(Clone)]
pub struct LayoutContext {
@ -55,6 +64,9 @@ pub struct LayoutContext {
/// FIXME(pcwalton): Make this no longer an unsafe pointer once we have fast `RWArc`s.
stylist: *Stylist,
/// The initial set of CSS properties.
initial_css_values: Arc<ComputedValues>,
/// The root node at which we're starting the layout.
reflow_root: OpaqueNode,
}
@ -79,5 +91,49 @@ impl LayoutContext {
cast::transmute(FONT_CONTEXT)
}
}
pub fn applicable_declarations_cache<'a>(&'a self) -> &'a mut ApplicableDeclarationsCache {
// Sanity check.
{
let mut task = Local::borrow(None::<Task>);
match task.get().maybe_take_runtime::<GreenTask>() {
Some(green) => {
task.get().put_runtime(green as ~Runtime);
fail!("can't call this on a green task!")
}
None => {}
}
}
unsafe {
if APPLICABLE_DECLARATIONS_CACHE == ptr::mut_null() {
let cache = ~ApplicableDeclarationsCache::new();
APPLICABLE_DECLARATIONS_CACHE = cast::transmute(cache)
}
cast::transmute(APPLICABLE_DECLARATIONS_CACHE)
}
}
pub fn style_sharing_candidate_cache<'a>(&'a self) -> &'a mut StyleSharingCandidateCache {
// Sanity check.
{
let mut task = Local::borrow(None::<Task>);
match task.get().maybe_take_runtime::<GreenTask>() {
Some(green) => {
task.get().put_runtime(green as ~Runtime);
fail!("can't call this on a green task!")
}
None => {}
}
}
unsafe {
if STYLE_SHARING_CANDIDATE_CACHE == ptr::mut_null() {
let cache = ~StyleSharingCandidateCache::new();
STYLE_SHARING_CANDIDATE_CACHE = cast::transmute(cache)
}
cast::transmute(STYLE_SHARING_CANDIDATE_CACHE)
}
}
}

View File

@ -309,7 +309,7 @@ static TEXT_ALIGN_SHIFT: u8 = 4;
impl FlowFlagsInfo {
/// Creates a new set of flow flags from the given style.
pub fn new(style: &ComputedValues) -> FlowFlagsInfo {
let text_decoration = style.Text.text_decoration;
let text_decoration = style.Text.get().text_decoration;
let mut flags = FlowFlags(0);
flags.set_override_underline(text_decoration.underline);
flags.set_override_overline(text_decoration.overline);
@ -318,9 +318,9 @@ impl FlowFlagsInfo {
// TODO(ksh8281) compute text-decoration-color,style,line
let rare_flow_flags = if flags.is_text_decoration_enabled() {
Some(~RareFlowFlags {
underline_color: style.Color.color.to_gfx_color(),
overline_color: style.Color.color.to_gfx_color(),
line_through_color: style.Color.color.to_gfx_color(),
underline_color: style.Color.get().color.to_gfx_color(),
overline_color: style.Color.get().color.to_gfx_color(),
line_through_color: style.Color.get().color.to_gfx_color(),
})
} else {
None

View File

@ -107,7 +107,7 @@ impl RestyleDamage {
macro_rules! add_if_not_equal(
($old:ident, $new:ident, $damage:ident,
[ $($effect:ident),* ], [ $($style_struct:ident.$name:ident),* ]) => ({
if $( ($old.$style_struct.$name != $new.$style_struct.$name) )||* {
if $( ($old.$style_struct.get().$name != $new.$style_struct.get().$name) )||* {
$damage.union_in_place( restyle_damage!( $($effect),* ) );
}
})
@ -134,7 +134,7 @@ pub fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDama
Padding.padding_top, Padding.padding_right, Padding.padding_bottom, Padding.padding_left,
Box.position, Box.width, Box.height, Box.float, Box.display,
Font.font_family, Font.font_size, Font.font_style, Font.font_weight,
Text.text_align, Text.text_decoration, Box.line_height ]);
InheritedText.text_align, Text.text_decoration, InheritedBox.line_height ]);
// FIXME: test somehow that we checked every CSS property

View File

@ -794,7 +794,7 @@ impl Flow for InlineFlow {
//
// The spec does not state which font to use. Previous versions of the code used
// the parent's font; this code uses the current font.
let parent_text_top = cur_box.style().Font.font_size;
let parent_text_top = cur_box.style().Font.get().font_size;
// We should calculate the distance from baseline to the bottom of the parent's
// content area. But for now we assume it's zero.

View File

@ -5,7 +5,8 @@
//! The layout task. Performs layout on the DOM, builds display lists and sends them to be
/// rendered.
use css::matching::MatchMethods;
use css::matching::{ApplicableDeclarations, ApplicableDeclarationsCache, MatchMethods};
use css::matching::{StyleSharingCandidateCache};
use css::select::new_stylist;
use css::node_style::StyledNode;
use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
@ -55,7 +56,8 @@ use std::comm::Port;
use std::ptr;
use std::task;
use std::util;
use style::{AuthorOrigin, Stylesheet, Stylist};
use style::{AuthorOrigin, ComputedValues, Stylesheet, Stylist};
use style;
/// Information needed by the layout task.
pub struct LayoutTask {
@ -97,6 +99,9 @@ pub struct LayoutTask {
stylist: ~Stylist,
/// The initial set of CSS values.
initial_css_values: Arc<ComputedValues>,
/// The workers that we use for parallel operation.
parallel_traversal: Option<WorkQueue<*mut LayoutContext,UnsafeFlow>>,
@ -302,6 +307,7 @@ impl LayoutTask {
display_list_collection: None,
stylist: ~new_stylist(),
initial_css_values: Arc::new(style::initial_values()),
parallel_traversal: parallel_traversal,
profiler_chan: profiler_chan,
opts: opts.clone()
@ -332,6 +338,7 @@ impl LayoutTask {
layout_chan: self.chan.clone(),
font_context_info: font_context_info,
stylist: &*self.stylist,
initial_css_values: self.initial_css_values.clone(),
reflow_root: OpaqueNode::from_layout_node(reflow_root),
}
}
@ -561,8 +568,17 @@ impl LayoutTask {
profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone(), || {
match self.parallel_traversal {
None => {
let mut applicable_declarations = ApplicableDeclarations::new();
let mut applicable_declarations_cache =
ApplicableDeclarationsCache::new();
let mut style_sharing_candidate_cache =
StyleSharingCandidateCache::new();
node.match_and_cascade_subtree(self.stylist,
&layout_ctx.layout_chan,
&mut applicable_declarations,
layout_ctx.initial_css_values.get(),
&mut applicable_declarations_cache,
&mut style_sharing_candidate_cache,
None)
}
Some(ref mut traversal) => {
@ -636,6 +652,7 @@ impl LayoutTask {
.resolve_color(thread_safe_child.style()
.get()
.Background
.get()
.background_color)
.to_gfx_color()
};

View File

@ -6,7 +6,7 @@
//!
//! This code is highly unsafe. Keep this file small and easy to audit.
use css::matching::MatchMethods;
use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasShared};
use layout::context::LayoutContext;
use layout::extra::LayoutAuxMethods;
use layout::flow::{Flow, FlowLeafSet, PostorderFlowTraversal};
@ -149,19 +149,42 @@ fn match_and_cascade_node(unsafe_layout_node: UnsafeLayoutNode,
// parser.
node.initialize_layout_data(layout_context.layout_chan.clone());
if node.is_element() {
// Perform the CSS selector matching.
let stylist: &Stylist = cast::transmute(layout_context.stylist);
node.match_node(stylist);
}
// Perform the CSS cascade.
// Get the parent node.
let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root {
None
} else {
node.parent_node()
};
node.cascade_node(parent_opt);
// First, check to see whether we can share a style with someone.
let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
let sharing_result = node.share_style_if_possible(style_sharing_candidate_cache,
parent_opt);
// Otherwise, match and cascade selectors.
match sharing_result {
CannotShare(mut shareable) => {
let mut applicable_declarations = ApplicableDeclarations::new();
if node.is_element() {
// Perform the CSS selector matching.
let stylist: &Stylist = cast::transmute(layout_context.stylist);
node.match_node(stylist, &mut applicable_declarations, &mut shareable);
}
// Perform the CSS cascade.
node.cascade_node(parent_opt,
layout_context.initial_css_values.get(),
&applicable_declarations,
layout_context.applicable_declarations_cache());
// Add ourselves to the LRU cache.
if shareable {
style_sharing_candidate_cache.insert_if_possible(&node);
}
}
StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
}
// Enqueue kids.
let mut child_count = 0;

View File

@ -12,13 +12,12 @@ use script::dom::bindings::utils::Reflectable;
use script::dom::node::AbstractNode;
use script::layout_interface::{LayoutChan, UntrustedNodeAddress};
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec0, SmallVec16};
use std::cast;
use std::cell::{Ref, RefMut};
use std::iter::Enumerate;
use std::libc::uintptr_t;
use std::vec::VecIterator;
use style::{ComputedValues, PropertyDeclaration};
use style::ComputedValues;
/// A range of nodes.
pub struct NodeRange {
@ -130,18 +129,13 @@ impl ElementMapping {
/// Data that layout associates with a node.
pub struct PrivateLayoutData {
/// The results of CSS matching for this node.
applicable_declarations: SmallVec16<Arc<~[PropertyDeclaration]>>,
before_applicable_declarations: SmallVec0<Arc<~[PropertyDeclaration]>>,
after_applicable_declarations: SmallVec0<Arc<~[PropertyDeclaration]>>,
/// The results of CSS styling for this node.
before_style: Option<Arc<ComputedValues>>,
style: Option<Arc<ComputedValues>>,
/// The results of CSS styling for this node's `before` pseudo-element, if any.
before_style: Option<Arc<ComputedValues>>,
/// The results of CSS styling for this node's `after` pseudo-element, if any.
after_style: Option<Arc<ComputedValues>>,
/// Description of how to account for recent style changes.
@ -159,9 +153,6 @@ impl PrivateLayoutData {
/// Creates new layout data.
pub fn new() -> PrivateLayoutData {
PrivateLayoutData {
applicable_declarations: SmallVec16::new(),
before_applicable_declarations: SmallVec0::new(),
after_applicable_declarations: SmallVec0::new(),
before_style: None,
style: None,
after_style: None,
@ -170,14 +161,6 @@ impl PrivateLayoutData {
parallel: DomParallelInfo::new(),
}
}
/// Initialize the function for applicable_declarations.
pub fn init_applicable_declarations(&mut self) {
//FIXME To implement a clear() on SmallVec and use it(init_applicable_declarations).
self.applicable_declarations = SmallVec16::new();
self.before_applicable_declarations = SmallVec0::new();
self.after_applicable_declarations = SmallVec0::new();
}
}
pub struct LayoutDataWrapper {

View File

@ -160,27 +160,108 @@ pub mod specified {
}
pub mod computed {
use cssparser;
pub use CSSColor = cssparser::Color;
pub use compute_CSSColor = super::super::longhands::computed_as_specified;
use super::*;
use super::super::longhands;
use super::super::{longhands, style_structs};
use servo_util::cowarc::CowArc;
pub use servo_util::geometry::Au;
pub struct Context {
current_color: cssparser::RGBA,
font_size: Au,
font_weight: longhands::font_weight::computed_value::T,
position: longhands::position::SpecifiedValue,
float: longhands::float::SpecifiedValue,
color: longhands::color::computed_value::T,
parent_font_weight: longhands::font_weight::computed_value::T,
parent_font_size: longhands::font_size::computed_value::T,
font_size: longhands::font_size::computed_value::T,
positioned: bool,
floated: bool,
border_top_present: bool,
border_right_present: bool,
border_bottom_present: bool,
border_left_present: bool,
is_root_element: bool,
has_border_top: bool,
has_border_right: bool,
has_border_bottom: bool,
has_border_left: bool,
use_parent_font_size: bool,
// TODO, as needed: root font size, viewport size, etc.
}
fn border_is_present(border_style: longhands::border_top_style::computed_value::T) -> bool {
match border_style {
longhands::border_top_style::none | longhands::border_top_style::hidden => false,
_ => true,
}
}
impl Context {
#[inline]
pub fn new(color: &CowArc<style_structs::Color>,
font: &CowArc<style_structs::Font>,
css_box: &CowArc<style_structs::Box>,
border: &CowArc<style_structs::Border>,
is_root_element: bool)
-> Context {
let mut context = Context {
color: color.get().color,
parent_font_weight: font.get().font_weight,
parent_font_size: font.get().font_size,
font_size: font.get().font_size,
positioned: false,
floated: false,
border_top_present: false,
border_right_present: false,
border_bottom_present: false,
border_left_present: false,
is_root_element: is_root_element,
use_parent_font_size: true,
};
context.set_position(css_box.get().position);
context.set_float(css_box.get().float);
context.set_border_top_style(border.get().border_top_style);
context.set_border_right_style(border.get().border_right_style);
context.set_border_bottom_style(border.get().border_bottom_style);
context.set_border_left_style(border.get().border_left_style);
context
}
pub fn set_color(&mut self, color: longhands::color::computed_value::T) {
self.color = color
}
pub fn set_position(&mut self, position: longhands::position::computed_value::T) {
self.positioned = match position {
longhands::position::absolute | longhands::position::fixed => true,
_ => false,
}
}
pub fn set_font_size(&mut self, font_size: longhands::font_size::computed_value::T) {
self.font_size = font_size
}
pub fn set_float(&mut self, float: longhands::float::computed_value::T) {
self.floated = float != longhands::float::none
}
pub fn set_border_top_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_top_present = border_is_present(style)
}
pub fn set_border_right_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_right_present = border_is_present(style)
}
pub fn set_border_bottom_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_bottom_present = border_is_present(style)
}
pub fn set_border_left_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_left_present = border_is_present(style)
}
}
#[inline]
pub fn compute_Au(value: specified::Length, context: &Context) -> Au {
match value {
specified::Au_(value) => value,
@ -192,6 +273,19 @@ pub mod computed {
}
}
/// A special version of `compute_Au` used for `font-size`.
#[inline]
pub fn compute_Au_from_parent(value: specified::Length, context: &Context) -> Au {
match value {
specified::Au_(value) => value,
specified::Em(value) => context.parent_font_size.scale_by(value),
specified::Ex(value) => {
let x_height = 0.5; // TODO: find that from the font
context.font_size.scale_by(value * x_height)
},
}
}
#[deriving(Eq, Clone)]
pub enum LengthOrPercentage {
LP_Length(Au),

View File

@ -6,12 +6,14 @@
use std::ascii::StrAsciiExt;
pub use extra::arc::Arc;
use servo_util::cowarc::CowArc;
pub use cssparser::*;
pub use cssparser::ast::*;
use errors::{ErrorLoggerIterator, log_css_error};
pub use parsing_utils::*;
pub use self::common_types::*;
use selector_matching::MatchedProperty;
pub mod common_types;
@ -25,11 +27,11 @@ def to_rust_ident(name):
return name
class Longhand(object):
def __init__(self, name, is_inherited):
def __init__(self, name, needed_for_context):
self.name = name
self.ident = to_rust_ident(name)
self.is_inherited = is_inherited
self.style_struct = THIS_STYLE_STRUCT
self.needed_for_context = needed_for_context
class Shorthand(object):
def __init__(self, name, sub_properties):
@ -37,31 +39,48 @@ class Shorthand(object):
self.ident = to_rust_ident(name)
self.sub_properties = [LONGHANDS_BY_NAME[s] for s in sub_properties]
LONGHANDS_PER_STYLE_STRUCT = []
THIS_STYLE_STRUCT_LONGHANDS = None
class StyleStruct(object):
def __init__(self, name, inherited):
self.name = name
self.longhands = []
self.inherited = inherited
STYLE_STRUCTS = []
THIS_STYLE_STRUCT = None
LONGHANDS = []
LONGHANDS_BY_NAME = {}
SHORTHANDS = []
def new_style_struct(name):
longhands = []
LONGHANDS_PER_STYLE_STRUCT.append((name, longhands))
global THIS_STYLE_STRUCT_LONGHANDS
THIS_STYLE_STRUCT_LONGHANDS = longhands
def new_style_struct(name, is_inherited):
global THIS_STYLE_STRUCT
style_struct = StyleStruct(name, is_inherited)
STYLE_STRUCTS.append(style_struct)
THIS_STYLE_STRUCT = style_struct
return ""
def switch_to_style_struct(name):
global THIS_STYLE_STRUCT
for style_struct in STYLE_STRUCTS:
if style_struct.name == name:
THIS_STYLE_STRUCT = style_struct
return ""
fail()
%>
pub mod longhands {
pub use super::*;
pub use std;
pub fn computed_as_specified<T>(value: T, _context: &computed::Context) -> T { value }
pub fn computed_as_specified<T>(value: T, _context: &computed::Context) -> T {
value
}
<%def name="raw_longhand(name, inherited=False, no_super=False)">
<%def name="raw_longhand(name, needed_for_context=False, no_super=False)">
<%
property = Longhand(name, inherited)
THIS_STYLE_STRUCT_LONGHANDS.append(property)
property = Longhand(name, needed_for_context)
THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property
%>
@ -76,15 +95,15 @@ pub mod longhands {
match CSSWideKeyword::parse(input) {
Some(Some(keyword)) => Some(CSSWideKeyword(keyword)),
Some(None) => Some(CSSWideKeyword(${
"Inherit" if inherited else "Initial"})),
"Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"})),
None => parse_specified(input),
}
}
}
</%def>
<%def name="longhand(name, inherited=False, no_super=False)">
<%self:raw_longhand name="${name}" inherited="${inherited}">
<%def name="longhand(name, needed_for_context=False, no_super=False)">
<%self:raw_longhand name="${name}" needed_for_context="${needed_for_context}">
${caller.body()}
pub fn parse_specified(input: &[ComponentValue])
-> Option<DeclaredValue<SpecifiedValue>> {
@ -93,8 +112,8 @@ pub mod longhands {
</%self:raw_longhand>
</%def>
<%def name="single_component_value(name, inherited=False)">
<%self:longhand name="${name}" inherited="${inherited}">
<%def name="single_component_value(name, needed_for_context=False)">
<%self:longhand name="${name}" needed_for_context="${needed_for_context}">
${caller.body()}
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
one_component_value(input).and_then(from_component_value)
@ -102,8 +121,8 @@ pub mod longhands {
</%self:longhand>
</%def>
<%def name="single_keyword_computed(name, values, inherited=False)">
<%self:single_component_value name="${name}" inherited="${inherited}">
<%def name="single_keyword_computed(name, values, needed_for_context=False)">
<%self:single_component_value name="${name}" needed_for_context="${needed_for_context}">
${caller.body()}
pub mod computed_value {
#[deriving(Eq, Clone, FromPrimitive)]
@ -130,15 +149,17 @@ pub mod longhands {
</%self:single_component_value>
</%def>
<%def name="single_keyword(name, values, inherited=False)">
<%self:single_keyword_computed name="${name}" values="${values}" inherited="${inherited}">
<%def name="single_keyword(name, values, needed_for_context=False)">
<%self:single_keyword_computed name="${name}"
values="${values}"
needed_for_context="${needed_for_context}">
// The computed value is the same as the specified value.
pub use to_computed_value = super::computed_as_specified;
</%self:single_keyword_computed>
</%def>
<%def name="predefined_type(name, type, initial_value, parse_method='parse', inherited=False)">
<%self:single_component_value name="${name}" inherited="${inherited}">
<%def name="predefined_type(name, type, initial_value, parse_method='parse')">
<%self:single_component_value name="${name}">
pub use to_computed_value = super::super::common_types::computed::compute_${type};
pub type SpecifiedValue = specified::${type};
pub mod computed_value {
@ -154,14 +175,14 @@ pub mod longhands {
// CSS 2.1, Section 8 - Box model
${new_style_struct("Margin")}
${new_style_struct("Margin", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("margin-" + side, "LengthOrPercentageOrAuto",
"computed::LPA_Length(Au(0))")}
% endfor
${new_style_struct("Padding")}
${new_style_struct("Padding", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("padding-" + side, "LengthOrPercentage",
@ -169,17 +190,19 @@ pub mod longhands {
"parse_non_negative")}
% endfor
${new_style_struct("Border")}
${new_style_struct("Border", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type("border-%s-color" % side, "CSSColor", "CurrentColor")}
% endfor
// double groove ridge insed outset
${single_keyword("border-top-style", "none solid dotted dashed hidden")}
${single_keyword("border-top-style", values="none solid dotted dashed hidden", \
needed_for_context=True)}
% for side in ["right", "bottom", "left"]:
<%self:longhand name="border-${side}-style", no_super="True">
pub use super::border_top_style::*;
<%self:longhand name="border-${side}-style", no_super="True", needed_for_context="True">
pub use super::border_top_style::{get_initial_value, parse, to_computed_value};
pub type SpecifiedValue = super::border_top_style::SpecifiedValue;
pub mod computed_value {
pub type T = super::super::border_top_style::computed_value::T;
@ -215,15 +238,19 @@ pub mod longhands {
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
one_component_value(input).and_then(parse_border_width)
}
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
if context.has_border_${side} { computed::compute_Au(value, context) }
else { Au(0) }
if !context.border_${side}_present {
Au(0)
} else {
computed::compute_Au(value, context)
}
}
</%self:longhand>
% endfor
${new_style_struct("PositionOffsets")}
${new_style_struct("PositionOffsets", is_inherited=False)}
% for side in ["top", "right", "bottom", "left"]:
${predefined_type(side, "LengthOrPercentageOrAuto",
@ -232,7 +259,7 @@ pub mod longhands {
// CSS 2.1, Section 9 - Visual formatting model
${new_style_struct("Box")}
${new_style_struct("Box", is_inherited=False)}
// TODO: don't parse values we don't support
<%self:single_keyword_computed name="display"
@ -241,16 +268,13 @@ pub mod longhands {
table-row table-column-group table-column table-cell table-caption
list-item
none">
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
// if context.is_root_element && value == list_item {
// return block
// }
let positioned = match context.position {
position::absolute | position::fixed => true,
_ => false
};
if positioned || context.float != float::none || context.is_root_element {
if context.positioned || context.floated || context.is_root_element {
match value {
// inline_table => table,
inline | inline_block
@ -266,8 +290,8 @@ pub mod longhands {
}
</%self:single_keyword_computed>
${single_keyword("position", "static absolute relative fixed")}
${single_keyword("float", "none left right")}
${single_keyword("position", "static absolute relative fixed", needed_for_context="True")}
${single_keyword("float", "none left right", needed_for_context="True")}
${single_keyword("clear", "none left right both")}
// CSS 2.1, Section 10 - Visual formatting model details
@ -286,8 +310,9 @@ pub mod longhands {
"computed::LPN_None",
"parse_non_negative")}
${new_style_struct("InheritedBox", is_inherited=True)}
<%self:single_component_value name="line-height" inherited="True">
<%self:single_component_value name="line-height">
#[deriving(Clone)]
pub enum SpecifiedValue {
SpecifiedNormal,
@ -319,7 +344,9 @@ pub mod longhands {
Number(CSSFloat),
}
}
#[inline] pub fn get_initial_value() -> computed_value::T { Normal }
#[inline]
pub fn get_initial_value() -> computed_value::T { Normal }
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
match value {
@ -330,6 +357,8 @@ pub mod longhands {
}
</%self:single_component_value>
${switch_to_style_struct("Box")}
<%self:single_component_value name="vertical-align">
<% vertical_align_keywords = (
"baseline sub super top text-top middle bottom text-bottom".split()) %>
@ -369,7 +398,9 @@ pub mod longhands {
Percentage(CSSFloat),
}
}
#[inline] pub fn get_initial_value() -> computed_value::T { baseline }
#[inline]
pub fn get_initial_value() -> computed_value::T { baseline }
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
match value {
@ -387,14 +418,18 @@ pub mod longhands {
// CSS 2.1, Section 11 - Visual effects
${single_keyword("overflow", "visible hidden", inherited=False)} // TODO: scroll auto
${single_keyword("overflow", "visible hidden")} // TODO: scroll auto
${switch_to_style_struct("InheritedBox")}
// TODO: collapse. Well, do tables first.
${single_keyword("visibility", "visible hidden", inherited=True)}
${single_keyword("visibility", "visible hidden")}
// CSS 2.1, Section 12 - Generated content, automatic numbering, and lists
<%self:longhand name="content" inherited="False">
${switch_to_style_struct("Box")}
<%self:longhand name="content">
pub use to_computed_value = super::computed_as_specified;
pub mod computed_value {
#[deriving(Eq, Clone)]
@ -437,15 +472,15 @@ pub mod longhands {
// CSS 2.1, Section 14 - Colors and Backgrounds
${new_style_struct("Background")}
${new_style_struct("Background", is_inherited=False)}
${predefined_type("background-color", "CSSColor",
"RGBA(RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")}
${new_style_struct("Color")}
${new_style_struct("Color", is_inherited=True)}
<%self:raw_longhand name="color" inherited="True">
<%self:raw_longhand name="color" needed_for_context="True">
pub use to_computed_value = super::computed_as_specified;
pub type SpecifiedValue = RGBA;
pub mod computed_value {
@ -465,9 +500,9 @@ pub mod longhands {
// CSS 2.1, Section 15 - Fonts
${new_style_struct("Font")}
${new_style_struct("Font", is_inherited=True)}
<%self:longhand name="font-family" inherited="True">
<%self:longhand name="font-family">
pub use to_computed_value = super::computed_as_specified;
pub mod computed_value {
#[deriving(Eq, Clone)]
@ -545,14 +580,14 @@ pub mod longhands {
</%self:longhand>
${single_keyword("font-style", "normal italic oblique", inherited=True)}
${single_keyword("font-variant", "normal", inherited=True)} // Add small-caps when supported
${single_keyword("font-style", "normal italic oblique")}
${single_keyword("font-variant", "normal")} // Add small-caps when supported
<%self:single_component_value name="font-weight" inherited="True">
<%self:single_component_value name="font-weight">
#[deriving(Clone)]
pub enum SpecifiedValue {
Bolder,
Lighther,
Lighter,
% for weight in range(100, 901, 100):
SpecifiedWeight${weight},
% endfor
@ -567,7 +602,7 @@ pub mod longhands {
"bold" => Some(SpecifiedWeight700),
"normal" => Some(SpecifiedWeight400),
"bolder" => Some(Bolder),
"lighter" => Some(Lighther),
"lighter" => Some(Lighter),
_ => None,
}
},
@ -602,14 +637,16 @@ pub mod longhands {
}
}
}
#[inline] pub fn get_initial_value() -> computed_value::T { Weight400 } // normal
#[inline]
pub fn get_initial_value() -> computed_value::T { Weight400 } // normal
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
match value {
% for weight in range(100, 901, 100):
SpecifiedWeight${weight} => Weight${weight},
% endfor
Bolder => match context.font_weight {
Bolder => match context.parent_font_weight {
Weight100 => Weight400,
Weight200 => Weight400,
Weight300 => Weight400,
@ -620,7 +657,7 @@ pub mod longhands {
Weight800 => Weight900,
Weight900 => Weight900,
},
Lighther => match context.font_weight {
Lighter => match context.parent_font_weight {
Weight100 => Weight100,
Weight200 => Weight100,
Weight300 => Weight100,
@ -635,8 +672,8 @@ pub mod longhands {
}
</%self:single_component_value>
<%self:single_component_value name="font-size" inherited="True">
pub use to_computed_value = super::super::common_types::computed::compute_Au;
<%self:single_component_value name="font-size" needed_for_context="True">
use super::super::common_types::computed::compute_Au_from_parent;
pub type SpecifiedValue = specified::Length; // Percentages are the same as em.
pub mod computed_value {
use super::super::Au;
@ -645,6 +682,15 @@ pub mod longhands {
#[inline] pub fn get_initial_value() -> computed_value::T {
Au::from_px(16) // medium
}
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
if !context.use_parent_font_size {
// We already computed this element's font size; no need to compute it again.
return context.font_size
}
compute_Au_from_parent(value, context)
}
/// <length> | <percentage>
/// TODO: support <absolute-size> and <relative-size>
pub fn from_component_value(input: &ComponentValue) -> Option<SpecifiedValue> {
@ -659,10 +705,12 @@ pub mod longhands {
// CSS 2.1, Section 16 - Text
${new_style_struct("Text")}
${new_style_struct("InheritedText", is_inherited=True)}
// TODO: initial value should be 'start' (CSS Text Level 3, direction-dependent.)
${single_keyword("text-align", "left right center justify", inherited=True)}
${single_keyword("text-align", "left right center justify")}
${new_style_struct("Text", is_inherited=False)}
<%self:longhand name="text-decoration">
pub use to_computed_value = super::computed_as_specified;
@ -709,7 +757,9 @@ pub mod longhands {
}
</%self:longhand>
${single_keyword("white-space", "normal pre", inherited=True)}
${switch_to_style_struct("InheritedText")}
${single_keyword("white-space", "normal pre")}
// CSS 2.1, Section 17 - Tables
@ -721,7 +771,7 @@ pub mod shorthands {
pub use super::*;
pub use super::longhands::*;
<%def name="shorthand(name, sub_properties)">
<%def name="shorthand(name, sub_properties, needed_for_context=False)">
<%
shorthand = Shorthand(name, sub_properties.split())
SHORTHANDS.append(shorthand)
@ -1029,7 +1079,7 @@ impl PropertyDeclaration {
% for sub_property in shorthand.sub_properties:
result_list.push(${sub_property.ident}_declaration(
CSSWideKeyword(${
"Inherit" if sub_property.is_inherited else "Initial"})
"Inherit" if sub_property.style_struct.inherited else "Initial"})
));
% endfor
},
@ -1057,10 +1107,10 @@ impl PropertyDeclaration {
pub mod style_structs {
use super::longhands;
% for name, longhands in LONGHANDS_PER_STYLE_STRUCT:
% for style_struct in STYLE_STRUCTS:
#[deriving(Eq, Clone)]
pub struct ${name} {
% for longhand in longhands:
pub struct ${style_struct.name} {
% for longhand in style_struct.longhands:
${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
% endfor
}
@ -1069,9 +1119,10 @@ pub mod style_structs {
#[deriving(Eq, Clone)]
pub struct ComputedValues {
% for name, longhands in LONGHANDS_PER_STYLE_STRUCT:
${name}: style_structs::${name},
% for style_struct in STYLE_STRUCTS:
${style_struct.name}: CowArc<style_structs::${style_struct.name}>,
% endfor
shareable: bool,
}
impl ComputedValues {
@ -1085,120 +1136,243 @@ impl ComputedValues {
pub fn resolve_color(&self, color: computed::CSSColor) -> RGBA {
match color {
RGBA(rgba) => rgba,
CurrentColor => self.Color.color,
CurrentColor => self.Color.get().color,
}
}
}
#[inline]
fn get_initial_values() -> ComputedValues {
/// Returns the initial values for all style structs as defined by the specification.
pub fn initial_values() -> ComputedValues {
ComputedValues {
% for style_struct, longhands in LONGHANDS_PER_STYLE_STRUCT:
${style_struct}: style_structs::${style_struct} {
% for longhand in longhands:
% for style_struct in STYLE_STRUCTS:
${style_struct.name}: CowArc::new(style_structs::${style_struct.name} {
% for longhand in style_struct.longhands:
${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
% endfor
},
}),
% endfor
shareable: true,
}
}
/// Fast path for the function below. Only computes new inherited styles.
#[allow(unused_mut)]
fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
shareable: bool,
parent_style: &ComputedValues,
cached_style: &ComputedValues)
-> ComputedValues {
% for style_struct in STYLE_STRUCTS:
% if style_struct.inherited:
let mut style_${style_struct.name} = parent_style.${style_struct.name}.clone();
% else:
let mut style_${style_struct.name} = cached_style.${style_struct.name}.clone();
% endif
% endfor
// Most specific/important declarations last
pub fn cascade(applicable_declarations: &[Arc<~[PropertyDeclaration]>],
parent_style: Option< &ComputedValues>)
-> ComputedValues {
let initial_keep_alive;
let (parent_style, is_root_element) = match parent_style {
Some(s) => (s, false),
None => {
initial_keep_alive = ~get_initial_values();
(&*initial_keep_alive, true)
}
};
struct AllDeclaredValues {
% for property in LONGHANDS:
${property.ident}: DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
% endfor
}
let mut specified = AllDeclaredValues {
% for property in LONGHANDS:
${property.ident}: CSSWideKeyword(${
"Inherit" if property.is_inherited else "Initial"}),
% endfor
};
for sub_list in applicable_declarations.iter() {
for declaration in sub_list.get().iter() {
match declaration {
% for property in LONGHANDS:
&${property.ident}_declaration(ref value) => {
// Overwrite earlier declarations.
// TODO: can we avoid a copy?
specified.${property.ident} = (*value).clone()
}
% endfor
let mut context = computed::Context::new(&style_Color,
&style_Font,
&style_Box,
&style_Border,
false);
<%def name="apply_cached(priority)">
for sub_list in applicable_declarations.iter() {
for declaration in sub_list.declarations.get().iter() {
match declaration {
% for style_struct in STYLE_STRUCTS:
% if style_struct.inherited:
% for property in style_struct.longhands:
% if (property.needed_for_context and needed_for_context) or not \
needed_for_context:
&${property.ident}_declaration(SpecifiedValue(ref value)) => {
% if property.needed_for_context and needed_for_context:
context.set_${property.ident}(computed_value)
% elif not needed_for_context:
// Overwrite earlier declarations.
let computed_value =
longhands::${property.ident}::to_computed_value(
(*value).clone(),
&context);
style_${style_struct.name}.get_mut()
.${property.ident} =
computed_value
% endif
}
&${property.ident}_declaration(CSSWideKeyword(Initial)) => {
let computed_value =
longhands::${property.ident}::get_initial_value();
% if property.needed_for_context and needed_for_context:
context.set_${property.ident}(computed_value)
% elif not needed_for_context:
// Overwrite earlier declarations.
style_${style_struct.name}.get_mut()
.${property.ident} =
computed_value
% endif
}
% endif
% endfor
% endif
% endfor
_ => {}
}
}
}
}
// This assumes that the computed and specified values have the same Rust type.
macro_rules! get_specified(
($style_struct: ident, $property: ident) => {
match specified.$property {
SpecifiedValue(value) => value,
CSSWideKeyword(Initial) => longhands::$property::get_initial_value(),
CSSWideKeyword(Inherit) => parent_style.$style_struct.$property.clone(),
}
};
)
macro_rules! has_border(
($property: ident) => {
match get_specified!(Border, $property) {
longhands::border_top_style::none
| longhands::border_top_style::hidden => false,
_ => true,
}
};
)
let context = &mut computed::Context {
current_color: get_specified!(Color, color),
font_size: parent_style.Font.font_size,
font_weight: parent_style.Font.font_weight,
position: get_specified!(Box, position),
float: get_specified!(Box, float),
is_root_element: is_root_element,
has_border_top: has_border!(border_top_style),
has_border_right: has_border!(border_right_style),
has_border_bottom: has_border!(border_bottom_style),
has_border_left: has_border!(border_left_style),
};
macro_rules! get_computed(
($style_struct: ident, $property: ident) => {
match specified.$property {
SpecifiedValue(ref value)
// TODO: avoid a copy?
=> longhands::$property::to_computed_value(value.clone(), context),
CSSWideKeyword(Initial) => longhands::$property::get_initial_value(),
CSSWideKeyword(Inherit) => parent_style.$style_struct.$property.clone(),
}
};
)
context.font_size = get_computed!(Font, font_size);
</%def>
${apply_cached(True)}
context.use_parent_font_size = false;
${apply_cached(False)}
ComputedValues {
% for style_struct, longhands in LONGHANDS_PER_STYLE_STRUCT:
${style_struct}: style_structs::${style_struct} {
% for longhand in longhands:
${longhand.ident}:
% if longhand.ident == 'font_size':
context.font_size,
% else:
get_computed!(${style_struct}, ${longhand.ident}),
% endif
% endfor
},
% for style_struct in STYLE_STRUCTS:
${style_struct.name}: style_${style_struct.name},
% endfor
shareable: shareable,
}
}
/// Performs the CSS cascade, computing new styles for an element from its parent style and
/// optionally a cached related style. The arguments are:
///
/// * `applicable_declarations`: The list of CSS rules that matched.
///
/// * `shareable`: Whether the `ComputedValues` structure to be constructed should be considered
/// shareable.
///
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
///
/// * `initial_values`: The initial set of CSS values as defined by the specification.
///
/// * `cached_style`: If present, cascading is short-circuited for everything but inherited
/// values and these values are used instead. Obviously, you must be careful when supplying
/// this that it is safe to only provide inherited declarations. If `parent_style` is `None`,
/// this is ignored.
///
/// Returns the computed values and a boolean indicating whether the result is cacheable.
pub fn cascade(applicable_declarations: &[MatchedProperty],
shareable: bool,
parent_style: Option< &ComputedValues >,
initial_values: &ComputedValues,
cached_style: Option< &ComputedValues >)
-> (ComputedValues, bool) {
match (cached_style, parent_style) {
(Some(cached_style), Some(parent_style)) => {
return (cascade_with_cached_declarations(applicable_declarations,
shareable,
parent_style,
cached_style), false)
}
(_, _) => {}
}
let is_root_element;
% for style_struct in STYLE_STRUCTS:
let mut style_${style_struct.name};
% endfor
match parent_style {
Some(parent_style) => {
is_root_element = false;
% for style_struct in STYLE_STRUCTS:
% if style_struct.inherited:
style_${style_struct.name} = parent_style.${style_struct.name}.clone();
% else:
style_${style_struct.name} = initial_values.${style_struct.name}.clone();
% endif
% endfor
}
None => {
is_root_element = true;
% for style_struct in STYLE_STRUCTS:
style_${style_struct.name} = initial_values.${style_struct.name}.clone();
% endfor
}
}
let mut context = computed::Context::new(&style_Color,
&style_Font,
&style_Box,
&style_Border,
is_root_element);
let mut cacheable = true;
<%def name="apply(needed_for_context)">
for sub_list in applicable_declarations.iter() {
for declaration in sub_list.declarations.get().iter() {
match declaration {
% for style_struct in STYLE_STRUCTS:
% for property in style_struct.longhands:
% if (property.needed_for_context and needed_for_context) or not \
needed_for_context:
&${property.ident}_declaration(SpecifiedValue(ref value)) => {
let computed_value =
longhands::${property.ident}::to_computed_value(
(*value).clone(),
&context);
% if property.needed_for_context and needed_for_context:
context.set_${property.ident}(computed_value)
% elif not needed_for_context:
// Overwrite earlier declarations.
style_${style_struct.name}.get_mut().${property.ident} =
computed_value
% endif
}
&${property.ident}_declaration(CSSWideKeyword(Initial)) => {
let computed_value =
longhands::${property.ident}::get_initial_value();
% if property.needed_for_context and needed_for_context:
context.set_${property.ident}(computed_value)
% elif not needed_for_context:
// Overwrite earlier declarations.
style_${style_struct.name}.get_mut().${property.ident} =
computed_value
% endif
}
% endif
% if not needed_for_context:
&${property.ident}_declaration(CSSWideKeyword(Inherit)) => {
// This is a bit slow, but this is rare so it shouldn't matter.
cacheable = false;
match parent_style {
None => {
style_${style_struct.name}.get_mut()
.${property.ident} =
longhands::${property.ident}::get_initial_value()
}
Some(ref parent_style) => {
style_${style_struct.name}.get_mut()
.${property.ident} =
parent_style.${style_struct.name}
.get()
.${property.ident}
.clone()
}
}
}
% endif
% endfor
% endfor
% if needed_for_context:
_ => {}
% endif
}
}
}
</%def>
${apply(True)}
context.use_parent_font_size = false;
${apply(False)}
(ComputedValues {
% for style_struct in STYLE_STRUCTS:
${style_struct.name}: style_${style_struct.name},
% endfor
shareable: shareable,
}, cacheable)
}
// Only re-export the types for computed values.
pub mod computed_values {

View File

@ -9,7 +9,7 @@ use std::str;
use std::to_bytes;
use servo_util::namespace;
use servo_util::smallvec::{SmallVec, SmallVec16};
use servo_util::smallvec::SmallVec;
use servo_util::sort;
use media_queries::{Device, Screen};
@ -104,10 +104,12 @@ impl SelectorMap {
/// Extract matching rules as per node's ID, classes, tag name, etc..
/// Sort the Rules at the end to maintain cascading order.
fn get_all_matching_rules<E:TElement,
N:TNode<E>>(
N:TNode<E>,
V:SmallVec<MatchedProperty>>(
&self,
node: &N,
matching_rules_list: &mut SmallVec16<Rule>) {
matching_rules_list: &mut V,
shareable: &mut bool) {
if self.empty {
return
}
@ -120,7 +122,8 @@ impl SelectorMap {
SelectorMap::get_matching_rules_from_hash(node,
&self.id_hash,
id,
matching_rules_list)
matching_rules_list,
shareable)
}
None => {}
}
@ -131,7 +134,8 @@ impl SelectorMap {
SelectorMap::get_matching_rules_from_hash(node,
&self.class_hash,
class,
matching_rules_list);
matching_rules_list,
shareable);
}
}
None => {}
@ -142,10 +146,13 @@ impl SelectorMap {
SelectorMap::get_matching_rules_from_hash_ignoring_case(node,
&self.element_hash,
element.get_local_name(),
matching_rules_list);
matching_rules_list,
shareable);
SelectorMap::get_matching_rules(node,
self.universal_rules,
matching_rules_list);
matching_rules_list,
shareable);
});
// Sort only the rules we just added.
@ -153,28 +160,32 @@ impl SelectorMap {
}
fn get_matching_rules_from_hash<E:TElement,
N:TNode<E>>(
N:TNode<E>,
V:SmallVec<MatchedProperty>>(
node: &N,
hash: &HashMap<~str,~[Rule]>,
key: &str,
matching_rules: &mut SmallVec16<Rule>) {
matching_rules: &mut V,
shareable: &mut bool) {
match hash.find_equiv(&key) {
Some(rules) => {
SelectorMap::get_matching_rules(node, *rules, matching_rules)
SelectorMap::get_matching_rules(node, *rules, matching_rules, shareable)
}
None => {}
}
}
fn get_matching_rules_from_hash_ignoring_case<E:TElement,
N:TNode<E>>(
N:TNode<E>,
V:SmallVec<MatchedProperty>>(
node: &N,
hash: &HashMap<~str,~[Rule]>,
key: &str,
matching_rules: &mut SmallVec16<Rule>) {
matching_rules: &mut V,
shareable: &mut bool) {
match hash.find_equiv(&LowercaseAsciiString(key)) {
Some(rules) => {
SelectorMap::get_matching_rules(node, *rules, matching_rules)
SelectorMap::get_matching_rules(node, *rules, matching_rules, shareable)
}
None => {}
}
@ -182,14 +193,16 @@ impl SelectorMap {
/// Adds rules in `rules` that match `node` to the `matching_rules` list.
fn get_matching_rules<E:TElement,
N:TNode<E>>(
N:TNode<E>,
V:SmallVec<MatchedProperty>>(
node: &N,
rules: &[Rule],
matching_rules: &mut SmallVec16<Rule>) {
matching_rules: &mut V,
shareable: &mut bool) {
for rule in rules.iter() {
if matches_compound_selector(rule.selector.get(), node) {
if matches_compound_selector(rule.selector.get(), node, shareable) {
// TODO(pradeep): Is the cloning inefficient?
matching_rules.push(rule.clone());
matching_rules.push(rule.property.clone());
}
}
}
@ -339,9 +352,11 @@ impl Stylist {
};
map.$priority.insert(Rule {
selector: selector.compound_selectors.clone(),
specificity: selector.specificity,
declarations: style_rule.declarations.$priority.clone(),
source_order: self.rules_source_order,
property: MatchedProperty {
specificity: selector.specificity,
declarations: style_rule.declarations.$priority.clone(),
source_order: self.rules_source_order,
},
});
}
}
@ -358,14 +373,19 @@ impl Stylist {
/// Returns the applicable CSS declarations for the given element. This corresponds to
/// `ElementRuleCollector` in WebKit.
///
/// The returned boolean indicates whether the style is *shareable*; that is, whether the
/// matched selectors are simple enough to allow the matching logic to be reduced to the logic
/// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
pub fn push_applicable_declarations<E:TElement,
N:TNode<E>,
V:SmallVec<Arc<~[PropertyDeclaration]>>>(
V:SmallVec<MatchedProperty>>(
&self,
element: &N,
style_attribute: Option<&PropertyDeclarationBlock>,
pseudo_element: Option<PseudoElement>,
applicable_declarations: &mut V) {
applicable_declarations: &mut V)
-> bool {
assert!(element.is_element());
assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");
@ -375,63 +395,42 @@ impl Stylist {
Some(Before) => &self.before_map,
Some(After) => &self.after_map,
};
// In cascading order:
let rule_map_list = [
&map.user_agent.normal,
&map.user.normal,
&map.author.normal,
&map.author.important,
&map.user.important,
&map.user_agent.important
];
// We keep track of the indices of each of the rule maps in the list we're building so that
// we have the indices straight at the end.
let mut rule_map_indices = [ 0, ..6 ];
let mut matching_rules_list = SmallVec16::new();
for (i, rule_map) in rule_map_list.iter().enumerate() {
rule_map_indices[i] = matching_rules_list.len();
rule_map.get_all_matching_rules(element, &mut matching_rules_list);
}
let count = matching_rules_list.len();
let mut declaration_iter = matching_rules_list.move_iter().map(|rule| {
let Rule {
declarations,
..
} = rule;
declarations
});
// Gather up all rules.
let mut i = 0;
let mut shareable = true;
// Step 1: Normal rules.
while i < rule_map_indices[3] {
applicable_declarations.push(declaration_iter.next().unwrap());
i += 1
}
map.user_agent.normal.get_all_matching_rules(element,
applicable_declarations,
&mut shareable);
map.user.normal.get_all_matching_rules(element, applicable_declarations, &mut shareable);
map.author.normal.get_all_matching_rules(element, applicable_declarations, &mut shareable);
// Step 2: Normal style attributes.
style_attribute.map(|sa| applicable_declarations.push(sa.normal.clone()));
style_attribute.map(|sa| {
shareable = false;
applicable_declarations.push(MatchedProperty::from_declarations(sa.normal.clone()))
});
// Step 3: Author-supplied `!important` rules.
while i < rule_map_indices[4] {
applicable_declarations.push(declaration_iter.next().unwrap());
i += 1
}
map.author.important.get_all_matching_rules(element,
applicable_declarations,
&mut shareable);
// Step 4: `!important` style attributes.
style_attribute.map(|sa| applicable_declarations.push(sa.important.clone()));
style_attribute.map(|sa| {
shareable = false;
applicable_declarations.push(MatchedProperty::from_declarations(sa.important.clone()))
});
// Step 5: User and UA `!important` rules.
while i < count {
applicable_declarations.push(declaration_iter.next().unwrap());
i += 1
}
map.user.important.get_all_matching_rules(element,
applicable_declarations,
&mut shareable);
map.user_agent.important.get_all_matching_rules(element,
applicable_declarations,
&mut shareable);
shareable
}
}
@ -473,34 +472,61 @@ struct Rule {
// that it matches. Selector contains an owned vector (through
// CompoundSelector) and we want to avoid the allocation.
selector: Arc<CompoundSelector>,
property: MatchedProperty,
}
/// A property declaration together with its precedence among rules of equal specificity so that
/// we can sort them.
#[deriving(Clone)]
pub struct MatchedProperty {
declarations: Arc<~[PropertyDeclaration]>,
// Precedence among rules of equal specificity
source_order: uint,
specificity: u32,
}
impl Ord for Rule {
impl MatchedProperty {
#[inline]
fn lt(&self, other: &Rule) -> bool {
let this_rank = (self.specificity, self.source_order);
let other_rank = (other.specificity, other.source_order);
this_rank < other_rank
pub fn from_declarations(declarations: Arc<~[PropertyDeclaration]>) -> MatchedProperty {
MatchedProperty {
declarations: declarations,
source_order: 0,
specificity: 0,
}
}
}
impl Eq for Rule {
impl Eq for MatchedProperty {
#[inline]
fn eq(&self, other: &Rule) -> bool {
fn eq(&self, other: &MatchedProperty) -> bool {
let this_rank = (self.specificity, self.source_order);
let other_rank = (other.specificity, other.source_order);
this_rank == other_rank
}
}
fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector, element: &N)
impl Ord for MatchedProperty {
#[inline]
fn lt(&self, other: &MatchedProperty) -> bool {
let this_rank = (self.specificity, self.source_order);
let other_rank = (other.specificity, other.source_order);
this_rank < other_rank
}
}
/// Determines whether the given element matches the given single or compound selector.
///
/// NB: If you add support for any new kinds of selectors to this routine, be sure to set
/// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things
/// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
/// `main/css/matching.rs`.)
fn matches_compound_selector<E:TElement,
N:TNode<E>>(
selector: &CompoundSelector,
element: &N,
shareable: &mut bool)
-> bool {
if !selector.simple_selectors.iter().all(|simple_selector| {
matches_simple_selector(simple_selector, element)
matches_simple_selector(simple_selector, element, shareable)
}) {
return false
}
@ -525,7 +551,7 @@ fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector,
Some(next_node) => node = next_node,
}
if node.is_element() {
if matches_compound_selector(&**next_selector, &node) {
if matches_compound_selector(&**next_selector, &node, shareable) {
return true
} else if just_one {
return false
@ -536,8 +562,19 @@ fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector,
}
}
/// Determines whether the given element matches the given single selector.
///
/// NB: If you add support for any new kinds of selectors to this routine, be sure to set
/// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things
/// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
/// `main/css/matching.rs`.)
#[inline]
fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, element: &N) -> bool {
fn matches_simple_selector<E:TElement,
N:TNode<E>>(
selector: &SimpleSelector,
element: &N,
shareable: &mut bool)
-> bool {
match *selector {
// TODO: case-sensitivity depends on the document type
// TODO: intern element names
@ -546,7 +583,9 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
element.get_local_name().eq_ignore_ascii_case(name.as_slice())
})
}
NamespaceSelector(ref namespace) => {
*shareable = false;
element.with_element(|element: &E| {
element.get_namespace() == namespace
})
@ -554,6 +593,7 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
// TODO: case-sensitivity depends on the document type and quirks mode
// TODO: cache and intern IDs on elements.
IDSelector(ref id) => {
*shareable = false;
element.with_element(|element: &E| {
match element.get_attr(&namespace::Null, "id") {
Some(attr) => str::eq_slice(attr, *id),
@ -561,7 +601,7 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
}
})
}
// TODO: cache and intern classe names on elements.
// TODO: cache and intern class names on elements.
ClassSelector(ref class) => {
element.with_element(|element: &E| {
match element.get_attr(&namespace::Null, "class") {
@ -573,32 +613,57 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
})
}
AttrExists(ref attr) => element.match_attr(attr, |_| true),
AttrEqual(ref attr, ref value) => element.match_attr(attr, |v| v == value.as_slice()),
AttrIncludes(ref attr, ref value) => element.match_attr(attr, |attr_value| {
attr_value.split(SELECTOR_WHITESPACE).any(|v| v == value.as_slice())
}),
AttrDashMatch(ref attr, ref value, ref dashing_value)
=> element.match_attr(attr, |attr_value| {
attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
}),
AttrPrefixMatch(ref attr, ref value) => element.match_attr(attr, |attr_value| {
attr_value.starts_with(value.as_slice())
}),
AttrSubstringMatch(ref attr, ref value) => element.match_attr(attr, |attr_value| {
attr_value.contains(value.as_slice())
}),
AttrSuffixMatch(ref attr, ref value) => element.match_attr(attr, |attr_value| {
attr_value.ends_with(value.as_slice())
}),
AttrExists(ref attr) => {
*shareable = false;
element.match_attr(attr, |_| true)
}
AttrEqual(ref attr, ref value) => {
if value.as_slice() != "DIR" {
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
// here because the UA style otherwise disables all style sharing completely.
*shareable = false
}
element.match_attr(attr, |v| v == value.as_slice())
}
AttrIncludes(ref attr, ref value) => {
*shareable = false;
element.match_attr(attr, |attr_value| {
attr_value.split(SELECTOR_WHITESPACE).any(|v| v == value.as_slice())
})
}
AttrDashMatch(ref attr, ref value, ref dashing_value) => {
*shareable = false;
element.match_attr(attr, |attr_value| {
attr_value == value.as_slice() || attr_value.starts_with(dashing_value.as_slice())
})
}
AttrPrefixMatch(ref attr, ref value) => {
*shareable = false;
element.match_attr(attr, |attr_value| {
attr_value.starts_with(value.as_slice())
})
}
AttrSubstringMatch(ref attr, ref value) => {
*shareable = false;
element.match_attr(attr, |attr_value| {
attr_value.contains(value.as_slice())
})
}
AttrSuffixMatch(ref attr, ref value) => {
*shareable = false;
element.match_attr(attr, |attr_value| {
attr_value.ends_with(value.as_slice())
})
}
AnyLink => {
*shareable = false;
element.with_element(|element: &E| {
element.get_link().is_some()
})
}
Link => {
*shareable = false;
element.with_element(|element: &E| {
match element.get_link() {
Some(url) => !url_is_visited(url),
@ -607,6 +672,7 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
})
}
Visited => {
*shareable = false;
element.with_element(|element: &E| {
match element.get_link() {
Some(url) => url_is_visited(url),
@ -616,29 +682,63 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
}
Hover => {
*shareable = false;
element.with_element(|element: &E| {
element.get_hover_state()
})
},
FirstChild => matches_first_child(element),
LastChild => matches_last_child(element),
OnlyChild => matches_first_child(element) &&
matches_last_child(element),
FirstChild => {
*shareable = false;
matches_first_child(element)
}
LastChild => {
*shareable = false;
matches_last_child(element)
}
OnlyChild => {
*shareable = false;
matches_first_child(element) && matches_last_child(element)
}
Root => matches_root(element),
Root => {
*shareable = false;
matches_root(element)
}
NthChild(a, b) => matches_generic_nth_child(element, a, b, false, false),
NthLastChild(a, b) => matches_generic_nth_child(element, a, b, false, true),
NthOfType(a, b) => matches_generic_nth_child(element, a, b, true, false),
NthLastOfType(a, b) => matches_generic_nth_child(element, a, b, true, true),
NthChild(a, b) => {
*shareable = false;
matches_generic_nth_child(element, a, b, false, false)
}
NthLastChild(a, b) => {
*shareable = false;
matches_generic_nth_child(element, a, b, false, true)
}
NthOfType(a, b) => {
*shareable = false;
matches_generic_nth_child(element, a, b, true, false)
}
NthLastOfType(a, b) => {
*shareable = false;
matches_generic_nth_child(element, a, b, true, true)
}
FirstOfType => matches_generic_nth_child(element, 0, 1, true, false),
LastOfType => matches_generic_nth_child(element, 0, 1, true, true),
OnlyOfType => matches_generic_nth_child(element, 0, 1, true, false) &&
matches_generic_nth_child(element, 0, 1, true, true),
FirstOfType => {
*shareable = false;
matches_generic_nth_child(element, 0, 1, true, false)
}
LastOfType => {
*shareable = false;
matches_generic_nth_child(element, 0, 1, true, true)
}
OnlyOfType => {
*shareable = false;
matches_generic_nth_child(element, 0, 1, true, false) &&
matches_generic_nth_child(element, 0, 1, true, true)
}
Negation(ref negated) => {
!negated.iter().all(|s| matches_simple_selector(s, element))
*shareable = false;
!negated.iter().all(|s| matches_simple_selector(s, element, shareable))
},
}
}
@ -765,7 +865,7 @@ fn matches_last_child<E:TElement,N:TNode<E>>(element: &N) -> bool {
#[cfg(test)]
mod tests {
use extra::arc::Arc;
use super::{Rule, SelectorMap};
use super::{MatchedProperty, Rule, SelectorMap};
/// Helper method to get some Rules from selector strings.
/// Each sublist of the result contains the Rules for one StyleRule.
@ -779,10 +879,12 @@ mod tests {
parse_selector_list(tokenize(*selectors).map(|(c, _)| c).to_owned_vec(), &namespaces)
.unwrap().move_iter().map(|s| {
Rule {
specificity: s.specificity,
selector: s.compound_selectors,
declarations: Arc::new(~[]),
source_order: i,
selector: s.compound_selectors.clone(),
property: MatchedProperty {
specificity: s.specificity,
declarations: Arc::new(~[]),
source_order: i,
}
}
}).to_owned_vec()
}).to_owned_vec()
@ -793,7 +895,7 @@ mod tests {
let rules_list = get_mock_rules(["a.intro", "img.sidebar"]);
let rule1 = rules_list[0][0].clone();
let rule2 = rules_list[1][0].clone();
assert!(rule1 < rule2, "The rule that comes later should win.");
assert!(rule1.property < rule2.property, "The rule that comes later should win.");
}
#[test]
@ -824,9 +926,9 @@ mod tests {
let rules_list = get_mock_rules([".intro.foo", "#top"]);
let mut selector_map = SelectorMap::new();
selector_map.insert(rules_list[1][0].clone());
assert_eq!(1, selector_map.id_hash.find(&~"top").unwrap()[0].source_order);
assert_eq!(1, selector_map.id_hash.find(&~"top").unwrap()[0].property.source_order);
selector_map.insert(rules_list[0][0].clone());
assert_eq!(0, selector_map.class_hash.find(&~"intro").unwrap()[0].source_order);
assert_eq!(0, selector_map.class_hash.find(&~"intro").unwrap()[0].property.source_order);
assert!(selector_map.class_hash.find(&~"foo").is_none());
}
}

View File

@ -19,8 +19,10 @@ extern mod servo_util = "util";
// Public API
pub use stylesheets::Stylesheet;
pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin};
pub use selector_matching::{MatchedProperty};
pub use properties::{cascade, PropertyDeclaration, ComputedValues, computed_values};
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
pub use properties::{initial_values};
pub use errors::with_errors_silenced;
pub use node::{TElement, TNode};
pub use selectors::{PseudoElement, Before, After, AttrSelector, SpecificNamespace, AnyNamespace};

View File

@ -3,6 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::hashmap::HashMap;
use std::rand::Rng;
use std::rand;
use std::vec::VecIterator;
use std::vec;
pub trait Cache<K: Eq, V: Clone> {
fn insert(&mut self, key: K, value: V);
@ -124,6 +128,7 @@ impl<K: Clone + Eq, V: Clone> LRUCache<K,V> {
}
}
#[inline]
pub fn touch(&mut self, pos: uint) -> V {
let last_index = self.entries.len() - 1;
if pos != last_index {
@ -132,6 +137,10 @@ impl<K: Clone + Eq, V: Clone> LRUCache<K,V> {
}
self.entries[last_index].second_ref().clone()
}
pub fn iter<'a>(&'a self) -> VecIterator<'a,(K,V)> {
self.entries.iter()
}
}
impl<K: Clone + Eq, V: Clone> Cache<K,V> for LRUCache<K,V> {
@ -165,6 +174,73 @@ impl<K: Clone + Eq, V: Clone> Cache<K,V> for LRUCache<K,V> {
}
}
pub struct SimpleHashCache<K,V> {
entries: ~[Option<(K,V)>],
k0: u64,
k1: u64,
}
impl<K:Clone+Eq+Hash,V:Clone> SimpleHashCache<K,V> {
pub fn new(cache_size: uint) -> SimpleHashCache<K,V> {
let mut r = rand::task_rng();
SimpleHashCache {
entries: vec::from_elem(cache_size, None),
k0: r.gen(),
k1: r.gen(),
}
}
#[inline]
fn to_bucket(&self, h: uint) -> uint {
h % self.entries.len()
}
#[inline]
fn bucket_for_key<Q:Hash>(&self, key: &Q) -> uint {
self.to_bucket(key.hash_keyed(self.k0, self.k1) as uint)
}
#[inline]
pub fn find_equiv<'a,Q:Hash+Equiv<K>>(&'a self, key: &Q) -> Option<&'a V> {
let bucket_index = self.bucket_for_key(key);
match self.entries[bucket_index] {
Some((ref existing_key, ref value)) if key.equiv(existing_key) => Some(value),
_ => None,
}
}
}
impl<K:Clone+Eq+Hash,V:Clone> Cache<K,V> for SimpleHashCache<K,V> {
fn insert(&mut self, key: K, value: V) {
let bucket_index = self.bucket_for_key(&key);
self.entries[bucket_index] = Some((key, value))
}
fn find(&mut self, key: &K) -> Option<V> {
let bucket_index = self.bucket_for_key(key);
match self.entries[bucket_index] {
Some((ref existing_key, ref value)) if existing_key == key => Some((*value).clone()),
_ => None,
}
}
fn find_or_create(&mut self, key: &K, blk: |&K| -> V) -> V {
match self.find(key) {
Some(value) => return value,
None => {}
}
let value = blk(key);
self.insert((*key).clone(), value.clone());
value
}
fn evict_all(&mut self) {
for slot in self.entries.mut_iter() {
*slot = None
}
}
}
#[test]
fn test_lru_cache() {
let one = @"one";

View File

@ -0,0 +1,100 @@
/* 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/. */
//! An atomically reference counted type that copies itself on mutation.
use std::cast;
use std::ptr;
use std::sync::atomics::{AtomicUint, SeqCst};
struct CowArcAlloc<T> {
ref_count: AtomicUint,
data: T,
}
#[unsafe_no_drop_flag]
pub struct CowArc<T> {
priv ptr: *mut CowArcAlloc<T>,
}
#[unsafe_destructor]
impl<T> Drop for CowArc<T> {
#[inline]
fn drop(&mut self) {
unsafe {
if self.ptr != ptr::mut_null() && (*self.ptr).ref_count.fetch_sub(1, SeqCst) == 1 {
let _kill_it: ~CowArcAlloc<T> = cast::transmute(self.ptr);
self.ptr = ptr::mut_null()
}
}
}
}
impl<T:Eq + Freeze + Clone> Eq for CowArc<T> {
fn eq(&self, other: &CowArc<T>) -> bool {
self.get() == other.get()
}
}
impl<T:Freeze + Clone> Clone for CowArc<T> {
#[inline]
fn clone(&self) -> CowArc<T> {
unsafe {
drop((*self.ptr).ref_count.fetch_add(1, SeqCst));
}
CowArc {
ptr: self.ptr
}
}
}
impl<T:Freeze + Clone> CowArc<T> {
#[inline]
pub fn new(value: T) -> CowArc<T> {
let alloc = ~CowArcAlloc {
ref_count: AtomicUint::new(1),
data: value,
};
unsafe {
CowArc {
ptr: cast::transmute(alloc),
}
}
}
#[inline]
pub fn shared(&self) -> bool {
unsafe {
(*self.ptr).ref_count.load(SeqCst) != 1
}
}
#[inline]
pub fn get<'a>(&'a self) -> &'a T {
unsafe {
cast::transmute(&(*self.ptr).data)
}
}
#[inline(always)]
pub fn get_mut<'a>(&'a mut self) -> &'a mut T {
unsafe {
if (*self.ptr).ref_count.load(SeqCst) == 1 {
return cast::transmute(&mut (*self.ptr).data)
}
let copy = ~CowArcAlloc {
ref_count: AtomicUint::new(1),
data: (*self.ptr).data.clone(),
};
*self = CowArc {
ptr: cast::transmute(copy),
};
cast::transmute(&mut (*self.ptr).data)
}
}
}

View File

@ -324,6 +324,20 @@ macro_rules! def_small_vector_drop_impl(
)
)
macro_rules! def_small_vector_clone_impl(
($name:ident) => (
impl<T:Clone> Clone for $name<T> {
fn clone(&self) -> $name<T> {
let mut new_vector = $name::new();
for element in self.iter() {
new_vector.push((*element).clone())
}
new_vector
}
}
)
)
macro_rules! def_small_vector_impl(
($name:ident, $size:expr) => (
impl<T> $name<T> {
@ -396,47 +410,55 @@ impl<T> SmallVec0<T> {
}
def_small_vector_drop_impl!(SmallVec0, 0)
def_small_vector_clone_impl!(SmallVec0)
def_small_vector!(SmallVec1, 1)
def_small_vector_private_trait_impl!(SmallVec1, 1)
def_small_vector_trait_impl!(SmallVec1, 1)
def_small_vector_drop_impl!(SmallVec1, 1)
def_small_vector_clone_impl!(SmallVec1)
def_small_vector_impl!(SmallVec1, 1)
def_small_vector!(SmallVec2, 2)
def_small_vector_private_trait_impl!(SmallVec2, 2)
def_small_vector_trait_impl!(SmallVec2, 2)
def_small_vector_drop_impl!(SmallVec2, 2)
def_small_vector_clone_impl!(SmallVec2)
def_small_vector_impl!(SmallVec2, 2)
def_small_vector!(SmallVec4, 4)
def_small_vector_private_trait_impl!(SmallVec4, 4)
def_small_vector_trait_impl!(SmallVec4, 4)
def_small_vector_drop_impl!(SmallVec4, 4)
def_small_vector_clone_impl!(SmallVec4)
def_small_vector_impl!(SmallVec4, 4)
def_small_vector!(SmallVec8, 8)
def_small_vector_private_trait_impl!(SmallVec8, 8)
def_small_vector_trait_impl!(SmallVec8, 8)
def_small_vector_drop_impl!(SmallVec8, 8)
def_small_vector_clone_impl!(SmallVec8)
def_small_vector_impl!(SmallVec8, 8)
def_small_vector!(SmallVec16, 16)
def_small_vector_private_trait_impl!(SmallVec16, 16)
def_small_vector_trait_impl!(SmallVec16, 16)
def_small_vector_drop_impl!(SmallVec16, 16)
def_small_vector_clone_impl!(SmallVec16)
def_small_vector_impl!(SmallVec16, 16)
def_small_vector!(SmallVec24, 24)
def_small_vector_private_trait_impl!(SmallVec24, 24)
def_small_vector_trait_impl!(SmallVec24, 24)
def_small_vector_drop_impl!(SmallVec24, 24)
def_small_vector_clone_impl!(SmallVec24)
def_small_vector_impl!(SmallVec24, 24)
def_small_vector!(SmallVec32, 32)
def_small_vector_private_trait_impl!(SmallVec32, 32)
def_small_vector_trait_impl!(SmallVec32, 32)
def_small_vector_drop_impl!(SmallVec32, 32)
def_small_vector_clone_impl!(SmallVec32)
def_small_vector_impl!(SmallVec32, 32)
#[cfg(test)]

View File

@ -13,6 +13,7 @@ extern mod native;
pub mod cache;
pub mod concurrentmap;
pub mod cowarc;
pub mod debug;
pub mod geometry;
pub mod io;

File diff suppressed because it is too large Load Diff