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___ */