tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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