tor-browser

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

nsIContent.h (28378B)


      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 #ifndef nsIContent_h___
      7 #define nsIContent_h___
      8 
      9 #include "mozilla/FlushType.h"
     10 #include "nsINode.h"
     11 #include "nsStringFwd.h"
     12 
     13 // Forward declarations
     14 class nsIURI;
     15 class nsIFrame;
     16 
     17 namespace mozilla {
     18 enum class IsFocusableFlags : uint8_t;
     19 class EventChainPreVisitor;
     20 class HTMLEditor;
     21 struct URLExtraData;
     22 namespace dom {
     23 struct BindContext;
     24 class CharacterDataBuffer;
     25 struct UnbindContext;
     26 class ShadowRoot;
     27 class HTMLSlotElement;
     28 }  // namespace dom
     29 namespace widget {
     30 enum class IMEEnabled;
     31 struct IMEState;
     32 }  // namespace widget
     33 }  // namespace mozilla
     34 
     35 struct Focusable {
     36  bool mFocusable = false;
     37  // The computed tab index:
     38  //         < 0 if not tabbable
     39  //         == 0 if in normal tab order
     40  //         > 0 can be tabbed to in the order specified by this value
     41  int32_t mTabIndex = -1;
     42  explicit operator bool() const { return mFocusable; }
     43  [[nodiscard]] bool IsTabbable() const { return mFocusable && mTabIndex >= 0; }
     44 };
     45 
     46 // IID for the nsIContent interface
     47 // Must be kept in sync with xpcom/rust/xpcom/src/interfaces/nonidl.rs
     48 #define NS_ICONTENT_IID \
     49  {0x8e1bab9d, 0x8815, 0x4d2c, {0xa2, 0x4d, 0x7a, 0xba, 0x52, 0x39, 0xdc, 0x22}}
     50 
     51 /**
     52 * A node of content in a document's content model. This interface
     53 * is supported by all content objects.
     54 */
     55 class nsIContent : public nsINode {
     56 public:
     57  using IMEEnabled = mozilla::widget::IMEEnabled;
     58  using IMEState = mozilla::widget::IMEState;
     59  using BindContext = mozilla::dom::BindContext;
     60  using UnbindContext = mozilla::dom::UnbindContext;
     61 
     62  void ConstructUbiNode(void* storage) override;
     63 
     64 #ifdef MOZILLA_INTERNAL_API
     65  // If you're using the external API, the only thing you can know about
     66  // nsIContent is that it exists with an IID
     67 
     68  explicit nsIContent(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     69      : nsINode(std::move(aNodeInfo)) {
     70    MOZ_ASSERT(mNodeInfo);
     71    MOZ_ASSERT(static_cast<nsINode*>(this) == reinterpret_cast<nsINode*>(this));
     72    SetNodeIsContent();
     73  }
     74 #endif  // MOZILLA_INTERNAL_API
     75 
     76  NS_INLINE_DECL_STATIC_IID(NS_ICONTENT_IID)
     77 
     78  NS_DECL_ISUPPORTS_INHERITED
     79  NS_IMETHOD_(void) DeleteCycleCollectable(void) final;
     80 
     81  NS_DECL_CYCLE_COLLECTION_CLASS(nsIContent)
     82 
     83  NS_DECL_DOMARENA_DESTROY
     84 
     85  NS_IMPL_FROMNODE_HELPER(nsIContent, IsContent())
     86 
     87  /**
     88   * Bind this content node to a tree.  If this method throws, the caller must
     89   * call UnbindFromTree() on the node.  In the typical case of a node being
     90   * appended to a parent, this will be called after the node has been added to
     91   * the parent's child list and before nsIDocumentObserver notifications for
     92   * the addition are dispatched.
     93   * BindContext propagates various information down the subtree; see its
     94   * documentation to know how to set it up.
     95   * @param aParent The new parent node for the content node. May be a document.
     96   * @note This method must not be called by consumers of nsIContent on a node
     97   *       that is already bound to a tree.  Call UnbindFromTree first.
     98   * @note This method will handle rebinding descendants appropriately (eg
     99   *       changing their binding parent as needed).
    100   * @note This method does not add the content node to aParent's child list
    101   * @throws NS_ERROR_OUT_OF_MEMORY if that happens
    102   *
    103   * TODO(emilio): Should we move to nsIContent::BindToTree most of the
    104   * FragmentOrElement / CharacterData duplicated code?
    105   */
    106  virtual nsresult BindToTree(BindContext&, nsINode& aParent) = 0;
    107 
    108  /**
    109   * Unbind this content node from a tree.  This will set its current document
    110   * and binding parent to null.  In the typical case of a node being removed
    111   * from a parent, this will be called after it has been removed from the
    112   * parent's child list and after the nsIDocumentObserver notifications for
    113   * the removal have been dispatched.
    114   * @note This method is safe to call on nodes that are not bound to a tree.
    115   */
    116  virtual void UnbindFromTree(UnbindContext&) = 0;
    117  void UnbindFromTree(nsINode* aNewParent = nullptr,
    118                      const BatchRemovalState* aBatchState = nullptr);
    119 
    120  enum {
    121    /**
    122     * All XBL flattened tree children of the node, as well as :before and
    123     * :after anonymous content and native anonymous children.
    124     *
    125     * @note the result children order is
    126     *   1. :before generated node
    127     *   2. Shadow DOM flattened tree children of this node
    128     *   3. native anonymous nodes
    129     *   4. :after generated node
    130     */
    131    eAllChildren = 0,
    132 
    133    /**
    134     * Skip native anonymous content created for placeholder of HTML input.
    135     */
    136    eSkipPlaceholderContent = 1 << 0,
    137 
    138    /**
    139     * Skip native anonymous content created by ancestor frames of the root
    140     * element's primary frame, such as scrollbar elements created by the root
    141     * scroll frame.
    142     */
    143    eSkipDocumentLevelNativeAnonymousContent = 1 << 1,
    144  };
    145 
    146  /**
    147   * Makes this content anonymous
    148   * @see nsIAnonymousContentCreator
    149   */
    150  void SetIsNativeAnonymousRoot() {
    151    SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
    152             NODE_IS_NATIVE_ANONYMOUS_ROOT);
    153  }
    154 
    155  /**
    156   * Returns |this| if it is not chrome-only/native anonymous, otherwise
    157   * first non chrome-only/native anonymous ancestor.
    158   */
    159  nsIContent* FindFirstNonChromeOnlyAccessContent() const;
    160 
    161  /**
    162   * Return true iff this node is in an HTML document (in the HTML5 sense of
    163   * the term, i.e. not in an XHTML/XML document).
    164   */
    165  inline bool IsInHTMLDocument() const;
    166 
    167  /**
    168   * Returns true if in a chrome document
    169   */
    170  inline bool IsInChromeDocument() const;
    171 
    172  /**
    173   * Get the namespace that this element's tag is defined in
    174   * @return the namespace
    175   */
    176  inline int32_t GetNameSpaceID() const { return mNodeInfo->NamespaceID(); }
    177 
    178  inline bool IsHTMLElement() const {
    179    return IsInNamespace(kNameSpaceID_XHTML);
    180  }
    181 
    182  inline bool IsHTMLElement(const nsAtom* aTag) const {
    183    return mNodeInfo->Equals(aTag, kNameSpaceID_XHTML);
    184  }
    185 
    186  template <typename First, typename... Args>
    187  inline bool IsAnyOfHTMLElements(First aFirst, Args... aArgs) const {
    188    return IsHTMLElement() && IsNodeInternal(aFirst, aArgs...);
    189  }
    190 
    191  inline bool IsSVGElement() const { return IsInNamespace(kNameSpaceID_SVG); }
    192 
    193  inline bool IsSVGElement(const nsAtom* aTag) const {
    194    return mNodeInfo->Equals(aTag, kNameSpaceID_SVG);
    195  }
    196 
    197  template <typename First, typename... Args>
    198  inline bool IsAnyOfSVGElements(First aFirst, Args... aArgs) const {
    199    return IsSVGElement() && IsNodeInternal(aFirst, aArgs...);
    200  }
    201 
    202  inline bool IsXULElement() const { return IsInNamespace(kNameSpaceID_XUL); }
    203 
    204  inline bool IsXULElement(const nsAtom* aTag) const {
    205    return mNodeInfo->Equals(aTag, kNameSpaceID_XUL);
    206  }
    207 
    208  template <typename First, typename... Args>
    209  inline bool IsAnyOfXULElements(First aFirst, Args... aArgs) const {
    210    return IsXULElement() && IsNodeInternal(aFirst, aArgs...);
    211  }
    212 
    213  inline bool IsMathMLElement() const {
    214    return IsInNamespace(kNameSpaceID_MathML);
    215  }
    216 
    217  inline bool IsMathMLElement(const nsAtom* aTag) const {
    218    return mNodeInfo->Equals(aTag, kNameSpaceID_MathML);
    219  }
    220 
    221  template <typename First, typename... Args>
    222  inline bool IsAnyOfMathMLElements(First aFirst, Args... aArgs) const {
    223    return IsMathMLElement() && IsNodeInternal(aFirst, aArgs...);
    224  }
    225 
    226  /**
    227   * Get direct access (but read only) to the text in the text content.
    228   * NOTE: For elements this is *not* the concatenation of all text children,
    229   * it is simply null;
    230   */
    231  virtual const mozilla::dom::CharacterDataBuffer* GetCharacterDataBuffer()
    232      const = 0;
    233 
    234  /**
    235   * Get the length of the text content.
    236   * NOTE: This should not be called on elements.
    237   */
    238  virtual uint32_t TextLength() const = 0;
    239 
    240  /**
    241   * Determines if an event attribute name (such as onclick) is valid for
    242   * a given element type.
    243   * @note calls nsContentUtils::IsEventAttributeName with right flag
    244   * @note *Internal is overridden by subclasses as needed
    245   * @param aName the event name to look up
    246   */
    247  bool IsEventAttributeName(nsAtom* aName);
    248 
    249  virtual bool IsEventAttributeNameInternal(nsAtom* aName) { return false; }
    250 
    251  /**
    252   * Query method to see if the frame is nothing but whitespace
    253   * NOTE: Always returns false for elements
    254   */
    255  virtual bool TextIsOnlyWhitespace() = 0;
    256 
    257  /**
    258   * Thread-safe version of TextIsOnlyWhitespace.
    259   */
    260  virtual bool ThreadSafeTextIsOnlyWhitespace() const = 0;
    261 
    262  /**
    263   * Check if this content is focusable and in the current tab order.
    264   * Note: most callers should use nsIFrame::IsFocusable() instead as it
    265   *       checks visibility and other layout factors as well.
    266   * Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
    267   * For example, only the selected radio button in a group is in the
    268   * tab order, unless the radio group has no selection in which case
    269   * all of the visible, non-disabled radio buttons in the group are
    270   * in the tab order. On the other hand, all of the visible, non-disabled
    271   * radio buttons are always focusable via clicking or script.
    272   * Also, depending on either the accessibility.tabfocus pref or
    273   * a system setting (nowadays: Full keyboard access, mac only)
    274   * some widgets may be focusable but removed from the tab order.
    275   * @return whether the content is focusable via mouse, kbd or script.
    276   */
    277  virtual Focusable IsFocusableWithoutStyle(
    278      mozilla::IsFocusableFlags = mozilla::IsFocusableFlags(0));
    279 
    280  // https://html.spec.whatwg.org/multipage/interaction.html#focus-delegate
    281  mozilla::dom::Element* GetFocusDelegate(mozilla::IsFocusableFlags) const;
    282 
    283  // https://html.spec.whatwg.org/multipage/interaction.html#autofocus-delegate
    284  mozilla::dom::Element* GetAutofocusDelegate(mozilla::IsFocusableFlags) const;
    285 
    286  /*
    287   * Get desired IME state for the content.
    288   *
    289   * @return The desired IME status for the content.
    290   *         This is a combination of an IME enabled value and
    291   *         an IME open value of widget::IMEState.
    292   *         If you return IMEEnabled::Disabled, you should not set the OPEN
    293   *         nor CLOSE value.
    294   *         IMEEnabled::Password should be returned only from password editor,
    295   *         this value has a special meaning. It is used as alternative of
    296   *         IMEEnabled::Disabled. IMEENabled::Plugin should be returned only
    297   *         when plug-in has focus.  When a plug-in is focused content, we
    298   *         should send native events directly. Because we don't process some
    299   *         native events, but they may be needed by the plug-in.
    300   */
    301  virtual IMEState GetDesiredIMEState();
    302 
    303  /**
    304   * Gets the root of the node tree for this content if it is in a shadow tree.
    305   *
    306   * @return The ShadowRoot that is the root of the node tree.
    307   */
    308  mozilla::dom::ShadowRoot* GetContainingShadow() const {
    309    const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
    310    return slots ? slots->mContainingShadow.get() : nullptr;
    311  }
    312 
    313  /**
    314   * Gets the assigned slot associated with this content.
    315   *
    316   * @return The assigned slot element or null.
    317   */
    318  mozilla::dom::HTMLSlotElement* GetAssignedSlot() const {
    319    const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
    320    return slots ? slots->mAssignedSlot.get() : nullptr;
    321  }
    322 
    323  /**
    324   * Sets the assigned slot associated with this content.
    325   *
    326   * @param aSlot The assigned slot.
    327   */
    328  void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot);
    329 
    330  /**
    331   * Gets the assigned slot associated with this content based on parent's
    332   * shadow root mode. Returns null if parent's shadow root is "closed".
    333   * https://dom.spec.whatwg.org/#dom-slotable-assignedslot
    334   *
    335   * @return The assigned slot element or null.
    336   */
    337  mozilla::dom::HTMLSlotElement* GetAssignedSlotByMode() const;
    338 
    339  mozilla::dom::HTMLSlotElement* GetManualSlotAssignment() const {
    340    const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
    341    return slots ? slots->mManualSlotAssignment : nullptr;
    342  }
    343 
    344  void SetManualSlotAssignment(mozilla::dom::HTMLSlotElement* aSlot) {
    345    MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
    346    ExtendedContentSlots()->mManualSlotAssignment = aSlot;
    347  }
    348 
    349  /**
    350   * Same as GetFlattenedTreeParentNode, but returns null if the parent is
    351   * non-nsIContent.
    352   */
    353  inline nsIContent* GetFlattenedTreeParent() const;
    354 
    355  // This method is used to provide a similar CanStartSelection behaviour in
    356  // Chromium, see the link for exact Chromium's behaviour.
    357  // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/dom/node.cc;l=1909;drc=58fb75d86a0ad2642beec2d6c16b1e6c008e33cd;bpv=1;bpt=1
    358  //
    359  // Basically, Chromium has this method to decide if the selection should be
    360  // changed or remain at the current element when an element is focused. This
    361  // creates a webcompat issue for when window.getSelection().toString()
    362  // is called, web authors expect Firefox to serialize the old content, but
    363  // Firefox decides to serialize a different content.
    364  //
    365  // This method, along with PresShell::mLastSelectionForToString is used to
    366  // address this webcompat issue.
    367  //
    368  // THIS METHOD SHOULD BE USED WITH EXTRA CAUTIOUS.
    369  bool CanStartSelectionAsWebCompatHack() const;
    370 
    371 protected:
    372  // Handles getting inserted or removed directly under a <slot> element.
    373  // This is meant to only be called from the two functions below.
    374  inline void HandleInsertionToOrRemovalFromSlot();
    375 
    376  // Handles Shadow-DOM-related state tracking. Meant to be called near the
    377  // end of BindToTree(), only if the tree we're in actually changed, that is,
    378  // after the subtree has been bound to the new parent.
    379  inline void HandleShadowDOMRelatedInsertionSteps(bool aHadParent);
    380 
    381  // Handles Shadow-DOM related state tracking. Meant to be called near the
    382  // beginning of UnbindFromTree(), before the node has lost the reference to
    383  // its parent.
    384  inline void HandleShadowDOMRelatedRemovalSteps(bool aNullParent);
    385 
    386 public:
    387  /**
    388   * This method is called when the parser finishes creating the element.  This
    389   * particularly means that it has done everything you would expect it to have
    390   * done after it encounters the > at the end of the tag (for HTML or XML).
    391   * This includes setting the attributes, setting the document / form, and
    392   * placing the element into the tree at its proper place.
    393   *
    394   * For container elements, this is called *before* any of the children are
    395   * created or added into the tree.
    396   *
    397   * NOTE: this is only called for elements listed in
    398   * RequiresDoneCreatingElement. This is an efficiency measure.
    399   *
    400   * If you also need to determine whether the parser is the one creating your
    401   * element (through createElement() or cloneNode() generally) then add a
    402   * uint32_t aFromParser to the NS_NewXXX() constructor for your element and
    403   * have the parser pass the appropriate flags. See HTMLInputElement.cpp and
    404   * nsHtml5TreeBuilder::elementPopped().
    405   *
    406   * DO NOT USE THIS METHOD to get around the fact that it's hard to deal with
    407   * attributes dynamically.  If you make attributes affect your element from
    408   * this method, it will only happen on initialization and JavaScript will not
    409   * be able to create elements (which requires them to first create the
    410   * element and then call setAttribute() directly, at which point
    411   * DoneCreatingElement() has already been called and is out of the picture).
    412   */
    413  virtual void DoneCreatingElement() {}
    414 
    415  /**
    416   * This method is called when the parser finishes creating the element's
    417   * children, if any are present.
    418   *
    419   * NOTE: this is only called for elements listed in
    420   * RequiresDoneAddingChildren. This is an efficiency measure.
    421   *
    422   * If you also need to determine whether the parser is the one creating your
    423   * element (through createElement() or cloneNode() generally) then add a
    424   * boolean aFromParser to the NS_NewXXX() constructor for your element and
    425   * have the parser pass true.  See HTMLInputElement.cpp and
    426   * nsHtml5TreeBuilder::elementPopped().
    427   *
    428   * @param aHaveNotified Whether there has been a
    429   *        ContentInserted/ContentAppended notification for this content node
    430   *        yet.
    431   */
    432  virtual void DoneAddingChildren(bool aHaveNotified) {}
    433 
    434  /**
    435   * Returns true if an element needs its DoneCreatingElement method to be
    436   * called after it has been created.
    437   * @see nsIContent::DoneCreatingElement
    438   *
    439   * @param aNamespaceID the node's namespace ID
    440   * @param aName the node's tag name
    441   */
    442  static inline bool RequiresDoneCreatingElement(int32_t aNamespace,
    443                                                 nsAtom* aName) {
    444    if (aNamespace == kNameSpaceID_XHTML) {
    445      if (aName == nsGkAtoms::input || aName == nsGkAtoms::button ||
    446          aName == nsGkAtoms::audio || aName == nsGkAtoms::video) {
    447        MOZ_ASSERT(!RequiresDoneAddingChildren(aNamespace, aName),
    448                   "Both DoneCreatingElement and DoneAddingChildren on a "
    449                   "same element isn't supported.");
    450        return true;
    451      }
    452      if (aName->IsDynamic()) {
    453        // This could be a form-associated custom element, so check if its
    454        // name includes a -.
    455        nsDependentString name(aName->GetUTF16String());
    456        return name.Contains('-');
    457      }
    458    }
    459    return false;
    460  }
    461 
    462  /**
    463   * Returns true if an element needs its DoneAddingChildren method to be
    464   * called after all of its children have been added.
    465   * @see nsIContent::DoneAddingChildren
    466   *
    467   * @param aNamespace the node's namespace ID
    468   * @param aName the node's tag name
    469   */
    470  static inline bool RequiresDoneAddingChildren(int32_t aNamespace,
    471                                                nsAtom* aName) {
    472    return (aNamespace == kNameSpaceID_XHTML &&
    473            (aName == nsGkAtoms::select || aName == nsGkAtoms::textarea ||
    474             aName == nsGkAtoms::head || aName == nsGkAtoms::title ||
    475             aName == nsGkAtoms::object || aName == nsGkAtoms::output)) ||
    476           (aNamespace == kNameSpaceID_SVG && aName == nsGkAtoms::title) ||
    477           (aNamespace == kNameSpaceID_XUL && aName == nsGkAtoms::linkset);
    478  }
    479 
    480  /**
    481   * Get the ID of this content node (the atom corresponding to the
    482   * value of the id attribute).  This may be null if there is no ID.
    483   */
    484  nsAtom* GetID() const {
    485    if (HasID()) {
    486      return DoGetID();
    487    }
    488    return nullptr;
    489  }
    490 
    491  /**
    492   * Should be called when the node can become editable or when it can stop
    493   * being editable (for example when its contentEditable attribute changes,
    494   * when it is moved into an editable parent, ...).  If aNotify is true and
    495   * the node is an element, this will notify the state change.
    496   */
    497  virtual void UpdateEditableState(bool aNotify);
    498 
    499  /**
    500   * Destroy this node and its children. Ideally this shouldn't be needed
    501   * but for now we need to do it to break cycles.
    502   */
    503  virtual void DestroyContent() {}
    504 
    505  /**
    506   * Saves the form state of this node and its children.
    507   */
    508  virtual void SaveSubtreeState() = 0;
    509 
    510  /**
    511   * Getter and setter for our primary frame pointer.  This is the frame that
    512   * is most closely associated with the content. A frame is more closely
    513   * associated with the content than another frame if the one frame contains
    514   * directly or indirectly the other frame (e.g., when a frame is scrolled
    515   * there is a scroll frame that contains the frame being scrolled). This
    516   * frame is always the first continuation.
    517   *
    518   * In the case of absolutely positioned elements and floated elements, this
    519   * frame is the out of flow frame, not the placeholder.
    520   */
    521  nsIFrame* GetPrimaryFrame() const {
    522    return IsInComposedDoc() ? mPrimaryFrame : nullptr;
    523  }
    524 
    525  /**
    526   * Get the primary frame for this content with flushing
    527   *
    528   * @param aType the kind of flush to do, typically FlushType::Frames or
    529   *              FlushType::Layout
    530   * @return the primary frame
    531   */
    532  nsIFrame* GetPrimaryFrame(mozilla::FlushType aType);
    533 
    534  /**
    535   * Return true if the related frame is selectable or we need to treat the
    536   * content as selectable (e.g., an editable node, a text control).  If the
    537   * content does not have primary frame due to e.g., `display:contents`,
    538   * `display:none`, `ShadowRoot`, etc, this refers the computed `user-select`
    539   * style of this node.  If the `user-select` is `auto`, referring the same
    540   * things of closest ancestor elements or shadow DOM host.
    541   * NOTE: If this is a generated content like ::before or ::after or not
    542   * connected to a Document, this returns false.  I.e., this returns false for
    543   * DocumentFragment.
    544   * NOTE: Returning true does NOT mean that the content is selectable with a
    545   * user's operation.  E.g., can be selectable but invisible.
    546   */
    547  [[nodiscard]] bool IsSelectable() const;
    548 
    549  // Defined in nsIContentInlines.h because it needs nsIFrame.
    550  inline void SetPrimaryFrame(nsIFrame* aFrame);
    551 
    552  nsresult LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
    553                                      nsAString& aNamespaceURI) const;
    554 
    555  /**
    556   * If this content has independent selection, e.g., if this is input field
    557   * or textarea, this return TRUE.  Otherwise, false.
    558   */
    559  bool HasIndependentSelection() const;
    560 
    561  /**
    562   * If the content is a part of HTML editor, this returns editing
    563   * host content.  When the content is in designMode, this returns its body
    564   * element.  Also, when the content isn't editable, this returns null.
    565   */
    566  mozilla::dom::Element* GetEditingHost() const;
    567 
    568  bool SupportsLangAttr() const {
    569    return IsHTMLElement() || IsSVGElement() || IsXULElement();
    570  }
    571 
    572  /**
    573   * Determining language. Look at the nearest ancestor element that has a lang
    574   * attribute in the XML namespace or is an HTML/SVG element and has a lang in
    575   * no namespace attribute.
    576   *
    577   * Returns null if no language was specified. Can return the empty atom.
    578   */
    579  nsAtom* GetLang() const;
    580 
    581  bool GetLang(nsAString& aResult) const {
    582    if (auto* lang = GetLang()) {
    583      aResult.Assign(nsDependentAtomString(lang));
    584      return true;
    585    }
    586 
    587    return false;
    588  }
    589 
    590  // Overloaded from nsINode
    591  nsIURI* GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override;
    592 
    593  // Returns base URI for style attribute.
    594  nsIURI* GetBaseURIForStyleAttr() const;
    595 
    596  // Returns the URL data for style attribute.
    597  // If aSubjectPrincipal is passed, it should be the scripted principal
    598  // responsible for generating the URL data.
    599  already_AddRefed<mozilla::URLExtraData> GetURLDataForStyleAttr(
    600      nsIPrincipal* aSubjectPrincipal = nullptr) const;
    601 
    602  void GetEventTargetParent(mozilla::EventChainPreVisitor& aVisitor) override;
    603 
    604  bool IsPurple() const { return mRefCnt.IsPurple(); }
    605 
    606  void RemovePurple() { mRefCnt.RemovePurple(); }
    607 
    608  // Note, currently this doesn't handle the case when frame tree has multiple
    609  // references to the nsIContent object.
    610  bool OwnedOnlyByTheDOMAndFrameTrees() {
    611    return OwnedOnlyByTheDOMTree(GetPrimaryFrame() ? 1 : 0);
    612  }
    613 
    614  bool OwnedOnlyByTheDOMTree(uint32_t aExpectedRefs = 0) {
    615    uint32_t rc = mRefCnt.get();
    616    if (GetParent()) {
    617      --rc;
    618    }
    619    rc -= GetChildCount();
    620    return rc == aExpectedRefs;
    621  }
    622 
    623  /**
    624   * Use this method with designMode and contentEditable to check if the
    625   * node may need spellchecking.
    626   */
    627  bool InclusiveDescendantMayNeedSpellchecking(mozilla::HTMLEditor* aEditor);
    628 
    629 protected:
    630  /**
    631   * Lazily allocated extended slots to avoid
    632   * that may only be instantiated when a content object is accessed
    633   * through the DOM. Rather than burn actual slots in the content
    634   * objects for each of these instance variables, we put them off
    635   * in a side structure that's only allocated when the content is
    636   * accessed through the DOM.
    637   */
    638  class nsExtendedContentSlots {
    639   public:
    640    nsExtendedContentSlots();
    641    virtual ~nsExtendedContentSlots();
    642 
    643    virtual void TraverseExtendedSlots(nsCycleCollectionTraversalCallback&);
    644    virtual void UnlinkExtendedSlots(nsIContent&);
    645 
    646    virtual size_t SizeOfExcludingThis(
    647        mozilla::MallocSizeOf aMallocSizeOf) const;
    648 
    649    /**
    650     * @see nsIContent::GetContainingShadow
    651     */
    652    RefPtr<mozilla::dom::ShadowRoot> mContainingShadow;
    653 
    654    /**
    655     * @see nsIContent::GetAssignedSlot
    656     */
    657    RefPtr<mozilla::dom::HTMLSlotElement> mAssignedSlot;
    658 
    659    mozilla::dom::HTMLSlotElement* mManualSlotAssignment = nullptr;
    660  };
    661 
    662  class nsContentSlots : public nsINode::nsSlots {
    663   public:
    664    nsContentSlots() : mExtendedSlots(0) {}
    665 
    666    ~nsContentSlots() {
    667      if (!(mExtendedSlots & sNonOwningExtendedSlotsFlag)) {
    668        delete GetExtendedContentSlots();
    669      }
    670    }
    671 
    672    void Traverse(nsCycleCollectionTraversalCallback& aCb) override {
    673      nsINode::nsSlots::Traverse(aCb);
    674      if (mExtendedSlots) {
    675        GetExtendedContentSlots()->TraverseExtendedSlots(aCb);
    676      }
    677    }
    678 
    679    void Unlink(nsINode& aNode) override {
    680      nsINode::nsSlots::Unlink(aNode);
    681      if (mExtendedSlots) {
    682        GetExtendedContentSlots()->UnlinkExtendedSlots(*aNode.AsContent());
    683      }
    684    }
    685 
    686    void SetExtendedContentSlots(nsExtendedContentSlots* aSlots, bool aOwning) {
    687      mExtendedSlots = reinterpret_cast<uintptr_t>(aSlots);
    688      if (!aOwning) {
    689        mExtendedSlots |= sNonOwningExtendedSlotsFlag;
    690      }
    691    }
    692 
    693    // OwnsExtendedSlots returns true if we have no extended slots or if we
    694    // have extended slots and own them.
    695    bool OwnsExtendedSlots() const {
    696      return !(mExtendedSlots & sNonOwningExtendedSlotsFlag);
    697    }
    698 
    699    nsExtendedContentSlots* GetExtendedContentSlots() const {
    700      return reinterpret_cast<nsExtendedContentSlots*>(
    701          mExtendedSlots & ~sNonOwningExtendedSlotsFlag);
    702    }
    703 
    704   private:
    705    static const uintptr_t sNonOwningExtendedSlotsFlag = 1u;
    706 
    707    uintptr_t mExtendedSlots;
    708  };
    709 
    710  // Override from nsINode
    711  nsContentSlots* CreateSlots() override { return new nsContentSlots(); }
    712 
    713  nsContentSlots* ContentSlots() {
    714    return static_cast<nsContentSlots*>(Slots());
    715  }
    716 
    717  const nsContentSlots* GetExistingContentSlots() const {
    718    return static_cast<nsContentSlots*>(GetExistingSlots());
    719  }
    720 
    721  nsContentSlots* GetExistingContentSlots() {
    722    return static_cast<nsContentSlots*>(GetExistingSlots());
    723  }
    724 
    725  virtual nsExtendedContentSlots* CreateExtendedSlots() {
    726    return new nsExtendedContentSlots();
    727  }
    728 
    729  const nsExtendedContentSlots* GetExistingExtendedContentSlots() const {
    730    const nsContentSlots* slots = GetExistingContentSlots();
    731    return slots ? slots->GetExtendedContentSlots() : nullptr;
    732  }
    733 
    734  nsExtendedContentSlots* GetExistingExtendedContentSlots() {
    735    nsContentSlots* slots = GetExistingContentSlots();
    736    return slots ? slots->GetExtendedContentSlots() : nullptr;
    737  }
    738 
    739  nsExtendedContentSlots* ExtendedContentSlots() {
    740    nsContentSlots* slots = ContentSlots();
    741    if (!slots->GetExtendedContentSlots()) {
    742      slots->SetExtendedContentSlots(CreateExtendedSlots(), true);
    743    }
    744    return slots->GetExtendedContentSlots();
    745  }
    746 
    747  /**
    748   * Hook for implementing GetID.  This is guaranteed to only be
    749   * called if HasID() is true.
    750   */
    751  nsAtom* DoGetID() const;
    752 
    753  ~nsIContent() = default;
    754 
    755 public:
    756 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING)
    757 #  define MOZ_DOM_LIST
    758 #endif
    759 
    760 #ifdef MOZ_DOM_LIST
    761  /**
    762   * An alias for List() with default arguments. Since some debuggers can't
    763   * figure the default arguments easily, having an out-of-line, non-static
    764   * function helps quite a lot.
    765   */
    766  void Dump();
    767 
    768  /**
    769   * List the content (and anything it contains) out to the given
    770   * file stream. Use aIndent as the base indent during formatting.
    771   */
    772  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
    773 
    774  /**
    775   * Dump the content (and anything it contains) out to the given
    776   * file stream. Use aIndent as the base indent during formatting.
    777   */
    778  virtual void DumpContent(FILE* out = stdout, int32_t aIndent = 0,
    779                           bool aDumpAll = true) const = 0;
    780 #endif
    781 };
    782 
    783 NON_VIRTUAL_ADDREF_RELEASE(nsIContent)
    784 
    785 #endif /* nsIContent_h___ */