--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1729,22 +1729,19 @@ Element::BindToTree(nsIDocument* aDocume
// Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert
nsContentUtils::TryToUpgradeElement(this);
}
}
}
// Propagate scoped style sheet tracking bit.
if (mParent->IsContent()) {
- nsIContent* parent;
- ShadowRoot* shadowRootParent = ShadowRoot::FromNode(mParent);
- if (shadowRootParent) {
+ nsIContent* parent = mParent->AsContent();
+ if (ShadowRoot* shadowRootParent = ShadowRoot::FromNode(parent)) {
parent = shadowRootParent->GetHost();
- } else {
- parent = mParent->AsContent();
}
bool inStyleScope = parent->IsElementInStyleScope();
SetIsElementInStyleScope(inStyleScope);
SetIsElementInStyleScopeFlagOnShadowTree(inStyleScope);
}
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1312,19 +1312,17 @@ public:
ErrorResult& aError);
ShadowRoot* GetShadowRootByMode() const;
void SetSlot(const nsAString& aName, ErrorResult& aError);
void GetSlot(nsAString& aName);
// [deprecated] Shadow DOM v0
already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
- // FIXME(emilio): Should just shadow GetShadowRoot(), that way we get the fast
- // version by default everywhere we already have an Element...
- ShadowRoot* FastGetShadowRoot() const
+ ShadowRoot* GetShadowRoot() const
{
const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
return slots ? slots->mShadowRoot.get() : nullptr;
}
private:
void ScrollIntoView(const ScrollIntoViewOptions &aOptions);
public:
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -179,118 +179,45 @@ nsIContent::GetAssignedSlotByMode() cons
if (GetParent()->GetShadowRoot()->IsClosed()) {
return nullptr;
}
return slot;
}
nsINode*
-nsIContent::GetFlattenedTreeParentForMaybeAssignedNode() const
-{
- if (HTMLSlotElement* assignedSlot = GetAssignedSlot()) {
- return assignedSlot;
- }
-
- HTMLSlotElement* parentSlot = HTMLSlotElement::FromContent(GetParent());
- if (!parentSlot) {
- return nullptr;
- }
-
- // If this is not an unassigned node, then it must be a fallback content.
- MOZ_ASSERT(parentSlot->AssignedNodes().IsEmpty());
-
- return parentSlot;
-}
-
-nsINode*
-nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const
+nsIContent::GetFlattenedTreeParentForDocumentElementNAC() const
{
- nsINode* parentNode = GetParentNode();
- if (!parentNode || !parentNode->IsContent()) {
- MOZ_ASSERT(!parentNode || parentNode == OwnerDoc());
- return parentNode;
- }
- nsIContent* parent = parentNode->AsContent();
+ MOZ_ASSERT(IsRootOfNativeAnonymousSubtree());
+ MOZ_ASSERT(GetParent());
+ MOZ_ASSERT(GetParent() == OwnerDoc()->GetRootElement());
+ MOZ_ASSERT(!IsGeneratedContentContainerForAfter());
+ MOZ_ASSERT(!IsGeneratedContentContainerForBefore());
- // When getting the flattened tree parent for style, we return null
- // for any "document level" native anonymous content subtree root.
- // This is NAC generated by an ancestor frame of the document element's
- // primary frame, and includes scrollbar elements created by the root
- // scroll frame, and the "custom content container" and accessible caret
- // generated by the nsCanvasFrame. We distinguish document level NAC
- // from NAC generated by the root element's primary frame below.
- if (aType == eForStyle &&
- IsRootOfNativeAnonymousSubtree() &&
- OwnerDoc()->GetRootElement() == parent) {
- // First, check if this is generated ::before/::after content for the root.
- // If it is, we know what to do.
- if (IsGeneratedContentContainerForBefore() ||
- IsGeneratedContentContainerForAfter()) {
- return parent;
- }
-
- AutoTArray<nsIContent*, 8> rootElementNAC;
- nsContentUtils::AppendNativeAnonymousChildren(
- parent, rootElementNAC, nsIContent::eSkipDocumentLevelNativeAnonymousContent);
- bool isDocLevelNAC = !rootElementNAC.Contains(this);
+ nsIContent* parent = GetParent();
+ AutoTArray<nsIContent*, 8> rootElementNAC;
+ nsContentUtils::AppendNativeAnonymousChildren(
+ parent, rootElementNAC, nsIContent::eSkipDocumentLevelNativeAnonymousContent);
+ const bool isDocLevelNAC = !rootElementNAC.Contains(this);
#ifdef DEBUG
- {
- // The code below would be slightly more direct, but it gets the wrong
- // answer when the root scrollframe is being bootstrapped and we're
- // trying to style the scrollbars (since GetRootScrollFrame() still returns
- // null at that point). Verify that the results match otherwise.
- AutoTArray<nsIContent*, 8> docLevelNAC;
- nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(OwnerDoc(), docLevelNAC);
- nsIPresShell* shell = OwnerDoc()->GetShell();
- MOZ_ASSERT_IF(shell && shell->GetRootScrollFrame(),
- isDocLevelNAC == docLevelNAC.Contains(this));
- }
+ {
+ // The code below would be slightly more direct, but it gets the wrong
+ // answer when the root scrollframe is being bootstrapped and we're
+ // trying to style the scrollbars (since GetRootScrollFrame() still returns
+ // null at that point). Verify that the results match otherwise.
+ AutoTArray<nsIContent*, 8> docLevelNAC;
+ nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(OwnerDoc(), docLevelNAC);
+ nsIPresShell* shell = OwnerDoc()->GetShell();
+ MOZ_ASSERT_IF(shell && shell->GetRootScrollFrame(),
+ isDocLevelNAC == docLevelNAC.Contains(this));
+ }
#endif
- if (isDocLevelNAC) {
- return OwnerDoc();
- }
- }
-
- if (IsRootOfAnonymousSubtree()) {
- return parent;
- }
-
- if (nsContentUtils::HasDistributedChildren(parent)) {
- return GetFlattenedTreeParentForMaybeAssignedNode();
- }
-
- if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
- parent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
- // We need to check `parent` to properly handle the unassigned child case
- // below, since if we were never assigned we would never have the flag set.
- //
- // Note that unassigned child nodes _could_ have the flag set, if they were
- // ever assigned, or if they have a binding themselves.
- if (nsIContent* insertionPoint = GetXBLInsertionPoint()) {
- parent = insertionPoint->GetParent();
- MOZ_ASSERT(parent);
- } else if (parent->OwnerDoc()->BindingManager()->GetBindingWithContent(parent)) {
- // This is an unassigned node child of the bound element, so it isn't part
- // of the flat tree.
- return nullptr;
- }
- }
-
- // Shadow roots never shows up in the flattened tree. Return the host
- // instead.
- if (parent->IsInShadowTree()) {
- if (ShadowRoot* parentShadowRoot = ShadowRoot::FromNode(parent)) {
- return parentShadowRoot->GetHost();
- }
- }
-
- return parent;
+ return isDocLevelNAC ? OwnerDocAsNode() : parent;
}
nsIContent::IMEState
nsIContent::GetDesiredIMEState()
{
if (!IsEditableInternal()) {
// Check for the special case where we're dealing with elements which don't
// have the editable flag set, but are readwrite (such as text controls).
@@ -2373,18 +2300,17 @@ FragmentOrElement::SetInnerHTMLInternal(
}
mb.RemovalDone();
nsAutoScriptLoaderDisabler sld(doc);
nsAtom* contextLocalName = NodeInfo()->NameAtom();
int32_t contextNameSpaceID = GetNameSpaceID();
- ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
- if (shadowRoot) {
+ if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
// Fix up the context to be the host of the ShadowRoot.
contextLocalName = shadowRoot->GetHost()->NodeInfo()->NameAtom();
contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
}
if (doc->IsHTMLDocument()) {
int32_t oldChildCount = target->GetChildCount();
aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -95,28 +95,16 @@ ShadowRoot::~ShadowRoot()
}
JSObject*
ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
}
-ShadowRoot*
-ShadowRoot::FromNode(nsINode* aNode)
-{
- if (aNode->IsInShadowTree() && !aNode->GetParentNode()) {
- MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
- "ShadowRoot is a document fragment.");
- return static_cast<ShadowRoot*>(aNode);
- }
-
- return nullptr;
-}
-
void
ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
{
MOZ_ASSERT(aSlot);
// Note that if name attribute missing, the slot is a default slot.
nsAutoString name;
aSlot->GetName(name);
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -6,17 +6,16 @@
#ifndef mozilla_dom_shadowroot_h__
#define mozilla_dom_shadowroot_h__
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DocumentOrShadowRoot.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
-#include "nsIContentInlines.h"
#include "nsIdentifierMapEntry.h"
#include "nsTHashtable.h"
class nsAtom;
class nsIContent;
class nsXBLPrototypeBinding;
namespace mozilla {
@@ -27,16 +26,36 @@ namespace dom {
class Element;
class ShadowRoot final : public DocumentFragment,
public DocumentOrShadowRoot,
public nsStubMutationObserver
{
public:
+ static ShadowRoot* FromNode(nsINode* aNode)
+ {
+ return aNode->IsShadowRoot() ? static_cast<ShadowRoot*>(aNode) : nullptr;
+ }
+
+ static const ShadowRoot* FromNode(const nsINode* aNode)
+ {
+ return aNode->IsShadowRoot() ? static_cast<const ShadowRoot*>(aNode) : nullptr;
+ }
+
+ static ShadowRoot* FromNodeOrNull(nsINode* aNode)
+ {
+ return aNode ? FromNode(aNode) : nullptr;
+ }
+
+ static const ShadowRoot* FromNodeOrNull(const nsINode* aNode)
+ {
+ return aNode ? FromNode(aNode) : nullptr;
+ }
+
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot,
DocumentFragment)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
@@ -108,18 +127,16 @@ public:
void RemoveSlot(HTMLSlotElement* aSlot);
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
- static ShadowRoot* FromNode(nsINode* aNode);
-
void AddToIdTable(Element* aElement, nsAtom* aId);
void RemoveFromIdTable(Element* aElement, nsAtom* aId);
// WebIDL methods.
using mozilla::dom::DocumentOrShadowRoot::GetElementById;
void GetInnerHTML(nsAString& aInnerHTML);
void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
void StyleSheetChanged();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6537,17 +6537,17 @@ nsIDocument::ImportNode(nsINode& aNode,
switch (imported->NodeType()) {
case nsIDOMNode::DOCUMENT_NODE:
{
break;
}
case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
{
- if (ShadowRoot::FromNode(imported)) {
+ if (imported->IsShadowRoot()) {
break;
}
MOZ_FALLTHROUGH;
}
case nsIDOMNode::ATTRIBUTE_NODE:
case nsIDOMNode::ELEMENT_NODE:
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
case nsIDOMNode::TEXT_NODE:
@@ -7705,17 +7705,17 @@ nsIDocument::AdoptNode(nsINode& aAdopted
newAttr.swap(adoptedAttr);
}
break;
}
case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
{
- if (ShadowRoot::FromNode(adoptedNode)) {
+ if (adoptedNode->IsShadowRoot()) {
rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}
MOZ_FALLTHROUGH;
}
case nsIDOMNode::ELEMENT_NODE:
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
case nsIDOMNode::TEXT_NODE:
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -105,22 +105,19 @@ protected:
bool IsVisibleNode(nsINode* aNode)
{
NS_PRECONDITION(aNode, "");
if (mFlags & SkipInvisibleContent) {
// Treat the visibility of the ShadowRoot as if it were
// the host content.
- nsCOMPtr<nsIContent> content;
- ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode);
- if (shadowRoot) {
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+ if (ShadowRoot* shadowRoot = ShadowRoot::FromNodeOrNull(content)) {
content = shadowRoot->GetHost();
- } else {
- content = do_QueryInterface(aNode);
}
if (content) {
nsIFrame* frame = content->GetPrimaryFrame();
if (!frame) {
if (aNode->IsNodeOfType(nsINode::eTEXT)) {
// We have already checked that our parent is visible.
return true;
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -529,17 +529,17 @@ public:
virtual nsXBLBinding* DoGetXBLBinding() const = 0;
/**
* Gets the ShadowRoot binding for this element.
*
* @return The ShadowRoot currently bound to this element.
*/
- inline mozilla::dom::ShadowRoot *GetShadowRoot() const;
+ inline mozilla::dom::ShadowRoot* GetShadowRoot() const;
/**
* Gets the root of the node tree for this content if it is in a shadow tree.
* This method is called |GetContainingShadow| instead of |GetRootShadowRoot|
* to avoid confusion with |GetShadowRoot|.
*
* @return The ShadowRoot that is the root of the node tree.
*/
@@ -600,21 +600,27 @@ public:
* @param aContent The insertion parent element.
*/
void SetXBLInsertionPoint(nsIContent* aContent);
/**
* Same as GetFlattenedTreeParentNode, but returns null if the parent is
* non-nsIContent.
*/
- inline nsIContent *GetFlattenedTreeParent() const;
+ inline nsIContent* GetFlattenedTreeParent() const;
- // Helper method, which we leave public so that it's accessible from nsINode.
- enum FlattenedParentType { eNotForStyle, eForStyle };
- nsINode* GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const;
+ /**
+ * Get the flattened tree parent for NAC holding from the document element,
+ * from the point of view of the style system.
+ *
+ * Document-level anonymous content holds from the document element, even
+ * though they should not be treated as such (they should be parented to the
+ * document instead, and shouldn't inherit from the document element).
+ */
+ nsINode* GetFlattenedTreeParentForDocumentElementNAC() const;
/**
* API to check if this is a link that's traversed in response to user input
* (e.g. a click event). Specializations for HTML/SVG/generic XML allow for
* different types of link in different types of content.
*
* @param aURI Required out param. If this content is a link, a new nsIURI
* set to this link's URI will be passed out.
@@ -950,22 +956,16 @@ protected:
}
/**
* Hook for implementing GetID. This is guaranteed to only be
* called if HasID() is true.
*/
nsAtom* DoGetID() const;
- /**
- * Returns the assigned slot, if it exists, or the direct parent, if it's a
- * fallback content of a slot.
- */
- nsINode* GetFlattenedTreeParentForMaybeAssignedNode() const;
-
public:
#ifdef DEBUG
/**
* List the content (and anything it contains) out to the given
* file stream. Use aIndent as the base indent during formatting.
*/
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -8,16 +8,18 @@
#define nsIContentInlines_h
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsContentUtils.h"
#include "nsAtom.h"
#include "nsIFrame.h"
#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLSlotElement.h"
+#include "mozilla/dom/ShadowRoot.h"
inline bool
nsIContent::IsInHTMLDocument() const
{
return OwnerDoc()->IsHTMLDocument();
}
inline bool
@@ -49,75 +51,91 @@ nsIContent::SetPrimaryFrame(nsIFrame* aF
}
inline mozilla::dom::ShadowRoot* nsIContent::GetShadowRoot() const
{
if (!IsElement()) {
return nullptr;
}
- return AsElement()->FastGetShadowRoot();
+ return AsElement()->GetShadowRoot();
}
-template<nsIContent::FlattenedParentType Type>
-static inline bool FlattenedTreeParentIsParent(const nsINode* aNode)
-{
- // Try to short-circuit past the complicated and not-exactly-fast logic for
- // computing the flattened parent.
- //
- // WARNING! This logic is replicated in Servo to avoid out of line calls.
- // If you change the cases here, you need to change them in Servo as well!
-
- // Check if the node is an explicit child of an XBL-bound element, re-bound to
- // an XBL insertion point, or is a top-level element in a shadow tree, whose
- // flattened parent is the host element (as opposed to the actual parent which
- // is the shadow root).
- if (aNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR | NODE_IS_IN_SHADOW_TREE)) {
- return false;
- }
-
- // Check if we want the flattened parent for style, and the node is the root
- // of a native anonymous content subtree parented to the document's root
- // element.
- if (Type == nsIContent::eForStyle &&
- aNode->HasFlag(NODE_IS_NATIVE_ANONYMOUS_ROOT) &&
- aNode->OwnerDoc()->GetRootElement() == aNode->GetParentNode())
- {
- return false;
- }
-
- // Check if the node is an explicit child of an element with a shadow root or
- // an element with an XBL binding, which may be re-bound to an insertion
- // point, or not be bound to any, and thus won't appear on the flattened tree
- // at all.
- nsIContent* parent = aNode->GetParent();
- if (parent && (parent->GetShadowRoot() || parent->GetXBLBinding())) {
- return false;
- }
-
- // Common case.
- return true;
-}
-
-template<nsIContent::FlattenedParentType Type>
+template<nsINode::FlattenedParentType aType>
static inline nsINode*
GetFlattenedTreeParentNode(const nsINode* aNode)
{
- if (MOZ_LIKELY(FlattenedTreeParentIsParent<Type>(aNode))) {
- return aNode->GetParentNode();
+ if (!aNode->IsContent()) {
+ return nullptr;
+ }
+
+ nsINode* parent = aNode->GetParentNode();
+ if (!parent || !parent->IsContent()) {
+ return parent;
+ }
+
+ const nsIContent* content = aNode->AsContent();
+ nsIContent* parentAsContent = parent->AsContent();
+
+ if (aType == nsINode::eForStyle &&
+ content->IsRootOfNativeAnonymousSubtree() &&
+ parentAsContent == content->OwnerDoc()->GetRootElement() &&
+ !content->IsGeneratedContentContainerForAfter() &&
+ !content->IsGeneratedContentContainerForBefore()) {
+ return content->GetFlattenedTreeParentForDocumentElementNAC();
+ }
+
+ if (content->IsRootOfAnonymousSubtree()) {
+ return parent;
+ }
+
+ if (parentAsContent->GetShadowRoot()) {
+ // If it's not assigned to any slot it's not part of the flat tree, and thus
+ // we return null.
+ return content->GetAssignedSlot();
}
- MOZ_ASSERT(aNode->IsContent());
- return aNode->AsContent()->GetFlattenedTreeParentNodeInternal(Type);
+ if (parentAsContent->IsInShadowTree()) {
+ if (auto* slot = mozilla::dom::HTMLSlotElement::FromContent(parentAsContent)) {
+ // If the assigned nodes list is empty, we're fallback content which is
+ // active, otherwise we are not part of the flat tree.
+ return slot->AssignedNodes().IsEmpty()
+ ? parent
+ : nullptr;
+ }
+
+ if (auto* shadowRoot = mozilla::dom::ShadowRoot::FromNode(parentAsContent)) {
+ return shadowRoot->GetHost();
+ }
+ }
+
+ if (content->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
+ parent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
+ if (nsIContent* xblInsertionPoint = content->GetXBLInsertionPoint()) {
+ return xblInsertionPoint->GetParent();
+ }
+
+ if (parent->OwnerDoc()->BindingManager()->GetBindingWithContent(parentAsContent)) {
+ // This is an unassigned node child of the bound element, so it isn't part
+ // of the flat tree.
+ return nullptr;
+ }
+ }
+
+ MOZ_ASSERT(!parentAsContent->IsActiveChildrenElement(),
+ "<xbl:children> isn't in the flattened tree");
+
+ // Common case.
+ return parent;
}
inline nsINode*
nsINode::GetFlattenedTreeParentNode() const
{
- return ::GetFlattenedTreeParentNode<nsIContent::eNotForStyle>(this);
+ return ::GetFlattenedTreeParentNode<nsINode::eNotForStyle>(this);
}
inline nsIContent*
nsIContent::GetFlattenedTreeParent() const
{
nsINode* parent = GetFlattenedTreeParentNode();
return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
}
@@ -131,17 +149,17 @@ nsIContent::IsEventAttributeName(nsAtom*
}
return IsEventAttributeNameInternal(aName);
}
inline nsINode*
nsINode::GetFlattenedTreeParentNodeForStyle() const
{
- return ::GetFlattenedTreeParentNode<nsIContent::eForStyle>(this);
+ return ::GetFlattenedTreeParentNode<nsINode::eForStyle>(this);
}
inline bool
nsINode::NodeOrAncestorHasDirAuto() const
{
return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto());
}
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -255,24 +255,23 @@ nsINode::GetTextEditorRootContent(TextEd
nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
{
if (aOptions.mComposed) {
if (IsInComposedDoc() && GetComposedDoc()) {
return OwnerDoc();
}
nsINode* node = this;
- ShadowRoot* shadowRootParent = nullptr;
while(node) {
node = node->SubtreeRoot();
- shadowRootParent = ShadowRoot::FromNode(node);
- if (!shadowRootParent) {
- break;
+ ShadowRoot* shadow = ShadowRoot::FromNode(node);
+ if (!shadow) {
+ break;
}
- node = shadowRootParent->GetHost();
+ node = shadow->GetHost();
}
return node;
}
return SubtreeRoot();
}
@@ -390,18 +389,17 @@ nsINode::GetSelectionRootContent(nsIPres
// This node might be in another subtree, if so, we should find this subtree's
// root. Otherwise, we can return the content simply.
NS_ENSURE_TRUE(content, nullptr);
if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
content = GetRootForContentSubtree(static_cast<nsIContent*>(this));
// Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
// Use the host as the root.
- ShadowRoot* shadowRoot = ShadowRoot::FromNode(content);
- if (shadowRoot) {
+ if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) {
content = shadowRoot->GetHost();
}
}
return content;
}
nsINodeList*
@@ -3057,22 +3055,19 @@ nsINode::GetParentElementCrossingShadowR
if (!mParent) {
return nullptr;
}
if (mParent->IsElement()) {
return mParent->AsElement();
}
- ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent);
- if (shadowRoot) {
- nsIContent* host = shadowRoot->GetHost();
- MOZ_ASSERT(host, "ShowRoots should always have a host");
- MOZ_ASSERT(host->IsElement(), "ShadowRoot hosts should always be Elements");
- return host->AsElement();
+ if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent)) {
+ MOZ_ASSERT(shadowRoot->GetHost(), "ShowRoots should always have a host");
+ return shadowRoot->GetHost();
}
return nullptr;
}
bool
nsINode::HasBoxQuadsSupport(JSContext* aCx, JSObject* /* unused */)
{
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -700,16 +700,24 @@ public:
}
template<typename First, typename... Args>
inline bool IsAnyOfMathMLElements(First aFirst, Args... aArgs) const
{
return IsMathMLElement() && IsNodeInternal(aFirst, aArgs...);
}
+ bool IsShadowRoot() const
+ {
+ const bool isShadowRoot = IsInShadowTree() && !GetParentNode();
+ MOZ_ASSERT_IF(isShadowRoot,
+ NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE);
+ return isShadowRoot;
+ }
+
/**
* Insert a content node at a particular index. This method handles calling
* BindToTree on the child appropriately.
*
* @param aKid the content to insert
* @param aIndex the index it is being inserted at (the index it will have
* after it is inserted)
* @param aNotify whether to notify the document (current document for
@@ -937,31 +945,32 @@ public:
* an nsIDocument or an nsIAttribute.
* @return the parent node
*/
nsINode* GetParentNode() const
{
return mParent;
}
+ enum FlattenedParentType { eNotForStyle, eForStyle };
+
/**
* Returns the node that is the parent of this node in the flattened
* tree. This differs from the normal parent if the node is filtered
* into an insertion point, or if the node is a direct child of a
* shadow root.
*
* @return the flattened tree parent
*/
inline nsINode* GetFlattenedTreeParentNode() const;
/**
- * Like GetFlattenedTreeParentNode, but returns null for any native
- * anonymous content that was generated for ancestor frames of the
- * root element's primary frame, such as scrollbar elements created
- * by the root scroll frame.
+ * Like GetFlattenedTreeParentNode, but returns null for any native anonymous
+ * content that was generated for ancestor frames of the document element's
+ * primary frame, such as scrollbar elements created by the root scroll frame.
*/
inline nsINode* GetFlattenedTreeParentNodeForStyle() const;
inline mozilla::dom::Element* GetFlattenedTreeParentElement() const;
inline mozilla::dom::Element* GetFlattenedTreeParentElementForStyle() const;
/**
* Get the parent nsINode for this node if it is an Element.
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -4,16 +4,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsNodeUtils.h"
#include "nsContentUtils.h"
#include "nsCSSPseudoElements.h"
#include "nsINode.h"
#include "nsIContent.h"
+#include "nsIContentInlines.h"
#include "mozilla/dom/Element.h"
#include "nsIMutationObserver.h"
#include "nsIDocument.h"
#include "mozilla/EventListenerManager.h"
#include "nsIXPConnect.h"
#include "PLDHashTable.h"
#include "nsIDOMAttr.h"
#include "nsCOMArray.h"
@@ -112,18 +113,17 @@ enum class IsRemoveNotification
nsINode* node = content_; \
do { \
nsINode::nsSlots* slots = node->GetExistingSlots(); \
if (slots && !slots->mMutationObservers.IsEmpty()) { \
NS_OBSERVER_AUTO_ARRAY_NOTIFY_OBSERVERS_WITH_QI( \
slots->mMutationObservers, nsIMutationObserver, 1, \
nsIAnimationObserver, func_, params_); \
} \
- ShadowRoot* shadow = ShadowRoot::FromNode(node); \
- if (shadow) { \
+ if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) { \
node = shadow->GetHost(); \
} else { \
node = node->GetParentNode(); \
} \
} while (node); \
if (needsEnterLeave) { \
nsDOMMutationObserver::LeaveMutationHandling(); \
} \
--- a/dom/html/HTMLLabelElement.cpp
+++ b/dom/html/HTMLLabelElement.cpp
@@ -7,16 +7,17 @@
/**
* Implementation of HTML <label> elements.
*/
#include "HTMLLabelElement.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/dom/HTMLLabelElementBinding.h"
#include "nsFocusManager.h"
+#include "nsContentUtils.h"
#include "nsIDOMMouseEvent.h"
#include "nsQueryObject.h"
#include "mozilla/dom/ShadowRoot.h"
// construction, destruction
NS_IMPL_NS_NEW_HTML_ELEMENT(Label)
@@ -232,29 +233,24 @@ HTMLLabelElement::GetLabeledElement() co
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::_for, elementId)) {
// No @for, so we are a label for our first form control element.
// Do a depth-first traversal to look for the first form control element.
return GetFirstLabelableDescendant();
}
// We have a @for. The id has to be linked to an element in the same tree
// and this element should be a labelable form control.
- nsINode* root = SubtreeRoot();
- ShadowRoot* shadow = ShadowRoot::FromNode(root);
Element* element = nullptr;
- if (shadow) {
- element = shadow->GetElementById(elementId);
+ if (ShadowRoot* shadowRoot = GetContainingShadow()) {
+ element = shadowRoot->GetElementById(elementId);
+ } else if (nsIDocument* doc = GetUncomposedDoc()) {
+ element = doc->GetElementById(elementId);
} else {
- nsIDocument* doc = GetUncomposedDoc();
- if (doc) {
- element = doc->GetElementById(elementId);
- } else {
- element = nsContentUtils::MatchElementId(root->AsContent(), elementId);
- }
+ element = nsContentUtils::MatchElementId(SubtreeRoot()->AsContent(), elementId);
}
if (element && element->IsLabelable()) {
return static_cast<nsGenericHTMLElement*>(element);
}
return nullptr;
}
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8028,30 +8028,29 @@ nsCSSFrameConstructor::ContentRangeInser
return;
}
nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
// The xbl:children element won't have a frame, but default content can have the children as
// a parent. While its uncommon to change the structure of the default content itself, a label,
// for example, can be reframed by having its value attribute set or removed.
if (!parentFrame &&
- !(aContainer->IsActiveChildrenElement() ||
- ShadowRoot::FromNode(aContainer))) {
+ !(aContainer->IsActiveChildrenElement() || aContainer->IsShadowRoot())) {
// We're punting on frame construction because there's no container frame.
// The Servo-backed style system handles this case like the lazy frame
// construction case, except when we're already constructing frames, in
// which case we shouldn't need to do anything else.
if (aContainer->IsStyledByServo() &&
aInsertionKind == InsertionKind::Async) {
LazilyStyleNewChildRange(aStartChild, aEndChild);
}
return;
}
- MOZ_ASSERT_IF(ShadowRoot::FromNode(aContainer), !parentFrame);
+ MOZ_ASSERT_IF(aContainer->IsShadowRoot(), !parentFrame);
// Otherwise, we've got parent content. Find its frame.
NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
if (aInsertionKind == InsertionKind::Async &&
MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
if (aContainer->IsStyledByServo()) {
@@ -9340,22 +9339,24 @@ nsCSSFrameConstructor::GetInsertionPoint
// anonymous.
if (aChild->GetBindingParent() == aContainer) {
// This child content is anonymous. Don't use the insertion
// point, since that's only for the explicit kids.
return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
}
if (nsContentUtils::HasDistributedChildren(aContainer) ||
- ShadowRoot::FromNode(aContainer)) {
+ aContainer->IsShadowRoot()) {
// The container distributes nodes or is a shadow root, use the frame of
// the flattened tree parent.
//
// It may be the case that the node is distributed but not matched to any
// insertion points, so there is no flattened parent.
+ //
+ // FIXME(emilio): We should be able to use this path all the time.
nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
if (flattenedParent) {
return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
flattenedParent);
}
return InsertionPoint();
}
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -19,16 +19,17 @@
#include "nsIFormControl.h"
#include "nsNameSpaceManager.h"
#include "nsIListControlFrame.h"
#include "nsPIDOMWindow.h"
#include "nsIPresShell.h"
#include "nsPresState.h"
#include "nsView.h"
#include "nsViewManager.h"
+#include "nsIContentInlines.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMNode.h"
#include "nsISelectControlFrame.h"
#include "nsContentUtils.h"
#include "mozilla/dom/HTMLSelectElement.h"
#include "nsIDocument.h"
#include "nsIScrollableFrame.h"
#include "nsListControlFrame.h"
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -1696,20 +1696,18 @@ nsFrameSelection::GetFrameForNodeOffset(
}
}
}
}
// If the node is a ShadowRoot, the frame needs to be adjusted,
// because a ShadowRoot does not get a frame. Its children are rendered
// as children of the host.
- mozilla::dom::ShadowRoot* shadowRoot =
- mozilla::dom::ShadowRoot::FromNode(theNode);
- if (shadowRoot) {
- theNode = shadowRoot->GetHost();
+ if (ShadowRoot* shadow = ShadowRoot::FromNode(theNode)) {
+ theNode = shadow->GetHost();
}
returnFrame = theNode->GetPrimaryFrame();
if (!returnFrame) {
if (aHint == CARET_ASSOCIATE_BEFORE) {
if (aOffset > 0) {
--aOffset;
continue;
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -16,16 +16,17 @@
#include "mozilla/layout/RenderFrameParent.h"
#include "nsCOMPtr.h"
#include "nsGenericHTMLElement.h"
#include "nsGenericHTMLFrameElement.h"
#include "nsAttrValueInlines.h"
#include "nsIDocShell.h"
#include "nsIContentViewer.h"
+#include "nsIContentInlines.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsIDocument.h"
#include "nsView.h"
#include "nsViewManager.h"
#include "nsGkAtoms.h"
#include "nsStyleConsts.h"
#include "nsFrameSetFrame.h"
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -148,26 +148,16 @@ Gecko_RecordTraversalStatistics(uint32_t
}
bool
Gecko_IsInDocument(RawGeckoNodeBorrowed aNode)
{
return aNode->IsInComposedDoc();
}
-#ifdef MOZ_DEBUG_RUST
-bool
-Gecko_FlattenedTreeParentIsParent(RawGeckoNodeBorrowed aNode)
-{
- // Servo calls this in debug builds to verify the result of its own
- // flattened_tree_parent_is_parent() function.
- return FlattenedTreeParentIsParent<nsIContent::eForStyle>(aNode);
-}
-#endif
-
/*
* Does this child count as significant for selector matching?
*
* See nsStyleUtil::IsSignificantChild for details.
*/
bool
Gecko_IsSignificantChild(RawGeckoNodeBorrowed aNode, bool aTextIsSignificant,
bool aWhitespaceIsSignificant)
@@ -181,21 +171,17 @@ RawGeckoNodeBorrowedOrNull
Gecko_GetLastChild(RawGeckoNodeBorrowed aNode)
{
return aNode->GetLastChild();
}
RawGeckoNodeBorrowedOrNull
Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed aNode)
{
- MOZ_ASSERT(!FlattenedTreeParentIsParent<nsIContent::eForStyle>(aNode),
- "Should have taken the inline path");
- MOZ_ASSERT(aNode->IsContent(), "Slow path only applies to content");
- const nsIContent* c = aNode->AsContent();
- return c->GetFlattenedTreeParentNodeInternal(nsIContent::eForStyle);
+ return aNode->GetFlattenedTreeParentNodeForStyle();
}
RawGeckoElementBorrowedOrNull
Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed aElement, bool aIsBefore)
{
MOZ_ASSERT(aElement);
MOZ_ASSERT(aElement->HasProperties());
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -147,17 +147,16 @@ struct FontSizePrefs
nscoord mDefaultFantasySize;
};
// DOM Traversal.
void Gecko_RecordTraversalStatistics(uint32_t total, uint32_t parallel,
uint32_t total_t, uint32_t parallel_t,
uint32_t total_s, uint32_t parallel_s);
bool Gecko_IsInDocument(RawGeckoNodeBorrowed node);
-bool Gecko_FlattenedTreeParentIsParent(RawGeckoNodeBorrowed node);
bool Gecko_IsSignificantChild(RawGeckoNodeBorrowed node,
bool text_is_significant,
bool whitespace_is_significant);
RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed node);
RawGeckoNodeBorrowedOrNull Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed node);
RawGeckoElementBorrowedOrNull Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed element, bool is_before);
nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(RawGeckoElementBorrowed element);
void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* anon_content);
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -231,23 +231,33 @@ impl<'ln> GeckoNode<'ln> {
}
}
true
}
#[inline]
fn flattened_tree_parent(&self) -> Option<Self> {
- let fast_path = self.flattened_tree_parent_is_parent();
- debug_assert!(fast_path == unsafe { bindings::Gecko_FlattenedTreeParentIsParent(self.0) });
- if fast_path {
- unsafe { self.0.mParent.as_ref().map(GeckoNode) }
- } else {
- unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) }
+ // 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 { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) },
+ 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.
+ unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) }
}
#[inline]
fn contains_non_whitespace_content(&self) -> bool {
unsafe { Gecko_IsSignificantChild(self.0, true, false) }
}
}