Bug 1825825 - Simplify NAC setup. r=smaug

Make all UA widgets also NAC.

Keep the UA widget flag but break at anonymous subtree boundaries, so
that only nodes inside the UA widget directly (and not NAC from those)
get the flag.

This is important because two callers depend on this difference:

 * The style system, since we still want to match content rules from
   stylesheets in the UA widget. We also match user rules, which is a
   bit sketchy, but that was the previous behavior, will file a
   follow-up for that.

 * The reflector code, since we want the scope for UA widgets to not
   include the NAC nodes inside that UA widget. nsINode::IsInUAWidget
   got it wrong.

After this patch, ChromeOnlyAccess is equivalent to
IsInNativeAnonymousSubtree, so we should probably unify the naming.
That's left for a follow-up patch because I don't have a strong
preference.

Differential Revision: https://phabricator.services.mozilla.com/D174310
This commit is contained in:
Emilio Cobos Álvarez 2023-04-05 09:19:15 +00:00
parent ae16da3b5f
commit 641fa20731
29 changed files with 190 additions and 218 deletions

View file

@ -657,7 +657,7 @@ void DocAccessible::HandleScroll(nsINode* aTarget) {
if (!targetAcc && target->IsInNativeAnonymousSubtree()) {
// The scroll event for textareas comes from a native anonymous div. We need
// the closest non-anonymous ancestor to get the right Accessible.
target = target->GetClosestNativeAnonymousSubtreeRootParent();
target = target->GetClosestNativeAnonymousSubtreeRootParentOrHost();
targetAcc = GetAccessible(target);
}
// Regardless of our scroll timer, we need to send a cache update

View file

@ -416,11 +416,10 @@ nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) {
if (aParent.IsInNativeAnonymousSubtree()) {
SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
}
if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
}
if (IsRootOfNativeAnonymousSubtree()) {
aParent.SetMayHaveAnonymousChildren();
} else if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
}
// Set parent

View file

@ -1810,11 +1810,10 @@ nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) {
if (aParent.IsInNativeAnonymousSubtree()) {
SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
}
if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
}
if (IsRootOfNativeAnonymousSubtree()) {
aParent.SetMayHaveAnonymousChildren();
} else if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
}
if (aParent.HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR)) {
SetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR);

View file

