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"
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");
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 {
56 return AsElement()->GetShadowRoot();
59 template <nsINode::FlattenedParentType aType
>
60 static inline nsINode
* GetFlattenedTreeParentNode(const nsINode
* aNode
) {
61 if (!aNode
->IsContent()) {
65 nsINode
* parent
= aNode
->GetParentNode();
66 if (!parent
|| !parent
->IsContent()) {
70 const nsIContent
* content
= aNode
->AsContent();
71 nsIContent
* parentAsContent
= parent
->AsContent();
73 if (aType
== nsINode::eForStyle
&&
74 content
->IsRootOfNativeAnonymousSubtree() &&
75 parentAsContent
== content
->OwnerDoc()->GetRootElement()) {
77 content
->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent
);
78 return docLevel
? content
->OwnerDocAsNode() : parent
;
81 if (content
->IsRootOfNativeAnonymousSubtree()) {
85 if (parentAsContent
->GetShadowRoot()) {
86 // If it's not assigned to any slot it's not part of the flat tree, and thus
88 return content
->GetAssignedSlot();
91 if (parentAsContent
->IsInShadowTree()) {
92 if (auto* slot
= mozilla::dom::HTMLSlotElement::FromNode(parentAsContent
)) {
93 // If the assigned nodes list is empty, we're fallback content which is
94 // active, otherwise we are not part of the flat tree.
95 return slot
->AssignedNodes().IsEmpty() ? parent
: nullptr;
98 if (auto* shadowRoot
=
99 mozilla::dom::ShadowRoot::FromNode(parentAsContent
)) {
100 return shadowRoot
->GetHost();
108 inline nsINode
* nsINode::GetFlattenedTreeParentNode() const {
109 return ::GetFlattenedTreeParentNode
<nsINode::eNotForStyle
>(this);
112 inline nsIContent
* nsIContent::GetFlattenedTreeParent() const {
113 nsINode
* parent
= GetFlattenedTreeParentNode();
114 return (parent
&& parent
->IsContent()) ? parent
->AsContent() : nullptr;
117 inline bool nsIContent::IsEventAttributeName(nsAtom
* aName
) {
118 const char16_t
* name
= aName
->GetUTF16String();
119 if (name
[0] != 'o' || name
[1] != 'n') {
123 return IsEventAttributeNameInternal(aName
);
126 inline nsINode
* nsINode::GetFlattenedTreeParentNodeForStyle() const {
127 return ::GetFlattenedTreeParentNode
<nsINode::eForStyle
>(this);
130 inline bool nsINode::NodeOrAncestorHasDirAuto() const {
131 return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto());
134 inline bool nsINode::IsEditable() const {
135 if (HasFlag(NODE_IS_EDITABLE
)) {
136 // The node is in an editable contentEditable subtree.
140 // All editable anonymous content should be made explicitly editable via the
141 // NODE_IS_EDITABLE flag.
142 if (IsInNativeAnonymousSubtree()) {
146 // Check if the node is in a document and the document is in designMode.
147 return IsInDesignMode();
150 inline bool nsINode::IsInDesignMode() const {
151 if (!OwnerDoc()->HasFlag(NODE_IS_EDITABLE
)) {
156 return HasFlag(NODE_IS_EDITABLE
);
159 // NOTE(emilio): If you change this to be the composed doc you also need to
160 // change NotifyEditableStateChange() in Document.cpp.
161 // NOTE(masayuki): Perhaps, we should keep this behavior because of
163 if (IsInUncomposedDoc() && GetUncomposedDoc()->HasFlag(NODE_IS_EDITABLE
)) {
167 // FYI: In design mode, form controls don't work as usual. For example,
168 // <input type=text> isn't focusable but can be deleted and replaced
169 // with typed text. <select> is also not focusable but always selected
170 // all to be deleted or replaced. On the other hand, newer controls
171 // don't behave as the traditional controls. For example, data/time
172 // picker can be opened and change the value from the picker. And also
173 // the buttons of <video controls> work as usual. On the other hand,
174 // their UI (i.e., nodes in their shadow tree) are not editable.
175 // Therefore, we need special handling for nodes in anonymous subtree
176 // unless we fix <https://bugzilla.mozilla.org/show_bug.cgi?id=1734512>.
178 // If the shadow host is not in design mode, this can never be in design
179 // mode. Otherwise, the content is never editable by design mode of
180 // composed document. If we're in a native anonymous subtree, we should
181 // consider it with the host.
182 if (IsInNativeAnonymousSubtree()) {
183 nsIContent
* host
= GetClosestNativeAnonymousSubtreeRootParentOrHost();
184 MOZ_DIAGNOSTIC_ASSERT(host
!= this);
185 return host
&& host
->IsInDesignMode();
188 // Otherwise, i.e., when it's in a shadow tree which is not created by us,
189 // the node is not editable by design mode (but it's possible that it may be
190 // editable if this node is in `contenteditable` element in the shadow tree).
194 inline void nsIContent::HandleInsertionToOrRemovalFromSlot() {
195 using mozilla::dom::HTMLSlotElement
;
197 MOZ_ASSERT(GetParentElement());
198 if (!IsInShadowTree() || IsRootOfNativeAnonymousSubtree()) {
201 HTMLSlotElement
* slot
= HTMLSlotElement::FromNode(mParent
);
205 // If parent's root is a shadow root, and parent is a slot whose
206 // assigned nodes is the empty list, then run signal a slot change for
208 if (slot
->AssignedNodes().IsEmpty()) {
209 slot
->EnqueueSlotChangeEvent();
213 inline void nsIContent::HandleShadowDOMRelatedInsertionSteps(bool aHadParent
) {
214 using mozilla::dom::Element
;
215 using mozilla::dom::ShadowRoot
;
218 if (Element
* parentElement
= Element::FromNode(mParent
)) {
219 if (ShadowRoot
* shadow
= parentElement
->GetShadowRoot()) {
220 shadow
->MaybeSlotHostChild(*this);
222 HandleInsertionToOrRemovalFromSlot();
227 inline void nsIContent::HandleShadowDOMRelatedRemovalSteps(bool aNullParent
) {
228 using mozilla::dom::Element
;
229 using mozilla::dom::ShadowRoot
;
232 // FIXME(emilio, bug 1577141): FromNodeOrNull rather than just FromNode
233 // because XBL likes to call UnbindFromTree at very odd times (with already
234 // disconnected anonymous content subtrees).
235 if (Element
* parentElement
= Element::FromNodeOrNull(mParent
)) {
236 if (ShadowRoot
* shadow
= parentElement
->GetShadowRoot()) {
237 shadow
->MaybeUnslotHostChild(*this);
239 HandleInsertionToOrRemovalFromSlot();
244 #endif // nsIContentInlines_h