mirror of
https://github.com/servo/servo.git
synced 2025-06-17 12:54:28 +00:00
layout: Support more types of selectors for style sharing.
This helps avoid problems with style sharing in common cases, often caused by the user agent stylesheet.
This commit is contained in:
parent
2d8bd10abe
commit
4bf0acbe38
3 changed files with 146 additions and 10 deletions
|
@ -17,10 +17,10 @@ use servo_util::arc_ptr_eq;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::hash::{Hash, sip};
|
use std::hash::{Hash, sip};
|
||||||
use std::slice::Items;
|
use std::slice::Items;
|
||||||
use style::{After, Before, ComputedValues, DeclarationBlock, Stylist, TElement, TNode};
|
use string_cache::{Atom, Namespace};
|
||||||
use style::cascade;
|
use style::{mod, After, Before, ComputedValues, DeclarationBlock, Stylist, TElement, TNode};
|
||||||
|
use style::{AttrIsEqualMode, AttrIsPresentMode, CommonStyleAffectingAttributes, cascade};
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
use string_cache::Atom;
|
|
||||||
|
|
||||||
pub struct ApplicableDeclarations {
|
pub struct ApplicableDeclarations {
|
||||||
pub normal: SmallVec16<DeclarationBlock>,
|
pub normal: SmallVec16<DeclarationBlock>,
|
||||||
|
@ -148,6 +148,29 @@ pub struct StyleSharingCandidateCache {
|
||||||
cache: LRUCache<StyleSharingCandidate,()>,
|
cache: LRUCache<StyleSharingCandidate,()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_common_style_affecting_attributes_from_element(element: &LayoutElement)
|
||||||
|
-> CommonStyleAffectingAttributes {
|
||||||
|
let mut flags = CommonStyleAffectingAttributes::empty();
|
||||||
|
for attribute_info in style::common_style_affecting_attributes().iter() {
|
||||||
|
match attribute_info.mode {
|
||||||
|
AttrIsPresentMode(flag) => {
|
||||||
|
if element.get_attr(&ns!(""), &attribute_info.atom).is_some() {
|
||||||
|
flags.insert(flag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AttrIsEqualMode(target_value, flag) => {
|
||||||
|
match element.get_attr(&ns!(""), &attribute_info.atom) {
|
||||||
|
Some(element_value) if element_value == target_value => {
|
||||||
|
flags.insert(flag)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flags
|
||||||
|
}
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct StyleSharingCandidate {
|
pub struct StyleSharingCandidate {
|
||||||
pub style: Arc<ComputedValues>,
|
pub style: Arc<ComputedValues>,
|
||||||
|
@ -155,6 +178,9 @@ pub struct StyleSharingCandidate {
|
||||||
pub local_name: Atom,
|
pub local_name: Atom,
|
||||||
// FIXME(pcwalton): Should be a list of atoms instead.
|
// FIXME(pcwalton): Should be a list of atoms instead.
|
||||||
pub class: Option<String>,
|
pub class: Option<String>,
|
||||||
|
pub namespace: Namespace,
|
||||||
|
pub common_style_affecting_attributes: CommonStyleAffectingAttributes,
|
||||||
|
pub link: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for StyleSharingCandidate {
|
impl PartialEq for StyleSharingCandidate {
|
||||||
|
@ -162,7 +188,10 @@ impl PartialEq for StyleSharingCandidate {
|
||||||
arc_ptr_eq(&self.style, &other.style) &&
|
arc_ptr_eq(&self.style, &other.style) &&
|
||||||
arc_ptr_eq(&self.parent_style, &other.parent_style) &&
|
arc_ptr_eq(&self.parent_style, &other.parent_style) &&
|
||||||
self.local_name == other.local_name &&
|
self.local_name == other.local_name &&
|
||||||
self.class == other.class
|
self.class == other.class &&
|
||||||
|
self.link == other.link &&
|
||||||
|
self.namespace == other.namespace &&
|
||||||
|
self.common_style_affecting_attributes == other.common_style_affecting_attributes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +242,10 @@ impl StyleSharingCandidate {
|
||||||
local_name: element.get_local_name().clone(),
|
local_name: element.get_local_name().clone(),
|
||||||
class: element.get_attr(&ns!(""), &atom!("class"))
|
class: element.get_attr(&ns!(""), &atom!("class"))
|
||||||
.map(|string| string.to_string()),
|
.map(|string| string.to_string()),
|
||||||
|
link: element.get_link().is_some(),
|
||||||
|
namespace: (*element.get_namespace()).clone(),
|
||||||
|
common_style_affecting_attributes:
|
||||||
|
create_common_style_affecting_attributes_from_element(&element)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +264,44 @@ impl StyleSharingCandidate {
|
||||||
(&Some(_), Some(_)) | (&None, None) => {}
|
(&Some(_), Some(_)) | (&None, None) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *element.get_namespace() != self.namespace {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for attribute_info in style::common_style_affecting_attributes().iter() {
|
||||||
|
match attribute_info.mode {
|
||||||
|
AttrIsPresentMode(flag) => {
|
||||||
|
if self.common_style_affecting_attributes.contains(flag) !=
|
||||||
|
element.get_attr(&ns!(""), &attribute_info.atom).is_some() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AttrIsEqualMode(target_value, flag) => {
|
||||||
|
match element.get_attr(&ns!(""), &attribute_info.atom) {
|
||||||
|
Some(ref element_value) if self.common_style_affecting_attributes
|
||||||
|
.contains(flag) &&
|
||||||
|
*element_value != target_value => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Some(_) if !self.common_style_affecting_attributes.contains(flag) => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
None if self.common_style_affecting_attributes.contains(flag) => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if element.get_link().is_some() != self.link {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): We don't support visited links yet, but when we do there will need to
|
||||||
|
// be some logic here.
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,10 @@ extern crate "util" as servo_util;
|
||||||
pub use media_queries::{Device, Screen};
|
pub use media_queries::{Device, Screen};
|
||||||
pub use stylesheets::{Stylesheet, iter_font_face_rules};
|
pub use stylesheets::{Stylesheet, iter_font_face_rules};
|
||||||
pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin};
|
pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin};
|
||||||
pub use selector_matching::{DeclarationBlock, matches,matches_simple_selector};
|
pub use selector_matching::{DeclarationBlock, CommonStyleAffectingAttributes};
|
||||||
|
pub use selector_matching::{CommonStyleAffectingAttributeInfo, CommonStyleAffectingAttributeMode};
|
||||||
|
pub use selector_matching::{AttrIsPresentMode, AttrIsEqualMode};
|
||||||
|
pub use selector_matching::{matches, matches_simple_selector, common_style_affecting_attributes};
|
||||||
pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE,SELECTOR_WHITESPACE};
|
pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE,SELECTOR_WHITESPACE};
|
||||||
pub use properties::{cascade, cascade_anonymous};
|
pub use properties::{cascade, cascade_anonymous};
|
||||||
pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs};
|
pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs};
|
||||||
|
|
|
@ -761,6 +761,53 @@ fn matches_compound_selector_internal<'a,E,N>(selector: &CompoundSelector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
flags CommonStyleAffectingAttributes: u8 {
|
||||||
|
static HiddenAttribute = 0x01,
|
||||||
|
static NoWrapAttribute = 0x02,
|
||||||
|
static AlignLeftAttribute = 0x04,
|
||||||
|
static AlignCenterAttribute = 0x08,
|
||||||
|
static AlignRightAttribute = 0x10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommonStyleAffectingAttributeInfo {
|
||||||
|
pub atom: Atom,
|
||||||
|
pub mode: CommonStyleAffectingAttributeMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum CommonStyleAffectingAttributeMode {
|
||||||
|
AttrIsPresentMode(CommonStyleAffectingAttributes),
|
||||||
|
AttrIsEqualMode(&'static str, CommonStyleAffectingAttributes),
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: This must match the order in `layout::css::matching::CommonStyleAffectingAttributes`.
|
||||||
|
#[inline]
|
||||||
|
pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo, ..5] {
|
||||||
|
[
|
||||||
|
CommonStyleAffectingAttributeInfo {
|
||||||
|
atom: atom!("hidden"),
|
||||||
|
mode: AttrIsPresentMode(HiddenAttribute),
|
||||||
|
},
|
||||||
|
CommonStyleAffectingAttributeInfo {
|
||||||
|
atom: atom!("nowrap"),
|
||||||
|
mode: AttrIsPresentMode(NoWrapAttribute),
|
||||||
|
},
|
||||||
|
CommonStyleAffectingAttributeInfo {
|
||||||
|
atom: atom!("align"),
|
||||||
|
mode: AttrIsEqualMode("left", AlignLeftAttribute),
|
||||||
|
},
|
||||||
|
CommonStyleAffectingAttributeInfo {
|
||||||
|
atom: atom!("align"),
|
||||||
|
mode: AttrIsEqualMode("center", AlignCenterAttribute),
|
||||||
|
},
|
||||||
|
CommonStyleAffectingAttributeInfo {
|
||||||
|
atom: atom!("align"),
|
||||||
|
mode: AttrIsEqualMode("right", AlignRightAttribute),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines whether the given element matches the given single selector.
|
/// 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
|
/// NB: If you add support for any new kinds of selectors to this routine, be sure to set
|
||||||
|
@ -781,7 +828,6 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
|
||||||
}
|
}
|
||||||
|
|
||||||
NamespaceSelector(ref namespace) => {
|
NamespaceSelector(ref namespace) => {
|
||||||
*shareable = false;
|
|
||||||
let element = element.as_element();
|
let element = element.as_element();
|
||||||
element.get_namespace() == namespace
|
element.get_namespace() == namespace
|
||||||
}
|
}
|
||||||
|
@ -799,11 +845,26 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
|
||||||
}
|
}
|
||||||
|
|
||||||
AttrExists(ref attr) => {
|
AttrExists(ref attr) => {
|
||||||
|
// NB(pcwalton): If you update this, remember to update the corresponding list in
|
||||||
|
// `can_share_style_with()` as well.
|
||||||
|
if common_style_affecting_attributes().iter().all(|common_attr_info| {
|
||||||
|
!(common_attr_info.atom == attr.name && match common_attr_info.mode {
|
||||||
|
AttrIsPresentMode(_) => true,
|
||||||
|
AttrIsEqualMode(..) => false,
|
||||||
|
})
|
||||||
|
}) {
|
||||||
*shareable = false;
|
*shareable = false;
|
||||||
|
}
|
||||||
element.match_attr(attr, |_| true)
|
element.match_attr(attr, |_| true)
|
||||||
}
|
}
|
||||||
AttrEqual(ref attr, ref value, case_sensitivity) => {
|
AttrEqual(ref attr, ref value, case_sensitivity) => {
|
||||||
if value.as_slice() != "DIR" {
|
if value.as_slice() != "DIR" &&
|
||||||
|
common_style_affecting_attributes().iter().all(|common_attr_info| {
|
||||||
|
!(common_attr_info.atom == attr.name && match common_attr_info.mode {
|
||||||
|
AttrIsEqualMode(target_value, _) => target_value == value.as_slice(),
|
||||||
|
AttrIsPresentMode(_) => false,
|
||||||
|
})
|
||||||
|
}) {
|
||||||
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
|
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
|
||||||
// here because the UA style otherwise disables all style sharing completely.
|
// here because the UA style otherwise disables all style sharing completely.
|
||||||
*shareable = false
|
*shareable = false
|
||||||
|
@ -853,7 +914,6 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
|
||||||
element.get_link().is_some()
|
element.get_link().is_some()
|
||||||
}
|
}
|
||||||
Link => {
|
Link => {
|
||||||
*shareable = false;
|
|
||||||
let elem = element.as_element();
|
let elem = element.as_element();
|
||||||
match elem.get_link() {
|
match elem.get_link() {
|
||||||
Some(url) => !url_is_visited(url),
|
Some(url) => !url_is_visited(url),
|
||||||
|
@ -861,7 +921,8 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Visited => {
|
Visited => {
|
||||||
*shareable = false;
|
// NB(pcwalton): When we actually start supporting visited links, remember to update
|
||||||
|
// `can_share_style_with`.
|
||||||
let elem = element.as_element();
|
let elem = element.as_element();
|
||||||
match elem.get_link() {
|
match elem.get_link() {
|
||||||
Some(url) => url_is_visited(url),
|
Some(url) => url_is_visited(url),
|
||||||
|
@ -947,6 +1008,7 @@ fn url_is_visited(_url: &str) -> bool {
|
||||||
// FIXME: implement this.
|
// FIXME: implement this.
|
||||||
// This function will probably need to take a "session"
|
// This function will probably need to take a "session"
|
||||||
// or something containing browsing history as an additional parameter.
|
// or something containing browsing history as an additional parameter.
|
||||||
|
// NB(pcwalton): When you implement this, remember to update `can_share_style_with`!
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue