mirror of
https://github.com/servo/servo.git
synced 2025-07-25 08:10:21 +01:00
Auto merge of #20223 - emilio:share-xbl, r=xidorn,bholley
style: Allow to share style across elements with similar XBL bindings. Also move the checks so that the tag name checks (presumably cheaper) come earlier. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20223) <!-- Reviewable:end -->
This commit is contained in:
commit
087cf21d79
4 changed files with 48 additions and 44 deletions
|
@ -151,10 +151,10 @@ impl<'ln> NodeInfo for ServoLayoutNode<'ln> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
enum Impossible { }
|
enum Impossible { }
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub struct ShadowRoot<'lr>(Impossible, PhantomData<&'lr ()>);
|
pub struct ShadowRoot<'lr>(Impossible, PhantomData<&'lr ()>);
|
||||||
|
|
||||||
impl<'lr> TShadowRoot for ShadowRoot<'lr> {
|
impl<'lr> TShadowRoot for ShadowRoot<'lr> {
|
||||||
|
|
|
@ -321,7 +321,7 @@ fn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The ShadowRoot trait.
|
/// The ShadowRoot trait.
|
||||||
pub trait TShadowRoot : Sized + Copy + Clone {
|
pub trait TShadowRoot : Sized + Copy + Clone + PartialEq {
|
||||||
/// The concrete node type.
|
/// The concrete node type.
|
||||||
type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
|
type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
|
||||||
|
|
||||||
|
@ -390,15 +390,6 @@ pub trait TElement
|
||||||
depth
|
depth
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The style scope of this element is a node that represents which rules
|
|
||||||
/// apply to the element.
|
|
||||||
///
|
|
||||||
/// In Servo, where we don't know about Shadow DOM or XBL, the style scope
|
|
||||||
/// is always the document.
|
|
||||||
fn style_scope(&self) -> Self::ConcreteNode {
|
|
||||||
self.as_node().owner_doc().as_node()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get this node's parent element from the perspective of a restyle
|
/// Get this node's parent element from the perspective of a restyle
|
||||||
/// traversal.
|
/// traversal.
|
||||||
fn traversal_parent(&self) -> Option<Self> {
|
fn traversal_parent(&self) -> Option<Self> {
|
||||||
|
@ -748,6 +739,9 @@ pub trait TElement
|
||||||
/// The shadow root which roots the subtree this element is contained in.
|
/// The shadow root which roots the subtree this element is contained in.
|
||||||
fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;
|
fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;
|
||||||
|
|
||||||
|
/// XBL hack for style sharing. :(
|
||||||
|
fn has_same_xbl_proto_binding_as(&self, _other: Self) -> bool { true }
|
||||||
|
|
||||||
/// Return the element which we can use to look up rules in the selector
|
/// Return the element which we can use to look up rules in the selector
|
||||||
/// maps.
|
/// maps.
|
||||||
///
|
///
|
||||||
|
|
|
@ -135,6 +135,13 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
|
pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
|
||||||
|
|
||||||
|
impl<'lr> PartialEq for GeckoShadowRoot<'lr> {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 as *const _ == other.0 as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
|
impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
|
||||||
type ConcreteNode = GeckoNode<'lr>;
|
type ConcreteNode = GeckoNode<'lr>;
|
||||||
|
|
||||||
|
@ -1040,29 +1047,6 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
self.before_or_after_pseudo(/* is_before = */ false)
|
self.before_or_after_pseudo(/* is_before = */ false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure this accurately represents the rules that an element may ever
|
|
||||||
/// match, even in the native anonymous content case.
|
|
||||||
fn style_scope(&self) -> Self::ConcreteNode {
|
|
||||||
if self.implemented_pseudo_element().is_some() {
|
|
||||||
return self.closest_non_native_anonymous_ancestor().unwrap().style_scope();
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_in_native_anonymous_subtree() {
|
|
||||||
return self.as_node().owner_doc().as_node();
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.xbl_binding().is_some() || self.shadow_root().is_some() {
|
|
||||||
return self.as_node();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(parent) = self.xbl_binding_parent() {
|
|
||||||
return parent.as_node();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.as_node().owner_doc().as_node()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_html_element(&self) -> bool {
|
fn is_html_element(&self) -> bool {
|
||||||
self.namespace_id() == (structs::root::kNameSpaceID_XHTML as i32)
|
self.namespace_id() == (structs::root::kNameSpaceID_XHTML as i32)
|
||||||
|
@ -1108,8 +1092,16 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
unsafe { slots._base.mContainingShadow.mRawPtr.as_ref().map(GeckoShadowRoot) }
|
unsafe { slots._base.mContainingShadow.mRawPtr.as_ref().map(GeckoShadowRoot) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute `f` for each anonymous content child element (apart from
|
fn has_same_xbl_proto_binding_as(&self, other: Self) -> bool {
|
||||||
/// ::before and ::after) whose originating element is `self`.
|
match (self.xbl_binding(), other.xbl_binding()) {
|
||||||
|
(None, None) => true,
|
||||||
|
(Some(a), Some(b)) => {
|
||||||
|
a.0.mPrototypeBinding == b.0.mPrototypeBinding
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn each_anonymous_content_child<F>(&self, mut f: F)
|
fn each_anonymous_content_child<F>(&self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(Self),
|
F: FnMut(Self),
|
||||||
|
|
|
@ -679,12 +679,28 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that in the XBL case, we should be able to assert that the
|
// If two elements belong to different shadow trees, different rules may
|
||||||
// scopes are different, since two elements with different XBL bindings
|
// apply to them, from the respective trees.
|
||||||
// need to necessarily have different style (and thus children of them
|
if target.element.containing_shadow() != candidate.element.containing_shadow() {
|
||||||
// would never pass the parent check).
|
trace!("Miss: Different containing shadow roots");
|
||||||
if target.element.style_scope() != candidate.element.style_scope() {
|
return None;
|
||||||
trace!("Miss: Different style scopes");
|
}
|
||||||
|
|
||||||
|
// Note that in theory we shouldn't need this XBL check. However, XBL is
|
||||||
|
// absolutely broken in all sorts of ways.
|
||||||
|
//
|
||||||
|
// A style change that changes which XBL binding applies to an element
|
||||||
|
// arrives there, with the element still having the old prototype
|
||||||
|
// binding attached. And thus we try to match revalidation selectors
|
||||||
|
// with the old XBL binding, because we can't look at the new ones of
|
||||||
|
// course. And that causes us to revalidate with the wrong selectors and
|
||||||
|
// hit assertions.
|
||||||
|
//
|
||||||
|
// Other than this, we don't need anything else like the containing XBL
|
||||||
|
// binding parent or what not, since two elements with different XBL
|
||||||
|
// bindings will necessarily end up with different style.
|
||||||
|
if !target.element.has_same_xbl_proto_binding_as(candidate.element) {
|
||||||
|
trace!("Miss: Different proto bindings");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,7 +711,9 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
// shadow root, they could match different rules, due to the slot being
|
// shadow root, they could match different rules, due to the slot being
|
||||||
// assigned to yet another slot in another shadow root.
|
// assigned to yet another slot in another shadow root.
|
||||||
if target.element.assigned_slot() != candidate.element.assigned_slot() {
|
if target.element.assigned_slot() != candidate.element.assigned_slot() {
|
||||||
trace!("Miss: Different style scopes");
|
// TODO(emilio): We could have a look at whether the shadow roots
|
||||||
|
// actually have slotted rules and such.
|
||||||
|
trace!("Miss: Different assigned slots");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue