Backed out changeset 1d9301697aa0 (bug 1887752) for causing failures on browser_all_f...
[gecko.git] / dom / base / nsIContentInlines.h
blob04e73b75e15b04840d9b6a3cdbfc31a0cde674a0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsIContentInlines_h
8 #define nsIContentInlines_h
10 #include "nsIContent.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsContentUtils.h"
13 #include "nsAtom.h"
14 #include "nsIFrame.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/HTMLSlotElement.h"
17 #include "mozilla/dom/ShadowRoot.h"
19 inline bool nsIContent::IsInHTMLDocument() const {
20 return OwnerDoc()->IsHTMLDocument();
23 inline bool nsIContent::IsInChromeDocument() const {
24 return nsContentUtils::IsChromeDoc(OwnerDoc());
27 inline void nsIContent::SetPrimaryFrame(nsIFrame* aFrame) {
28 MOZ_ASSERT(IsInUncomposedDoc() || IsInShadowTree(), "This will end badly!");
30 // <area> is known to trigger this, see bug 749326 and bug 135040.
31 MOZ_ASSERT(IsHTMLElement(nsGkAtoms::area) || !aFrame || !mPrimaryFrame ||
32 aFrame == mPrimaryFrame,
33 "Losing track of existing primary frame");
35 if (aFrame) {
36 MOZ_ASSERT(!aFrame->IsPlaceholderFrame());
37 if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
38 aFrame->GetContent() == this) {
39 aFrame->SetIsPrimaryFrame(true);
41 } else if (nsIFrame* currentPrimaryFrame = GetPrimaryFrame()) {
42 if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
43 currentPrimaryFrame->GetContent() == this) {
44 currentPrimaryFrame->SetIsPrimaryFrame(false);
48 mPrimaryFrame = aFrame;
51 inline mozilla::dom::ShadowRoot* nsIContent::GetShadowRoot() const {
52 if (!IsElement()) {
53 return nullptr;
56 return AsElement()->GetShadowRoot();
59 template <nsINode::FlattenedParentType aType>
60 static inline nsINode* GetFlattenedTreeParentNode(const nsINode* aNode) {
61 if (!aNode->IsContent()) {
62 return nullptr;
65 nsINode* parent = aNode->GetParentNode();
66 if (!parent || !parent->IsContent()) {
67 return parent;
70 const nsIContent* content = aNode->AsContent();
71 nsIContent* parentAsContent = parent->AsContent();
73 if (aType == nsINode::eForStyle &&
74 content->IsRootOfNativeAnonymousSubtree() &&
75 parentAsContent == content->OwnerDoc()->GetRootElement()) {
76 const bool docLevel =
77 content->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent);
78 return docLevel ? content->OwnerDocAsNode() : parent;
81 if (content->IsRootOfNativeAnonymousSubtree()) {
82 return parent;
85 // Use GetShadowRootForSelection for the selection case such that
86 // if the content is slotted into a UA shadow tree, use
87 // the parent of content as the flattened tree parent (instead of
88 // the slot element).
89 const nsINode* shadowRootForParent =
90 aType == nsINode::eForSelection
91 ? parentAsContent->GetShadowRootForSelection()
92 : parentAsContent->GetShadowRoot();
94 if (shadowRootForParent) {
95 // When aType is not nsINode::eForSelection, If it's not assigned to any
96 // slot it's not part of the flat tree, and thus we return null.
97 auto* assignedSlot = content->GetAssignedSlot();
98 if (assignedSlot || aType != nsINode::eForSelection) {
99 return assignedSlot;
102 MOZ_ASSERT(aType == nsINode::eForSelection);
103 // When aType is nsINode::eForSelection, we use the parent of the
104 // content even if it's not assigned to any slot.
105 return parent;
108 if (parentAsContent->IsInShadowTree()) {
109 if (auto* slot = mozilla::dom::HTMLSlotElement::FromNode(parentAsContent)) {
110 // If the assigned nodes list is empty, we're fallback content which is
111 // active, otherwise we are not part of the flat tree.
112 return slot->AssignedNodes().IsEmpty() ? parent : nullptr;
115 if (auto* shadowRoot =
116 mozilla::dom::ShadowRoot::FromNode(parentAsContent)) {
117 return shadowRoot->GetHost();
121 // Common case.
122 return parent;
125 inline nsINode* nsINode::GetFlattenedTreeParentNode() const {
126 return ::GetFlattenedTreeParentNode<nsINode::eNormal>(this);
129 inline nsIContent* nsIContent::GetFlattenedTreeParent() const {
130 nsINode* parent = GetFlattenedTreeParentNode();
131 return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
134 inline bool nsIContent::IsEventAttributeName(nsAtom* aName) {
135 const char16_t* name = aName->GetUTF16String();
136 if (name[0] != 'o' || name[1] != 'n') {
137 return false;
140 return IsEventAttributeNameInternal(aName);
143 inline nsINode* nsINode::GetFlattenedTreeParentNodeForStyle() const {
144 return ::GetFlattenedTreeParentNode<nsINode::eForStyle>(this);
147 inline nsIContent* nsINode::GetFlattenedTreeParentNodeForSelection() const {
148 nsINode* parent = ::GetFlattenedTreeParentNode<nsINode::eForSelection>(this);
149 return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
152 inline bool nsINode::NodeOrAncestorHasDirAuto() const {
153 return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto());
156 inline bool nsINode::IsEditable() const {
157 if (HasFlag(NODE_IS_EDITABLE)) {
158 // The node is in an editable contentEditable subtree.
159 return true;
162 // All editable anonymous content should be made explicitly editable via the
163 // NODE_IS_EDITABLE flag.
164 if (IsInNativeAnonymousSubtree()) {
165 return false;
168 // Check if the node is in a document and the document is in designMode.
169 return IsInDesignMode();
172 inline bool nsINode::IsEditingHost() const {
173 if (!IsInComposedDoc() || IsInDesignMode() || !IsEditable() ||
174 IsInNativeAnonymousSubtree()) {
175 return false;
177 nsIContent* const parent = GetParent();
178 return !parent || // The root element (IsInComposedDoc() is checked above)
179 !parent->IsEditable(); // or an editable node in a non-editable one
182 inline bool nsINode::IsInDesignMode() const {
183 if (!OwnerDoc()->HasFlag(NODE_IS_EDITABLE)) {
184 return false;
187 if (IsDocument()) {
188 return HasFlag(NODE_IS_EDITABLE);
191 // NOTE(emilio): If you change this to be the composed doc you also need to
192 // change NotifyEditableStateChange() in Document.cpp.
193 // NOTE(masayuki): Perhaps, we should keep this behavior because of
194 // web-compat.
195 if (IsInUncomposedDoc() && GetUncomposedDoc()->HasFlag(NODE_IS_EDITABLE)) {
196 return true;
199 // FYI: In design mode, form controls don't work as usual. For example,
200 // <input type=text> isn't focusable but can be deleted and replaced
201 // with typed text. <select> is also not focusable but always selected
202 // all to be deleted or replaced. On the other hand, newer controls
203 // don't behave as the traditional controls. For example, data/time
204 // picker can be opened and change the value from the picker. And also
205 // the buttons of <video controls> work as usual. On the other hand,
206 // their UI (i.e., nodes in their shadow tree) are not editable.
207 // Therefore, we need special handling for nodes in anonymous subtree
208 // unless we fix <https://bugzilla.mozilla.org/show_bug.cgi?id=1734512>.
210 // If the shadow host is not in design mode, this can never be in design
211 // mode. Otherwise, the content is never editable by design mode of
212 // composed document. If we're in a native anonymous subtree, we should
213 // consider it with the host.
214 if (IsInNativeAnonymousSubtree()) {
215 nsIContent* host = GetClosestNativeAnonymousSubtreeRootParentOrHost();
216 MOZ_DIAGNOSTIC_ASSERT(host != this);
217 return host && host->IsInDesignMode();
220 // Otherwise, i.e., when it's in a shadow tree which is not created by us,
221 // the node is not editable by design mode (but it's possible that it may be
222 // editable if this node is in `contenteditable` element in the shadow tree).
223 return false;
226 inline void nsIContent::HandleInsertionToOrRemovalFromSlot() {
227 using mozilla::dom::HTMLSlotElement;
229 MOZ_ASSERT(GetParentElement());
230 if (!IsInShadowTree() || IsRootOfNativeAnonymousSubtree()) {
231 return;
233 HTMLSlotElement* slot = HTMLSlotElement::FromNode(mParent);
234 if (!slot) {
235 return;
237 // If parent's root is a shadow root, and parent is a slot whose
238 // assigned nodes is the empty list, then run signal a slot change for
239 // parent.
240 if (slot->AssignedNodes().IsEmpty()) {
241 slot->EnqueueSlotChangeEvent();
245 inline void nsIContent::HandleShadowDOMRelatedInsertionSteps(bool aHadParent) {
246 using mozilla::dom::Element;
247 using mozilla::dom::ShadowRoot;
249 if (!aHadParent) {
250 if (Element* parentElement = Element::FromNode(mParent)) {
251 if (ShadowRoot* shadow = parentElement->GetShadowRoot()) {
252 shadow->MaybeSlotHostChild(*this);
254 HandleInsertionToOrRemovalFromSlot();
259 inline void nsIContent::HandleShadowDOMRelatedRemovalSteps(bool aNullParent) {
260 using mozilla::dom::Element;
261 using mozilla::dom::ShadowRoot;
263 if (aNullParent) {
264 // FIXME(emilio, bug 1577141): FromNodeOrNull rather than just FromNode
265 // because XBL likes to call UnbindFromTree at very odd times (with already
266 // disconnected anonymous content subtrees).
267 if (Element* parentElement = Element::FromNodeOrNull(mParent)) {
268 if (ShadowRoot* shadow = parentElement->GetShadowRoot()) {
269 shadow->MaybeUnslotHostChild(*this);
271 HandleInsertionToOrRemovalFromSlot();
276 #endif // nsIContentInlines_h