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