gecko-dev/dom/base/nsIContentInlines.h

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

275 lines
8.2 KiB
C
Raw Normal View History

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef nsIContentInlines_h
#define nsIContentInlines_h
#include "nsIContent.h"
#include "mozilla/dom/Document.h"
#include "nsBindingManager.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 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();
}
inline bool nsIContent::IsInChromeDocument() const {
return nsContentUtils::IsChromeDoc(OwnerDoc());
}
inline void nsIContent::SetPrimaryFrame(nsIFrame* aFrame) {
MOZ_ASSERT(IsInUncomposedDoc() || IsInShadowTree(), "This will end badly!");
// FIXME bug 749326
NS_ASSERTION(!aFrame || !mPrimaryFrame || aFrame == mPrimaryFrame,
"Losing track of existing primary frame");
if (aFrame) {
MOZ_ASSERT(!aFrame->IsPlaceholderFrame());
if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
aFrame->GetContent() == this) {
aFrame->SetIsPrimaryFrame(true);
}
} else if (nsIFrame* currentPrimaryFrame = GetPrimaryFrame()) {
if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
currentPrimaryFrame->GetContent() == this) {
currentPrimaryFrame->SetIsPrimaryFrame(false);
}
}
mPrimaryFrame = aFrame;
}
inline mozilla::dom::ShadowRoot* nsIContent::GetShadowRoot() const {
if (!IsElement()) {
return nullptr;
}
return AsElement()->GetShadowRoot();
}
template <nsINode::FlattenedParentType aType>
static inline nsINode* GetFlattenedTreeParentNode(const nsINode* aNode) {
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() &&
Bug 1440258: Flag doc level anonymous content instead of guessing. r=bholley Poking at the frame tree has problems: If we poke in negative (using eSkipNativeAnonymousContent), as we were doing, we mess up the case where we're actually _not_ doc-level, and _not_ ::before or ::after. This can't happen for content documents, but can happen for chrome (since nsDocElementBoxFrame implements nsIAnonymousContentCreator). If we poke in positive, as we used to, you get that right, but mess up the root scrollbar case. Instead, use a node property to mark doc level anon content. This is a case rare enough that it seems worth to not steal a node bit. To recap the failure: * The initial value of -moz-control-character-visiblity is different on beta and nightly. * XUL has a global rule setting -moz-control-character-visibility on the root, to a value so that it's the initial one on nightly, but the non-initial one on beta. * Changes to this property cause a reframe. * Reframes of a nsIAnonymousContentCreator anon content reframe the container. * We were failing to inherit correctly for the nsIAnonymousContentCreator content for the root XUL element on the initial styling, inheriting from the default computed values instead, since we failed to reach the root element's primary frame from GetFlattenedTreeParentForDocumentElementNAC -> AppendDocumentLevelNativeAnonymousContentTo, since the primary frame is set _after_ processing children. This seems somewhat risky to change, and inconsistent with any other stuff the frame constructor does, see bug 973390. * Given that, the next restyle of the root element, in this case caused due to the customizable UI, we _found_ the actual correct parent, recomputed the style, saw that -moz-control-character-visiblity had changed, and reframed. But we were reframing the whole window, not just the NAC, because of the fourth bullet point. Reframing the whole window caused us to lose the popup state (that's bug 1440506). Worse than that is the fact that given we reframe and reconstruct the anonymous countent again, we go back to the initial bogus state, just awaiting for the next restyle to reframe the whole window. I wish there was a bullet-proof way to test it that isn't just counting reframes and relying on which properties reframe or not, but due to the nature of nsIAnonymousContentCreator's NAC, it's not possible in any easy way I can think of. MozReview-Commit-ID: IPYB5trsN8R
2018-02-23 01:30:00 +00:00
parentAsContent == content->OwnerDoc()->GetRootElement()) {
const bool docLevel =
content->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent);
return docLevel ? content->OwnerDocAsNode() : parent;
}
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();
}
if (parentAsContent->IsInShadowTree()) {
if (auto* slot = mozilla::dom::HTMLSlotElement::FromNode(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<nsINode::eNotForStyle>(this);
}
inline nsIContent* nsIContent::GetFlattenedTreeParent() const {
nsINode* parent = GetFlattenedTreeParentNode();
return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
}
inline bool nsIContent::IsEventAttributeName(nsAtom* aName) {
const char16_t* name = aName->GetUTF16String();
if (name[0] != 'o' || name[1] != 'n') {
return false;
}
return IsEventAttributeNameInternal(aName);
}
inline nsINode* nsINode::GetFlattenedTreeParentNodeForStyle() const {
return ::GetFlattenedTreeParentNode<nsINode::eForStyle>(this);
}
inline bool nsINode::NodeOrAncestorHasDirAuto() const {
return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto());
}
inline bool nsINode::IsEditable() const {
if (HasFlag(NODE_IS_EDITABLE)) {
// The node is in an editable contentEditable subtree.
return true;
}
// All editable anonymous content should be made explicitly editable via the
// NODE_IS_EDITABLE flag.
if (IsInNativeAnonymousSubtree()) {
return false;
}
// Check if the node is in a document and the document is in designMode.
Document* doc = GetUncomposedDoc();
return doc && doc->HasFlag(NODE_IS_EDITABLE);
}
inline bool nsIContent::IsActiveChildrenElement() const {
if (!mNodeInfo->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
return false;
}
nsIContent* bindingParent = GetBindingParent();
if (!bindingParent) {
return false;
}
// We reuse the binding parent machinery for Shadow DOM too, so prevent that
// from getting us confused in this case.
return !bindingParent->GetShadowRoot();
}
inline bool nsIContent::IsInAnonymousSubtree() const {
NS_ASSERTION(
!IsInNativeAnonymousSubtree() || GetBindingParent() ||
(!IsInUncomposedDoc() && static_cast<nsIContent*>(SubtreeRoot())
->IsInNativeAnonymousSubtree()),
"Must have binding parent when in native anonymous subtree which is in "
"document.\n"
"Native anonymous subtree which is not in document must have native "
"anonymous root.");
if (IsInNativeAnonymousSubtree()) {
return true;
}
nsIContent* bindingParent = GetBindingParent();
if (!bindingParent) {
return false;
}
// We reuse the binding parent machinery for Shadow DOM too, so prevent that
// from getting us confused in this case.
return !bindingParent->GetShadowRoot();
}
inline void nsIContent::HandleInsertionToOrRemovalFromSlot() {
using mozilla::dom::HTMLSlotElement;
MOZ_ASSERT(GetParentElement());
if (!IsInShadowTree() || IsRootOfAnonymousSubtree()) {
return;
}
HTMLSlotElement* slot = HTMLSlotElement::FromNode(mParent);
if (!slot) {
return;
}
// If parent's root is a shadow root, and parent is a slot whose
// assigned nodes is the empty list, then run signal a slot change for
// parent.
if (slot->AssignedNodes().IsEmpty()) {
slot->EnqueueSlotChangeEvent();
}
}
inline void nsIContent::HandleShadowDOMRelatedInsertionSteps(bool aHadParent) {
using mozilla::dom::Element;
using mozilla::dom::ShadowRoot;
if (!aHadParent) {
if (Element* parentElement = Element::FromNode(mParent)) {
if (ShadowRoot* shadow = parentElement->GetShadowRoot()) {
shadow->MaybeSlotHostChild(*this);
}
HandleInsertionToOrRemovalFromSlot();
}
}
}
inline void nsIContent::HandleShadowDOMRelatedRemovalSteps(bool aNullParent) {
using mozilla::dom::Element;
using mozilla::dom::ShadowRoot;
if (aNullParent) {
// FIXME(emilio, bug 1577141): FromNodeOrNull rather than just FromNode
// because XBL likes to call UnbindFromTree at very odd times (with already
// disconnected anonymous content subtrees).
if (Element* parentElement = Element::FromNodeOrNull(mParent)) {
if (ShadowRoot* shadow = parentElement->GetShadowRoot()) {
shadow->MaybeUnslotHostChild(*this);
}
HandleInsertionToOrRemovalFromSlot();
}
}
}
#endif // nsIContentInlines_h