servo: Merge #20223 - style: Allow to share style across elements with similar XBL bindings (from emilio:share-xbl); r=xidorn,bholley

Also move the checks so that the tag name checks (presumably cheaper) come earlier.

Source-Repo: https://github.com/servo/servo
Source-Revision: 087cf21d79cb090c2a8b55b441e6004921b540aa

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 4dd2eb2330026ccf2c498898b75a1f7648f378ec
This commit is contained in:
Emilio Cobos Álvarez 2018-03-12 16:48:36 -04:00
Родитель 8080274aaa
Коммит 4a320a47a3
4 изменённых файлов: 48 добавлений и 44 удалений

Просмотреть файл

@ -151,10 +151,10 @@ impl<'ln> NodeInfo for ServoLayoutNode<'ln> {
}
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
enum Impossible { }
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
pub struct ShadowRoot<'lr>(Impossible, PhantomData<&'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.
pub trait TShadowRoot : Sized + Copy + Clone {
pub trait TShadowRoot : Sized + Copy + Clone + PartialEq {
/// The concrete node type.
type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
@ -390,15 +390,6 @@ pub trait TElement
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
/// traversal.
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.
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
/// maps.
///

Просмотреть файл

@ -135,6 +135,13 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
#[derive(Clone, Copy)]
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> {
type ConcreteNode = GeckoNode<'lr>;
@ -1040,29 +1047,6 @@ impl<'le> TElement for GeckoElement<'le> {
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]
fn is_html_element(&self) -> bool {
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) }
}
/// Execute `f` for each anonymous content child element (apart from
/// ::before and ::after) whose originating element is `self`.
fn has_same_xbl_proto_binding_as(&self, other: Self) -> bool {
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)
where
F: FnMut(Self),

Просмотреть файл

@ -679,12 +679,28 @@ impl<E: TElement> StyleSharingCache<E> {
return None;
}
// Note that in the XBL case, we should be able to assert that the
// scopes are different, since two elements with different XBL bindings
// need to necessarily have different style (and thus children of them
// would never pass the parent check).
if target.element.style_scope() != candidate.element.style_scope() {
trace!("Miss: Different style scopes");
// If two elements belong to different shadow trees, different rules may
// apply to them, from the respective trees.
if target.element.containing_shadow() != candidate.element.containing_shadow() {
trace!("Miss: Different containing shadow roots");
return None;
}
// 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;
}
@ -695,7 +711,9 @@ impl<E: TElement> StyleSharingCache<E> {
// shadow root, they could match different rules, due to the slot being
// assigned to yet another slot in another shadow root.
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;
}