@ -154,7 +154,7 @@ already_AddRefed<nsContentList> NS_GetContentList(nsINode* aRootNode,
NODE_FLAG_BIT(NODE_TYPE_SPECIFIC_BITS_OFFSET + (n_))
// Element-specific flags
enum {
enum : uint32_t {
// Whether this node has dirty descendants for Servo's style system.
ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO = ELEMENT_FLAG_BIT(0),
// Whether this node has dirty descendants for animation-only restyle for

View file

@ -866,7 +866,7 @@ void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
nsCOMPtr<nsIContent> content(
nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget));
if (content &&
content->GetClosestNativeAnonymousSubtreeRootParent() == parent) {
content->GetClosestNativeAnonymousSubtreeRootParentOrHost() == parent) {
aVisitor.mEventTargetAtParent = parent;
}
}

View file

@ -47,7 +47,8 @@ nsINode* RangeUtils::ComputeRootNode(nsINode* aNode) {
}
// If the node is in NAC, then the NAC parent should be the root.
if (nsINode* root = content->GetClosestNativeAnonymousSubtreeRootParent()) {
if (nsINode* root =
content->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
return root;
}
}

View file

@ -132,7 +132,7 @@ void SelectionChangeEventDispatcher::OnSelectionChange(Document* aDoc,
maybeHasSelectionChangeEventListeners) {
if (const nsFrameSelection* fs = aSel->GetFrameSelection()) {
if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
textControl = root->GetClosestNativeAnonymousSubtreeRootParent();
textControl = root->GetClosestNativeAnonymousSubtreeRootParentOrHost();
MOZ_ASSERT_IF(textControl,
textControl->IsTextControlElement() &&
!textControl->IsInNativeAnonymousSubtree());

View file

@ -10,6 +10,7 @@
#include "mozilla/dom/DocumentFragment.h"
#include "ChildIterator.h"
#include "nsContentUtils.h"
#include "nsINode.h"
#include "nsWindowSizes.h"
#include "mozilla/dom/DirectionalityUtils.h"
#include "mozilla/dom/Element.h"
@ -68,6 +69,17 @@ ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
ClearSubtreeRootPointer();
SetFlags(NODE_IS_IN_SHADOW_TREE);
if (Host()->IsInNativeAnonymousSubtree()) {
// NOTE(emilio): We could consider just propagating the
// IN_NATIVE_ANONYMOUS_SUBTREE flag (not making this an anonymous root), but
// that breaks the invariant that if two nodes have the same
// NativeAnonymousSubtreeRoot() they are in the same DOM tree, which we rely
// on a couple places and would need extra fixes.
//
// We don't hit this case for now anyways, bug 1824886 would start hitting
// it.
SetIsNativeAnonymousRoot();
}
Bind();
ExtendedDOMSlots()->mContainingShadow = this;
@ -115,6 +127,10 @@ void ShadowRoot::NodeInfoChanged(Document* aOldDoc) {
}
void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
if (aOther->IsRootOfNativeAnonymousSubtree()) {
SetIsNativeAnonymousRoot();
}
if (aOther->IsUAWidget()) {
SetIsUAWidget();
}
@ -747,7 +763,7 @@ nsINode* ShadowRoot::ImportNodeAndAppendChildAt(nsINode& aParentNode,
mozilla::ErrorResult& rv) {
MOZ_ASSERT(IsUAWidget());
if (!aParentNode.IsInUAWidget()) {
if (aParentNode.SubtreeRoot() != this) {
rv.Throw(NS_ERROR_INVALID_ARG);
return nullptr;
}
@ -765,7 +781,7 @@ nsINode* ShadowRoot::CreateElementAndAppendChildAt(nsINode& aParentNode,
mozilla::ErrorResult& rv) {
MOZ_ASSERT(IsUAWidget());
if (!aParentNode.IsInUAWidget()) {
if (aParentNode.SubtreeRoot() != this) {
rv.Throw(NS_ERROR_INVALID_ARG);
return nullptr;
}

View file

@ -222,6 +222,7 @@ class ShadowRoot final : public DocumentFragment,
void SetIsUAWidget() {
MOZ_ASSERT(!HasChildren());
SetIsNativeAnonymousRoot();
SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
}

View file

@ -16,23 +16,6 @@
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/dom/ShadowRoot.h"
inline bool nsINode::IsUAWidget() const {
auto* shadow = mozilla::dom::ShadowRoot::FromNode(this);
return shadow && shadow->IsUAWidget();
}
inline bool nsINode::IsInUAWidget() const {
if (!IsInShadowTree()) {
return false;
}
mozilla::dom::ShadowRoot* shadow = AsContent()->GetContainingShadow();
return shadow && shadow->IsUAWidget();
}
inline bool nsINode::IsRootOfChromeAccessOnlySubtree() const {
return IsRootOfNativeAnonymousSubtree() || IsUAWidget();
}
inline bool nsIContent::IsInHTMLDocument() const {
return OwnerDoc()->IsHTMLDocument();
}
@ -194,18 +177,10 @@ inline bool nsINode::IsInDesignMode() const {
// If the shadow host is not in design mode, this can never be in design
// mode. Otherwise, the content is never editable by design mode of
// composed document.
if (IsInUAWidget()) {
nsIContent* host = GetContainingShadowHost();
MOZ_DIAGNOSTIC_ASSERT(host != this);
return host && host->IsInDesignMode();
}
MOZ_ASSERT(!IsUAWidget());
// If we're in a native anonymous subtree, we should consider it with the
// host.
// composed document. If we're in a native anonymous subtree, we should
// consider it with the host.
if (IsInNativeAnonymousSubtree()) {
nsIContent* host = GetClosestNativeAnonymousSubtreeRootParent();
nsIContent* host = GetClosestNativeAnonymousSubtreeRootParentOrHost();
MOZ_DIAGNOSTIC_ASSERT(host != this);
return host && host->IsInDesignMode();
}

View file

@ -420,7 +420,7 @@ Element* nsINode::GetAnonymousRootElementOfTextEditor(
RefPtr<TextControlElement> textControlElement;
if (IsInNativeAnonymousSubtree()) {
textControlElement = TextControlElement::FromNodeOrNull(
GetClosestNativeAnonymousSubtreeRootParent());
GetClosestNativeAnonymousSubtreeRootParentOrHost());
} else {
textControlElement = TextControlElement::FromNode(this);
}
@ -474,15 +474,6 @@ nsIContent* nsINode::GetFirstChildOfTemplateOrNode() {
return GetFirstChild();
}
nsINode* nsINode::GetParentOrShadowHostNode() const {
if (mParent) {
return mParent;
}
const ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
return shadowRoot ? shadowRoot->GetHost() : nullptr;
}
nsINode* nsINode::SubtreeRoot() const {
auto RootOfNode = [](const nsINode* aStart) -> nsINode* {
const nsINode* node = aStart;
@ -797,6 +788,11 @@ std::ostream& operator<<(std::ostream& aStream, const nsINode& aNode) {
return aStream << str.get();
}
nsIContent* nsINode::DoGetShadowHost() const {
MOZ_ASSERT(IsShadowRoot());
return static_cast<const ShadowRoot*>(this)->GetHost();
}
ShadowRoot* nsINode::GetContainingShadow() const {
if (!IsInShadowTree()) {
return nullptr;
@ -3614,12 +3610,12 @@ nsINode* nsINode::GetFlattenedTreeParentNodeNonInline() const {
ParentObject nsINode::GetParentObject() const {
ParentObject p(OwnerDoc());
// Note that mReflectionScope is a no-op for chrome, and other places
// where we don't check this value.
if (ShouldUseNACScope(this)) {
p.mReflectionScope = ReflectionScope::NAC;
} else if (ShouldUseUAWidgetScope(this)) {
// Note that mReflectionScope is a no-op for chrome, and other places where we
// don't check this value.
if (ShouldUseUAWidgetScope(this)) {
p.mReflectionScope = ReflectionScope::UAWidget;
} else if (ShouldUseNACScope(this)) {
p.mReflectionScope = ReflectionScope::NAC;
}
return p;
}

View file

@ -115,7 +115,7 @@ enum class CallerType : uint32_t;
#define NODE_FLAG_BIT(n_) \
(nsWrapperCache::FlagsType(1U) << (WRAPPER_CACHE_FLAGS_BITS_USED + (n_)))
enum {
enum : uint32_t {
// This bit will be set if the node has a listener manager.
NODE_HAS_LISTENERMANAGER = NODE_FLAG_BIT(0),
@ -1034,7 +1034,19 @@ class nsINode : public mozilla::dom::EventTarget {
*/
nsINode* GetParentNode() const { return mParent; }
nsINode* GetParentOrShadowHostNode() const;
private:
nsIContent* DoGetShadowHost() const;
public:
nsINode* GetParentOrShadowHostNode() const {
if (MOZ_LIKELY(mParent)) {
return mParent;
}
// We could put this in nsIContentInlines.h or such to avoid this
// reinterpret_cast, but it doesn't seem worth it.
return IsInShadowTree() ? reinterpret_cast<nsINode*>(DoGetShadowHost())
: nullptr;
}
enum FlattenedParentType { eNotForStyle, eForStyle };
@ -1416,16 +1428,24 @@ class nsINode : public mozilla::dom::EventTarget {
/**
* If |this| or any ancestor is native anonymous, return the parent of the
* native anonymous subtree. Note that in case of nested native anonymous
* content, this returns the parent of the innermost root, not the outermost.
* content, this returns the parent or host of the innermost root, not the
* outermost.
*/
nsIContent* GetClosestNativeAnonymousSubtreeRootParent() const {
const nsIContent* root = GetClosestNativeAnonymousSubtreeRoot();
nsIContent* GetClosestNativeAnonymousSubtreeRootParentOrHost() const {
// We could put this in nsIContentInlines.h or such to avoid this
// reinterpret_cast, but it doesn't seem worth it.
const auto* root = reinterpret_cast<const nsINode*>(
GetClosestNativeAnonymousSubtreeRoot());
if (!root) {
return nullptr;
}
// We could put this in nsIContentInlines.h or such to avoid this
// reinterpret_cast, but it doesn't seem worth it.
return reinterpret_cast<const nsINode*>(root)->GetParent();
if (nsIContent* parent = root->GetParent()) {
return parent;
}
if (MOZ_UNLIKELY(root->IsInShadowTree())) {
return root->DoGetShadowHost();
}
return nullptr;
}
/**
@ -1456,20 +1476,10 @@ class nsINode : public mozilla::dom::EventTarget {
// True for native anonymous content and for content in UA widgets.
// Only nsIContent can fulfill this condition.
bool ChromeOnlyAccess() const {
return HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
NODE_HAS_BEEN_IN_UA_WIDGET);
}
bool ChromeOnlyAccess() const { return IsInNativeAnonymousSubtree(); }
const nsIContent* GetChromeOnlyAccessSubtreeRootParent() const {
if (!ChromeOnlyAccess()) {
return nullptr;
}
// We can have NAC in UA widgets, but not the other way around.
if (IsInNativeAnonymousSubtree()) {
return GetClosestNativeAnonymousSubtreeRootParent();
}
return GetContainingShadowHost();
return GetClosestNativeAnonymousSubtreeRootParentOrHost();
}
bool IsInShadowTree() const { return HasFlag(NODE_IS_IN_SHADOW_TREE); }
@ -1486,12 +1496,10 @@ class nsINode : public mozilla::dom::EventTarget {
return HasFlag(NODE_IS_NATIVE_ANONYMOUS_ROOT);
}
// Whether this node is a UA Widget ShadowRoot.
inline bool IsUAWidget() const;
// Whether this node is currently in a UA Widget Shadow tree.
inline bool IsInUAWidget() const;
// Whether this node is the root of a ChromeOnlyAccess DOM subtree.
inline bool IsRootOfChromeAccessOnlySubtree() const;
bool IsRootOfChromeAccessOnlySubtree() const {
return IsRootOfNativeAnonymousSubtree();
}
/**
* Returns true if |this| node is the closest common inclusive ancestor

View file

@ -1157,7 +1157,7 @@ bool EventStateManager::LookForAccessKeyAndExecute(
start = mAccessKeys.IndexOf(focusedElement);
if (start == -1 && focusedElement->IsInNativeAnonymousSubtree()) {
start = mAccessKeys.IndexOf(Element::FromNodeOrNull(
focusedElement->GetClosestNativeAnonymousSubtreeRootParent()));
focusedElement->GetClosestNativeAnonymousSubtreeRootParentOrHost()));
}
}
RefPtr<Element> element;
@ -1935,7 +1935,7 @@ void EventStateManager::BeginTrackingRemoteDragGesture(
mGestureDownInTextControl =
aContent && aContent->IsInNativeAnonymousSubtree() &&
TextControlElement::FromNodeOrNull(
aContent->GetClosestNativeAnonymousSubtreeRootParent());
aContent->GetClosestNativeAnonymousSubtreeRootParentOrHost());
mGestureDownDragStartData = aDragStartData;
}
@ -5887,7 +5887,8 @@ void EventStateManager::TextControlRootWillBeRemoved(
// caused by reframing aTextControlElement which may not be intended by the
// user.
if (&aTextControlElement ==
mGestureDownFrameOwner->GetClosestNativeAnonymousSubtreeRootParent()) {
mGestureDownFrameOwner
->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
mGestureDownFrameOwner = &aTextControlElement;
}
}

View file

@ -90,7 +90,7 @@ WheelHandlingUtils::GetDisregardedWheelScrollDirection(const nsIFrame* aFrame) {
}
TextControlElement* textControlElement = TextControlElement::FromNodeOrNull(
content->IsInNativeAnonymousSubtree()
? content->GetClosestNativeAnonymousSubtreeRootParent()
? content->GetClosestNativeAnonymousSubtreeRootParentOrHost()
: content);
if (!textControlElement || !textControlElement->IsSingleLineTextControl()) {
return Nothing();

View file

@ -4660,7 +4660,7 @@ void HTMLMediaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
return;
}
HTMLInputElement* el = nullptr;
if (node->IsInNativeAnonymousSubtree() || node->IsInUAWidget()) {
if (node->ChromeOnlyAccess()) {
if (node->IsHTMLElement(nsGkAtoms::input)) {
// The node is a <input type="range">
el = static_cast<HTMLInputElement*>(node);

View file

@ -4344,9 +4344,9 @@ nsresult EditorBase::HandleDropEvent(DragEvent* aDropEvent) {
// If the source node is in native anonymous tree, it must be in
// <input> or <textarea> element. If so, its TextEditor can remove it.
if (sourceNode->IsInNativeAnonymousSubtree()) {
if (RefPtr<TextControlElement> textControlElement =
TextControlElement::FromNodeOrNull(
sourceNode->GetClosestNativeAnonymousSubtreeRootParent())) {
if (RefPtr textControlElement = TextControlElement::FromNodeOrNull(
sourceNode
->GetClosestNativeAnonymousSubtreeRootParentOrHost())) {
editorToDeleteSelection = textControlElement->GetTextEditor();
}
}
@ -5296,7 +5296,7 @@ Element* EditorBase::GetExposedRoot() const {
return rootElement;
}
return Element::FromNodeOrNull(
rootElement->GetClosestNativeAnonymousSubtreeRootParent());
rootElement->GetClosestNativeAnonymousSubtreeRootParentOrHost());
}
nsresult EditorBase::DetermineCurrentDirection() {

View file

@ -1375,9 +1375,10 @@ class EditorDOMRangeBase final {
if (mStart.IsInNativeAnonymousSubtree()) {
nsIContent* parent = nullptr;
for (parent = mStart.template ContainerAs<nsIContent>()
->GetClosestNativeAnonymousSubtreeRootParent();
->GetClosestNativeAnonymousSubtreeRootParentOrHost();
parent && parent->IsInNativeAnonymousSubtree();
parent = parent->GetClosestNativeAnonymousSubtreeRootParent()) {
parent =
parent->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
}
if (MOZ_UNLIKELY(!parent)) {
return false;
@ -1387,9 +1388,10 @@ class EditorDOMRangeBase final {
if (mEnd.IsInNativeAnonymousSubtree()) {
nsIContent* parent = nullptr;
for (parent = mEnd.template ContainerAs<nsIContent>()
->GetClosestNativeAnonymousSubtreeRootParent();
->GetClosestNativeAnonymousSubtreeRootParentOrHost();
parent && parent->IsInNativeAnonymousSubtree();
parent = parent->GetClosestNativeAnonymousSubtreeRootParent()) {
parent =
parent->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
}
if (MOZ_UNLIKELY(!parent)) {
return false;

View file

@ -92,7 +92,7 @@ nsNumberControlFrame* nsNumberControlFrame::GetNumberControlFrameForSpinButton(
// be wrapped around any of the elements between aFrame and the
// nsNumberControlFrame that we're looking for (e.g. flex wrappers).
nsIContent* content = aFrame->GetContent();
auto* nacHost = content->GetClosestNativeAnonymousSubtreeRootParent();
auto* nacHost = content->GetClosestNativeAnonymousSubtreeRootParentOrHost();
if (!nacHost) {
return nullptr;
}

View file

@ -1244,7 +1244,7 @@ static bool IsMarqueeScrollbox(const nsIFrame& aScrollFrame) {
if (!aScrollFrame.GetContent()) {
return false;
}
if (MOZ_LIKELY(!aScrollFrame.GetContent()->IsInUAWidget())) {
if (MOZ_LIKELY(!aScrollFrame.GetContent()->HasBeenInUAWidget())) {
return false;
}
MOZ_ASSERT(aScrollFrame.GetParent() &&
@ -5486,7 +5486,7 @@ bool nsHTMLScrollFrame::IsForTextControlWithNoScrollbars() const {
if (!content) {
return false;
}
auto* input = content->GetClosestNativeAnonymousSubtreeRootParent();
auto* input = content->GetClosestNativeAnonymousSubtreeRootParentOrHost();
return input && input->IsHTMLElement(nsGkAtoms::input);
}

View file

@ -2389,7 +2389,7 @@ int16_t nsIFrame::DetermineDisplaySelection() {
static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
NS_ENSURE_TRUE(aContent, nullptr);
while (aContent && aContent->IsInNativeAnonymousSubtree()) {
aContent = aContent->GetClosestNativeAnonymousSubtreeRootParent();
aContent = aContent->GetClosestNativeAnonymousSubtreeRootParentOrHost();
}
NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
return aContent ? aContent->GetAsElementOrParentElement() : nullptr;

View file

@ -1857,8 +1857,8 @@ Result<Loader::LoadSheetResult, nsresult> Loader::LoadStyleLink(
if (!aInfo.mContent) {
return false;
}
const bool privilegedShadowTree = aInfo.mContent->IsInUAWidget() ||
(aInfo.mContent->IsInShadowTree() &&
const bool privilegedShadowTree = aInfo.mContent->IsInShadowTree() &&
(aInfo.mContent->ChromeOnlyAccess() ||
aInfo.mContent->IsInChromeDocument());
if (!privilegedShadowTree) {
return false;

View file

@ -388,10 +388,10 @@ pub trait TElement:
true
}
/// Whether this element should match user and author rules.
/// Whether this element should match user and content rules.
///
/// We use this for Native Anonymous Content in Gecko.
fn matches_user_and_author_rules(&self) -> bool {
fn matches_user_and_content_rules(&self) -> bool {
true
}
@ -651,11 +651,6 @@ pub trait TElement:
false
}
/// Returns true if this element is in a native anonymous subtree.
fn is_in_native_anonymous_subtree(&self) -> bool {
false
}
/// Returns the pseudo-element implemented by this element, if any.
///
/// Gecko traverses pseudo-elements during the style traversal, and we need
@ -791,11 +786,8 @@ pub trait TElement:
use crate::rule_collector::containing_shadow_ignoring_svg_use;
let target = self.rule_hash_target();
if !target.matches_user_and_author_rules() {
return false;
}
let mut doc_rules_apply = true;
let matches_user_and_content_rules = target.matches_user_and_content_rules();
let mut doc_rules_apply = matches_user_and_content_rules;
// Use the same rules to look for the containing host as we do for rule
// collection.
@ -843,9 +835,9 @@ pub trait TElement:
},
None => {
// TODO(emilio): Should probably distinguish with
// MatchesDocumentRules::{No,Yes,IfPart} or
// something so that we could skip some work.
doc_rules_apply = true;
// MatchesDocumentRules::{No,Yes,IfPart} or something so that we could
// skip some work.
doc_rules_apply = matches_user_and_content_rules;
break;
},
}

View file

@ -325,32 +325,34 @@ impl<'ln> GeckoNode<'ln> {
#[inline]
fn is_in_shadow_tree(&self) -> bool {
use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
self.flags() & (NODE_IS_IN_SHADOW_TREE as u32) != 0
self.flags() & NODE_IS_IN_SHADOW_TREE != 0
}
/// WARNING: This logic is duplicated in Gecko's FlattenedTreeParentIsParent.
/// Make sure to mirror any modifications in both places.
/// Returns true if we know for sure that `flattened_tree_parent` and `parent_node` return the
/// same thing.
///
/// TODO(emilio): Measure and consider not doing this fast-path, it's only a function call and
/// from profiles it seems that keeping this fast path makes the compiler not inline
/// `flattened_tree_parent` as a whole, so we're not gaining much either.
#[inline]
fn flattened_tree_parent_is_parent(&self) -> bool {
use crate::gecko_bindings::structs::*;
let flags = self.flags();
// FIXME(emilio): The shadow tree condition seems it shouldn't be needed
// anymore, if we check for slots.
if self.is_in_shadow_tree() {
let parent = match self.parent_node() {
Some(p) => p,
None => return true,
};
if parent.is_shadow_root() {
return false;
}
let parent = unsafe { self.0.mParent.as_ref() }.map(GeckoNode);
let parent_el = parent.and_then(|p| p.as_element());
if flags & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0 &&
parent_el.map_or(false, |el| el.is_root())
{
return false;
}
if let Some(parent) = parent_el {
if parent.shadow_root().is_some() {
if let Some(parent) = parent.as_element() {
if flags & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0 && parent.is_root() {
return false;
}
if parent.shadow_root().is_some() || parent.is_html_slot_element() {
return false;
}
}
@ -360,10 +362,6 @@ impl<'ln> GeckoNode<'ln> {
#[inline]
fn flattened_tree_parent(&self) -> Option<Self> {
// TODO(emilio): Measure and consider not doing this fast-path and take
// always the common path, it's only a function call and from profiles
// it seems that keeping this fast path makes the compiler not inline
// `flattened_tree_parent`.
if self.flattened_tree_parent_is_parent() {
debug_assert_eq!(
unsafe {
@ -374,12 +372,10 @@ impl<'ln> GeckoNode<'ln> {
self.parent_node(),
"Fast path stopped holding!"
);
return self.parent_node();
}
// NOTE(emilio): If this call is too expensive, we could manually
// inline more aggressively.
// NOTE(emilio): If this call is too expensive, we could manually inline more aggressively.
unsafe {
bindings::Gecko_GetFlattenedTreeParentNode(self.0)
.as_ref()
@ -650,20 +646,6 @@ impl<'le> GeckoElement<'le> {
snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class"))
}
#[inline]
fn closest_anon_subtree_root_parent(&self) -> Option<Self> {
debug_assert!(self.is_in_native_anonymous_subtree());
let mut current = *self;
loop {
if current.is_root_of_native_anonymous_subtree() {
return current.traversal_parent();
}
current = current.traversal_parent()?;
}
}
#[inline]
fn may_have_anonymous_children(&self) -> bool {
self.as_node()
@ -690,13 +672,13 @@ impl<'le> GeckoElement<'le> {
/// Returns true if this element has descendants for lazy frame construction.
#[inline]
pub fn descendants_need_frames(&self) -> bool {
self.flags() & (NODE_DESCENDANTS_NEED_FRAMES as u32) != 0
self.flags() & NODE_DESCENDANTS_NEED_FRAMES != 0
}
/// Returns true if this element needs lazy frame construction.
#[inline]
pub fn needs_frame(&self) -> bool {
self.flags() & (NODE_NEEDS_FRAME as u32) != 0
self.flags() & NODE_NEEDS_FRAME != 0
}
/// Returns a reference to the DOM slots for this Element, if they exist.
@ -754,7 +736,7 @@ impl<'le> GeckoElement<'le> {
fn has_properties(&self) -> bool {
use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES;
(self.flags() & NODE_HAS_PROPERTIES as u32) != 0
self.flags() & NODE_HAS_PROPERTIES != 0
}
#[inline]
@ -822,7 +804,7 @@ impl<'le> GeckoElement<'le> {
#[inline]
fn is_root_of_native_anonymous_subtree(&self) -> bool {
use crate::gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT;
return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0;
return self.flags() & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0;
}
/// Returns true if this node is the shadow root of an use-element shadow tree.
@ -905,19 +887,19 @@ fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
use crate::gecko_bindings::structs::*;
let mut gecko_flags = 0u32;
if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR as u32;
gecko_flags |= NODE_HAS_SLOW_SELECTOR;
}
if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS as u32;
gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS;
}
if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) {
gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF as u32;
gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF;
}
if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR as u32;
gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR;
}
if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {
gecko_flags |= NODE_HAS_EMPTY_SELECTOR as u32;
gecko_flags |= NODE_HAS_EMPTY_SELECTOR;
}
gecko_flags
@ -974,8 +956,7 @@ impl<'le> TElement for GeckoElement<'le> {
// StyleChildrenIterator::IsNeeded does, except that it might return
// true if we used to (but no longer) have anonymous content from
// ::before/::after, or nsIAnonymousContentCreators.
if self.is_in_native_anonymous_subtree() ||
self.is_html_slot_element() ||
if self.is_html_slot_element() ||
self.shadow_root().is_some() ||
self.may_have_anonymous_children()
{
@ -1296,52 +1277,52 @@ impl<'le> TElement for GeckoElement<'le> {
#[inline]
fn has_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HAS_SNAPSHOT as u32) != 0
self.flags() & ELEMENT_HAS_SNAPSHOT != 0
}
#[inline]
fn handled_snapshot(&self) -> bool {
self.flags() & (ELEMENT_HANDLED_SNAPSHOT as u32) != 0
self.flags() & ELEMENT_HANDLED_SNAPSHOT != 0
}
unsafe fn set_handled_snapshot(&self) {
debug_assert!(self.has_data());
self.set_flags(ELEMENT_HANDLED_SNAPSHOT as u32)
self.set_flags(ELEMENT_HANDLED_SNAPSHOT)
}
#[inline]
fn has_dirty_descendants(&self) -> bool {
self.flags() & (ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
self.flags() & ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO != 0
}
unsafe fn set_dirty_descendants(&self) {
debug_assert!(self.has_data());
debug!("Setting dirty descendants: {:?}", self);
self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
}
unsafe fn unset_dirty_descendants(&self) {
self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
}
#[inline]
fn has_animation_only_dirty_descendants(&self) -> bool {
self.flags() & (ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
self.flags() & ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO != 0
}
unsafe fn set_animation_only_dirty_descendants(&self) {
self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
}
unsafe fn unset_animation_only_dirty_descendants(&self) {
self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
}
unsafe fn clear_descendant_bits(&self) {
self.unset_flags(
ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
NODE_DESCENDANTS_NEED_FRAMES as u32,
ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO |
ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO |
NODE_DESCENDANTS_NEED_FRAMES,
)
}
@ -1349,21 +1330,19 @@ impl<'le> TElement for GeckoElement<'le> {
self.state().intersects(ElementState::VISITED)
}
/// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree.
/// We want to match rules from the same tree in all cases, except for native anonymous content
/// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or
/// pseudo-elements).
#[inline]
fn is_in_native_anonymous_subtree(&self) -> bool {
use crate::gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
}
#[inline]
fn matches_user_and_author_rules(&self) -> bool {
!self.is_in_native_anonymous_subtree()
fn matches_user_and_content_rules(&self) -> bool {
use crate::gecko_bindings::structs::{NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE, NODE_HAS_BEEN_IN_UA_WIDGET};
let flags = self.flags();
(flags & NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) == 0 || (flags & NODE_HAS_BEEN_IN_UA_WIDGET) != 0
}
#[inline]
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
if !self.is_in_native_anonymous_subtree() {
if self.matches_user_and_content_rules() {
return None;
}
@ -1371,8 +1350,7 @@ impl<'le> TElement for GeckoElement<'le> {
return None;
}
let pseudo_type = unsafe { bindings::Gecko_GetImplementedPseudo(self.0) };
PseudoElement::from_pseudo_type(pseudo_type)
PseudoElement::from_pseudo_type(unsafe { bindings::Gecko_GetImplementedPseudo(self.0) })
}
#[inline]
@ -1396,10 +1374,10 @@ impl<'le> TElement for GeckoElement<'le> {
unsafe fn clear_data(&self) {
let ptr = self.0.mServoData.get();
self.unset_flags(
ELEMENT_HAS_SNAPSHOT as u32 |
ELEMENT_HANDLED_SNAPSHOT as u32 |
ELEMENT_HAS_SNAPSHOT |
ELEMENT_HANDLED_SNAPSHOT |
structs::Element_kAllServoDescendantBits |
NODE_NEEDS_FRAME as u32,
NODE_NEEDS_FRAME,
);
if !ptr.is_null() {
debug!("Dropping ElementData for {:?}", self);
@ -1826,7 +1804,15 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
#[inline]
fn pseudo_element_originating_element(&self) -> Option<Self> {
debug_assert!(self.is_pseudo_element());
self.closest_anon_subtree_root_parent()
debug_assert!(!self.matches_user_and_content_rules());
let mut current = *self;
loop {
if current.is_root_of_native_anonymous_subtree() {
return current.traversal_parent();
}
current = current.traversal_parent()?;
}
}
#[inline]
@ -1977,6 +1963,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
.as_node()
.parent_node()
.map_or(false, |p| p.is_document()));
// XXX this should always return true at this point, shouldn't it?
unsafe { bindings::Gecko_IsRootElement(self.0) }
}
@ -2102,7 +2089,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
}
true
},
NonTSPseudoClass::MozNativeAnonymous => self.is_in_native_anonymous_subtree(),
NonTSPseudoClass::MozNativeAnonymous => !self.matches_user_and_content_rules(),
NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(),
NonTSPseudoClass::MozTableBorderNonzero => unsafe {
bindings::Gecko_IsTableBorderNonzero(self.0)

View file

@ -73,7 +73,7 @@ where
rule_inclusion: RuleInclusion,
rules: &'a mut ApplicableDeclarationList,
context: &'a mut MatchingContext<'b, E::Impl>,
matches_user_and_author_rules: bool,
matches_user_and_content_rules: bool,
matches_document_author_rules: bool,
in_sort_scope: bool,
}
@ -102,7 +102,7 @@ where
MatchingMode::Normal => element.rule_hash_target(),
};
let matches_user_and_author_rules = rule_hash_target.matches_user_and_author_rules();
let matches_user_and_content_rules = rule_hash_target.matches_user_and_content_rules();
// Gecko definitely has pseudo-elements with style attributes, like
// ::-moz-color-swatch.
@ -123,8 +123,8 @@ where
rule_inclusion,
context,
rules,
matches_user_and_author_rules,
matches_document_author_rules: matches_user_and_author_rules,
matches_user_and_content_rules,
matches_document_author_rules: matches_user_and_content_rules,
in_sort_scope: false,
}
}
@ -179,7 +179,7 @@ where
}
fn collect_user_rules(&mut self) {
if !self.matches_user_and_author_rules {
if !self.matches_user_and_content_rules {
return;
}
@ -257,7 +257,7 @@ where
while let Some(slot) = current {
debug_assert!(
self.matches_user_and_author_rules,
self.matches_user_and_content_rules,
"We should not slot NAC anywhere"
);
slots.push(slot);
@ -292,7 +292,7 @@ where
}
fn collect_rules_from_containing_shadow_tree(&mut self) {
if !self.matches_user_and_author_rules {
if !self.matches_user_and_content_rules {
return;
}
@ -341,11 +341,6 @@ where
None => return,
};
debug_assert!(
self.matches_user_and_author_rules,
"NAC should not be a shadow host"
);
let style_data = match shadow.style_data() {
Some(d) => d,
None => return,

View file

@ -584,8 +584,8 @@ impl<E: TElement> StyleSharingCache<E> {
},
};
if element.is_in_native_anonymous_subtree() {
debug!("Failing to insert into the cache: NAC");
if !element.matches_user_and_content_rules() {
debug!("Failing to insert into the cache: no tree rules:");
return;
}
@ -674,8 +674,8 @@ impl<E: TElement> StyleSharingCache<E> {
return None;
}
if target.is_in_native_anonymous_subtree() {
debug!("{:?} Cannot share style: NAC", target.element);
if !target.matches_user_and_content_rules() {
debug!("{:?} Cannot share style: content rules", target.element);
return None;
}
@ -699,7 +699,7 @@ impl<E: TElement> StyleSharingCache<E> {
nth_index_cache: &mut NthIndexCache,
shared_context: &SharedStyleContext,
) -> Option<ResolvedElementStyles> {
debug_assert!(!target.is_in_native_anonymous_subtree());
debug_assert!(target.matches_user_and_content_rules());
// Check that we have the same parent, or at least that the parents
// share styles and permit sharing across their children. The latter
@ -762,8 +762,8 @@ impl<E: TElement> StyleSharingCache<E> {
return None;
}
if target.matches_user_and_author_rules() !=
candidate.element.matches_user_and_author_rules()
if target.matches_user_and_content_rules() !=
candidate.element.matches_user_and_content_rules()
{
trace!("Miss: User and Author Rules");
return None;

View file

@ -219,7 +219,7 @@ where
) -> PrimaryStyle {
// Before doing the cascade, check the sharing cache and see if we can
// reuse the style via rule node identity.
let may_reuse = !self.element.is_in_native_anonymous_subtree() &&
let may_reuse = self.element.matches_user_and_content_rules() &&
parent_style.is_some() &&
inputs.rules.is_some();

View file

@ -422,7 +422,7 @@ pub fn recalc_style_at<E, D, F>(
if let Some(restyle_kind) = restyle_kind {
child_restyle_requirement = compute_style(traversal_data, context, element, data, restyle_kind);
if element.is_in_native_anonymous_subtree() {
if !element.matches_user_and_content_rules() {
// We must always cascade native anonymous subtrees, since they
// may have pseudo-elements underneath that would inherit from the
// closest non-NAC ancestor instead of us.

View file

@ -65,9 +65,9 @@ NS_IMPL_CYCLE_COLLECTION(nsFind)
# define DEBUG_FIND_PRINTF(...) /* nothing */
#endif
static nsIContent& AnonymousSubtreeRootParent(const nsINode& aNode) {
static nsIContent& AnonymousSubtreeRootParentOrHost(const nsINode& aNode) {
MOZ_ASSERT(aNode.IsInNativeAnonymousSubtree());
return *aNode.GetClosestNativeAnonymousSubtreeRootParent();
return *aNode.GetClosestNativeAnonymousSubtreeRootParentOrHost();
}
static void DumpNode(const nsINode* aNode) {
@ -163,8 +163,8 @@ static bool IsVisibleNode(const nsINode* aNode) {
static bool ShouldFindAnonymousContent(const nsIContent& aContent) {
MOZ_ASSERT(aContent.IsInNativeAnonymousSubtree());
nsIContent& parent = AnonymousSubtreeRootParent(aContent);
if (nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(&parent)) {
nsIContent& host = AnonymousSubtreeRootParentOrHost(aContent);
if (nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(&host)) {
if (formControl->IsTextControl(/* aExcludePassword = */ true)) {
// Only editable NAC in textfields should be findable. That is, we want to
// find "bar" in `<input value="bar">`, but not in `<input

View file

@ -322,7 +322,7 @@ nsBaseDragService::InvokeDragSession(
mIsDraggingTextInTextControl =
mSourceNode->IsInNativeAnonymousSubtree() &&
TextControlElement::FromNodeOrNull(
mSourceNode->GetClosestNativeAnonymousSubtreeRootParent());
mSourceNode->GetClosestNativeAnonymousSubtreeRootParentOrHost());
mContentPolicyType = aContentPolicyType;
mEndDragPoint = LayoutDeviceIntPoint(0, 0);