Skip to content

Commit 4bf0acb

Browse files
committed
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.
1 parent 2d8bd10 commit 4bf0acb

File tree

3 files changed

+146
-10
lines changed

3 files changed

+146
-10
lines changed

components/layout/css/matching.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ use servo_util::arc_ptr_eq;
1717
use std::mem;
1818
use std::hash::{Hash, sip};
1919
use std::slice::Items;
20-
use style::{After, Before, ComputedValues, DeclarationBlock, Stylist, TElement, TNode};
21-
use style::cascade;
20+
use string_cache::{Atom, Namespace};
21+
use style::{mod, After, Before, ComputedValues, DeclarationBlock, Stylist, TElement, TNode};
22+
use style::{AttrIsEqualMode, AttrIsPresentMode, CommonStyleAffectingAttributes, cascade};
2223
use sync::Arc;
23-
use string_cache::Atom;
2424

2525
pub struct ApplicableDeclarations {
2626
pub normal: SmallVec16<DeclarationBlock>,
@@ -148,21 +148,50 @@ pub struct StyleSharingCandidateCache {
148148
cache: LRUCache<StyleSharingCandidate,()>,
149149
}
150150

151+
fn create_common_style_affecting_attributes_from_element(element: &LayoutElement)
152+
-> CommonStyleAffectingAttributes {
153+
let mut flags = CommonStyleAffectingAttributes::empty();
154+
for attribute_info in style::common_style_affecting_attributes().iter() {
155+
match attribute_info.mode {
156+
AttrIsPresentMode(flag) => {
157+
if element.get_attr(&ns!(""), &attribute_info.atom).is_some() {
158+
flags.insert(flag)
159+
}
160+
}
161+
AttrIsEqualMode(target_value, flag) => {
162+
match element.get_attr(&ns!(""), &attribute_info.atom) {
163+
Some(element_value) if element_value == target_value => {
164+
flags.insert(flag)
165+
}
166+
_ => {}
167+
}
168+
}
169+
}
170+
}
171+
flags
172+
}
173+
151174
#[deriving(Clone)]
152175
pub struct StyleSharingCandidate {
153176
pub style: Arc<ComputedValues>,
154177
pub parent_style: Arc<ComputedValues>,
155178
pub local_name: Atom,
156179
// FIXME(pcwalton): Should be a list of atoms instead.
157180
pub class: Option<String>,
181+
pub namespace: Namespace,
182+
pub common_style_affecting_attributes: CommonStyleAffectingAttributes,
183+
pub link: bool,
158184
}
159185

160186
impl PartialEq for StyleSharingCandidate {
161187
fn eq(&self, other: &StyleSharingCandidate) -> bool {
162188
arc_ptr_eq(&self.style, &other.style) &&
163189
arc_ptr_eq(&self.parent_style, &other.parent_style) &&
164190
self.local_name == other.local_name &&
165-
self.class == other.class
191+
self.class == other.class &&
192+
self.link == other.link &&
193+
self.namespace == other.namespace &&
194+
self.common_style_affecting_attributes == other.common_style_affecting_attributes
166195
}
167196
}
168197

@@ -213,6 +242,10 @@ impl StyleSharingCandidate {
213242
local_name: element.get_local_name().clone(),
214243
class: element.get_attr(&ns!(""), &atom!("class"))
215244
.map(|string| string.to_string()),
245+
link: element.get_link().is_some(),
246+
namespace: (*element.get_namespace()).clone(),
247+
common_style_affecting_attributes:
248+
create_common_style_affecting_attributes_from_element(&element)
216249
})
217250
}
218251

@@ -231,6 +264,44 @@ impl StyleSharingCandidate {
231264
(&Some(_), Some(_)) | (&None, None) => {}
232265
}
233266

267+
if *element.get_namespace() != self.namespace {
268+
return false
269+
}
270+
271+
for attribute_info in style::common_style_affecting_attributes().iter() {
272+
match attribute_info.mode {
273+
AttrIsPresentMode(flag) => {
274+
if self.common_style_affecting_attributes.contains(flag) !=
275+
element.get_attr(&ns!(""), &attribute_info.atom).is_some() {
276+
return false
277+
}
278+
}
279+
AttrIsEqualMode(target_value, flag) => {
280+
match element.get_attr(&ns!(""), &attribute_info.atom) {
281+
Some(ref element_value) if self.common_style_affecting_attributes
282+
.contains(flag) &&
283+
*element_value != target_value => {
284+
return false
285+
}
286+
Some(_) if !self.common_style_affecting_attributes.contains(flag) => {
287+
return false
288+
}
289+
None if self.common_style_affecting_attributes.contains(flag) => {
290+
return false
291+
}
292+
_ => {}
293+
}
294+
}
295+
}
296+
}
297+
298+
if element.get_link().is_some() != self.link {
299+
return false
300+
}
301+
302+
// TODO(pcwalton): We don't support visited links yet, but when we do there will need to
303+
// be some logic here.
304+
234305
true
235306
}
236307
}

