Skip to content
Permalink
Browse files

style: Fix cascade order of shadow parts.

This moves the shadow cascade order into the cascade level, and refactors the
code a bit for that.

Differential Revision: https://phabricator.services.mozilla.com/D49988
  • Loading branch information
emilio committed Nov 14, 2019
1 parent 28110c0 commit 349492b5e2754e2cff9d0a353cf0e34adb7d5772
@@ -5,11 +5,10 @@
//! Applicable declarations management.

use crate::properties::PropertyDeclarationBlock;
use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder, StyleSource};
use crate::rule_tree::{CascadeLevel, StyleSource};
use crate::shared_lock::Locked;
use servo_arc::Arc;
use smallvec::SmallVec;
use std::fmt::{self, Debug};

/// List of applicable declarations. This is a transient structure that shuttles
/// declarations between selector matching and inserting into the rule tree, and
@@ -20,75 +19,27 @@ use std::fmt::{self, Debug};
/// However, it may depend a lot on workload, and stack space is cheap.
pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;

/// Blink uses 18 bits to store source order, and does not check overflow [1].
/// That's a limit that could be reached in realistic webpages, so we use
/// 24 bits and enforce defined behavior in the overflow case.
///
/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/
/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
const SOURCE_ORDER_SHIFT: usize = 0;
const SOURCE_ORDER_BITS: usize = 24;
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT;

/// We store up-to-15 shadow order levels.
///
/// You'd need an element slotted across 16 components with ::slotted rules to
/// trigger this as of this writing, which looks... Unlikely.
const SHADOW_CASCADE_ORDER_SHIFT: usize = SOURCE_ORDER_BITS;
const SHADOW_CASCADE_ORDER_BITS: usize = 4;
const SHADOW_CASCADE_ORDER_MAX: u8 = (1 << SHADOW_CASCADE_ORDER_BITS) - 1;
const SHADOW_CASCADE_ORDER_MASK: u32 =
(SHADOW_CASCADE_ORDER_MAX as u32) << SHADOW_CASCADE_ORDER_SHIFT;

const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS + SHADOW_CASCADE_ORDER_BITS;
const CASCADE_LEVEL_BITS: usize = 4;
const CASCADE_LEVEL_MAX: u8 = (1 << CASCADE_LEVEL_BITS) - 1;
const CASCADE_LEVEL_MASK: u32 = (CASCADE_LEVEL_MAX as u32) << CASCADE_LEVEL_SHIFT;

/// Stores the source order of a block, the cascade level it belongs to, and the
/// counter needed to handle Shadow DOM cascade order properly.
#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)]
struct ApplicableDeclarationBits(u32);
///
/// FIXME(emilio): Optimize storage.
#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq, Debug)]
struct ApplicableDeclarationBits {
source_order: u32,
cascade_level: CascadeLevel,
}

impl ApplicableDeclarationBits {
fn new(
source_order: u32,
cascade_level: CascadeLevel,
shadow_cascade_order: ShadowCascadeOrder,
) -> Self {
debug_assert!(
cascade_level as u8 <= CASCADE_LEVEL_MAX,
"Gotta find more bits!"
);
let mut bits = ::std::cmp::min(source_order, SOURCE_ORDER_MAX);
bits |= ((shadow_cascade_order & SHADOW_CASCADE_ORDER_MAX) as u32) <<
SHADOW_CASCADE_ORDER_SHIFT;
bits |= (cascade_level as u8 as u32) << CASCADE_LEVEL_SHIFT;
ApplicableDeclarationBits(bits)
fn new(source_order: u32, cascade_level: CascadeLevel) -> Self {
Self { source_order, cascade_level }
}

fn source_order(&self) -> u32 {
(self.0 & SOURCE_ORDER_MASK) >> SOURCE_ORDER_SHIFT
}

fn shadow_cascade_order(&self) -> ShadowCascadeOrder {
((self.0 & SHADOW_CASCADE_ORDER_MASK) >> SHADOW_CASCADE_ORDER_SHIFT) as ShadowCascadeOrder
self.source_order
}

fn level(&self) -> CascadeLevel {
let byte = ((self.0 & CASCADE_LEVEL_MASK) >> CASCADE_LEVEL_SHIFT) as u8;
unsafe { CascadeLevel::from_byte(byte) }
}
}

impl Debug for ApplicableDeclarationBits {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ApplicableDeclarationBits")
.field("source_order", &self.source_order())
.field("shadow_cascade_order", &self.shadow_cascade_order())
.field("level", &self.level())
.finish()
self.cascade_level
}
}

@@ -119,7 +70,7 @@ impl ApplicableDeclarationBlock {
) -> Self {
ApplicableDeclarationBlock {
source: StyleSource::from_declarations(declarations),
bits: ApplicableDeclarationBits::new(0, level, 0),
bits: ApplicableDeclarationBits::new(0, level),
specificity: 0,
}
}
@@ -131,11 +82,10 @@ impl ApplicableDeclarationBlock {
order: u32,
level: CascadeLevel,
specificity: u32,
shadow_cascade_order: ShadowCascadeOrder,
) -> Self {
ApplicableDeclarationBlock {
source,
bits: ApplicableDeclarationBits::new(order, level, shadow_cascade_order),
bits: ApplicableDeclarationBits::new(order, level),
specificity,
}
}
@@ -155,9 +105,8 @@ impl ApplicableDeclarationBlock {
/// Convenience method to consume self and return the right thing for the
/// rule tree to iterate over.
#[inline]
pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel, ShadowCascadeOrder) {
pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel) {
let level = self.level();
let cascade_order = self.bits.shadow_cascade_order();
(self.source, level, cascade_order)
(self.source, level)
}
}
@@ -140,6 +140,12 @@ impl PseudoElement {
*self == PseudoElement::FieldsetContent
}

/// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
#[inline]
pub fn is_color_swatch(&self) -> bool {
*self == PseudoElement::MozColorSwatch
}

/// Whether this pseudo-element is lazily-cascaded.
#[inline]
pub fn is_lazy(&self) -> bool {
@@ -137,12 +137,12 @@ trait PrivateMatchMethods: TElement {
if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
let style_attribute = self.style_attribute();
result |= replace_rule_node(
CascadeLevel::StyleAttributeNormal,
CascadeLevel::same_tree_author_normal(),
style_attribute,
primary_rules,
);
result |= replace_rule_node(
CascadeLevel::StyleAttributeImportant,
CascadeLevel::same_tree_author_important(),
style_attribute,
primary_rules,
);
@@ -346,16 +346,10 @@ fn should_ignore_declaration_when_ignoring_document_colors(
return false;
}

let is_style_attribute = matches!(
cascade_level,
CascadeLevel::StyleAttributeNormal | CascadeLevel::StyleAttributeImportant
);

// Don't override colors on pseudo-element's style attributes. The
// background-color on ::-moz-color-swatch is an example. Those are set
// as an author style (via the style attribute), but it's pretty
// important for it to show up for obvious reasons :)
if pseudo.is_some() && is_style_attribute {
// Don't override background-color on ::-moz-color-swatch. It is set as an
// author style (via the style attribute), but it's pretty important for it
// to show up for obvious reasons :)
if pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor {
return false;
}

@@ -79,7 +79,6 @@ where
rules: &'a mut ApplicableDeclarationList,
context: &'a mut MatchingContext<'b, E::Impl>,
flags_setter: &'a mut F,
shadow_cascade_order: ShadowCascadeOrder,
matches_user_and_author_rules: bool,
matches_document_author_rules: bool,
}
@@ -132,7 +131,6 @@ where
context,
flags_setter,
rules,
shadow_cascade_order: 0,
matches_user_and_author_rules,
matches_document_author_rules: matches_user_and_author_rules,
}
@@ -142,7 +140,7 @@ where
let cascade_level = match origin {
Origin::UserAgent => CascadeLevel::UANormal,
Origin::User => CascadeLevel::UserNormal,
Origin::Author => CascadeLevel::SameTreeAuthorNormal,
Origin::Author => CascadeLevel::same_tree_author_normal(),
};

let cascade_data = self.stylist.cascade_data().borrow_for_origin(origin);
@@ -198,7 +196,6 @@ where
) {
debug_assert!(shadow_host.shadow_root().is_some());
self.collect_rules_internal(Some(shadow_host), map, cascade_level);
self.shadow_cascade_order += 1;
}

#[inline]
@@ -212,7 +209,6 @@ where
let rule_hash_target = self.rule_hash_target;
let rules = &mut self.rules;
let flags_setter = &mut self.flags_setter;
let shadow_cascade_order = self.shadow_cascade_order;
let start = rules.len();
self.context.with_shadow_host(shadow_host, |context| {
map.get_all_matching_rules(
@@ -222,28 +218,35 @@ where
context,
flags_setter,
cascade_level,
shadow_cascade_order,
);
});
sort_rules_from(rules, start);
}

/// Collects the rules for the ::slotted pseudo-element.
fn collect_slotted_rules(&mut self) {
/// Collects the rules for the ::slotted pseudo-element and the :host
/// pseudo-class.
fn collect_host_and_slotted_rules(&mut self) {
let mut slots = SmallVec::<[_; 3]>::new();
let mut current = self.rule_hash_target.assigned_slot();
let mut shadow_cascade_order = ShadowCascadeOrder::for_outermost_shadow_tree();

while let Some(slot) = current {
debug_assert!(
self.matches_user_and_author_rules,
"We should not slot NAC anywhere"
);
slots.push(slot);
current = slot.assigned_slot();
shadow_cascade_order.dec();
}

self.collect_host_rules(shadow_cascade_order);

// Match slotted rules in reverse order, so that the outer slotted rules
// come before the inner rules (and thus have less priority).
for slot in slots.iter().rev() {
shadow_cascade_order.inc();

let shadow = slot.containing_shadow().unwrap();
let data = match shadow.style_data() {
Some(d) => d,
@@ -253,10 +256,11 @@ where
Some(r) => r,
None => continue,
};

self.collect_rules_in_shadow_tree(
shadow.host(),
slotted_rules,
CascadeLevel::InnerShadowNormal,
CascadeLevel::AuthorNormal { shadow_cascade_order },
);
}
}
@@ -277,12 +281,12 @@ where
let cascade_data = containing_shadow.style_data();
let host = containing_shadow.host();
if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) {
self.collect_rules_in_shadow_tree(host, map, CascadeLevel::SameTreeAuthorNormal);
self.collect_rules_in_shadow_tree(host, map, CascadeLevel::same_tree_author_normal());
}
}

/// Collects the rules for the :host pseudo-class.
fn collect_host_rules(&mut self) {
fn collect_host_rules(&mut self, shadow_cascade_order: ShadowCascadeOrder) {
let shadow = match self.rule_hash_target.shadow_root() {
Some(s) => s,
None => return,
@@ -307,7 +311,7 @@ where
self.collect_rules_in_shadow_tree(
rule_hash_target,
host_rules,
CascadeLevel::InnerShadowNormal,
CascadeLevel::AuthorNormal { shadow_cascade_order },
);
}

@@ -342,21 +346,18 @@ where
.part_rules(self.pseudo_element),
};

// TODO(emilio): SameTreeAuthorNormal is a bit of a lie here, we may
// need an OuterTreeAuthorNormal cascade level or such, and change the
// cascade order, if we allow to forward parts to even outer trees.
//
// Though the current thing kinda works because we apply them after
// the outer tree, so as long as we don't allow forwarding we're
// good.
// TODO(emilio): Cascade order will need to increment for each tree when
// we implement forwarding.
let shadow_cascade_order = ShadowCascadeOrder::for_innermost_containing_tree();
if let Some(part_rules) = part_rules {
let containing_host = containing_shadow.map(|s| s.host());
let element = self.element;
let rule_hash_target = self.rule_hash_target;
let rules = &mut self.rules;
let flags_setter = &mut self.flags_setter;
let shadow_cascade_order = self.shadow_cascade_order;
let cascade_level = CascadeLevel::SameTreeAuthorNormal;
let cascade_level = CascadeLevel::AuthorNormal {
shadow_cascade_order,
};
let start = rules.len();
self.context.with_shadow_host(containing_host, |context| {
rule_hash_target.each_part(|p| {
@@ -368,7 +369,6 @@ where
context,
flags_setter,
cascade_level,
shadow_cascade_order,
);
}
});
@@ -382,7 +382,7 @@ where
self.rules
.push(ApplicableDeclarationBlock::from_declarations(
sa.clone_arc(),
CascadeLevel::StyleAttributeNormal,
CascadeLevel::same_tree_author_normal(),
));
}
}
@@ -433,12 +433,11 @@ where
if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No {
return;
}
self.collect_host_rules();
self.collect_slotted_rules();
self.collect_host_and_slotted_rules();
self.collect_normal_rules_from_containing_shadow_tree();
self.collect_document_author_rules();
self.collect_part_rules();
self.collect_style_attribute();
self.collect_part_rules();
self.collect_animation_rules();
}
}

0 comments on commit 349492b

Please sign in to comment.
You can’t perform that action at this time.