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/. */
6 
7 #ifndef nsIContentInlines_h
8 #define nsIContentInlines_h
9 
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"
18 
IsUAWidget()19 inline bool nsINode::IsUAWidget() const {
20   auto* shadow = mozilla::dom::ShadowRoot::FromNode(this);
21   return shadow && shadow->IsUAWidget();
22 }
23 
IsInUAWidget()24 inline bool nsINode::IsInUAWidget() const {
25   if (!IsInShadowTree()) {
26     return false;
27   }
28   mozilla::dom::ShadowRoot* shadow = AsContent()->GetContainingShadow();
29   return shadow && shadow->IsUAWidget();
30 }
31 
IsRootOfChromeAccessOnlySubtree()32 inline bool nsINode::IsRootOfChromeAccessOnlySubtree() const {
33   return IsRootOfNativeAnonymousSubtree() || IsUAWidget();
34 }
35 
IsInHTMLDocument()36 inline bool nsIContent::IsInHTMLDocument() const {
37   return OwnerDoc()->IsHTMLDocument();
38 }
39 
IsInChromeDocument()40 inline bool nsIContent::IsInChromeDocument() const {
41   return nsContentUtils::IsChromeDoc(OwnerDoc());
42 }
43 
SetPrimaryFrame(nsIFrame * aFrame)44 inline void nsIContent::SetPrimaryFrame(nsIFrame* aFrame) {
45   MOZ_ASSERT(IsInUncomposedDoc() || IsInShadowTree(), "This will end badly!");
46 
47   // FIXME bug 749326
48   NS_ASSERTION(!aFrame || !mPrimaryFrame || aFrame == mPrimaryFrame,
49                "Losing track of existing primary frame");
50 
51   if (aFrame) {
52     MOZ_ASSERT(!aFrame->IsPlaceholderFrame());
53     if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
54         aFrame->GetContent() == this) {
55       aFrame->SetIsPrimaryFrame(true);
56     }
57   } else if (nsIFrame* currentPrimaryFrame = GetPrimaryFrame()) {
58     if (MOZ_LIKELY(!IsHTMLElement(nsGkAtoms::area)) ||
59         currentPrimaryFrame->GetContent() == this) {
60       currentPrimaryFrame->SetIsPrimaryFrame(false);
61     }
62   }
63 
64   mPrimaryFrame = aFrame;
65 }
66 
GetShadowRoot()67 inline mozilla::dom::ShadowRoot* nsIContent::GetShadowRoot() const {
68   if (!IsElement()) {
69     return nullptr;
70   }
71 
72   return AsElement()->GetShadowRoot();
73 }
74 
75 template <nsINode::FlattenedParentType aType>
GetFlattenedTreeParentNode(const nsINode * aNode)76 static inline nsINode* GetFlattenedTreeParentNode(const nsINode* aNode) {
77   if (!aNode->IsContent()) {
78     return nullptr;
79   }
80 
81   nsINode* parent = aNode->GetParentNode();
82   if (!parent || !parent->IsContent()) {
83     return parent;
84   }
85 
86   const nsIContent* content = aNode->AsContent();
87   nsIContent* parentAsContent = parent->AsContent();
88 
89   if (aType == nsINode::eForStyle &&
90       content->IsRootOfNativeAnonymousSubtree() &&
91       parentAsContent == content->OwnerDoc()->GetRootElement()) {
92     const bool docLevel =
93         content->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent);
94     return docLevel ? content->OwnerDocAsNode() : parent;
95   }
96 
97   if (content->IsRootOfNativeAnonymousSubtree()) {
98     return parent;
99   }
100 
101   if (parentAsContent->GetShadowRoot()) {
102     // If it's not assigned to any slot it's not part of the flat tree, and thus
103     // we return null.
104     return content->GetAssignedSlot();
105   }
106 
107   if (parentAsContent->IsInShadowTree()) {
108     if (auto* slot = mozilla::dom::HTMLSlotElement::FromNode(parentAsContent)) {
109       // If the assigned nodes list is empty, we're fallback content which is
110       // active, otherwise we are not part of the flat tree.
111       return slot->AssignedNodes().IsEmpty() ? parent : nullptr;
112     }
113 
114     if (auto* shadowRoot =
115             mozilla::dom::ShadowRoot::FromNode(parentAsContent)) {
116       return shadowRoot->GetHost();
117     }
118   }
119 
120   // Common case.
121   return parent;
122 }
123 
GetFlattenedTreeParentNode()124 inline nsINode* nsINode::GetFlattenedTreeParentNode() const {
125   return ::GetFlattenedTreeParentNode<nsINode::eNotForStyle>(this);
126 }
127 
GetFlattenedTreeParent()128 inline nsIContent* nsIContent::GetFlattenedTreeParent() const {
129   nsINode* parent = GetFlattenedTreeParentNode();
130   return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
131 }
132 
IsEventAttributeName(nsAtom * aName)133 inline bool nsIContent::IsEventAttributeName(nsAtom* aName) {
134   const char16_t* name = aName->GetUTF16String();
135   if (name[0] != 'o' || name[1] != 'n') {
136     return false;
137   }
138 
139   return IsEventAttributeNameInternal(aName);
140 }
141 
GetFlattenedTreeParentNodeForStyle()142 inline nsINode* nsINode::GetFlattenedTreeParentNodeForStyle() const {
143   return ::GetFlattenedTreeParentNode<nsINode::eForStyle>(this);
144 }
145 
NodeOrAncestorHasDirAuto()146 inline bool nsINode::NodeOrAncestorHasDirAuto() const {
147   return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto());
148 }
149 
IsEditable()150 inline bool nsINode::IsEditable() const {
151   if (HasFlag(NODE_IS_EDITABLE)) {
152     // The node is in an editable contentEditable subtree.
153     return true;
154   }
155 
156   // All editable anonymous content should be made explicitly editable via the
157   // NODE_IS_EDITABLE flag.
158   if (IsInNativeAnonymousSubtree()) {
159     return false;
160   }
161 
162   // Check if the node is in a document and the document is in designMode.
163   return IsInDesignMode();
164 }
165 
IsInDesignMode()166 inline bool nsINode::IsInDesignMode() const {
167   if (!OwnerDoc()->HasFlag(NODE_IS_EDITABLE)) {
168     return false;
169   }
170 
171   if (IsDocument()) {
172     return HasFlag(NODE_IS_EDITABLE);
173   }
174 
175   // NOTE(emilio): If you change this to be the composed doc you also need to
176   // change NotifyEditableStateChange() in Document.cpp.
177   // NOTE(masayuki): Perhaps, we should keep this behavior because of
178   // web-compat.
179   if (IsInUncomposedDoc() && GetUncomposedDoc()->HasFlag(NODE_IS_EDITABLE)) {
180     return true;
181   }
182 
183   // FYI: In design mode, form controls don't work as usual.  For example,
184   //      <input type=text> isn't focusable but can be deleted and replaced
185   //      with typed text. <select> is also not focusable but always selected
186   //      all to be deleted or replaced.  On the other hand, newer controls
187   //      don't behave as the traditional controls.  For example, data/time
188   //      picker can be opened and change the value from the picker.  And also
189   //      the buttons of <video controls> work as usual.  On the other hand,
190   //      their UI (i.e., nodes in their shadow tree) are not editable.
191   //      Therefore, we need special handling for nodes in anonymous subtree
192   //      unless we fix <https://bugzilla.mozilla.org/show_bug.cgi?id=1734512>.
193 
194   // If the shadow host is not in design mode, this can never be in design
195   // mode.  Otherwise, the content is never editable by design mode of
196   // composed document.
197   if (IsInUAWidget()) {
198     nsIContent* host = GetContainingShadowHost();
199     MOZ_DIAGNOSTIC_ASSERT(host != this);
200     return host && host->IsInDesignMode();
201   }
202   MOZ_ASSERT(!IsUAWidget());
203 
204   // If we're in a native anonymous subtree, we should consider it with the
205   // host.
206   if (IsInNativeAnonymousSubtree()) {
207     nsIContent* host = GetClosestNativeAnonymousSubtreeRootParent();
208     MOZ_DIAGNOSTIC_ASSERT(host != this);
209     return host && host->IsInDesignMode();
210   }
211 
212   // Otherwise, i.e., when it's in a shadow tree which is not created by us,
213   // the node is not editable by design mode (but it's possible that it may be
214   // editable if this node is in `contenteditable` element in the shadow tree).
215   return false;
216 }
217 
HandleInsertionToOrRemovalFromSlot()218 inline void nsIContent::HandleInsertionToOrRemovalFromSlot() {
219   using mozilla::dom::HTMLSlotElement;
220 
221   MOZ_ASSERT(GetParentElement());
222   if (!IsInShadowTree() || IsRootOfNativeAnonymousSubtree()) {
223     return;
224   }
225   HTMLSlotElement* slot = HTMLSlotElement::FromNode(mParent);
226   if (!slot) {
227     return;
228   }
229   // If parent's root is a shadow root, and parent is a slot whose
230   // assigned nodes is the empty list, then run signal a slot change for
231   // parent.
232   if (slot->AssignedNodes().IsEmpty()) {
233     slot->EnqueueSlotChangeEvent();
234   }
235 }
236 
HandleShadowDOMRelatedInsertionSteps(bool aHadParent)237 inline void nsIContent::HandleShadowDOMRelatedInsertionSteps(bool aHadParent) {
238   using mozilla::dom::Element;
239   using mozilla::dom::ShadowRoot;
240 
241   if (!aHadParent) {
242     if (Element* parentElement = Element::FromNode(mParent)) {
243       if (ShadowRoot* shadow = parentElement->GetShadowRoot()) {
244         shadow->MaybeSlotHostChild(*this);
245       }
246       HandleInsertionToOrRemovalFromSlot();
247     }
248   }
249 }
250 
HandleShadowDOMRelatedRemovalSteps(bool aNullParent)251 inline void nsIContent::HandleShadowDOMRelatedRemovalSteps(bool aNullParent) {
252   using mozilla::dom::Element;
253   using mozilla::dom::ShadowRoot;
254 
255   if (aNullParent) {
256     // FIXME(emilio, bug 1577141): FromNodeOrNull rather than just FromNode
257     // because XBL likes to call UnbindFromTree at very odd times (with already
258     // disconnected anonymous content subtrees).
259     if (Element* parentElement = Element::FromNodeOrNull(mParent)) {
260       if (ShadowRoot* shadow = parentElement->GetShadowRoot()) {
261         shadow->MaybeUnslotHostChild(*this);
262       }
263       HandleInsertionToOrRemovalFromSlot();
264     }
265   }
266 }
267 
268 #endif  // nsIContentInlines_h
269