components/style/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ extern crate "util" as servo_util;
3838
pub use media_queries::{Device, Screen};
3939
pub use stylesheets::{Stylesheet, iter_font_face_rules};
4040
pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin};
41-
pub use selector_matching::{DeclarationBlock, matches,matches_simple_selector};
41+
pub use selector_matching::{DeclarationBlock, CommonStyleAffectingAttributes};
42+
pub use selector_matching::{CommonStyleAffectingAttributeInfo, CommonStyleAffectingAttributeMode};
43+
pub use selector_matching::{AttrIsPresentMode, AttrIsEqualMode};
44+
pub use selector_matching::{matches, matches_simple_selector, common_style_affecting_attributes};
4245
pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE,SELECTOR_WHITESPACE};
4346
pub use properties::{cascade, cascade_anonymous};
4447
pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs};

components/style/selector_matching.rs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,53 @@ fn matches_compound_selector_internal<'a,E,N>(selector: &CompoundSelector,
761761
}
762762
}
763763

764+
bitflags! {
765+
flags CommonStyleAffectingAttributes: u8 {
766+
static HiddenAttribute = 0x01,
767+
static NoWrapAttribute = 0x02,
768+
static AlignLeftAttribute = 0x04,
769+
static AlignCenterAttribute = 0x08,
770+
static AlignRightAttribute = 0x10,
771+
}
772+
}
773+
774+
pub struct CommonStyleAffectingAttributeInfo {
775+
pub atom: Atom,
776+
pub mode: CommonStyleAffectingAttributeMode,
777+
}
778+
779+
pub enum CommonStyleAffectingAttributeMode {
780+
AttrIsPresentMode(CommonStyleAffectingAttributes),
781+
AttrIsEqualMode(&'static str, CommonStyleAffectingAttributes),
782+
}
783+
784+
// NB: This must match the order in `layout::css::matching::CommonStyleAffectingAttributes`.
785+
#[inline]
786+
pub fn common_style_affecting_attributes() -> [CommonStyleAffectingAttributeInfo, ..5] {
787+
[
788+
CommonStyleAffectingAttributeInfo {
789+
atom: atom!("hidden"),
790+
mode: AttrIsPresentMode(HiddenAttribute),
791+
},
792+
CommonStyleAffectingAttributeInfo {
793+
atom: atom!("nowrap"),
794+
mode: AttrIsPresentMode(NoWrapAttribute),
795+
},
796+
CommonStyleAffectingAttributeInfo {
797+
atom: atom!("align"),
798+
mode: AttrIsEqualMode("left", AlignLeftAttribute),
799+
},
800+
CommonStyleAffectingAttributeInfo {
801+
atom: atom!("align"),
802+
mode: AttrIsEqualMode("center", AlignCenterAttribute),
803+
},
804+
CommonStyleAffectingAttributeInfo {
805+
atom: atom!("align"),
806+
mode: AttrIsEqualMode("right", AlignRightAttribute),
807+
}
808+
]
809+
}
810+
764811
/// Determines whether the given element matches the given single selector.
765812
///
766813
/// 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,
781828
}
782829

783830
NamespaceSelector(ref namespace) => {
784-
*shareable = false;
785831
let element = element.as_element();
786832
element.get_namespace() == namespace
787833
}
@@ -799,11 +845,26 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
799845
}
800846

801847
AttrExists(ref attr) => {
802-
*shareable = false;
848+
// NB(pcwalton): If you update this, remember to update the corresponding list in
849+
// `can_share_style_with()` as well.
850+
if common_style_affecting_attributes().iter().all(|common_attr_info| {
851+
!(common_attr_info.atom == attr.name && match common_attr_info.mode {
852+
AttrIsPresentMode(_) => true,
853+
AttrIsEqualMode(..) => false,
854+
})
855+
}) {
856+
*shareable = false;
857+
}
803858
element.match_attr(attr, |_| true)
804859
}
805860
AttrEqual(ref attr, ref value, case_sensitivity) => {
806-
if value.as_slice() != "DIR" {
861+
if value.as_slice() != "DIR" &&
862+
common_style_affecting_attributes().iter().all(|common_attr_info| {
863+
!(common_attr_info.atom == attr.name && match common_attr_info.mode {
864+
AttrIsEqualMode(target_value, _) => target_value == value.as_slice(),
865+
AttrIsPresentMode(_) => false,
866+
})
867+
}) {
807868
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
808869
// here because the UA style otherwise disables all style sharing completely.
809870
*shareable = false
@@ -853,15 +914,15 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
853914
element.get_link().is_some()
854915
}
855916
Link => {
856-
*shareable = false;
857917
let elem = element.as_element();
858918
match elem.get_link() {
859919
Some(url) => !url_is_visited(url),
860920
None => false,
861921
}
862922
}
863923
Visited => {
864-
*shareable = false;
924+
// NB(pcwalton): When we actually start supporting visited links, remember to update
925+
// `can_share_style_with`.
865926
let elem = element.as_element();
866927
match elem.get_link() {
867928
Some(url) => url_is_visited(url),
@@ -947,6 +1008,7 @@ fn url_is_visited(_url: &str) -> bool {
9471008
// FIXME: implement this.
9481009
// This function will probably need to take a "session"
9491010
// or something containing browsing history as an additional parameter.
1011+
// NB(pcwalton): When you implement this, remember to update `can_share_style_with`!
9501012
false
9511013
}
9521014

0 commit comments

Comments
 (0)