nsINode.cpp (143745B)
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 /* 8 * Base class for all DOM nodes. 9 */ 10 11 #include "nsINode.h" 12 13 #include <algorithm> 14 15 #include "AccessCheck.h" 16 #include "GeometryUtils.h" 17 #include "HTMLLegendElement.h" 18 #include "WrapperFactory.h" 19 #include "XPathGenerator.h" 20 #include "js/ForOfIterator.h" // JS::ForOfIterator 21 #include "js/JSON.h" // JS_ParseJSON 22 #include "jsapi.h" 23 #include "mozAutoDocUpdate.h" 24 #include "mozilla/AsyncEventDispatcher.h" 25 #include "mozilla/CORSMode.h" 26 #include "mozilla/ClearOnShutdown.h" 27 #include "mozilla/EventDispatcher.h" 28 #include "mozilla/EventListenerManager.h" 29 #include "mozilla/HTMLEditor.h" 30 #include "mozilla/Likely.h" 31 #include "mozilla/Maybe.h" 32 #include "mozilla/MemoryReporting.h" 33 #include "mozilla/Preferences.h" 34 #include "mozilla/PresShell.h" 35 #include "mozilla/ProfilerLabels.h" 36 #include "mozilla/ServoBindings.h" 37 #include "mozilla/StaticPrefs_layout.h" 38 #include "mozilla/TextControlElement.h" 39 #include "mozilla/TextControlState.h" 40 #include "mozilla/TextEditor.h" 41 #include "mozilla/TimeStamp.h" 42 #include "mozilla/dom/AncestorIterator.h" 43 #include "mozilla/dom/Attr.h" 44 #include "mozilla/dom/BindContext.h" 45 #include "mozilla/dom/BindingDeclarations.h" 46 #include "mozilla/dom/CharacterData.h" 47 #include "mozilla/dom/ChildIterator.h" 48 #include "mozilla/dom/CustomElementRegistry.h" 49 #include "mozilla/dom/DebuggerNotificationBinding.h" 50 #include "mozilla/dom/Document.h" 51 #include "mozilla/dom/DocumentInlines.h" 52 #include "mozilla/dom/DocumentType.h" 53 #include "mozilla/dom/Element.h" 54 #include "mozilla/dom/ElementBinding.h" 55 #include "mozilla/dom/Event.h" 56 #include "mozilla/dom/Exceptions.h" 57 #include "mozilla/dom/HTMLButtonElement.h" 58 #include "mozilla/dom/HTMLDetailsElement.h" 59 #include "mozilla/dom/HTMLDialogElement.h" 60 #include "mozilla/dom/HTMLImageElement.h" 61 #include "mozilla/dom/HTMLMediaElement.h" 62 #include "mozilla/dom/HTMLTemplateElement.h" 63 #include "mozilla/dom/L10nOverlays.h" 64 #include "mozilla/dom/Link.h" 65 #include "mozilla/dom/MutationObservers.h" 66 #include "mozilla/dom/NodeBinding.h" 67 #include "mozilla/dom/NodeInfo.h" 68 #include "mozilla/dom/NodeInfoInlines.h" 69 #include "mozilla/dom/PolicyContainer.h" 70 #include "mozilla/dom/SVGUseElement.h" 71 #include "mozilla/dom/ScriptSettings.h" 72 #include "mozilla/dom/Selection.h" 73 #include "mozilla/dom/ShadowRoot.h" 74 #include "nsAtom.h" 75 #include "nsAttrValueOrString.h" 76 #include "nsCCUncollectableMarker.h" 77 #include "nsCOMArray.h" 78 #include "nsChildContentList.h" 79 #include "nsContentCreatorFunctions.h" 80 #include "nsContentList.h" 81 #include "nsContentUtils.h" 82 #include "nsCycleCollectionParticipant.h" 83 #include "nsDOMAttributeMap.h" 84 #include "nsDOMCID.h" 85 #include "nsDOMCSSAttrDeclaration.h" 86 #include "nsDOMMutationObserver.h" 87 #include "nsDOMString.h" 88 #include "nsDOMTokenList.h" 89 #include "nsError.h" 90 #include "nsExpirationTracker.h" 91 #include "nsFocusManager.h" 92 #include "nsFrameSelection.h" 93 #include "nsGenericHTMLElement.h" 94 #include "nsGkAtoms.h" 95 #include "nsGlobalWindowInner.h" 96 #include "nsIAnimationObserver.h" 97 #include "nsIAnonymousContentCreator.h" 98 #include "nsIContentInlines.h" 99 #include "nsIFrameInlines.h" 100 #include "nsIScriptGlobalObject.h" 101 #include "nsIWidget.h" 102 #include "nsLayoutUtils.h" 103 #include "nsNameSpaceManager.h" 104 #include "nsNodeInfoManager.h" 105 #include "nsObjectLoadingContent.h" 106 #include "nsPIDOMWindow.h" 107 #include "nsPresContext.h" 108 #include "nsPrintfCString.h" 109 #include "nsRange.h" 110 #include "nsString.h" 111 #include "nsStyleConsts.h" 112 #include "nsTextNode.h" 113 #include "nsUnicharUtils.h" 114 #include "nsWindowSizes.h" 115 #include "nsWrapperCacheInlines.h" 116 #include "xpcprivate.h" 117 #include "xpcpublic.h" 118 119 #ifdef ACCESSIBILITY 120 # include "mozilla/dom/AccessibleNode.h" 121 #endif 122 123 using namespace mozilla; 124 using namespace mozilla::dom; 125 126 static bool ShouldUseNACScope(const nsINode* aNode) { 127 return aNode->IsInNativeAnonymousSubtree(); 128 } 129 130 static bool ShouldUseUAWidgetScope(const nsINode* aNode) { 131 return aNode->HasBeenInUAWidget(); 132 } 133 134 void* nsINode::operator new(size_t aSize, nsNodeInfoManager* aManager) { 135 if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) { 136 MOZ_ASSERT(aManager, "nsNodeInfoManager needs to be initialized"); 137 return aManager->Allocate(aSize); 138 } 139 return ::operator new(aSize); 140 } 141 void nsINode::operator delete(void* aPtr) { free_impl(aPtr); } 142 143 bool nsINode::IsInclusiveDescendantOf(const nsINode* aNode) const { 144 MOZ_ASSERT(aNode, "The node is nullptr."); 145 146 if (aNode == this) { 147 return true; 148 } 149 150 if (!aNode->HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN)) { 151 return GetParentNode() == aNode; 152 } 153 154 for (nsINode* node : Ancestors(*this)) { 155 if (node == aNode) { 156 return true; 157 } 158 } 159 return false; 160 } 161 162 bool nsINode::IsInclusiveFlatTreeDescendantOf(const nsINode* aNode) const { 163 MOZ_ASSERT(aNode, "The node is nullptr."); 164 165 for (nsINode* node : InclusiveFlatTreeAncestors(*this)) { 166 if (node == aNode) { 167 return true; 168 } 169 } 170 return false; 171 } 172 173 bool nsINode::IsShadowIncludingDescendantOf(const nsINode* aNode) const { 174 MOZ_ASSERT(aNode, "The node is nullptr."); 175 176 const nsINode* node = this; 177 while ((node = node->GetParentOrShadowHostNode())) { 178 if (node == aNode) { 179 return true; 180 } 181 } 182 183 return false; 184 } 185 186 bool nsINode::IsShadowIncludingInclusiveDescendantOf( 187 const nsINode* aNode) const { 188 MOZ_ASSERT(aNode, "The node is nullptr."); 189 190 if (this->GetComposedDoc() == aNode || this == aNode) { 191 return true; 192 } 193 194 return IsShadowIncludingDescendantOf(aNode); 195 } 196 197 nsINode::nsSlots::nsSlots() : mWeakReference(nullptr) {} 198 199 nsINode::nsSlots::~nsSlots() { 200 if (mChildNodes) { 201 mChildNodes->InvalidateCacheIfAvailable(); 202 } 203 204 if (mWeakReference) { 205 mWeakReference->NoticeNodeDestruction(); 206 } 207 } 208 209 void nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback& cb) { 210 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildNodes"); 211 cb.NoteXPCOMChild(mChildNodes); 212 for (auto& object : mBoundObjects) { 213 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mBoundObjects[i]"); 214 cb.NoteXPCOMChild(object.mObject); 215 } 216 } 217 218 static void ClearBoundObjects(nsINode::nsSlots& aSlots, nsINode& aNode) { 219 auto objects = std::move(aSlots.mBoundObjects); 220 for (auto& object : objects) { 221 if (object.mDtor) { 222 object.mDtor(object.mObject, &aNode); 223 } 224 } 225 MOZ_ASSERT(aSlots.mBoundObjects.IsEmpty()); 226 } 227 228 void nsINode::nsSlots::Unlink(nsINode& aNode) { 229 if (mChildNodes) { 230 mChildNodes->InvalidateCacheIfAvailable(); 231 ImplCycleCollectionUnlink(mChildNodes); 232 } 233 ClearBoundObjects(*this, aNode); 234 } 235 236 //---------------------------------------------------------------------- 237 238 #ifdef MOZILLA_INTERNAL_API 239 nsINode::nsINode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 240 : mNodeInfo(std::move(aNodeInfo)), 241 mParent(nullptr) 242 # ifndef BOOL_FLAGS_ON_WRAPPER_CACHE 243 , 244 mBoolFlags(0) 245 # endif 246 , 247 mChildCount(0), 248 mPreviousOrLastSibling(nullptr), 249 mSubtreeRoot(this), 250 mSlots(nullptr) { 251 SetIsOnMainThread(); 252 } 253 #endif 254 255 nsINode::~nsINode() { 256 MOZ_ASSERT(!HasSlots(), "LastRelease was not called?"); 257 MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?"); 258 } 259 260 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 261 void nsINode::AssertInvariantsOnNodeInfoChange() { 262 MOZ_DIAGNOSTIC_ASSERT(!IsInComposedDoc()); 263 if (nsCOMPtr<Link> link = do_QueryInterface(this)) { 264 MOZ_DIAGNOSTIC_ASSERT(!link->HasPendingLinkUpdate()); 265 } 266 } 267 #endif 268 269 #ifdef DEBUG 270 void nsINode::AssertIsRootElementSlow(bool aIsRoot) const { 271 auto* root = OwnerDoc()->GetRootElement(); 272 const bool isRootSlow = this == root; 273 // If we're mid unbind of the root element, IsRootElement() might return true 274 // but the document might not be able to reach the root element anymore. 275 MOZ_ASSERT(aIsRoot == isRootSlow || !root); 276 } 277 #endif 278 279 void* nsINode::GetProperty(const nsAtom* aPropertyName, 280 nsresult* aStatus) const { 281 if (!HasProperties()) { // a fast HasFlag() test 282 if (aStatus) { 283 *aStatus = NS_PROPTABLE_PROP_NOT_THERE; 284 } 285 return nullptr; 286 } 287 return OwnerDoc()->PropertyTable().GetProperty(this, aPropertyName, aStatus); 288 } 289 290 nsresult nsINode::SetProperty(nsAtom* aPropertyName, void* aValue, 291 NSPropertyDtorFunc aDtor, bool aTransfer) { 292 nsresult rv = OwnerDoc()->PropertyTable().SetProperty( 293 this, aPropertyName, aValue, aDtor, nullptr, aTransfer); 294 if (NS_SUCCEEDED(rv)) { 295 SetFlags(NODE_HAS_PROPERTIES); 296 } 297 298 return rv; 299 } 300 301 void nsINode::RemoveProperty(const nsAtom* aPropertyName) { 302 OwnerDoc()->PropertyTable().RemoveProperty(this, aPropertyName); 303 } 304 305 void* nsINode::TakeProperty(const nsAtom* aPropertyName, nsresult* aStatus) { 306 return OwnerDoc()->PropertyTable().TakeProperty(this, aPropertyName, aStatus); 307 } 308 309 nsIPolicyContainer* nsINode::GetPolicyContainer() const { 310 return OwnerDoc()->GetPolicyContainer(); 311 } 312 313 nsINode::nsSlots* nsINode::CreateSlots() { return new nsSlots(); } 314 315 static const nsINode* GetClosestCommonInclusiveAncestorForRangeInSelection( 316 const nsINode* aNode) { 317 while (aNode && 318 !aNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) { 319 const bool isNodeInFlattenedShadowTree = 320 StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() && 321 (aNode->IsInShadowTree() || 322 (aNode->IsContent() && aNode->AsContent()->GetAssignedSlot())); 323 324 if (!aNode 325 ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection() && 326 !isNodeInFlattenedShadowTree) { 327 return nullptr; 328 } 329 330 if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 331 if (aNode->IsContent() && aNode->AsContent()->GetAssignedSlot()) { 332 aNode = aNode->AsContent()->GetAssignedSlot(); 333 } else { 334 aNode = aNode->GetParentOrShadowHostNode(); 335 } 336 continue; 337 } 338 aNode = aNode->GetParentNode(); 339 } 340 return aNode; 341 } 342 343 /** 344 * A Comparator suitable for mozilla::BinarySearchIf for searching a collection 345 * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset). 346 */ 347 class IsItemInRangeComparator { 348 public: 349 // @param aStartOffset has to be less or equal to aEndOffset. 350 IsItemInRangeComparator(const nsINode& aNode, const uint32_t aStartOffset, 351 const uint32_t aEndOffset, 352 nsContentUtils::NodeIndexCache* aCache) 353 : mNode(aNode), 354 mStartOffset(aStartOffset), 355 mEndOffset(aEndOffset), 356 mCache(aCache) { 357 MOZ_ASSERT(aStartOffset <= aEndOffset); 358 } 359 360 int operator()(const AbstractRange* const aRange) const { 361 auto ComparePoints = [](const nsINode* aNode1, const uint32_t aOffset1, 362 const nsINode* aNode2, const uint32_t aOffset2, 363 nsContentUtils::NodeIndexCache* aCache) { 364 if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 365 return nsContentUtils::ComparePointsWithIndices<TreeKind::Flat>( 366 aNode1, aOffset1, aNode2, aOffset2, aCache); 367 } 368 return nsContentUtils::ComparePointsWithIndices< 369 TreeKind::ShadowIncludingDOM>(aNode1, aOffset1, aNode2, aOffset2, 370 aCache); 371 }; 372 373 Maybe<int32_t> cmp = ComparePoints( 374 &mNode, mEndOffset, aRange->GetMayCrossShadowBoundaryStartContainer(), 375 aRange->MayCrossShadowBoundaryStartOffset(), mCache); 376 // nsContentUtils::ComparePoints would return Nothing when nodes 377 // are disconnected, ComparePoints_Deprecated used to return 1 378 // for that case. Hence valueOr(1) to keep the legacy result. 379 if (cmp.valueOr(1) == 1) { 380 cmp = ComparePoints(&mNode, mStartOffset, 381 aRange->GetMayCrossShadowBoundaryEndContainer(), 382 aRange->MayCrossShadowBoundaryEndOffset(), mCache); 383 // Same reason as above. 384 if (cmp.valueOr(1) == -1) { 385 return 0; 386 } 387 return 1; 388 } 389 return -1; 390 } 391 392 private: 393 const nsINode& mNode; 394 const uint32_t mStartOffset; 395 const uint32_t mEndOffset; 396 nsContentUtils::NodeIndexCache* mCache; 397 }; 398 399 bool nsINode::IsSelected(const uint32_t aStartOffset, const uint32_t aEndOffset, 400 SelectionNodeCache* aCache) const { 401 MOZ_ASSERT(aStartOffset <= aEndOffset); 402 const nsINode* n = GetClosestCommonInclusiveAncestorForRangeInSelection(this); 403 NS_ASSERTION(n || !IsMaybeSelected(), 404 "A node without a common inclusive ancestor for a range in " 405 "Selection is for sure not selected."); 406 407 // Collect the selection objects for potential ranges. 408 AutoTArray<Selection*, 1> ancestorSelections; 409 for (; n; n = GetClosestCommonInclusiveAncestorForRangeInSelection( 410 n->GetParentNode())) { 411 const LinkedList<AbstractRange>* ranges = 412 n->GetExistingClosestCommonInclusiveAncestorRanges(); 413 if (!ranges) { 414 continue; 415 } 416 for (const AbstractRange* range : *ranges) { 417 MOZ_ASSERT(range->IsInAnySelection(), 418 "Why is this range registered with a node?"); 419 // Looks like that IsInSelection() assert fails sometimes... 420 if (range->IsInAnySelection()) { 421 for (const WeakPtr<Selection>& selection : range->GetSelections()) { 422 if (selection && !ancestorSelections.Contains(selection)) { 423 ancestorSelections.AppendElement(selection); 424 } 425 } 426 } 427 } 428 } 429 if (aCache && aCache->MaybeCollectNodesAndCheckIfFullySelectedInAnyOf( 430 this, ancestorSelections)) { 431 return true; 432 } 433 434 nsContentUtils::NodeIndexCache cache; 435 IsItemInRangeComparator comparator{*this, aStartOffset, aEndOffset, &cache}; 436 for (Selection* selection : ancestorSelections) { 437 // Binary search the sorted ranges in this selection. 438 // (Selection::GetRangeAt returns its ranges ordered). 439 size_t low = 0; 440 size_t high = selection->RangeCount(); 441 442 while (high != low) { 443 size_t middle = low + (high - low) / 2; 444 445 const AbstractRange* const range = selection->GetAbstractRangeAt(middle); 446 int result = comparator(range); 447 if (result == 0) { 448 if (!range->Collapsed()) { 449 return true; 450 } 451 452 if (range->MayCrossShadowBoundary()) { 453 MOZ_ASSERT(range->IsDynamicRange(), 454 "range->MayCrossShadowBoundary() can only return true for " 455 "dynamic range"); 456 StaticRange* crossBoundaryRange = 457 range->AsDynamicRange()->GetCrossShadowBoundaryRange(); 458 MOZ_ASSERT(crossBoundaryRange); 459 if (!crossBoundaryRange->Collapsed()) { 460 return true; 461 } 462 } 463 464 auto ComparePoints = [](const ConstRawRangeBoundary& aBoundary1, 465 const RangeBoundary& aBoundary2, 466 nsContentUtils::NodeIndexCache* aCache) { 467 if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 468 return nsContentUtils::ComparePoints<TreeKind::Flat>( 469 aBoundary1, aBoundary2, aCache); 470 } 471 return nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>( 472 aBoundary1, aBoundary2, aCache); 473 }; 474 475 const AbstractRange* middlePlus1; 476 const AbstractRange* middleMinus1; 477 // if node end > start of middle+1, result = 1 478 if (middle + 1 < high && 479 (middlePlus1 = selection->GetAbstractRangeAt(middle + 1)) && 480 ComparePoints( 481 ConstRawRangeBoundary(this, aEndOffset, 482 RangeBoundaryIsMutationObserved::No), 483 middlePlus1->StartRef(), &cache) 484 .valueOr(1) > 0) { 485 result = 1; 486 // if node start < end of middle - 1, result = -1 487 } else if (middle >= 1 && 488 (middleMinus1 = selection->GetAbstractRangeAt(middle - 1)) && 489 ComparePoints(ConstRawRangeBoundary( 490 this, aStartOffset, 491 RangeBoundaryIsMutationObserved::No), 492 middleMinus1->EndRef(), &cache) 493 .valueOr(1) < 0) { 494 result = -1; 495 } else { 496 break; 497 } 498 } 499 500 if (result < 0) { 501 high = middle; 502 } else { 503 low = middle + 1; 504 } 505 } 506 } 507 508 return false; 509 } 510 511 Element* nsINode::GetAnonymousRootElementOfTextEditor( 512 TextEditor** aTextEditor) { 513 if (aTextEditor) { 514 *aTextEditor = nullptr; 515 } 516 RefPtr<TextControlElement> textControlElement; 517 if (IsInNativeAnonymousSubtree()) { 518 textControlElement = TextControlElement::FromNodeOrNull( 519 GetClosestNativeAnonymousSubtreeRootParentOrHost()); 520 } else { 521 textControlElement = TextControlElement::FromNode(this); 522 } 523 if (!textControlElement) { 524 return nullptr; 525 } 526 RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor(); 527 if (!textEditor) { 528 // The found `TextControlElement` may be an input element which is not a 529 // text control element. In this case, such element must not be in a 530 // native anonymous tree of a `TextEditor` so this node is not in any 531 // `TextEditor`. 532 return nullptr; 533 } 534 535 Element* rootElement = textEditor->GetRoot(); 536 if (aTextEditor) { 537 textEditor.forget(aTextEditor); 538 } 539 return rootElement; 540 } 541 542 void nsINode::QueueDevtoolsAnonymousEvent(bool aIsRemove) { 543 MOZ_ASSERT(IsRootOfNativeAnonymousSubtree()); 544 MOZ_ASSERT(OwnerDoc()->DevToolsAnonymousAndShadowEventsEnabled()); 545 AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher( 546 this, aIsRemove ? u"anonymousrootremoved"_ns : u"anonymousrootcreated"_ns, 547 CanBubble::eYes, ChromeOnlyDispatch::eYes, Composed::eYes); 548 dispatcher->PostDOMEvent(); 549 } 550 551 nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions) { 552 if (aOptions.mComposed) { 553 if (Document* doc = GetComposedDoc()) { 554 return doc; 555 } 556 557 nsINode* node = this; 558 while (node) { 559 node = node->SubtreeRoot(); 560 ShadowRoot* shadow = ShadowRoot::FromNode(node); 561 if (!shadow) { 562 break; 563 } 564 node = shadow->GetHost(); 565 } 566 567 return node; 568 } 569 570 return SubtreeRoot(); 571 } 572 573 nsIContent* nsINode::GetFirstChildOfTemplateOrNode() { 574 if (IsTemplateElement()) { 575 DocumentFragment* frag = static_cast<HTMLTemplateElement*>(this)->Content(); 576 return frag->GetFirstChild(); 577 } 578 579 return GetFirstChild(); 580 } 581 582 nsINode* nsINode::SubtreeRoot() const { 583 auto RootOfNode = [](const nsINode* aStart) -> nsINode* { 584 const nsINode* node = aStart; 585 const nsINode* iter = node; 586 while ((iter = iter->GetParentNode())) { 587 node = iter; 588 } 589 return const_cast<nsINode*>(node); 590 }; 591 592 // There are four cases of interest here. nsINodes that are really: 593 // 1. Document nodes - Are always in the document. 594 // 2.a nsIContent nodes not in a shadow tree - Are either in the document, 595 // or mSubtreeRoot is updated in BindToTree/UnbindFromTree. 596 // 2.b nsIContent nodes in a shadow tree - Are never in the document, 597 // ignore mSubtreeRoot and return the containing shadow root. 598 // 4. Attr nodes - Are never in the document, and mSubtreeRoot 599 // is always 'this' (as set in nsINode's ctor). 600 nsINode* node; 601 if (IsInUncomposedDoc()) { 602 node = OwnerDocAsNode(); 603 } else if (IsContent()) { 604 ShadowRoot* containingShadow = AsContent()->GetContainingShadow(); 605 node = containingShadow ? containingShadow : mSubtreeRoot; 606 if (!node) { 607 NS_WARNING("Using SubtreeRoot() on unlinked element?"); 608 node = RootOfNode(this); 609 } 610 } else { 611 node = mSubtreeRoot; 612 } 613 MOZ_ASSERT(node, "Should always have a node here!"); 614 #ifdef DEBUG 615 { 616 const nsINode* slowNode = RootOfNode(this); 617 MOZ_ASSERT(slowNode == node, "These should always be in sync!"); 618 } 619 #endif 620 return node; 621 } 622 623 static nsIContent* GetRootForContentSubtree(nsIContent* aContent) { 624 NS_ENSURE_TRUE(aContent, nullptr); 625 626 // Special case for ShadowRoot because the ShadowRoot itself is 627 // the root. This is necessary to prevent selection from crossing 628 // the ShadowRoot boundary. 629 // 630 // FIXME(emilio): The NAC check should probably be done before this? We can 631 // have NAC inside shadow DOM. 632 if (ShadowRoot* containingShadow = aContent->GetContainingShadow()) { 633 return containingShadow; 634 } 635 if (nsIContent* nativeAnonRoot = 636 aContent->GetClosestNativeAnonymousSubtreeRoot()) { 637 return nativeAnonRoot; 638 } 639 if (Document* doc = aContent->GetUncomposedDoc()) { 640 return doc->GetRootElement(); 641 } 642 return nsIContent::FromNode(aContent->SubtreeRoot()); 643 } 644 645 nsIContent* nsINode::GetSelectionRootContent( 646 PresShell* aPresShell, 647 IgnoreOwnIndependentSelection aIgnoreOwnIndependentSelection, 648 AllowCrossShadowBoundary aAllowCrossShadowBoundary) { 649 NS_ENSURE_TRUE(aPresShell, nullptr); 650 651 const bool isContent = IsContent(); 652 653 if (!isContent && !IsDocument()) { 654 return nullptr; 655 } 656 657 if (isContent) { 658 if (GetComposedDoc() != aPresShell->GetDocument()) { 659 return nullptr; 660 } 661 662 const bool computeTextEditorRoot = 663 IsInNativeAnonymousSubtree() || 664 (aIgnoreOwnIndependentSelection == IgnoreOwnIndependentSelection::No && 665 AsContent()->HasIndependentSelection()); 666 if (computeTextEditorRoot) { 667 // This node should be an inclusive descendant of input/textarea editor. 668 // In that case, the anonymous <div> for TextEditor should be always the 669 // selection root. 670 if (Element* anonymousDivElement = 671 GetAnonymousRootElementOfTextEditor()) { 672 return anonymousDivElement; 673 } 674 } 675 } 676 677 if (nsPresContext* presContext = aPresShell->GetPresContext()) { 678 if (nsContentUtils::GetHTMLEditor(presContext)) { 679 // When there is an HTMLEditor, selection root should be one of focused 680 // editing host, <body> or root of the (sub)tree which this node belong. 681 682 // If this node is in design mode or this node is not editable, selection 683 // root should be the <body> if this node is not in any subtrees and there 684 // is a <body> or the root of the shadow DOM if this node is in a shadow 685 // or the document element. 686 // XXX If this node is not connected, it seems that this should return 687 // nullptr because this node is not selectable. 688 if (!IsInComposedDoc() || IsInDesignMode() || 689 !HasFlag(NODE_IS_EDITABLE)) { 690 Element* const bodyOrDocumentElement = [&]() -> Element* { 691 if (Element* const bodyElement = OwnerDoc()->GetBodyElement()) { 692 return bodyElement; 693 } 694 return OwnerDoc()->GetDocumentElement(); 695 }(); 696 NS_ENSURE_TRUE(bodyOrDocumentElement, nullptr); 697 return nsContentUtils::IsInSameAnonymousTree(this, 698 bodyOrDocumentElement) 699 ? bodyOrDocumentElement 700 : GetRootForContentSubtree(AsContent()); 701 } 702 // If this node is editable but not in the design mode, this is always an 703 // editable node in an editing host of contenteditable. In this case, 704 // let's use the editing host element as selection root. 705 MOZ_ASSERT(IsEditable()); 706 MOZ_ASSERT(!IsInDesignMode()); 707 MOZ_ASSERT(IsContent()); 708 return AsContent()->GetEditingHost(); 709 } 710 } 711 712 if (!isContent) { 713 return nullptr; 714 } 715 716 RefPtr<nsFrameSelection> fs = aPresShell->FrameSelection(); 717 nsCOMPtr<nsIContent> content = fs->GetIndependentSelectionRootElement(); 718 if (!content) { 719 content = fs->GetAncestorLimiter(); 720 if (!content) { 721 Document* doc = aPresShell->GetDocument(); 722 NS_ENSURE_TRUE(doc, nullptr); 723 content = doc->GetRootElement(); 724 if (!content) { 725 return nullptr; 726 } 727 } 728 } 729 730 // This node might be in another subtree, if so, we should find this subtree's 731 // root. Otherwise, we can return the content simply. 732 NS_ENSURE_TRUE(content, nullptr); 733 if (!nsContentUtils::IsInSameAnonymousTree(this, content)) { 734 content = GetRootForContentSubtree(AsContent()); 735 // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame. 736 // Use the host as the root. 737 if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) { 738 content = shadowRoot->GetHost(); 739 if (content && bool(aAllowCrossShadowBoundary)) { 740 content = content->GetSelectionRootContent( 741 aPresShell, aIgnoreOwnIndependentSelection, 742 aAllowCrossShadowBoundary); 743 } 744 } 745 } 746 747 return content; 748 } 749 750 nsFrameSelection* nsINode::GetFrameSelection() const { 751 if (!IsInComposedDoc()) { 752 return nullptr; 753 } 754 if (IsInNativeAnonymousSubtree()) { 755 auto* const textControlElement = TextControlElement::FromNodeOrNull( 756 GetClosestNativeAnonymousSubtreeRootParentOrHost()); 757 if (textControlElement && 758 textControlElement->IsSingleLineTextControlOrTextArea()) { 759 nsFrameSelection* const independentFrameSelection = 760 textControlElement->GetIndependentFrameSelection(); 761 if (!independentFrameSelection) { 762 return nullptr; // not yet initialized or being destroyed? 763 } 764 const Element* const anonymousDiv = 765 independentFrameSelection->GetIndependentSelectionRootElement(); 766 if (!anonymousDiv || !IsInclusiveDescendantOf(anonymousDiv)) { 767 return nullptr; // not in the editor root, shouldn't be selectable 768 } 769 return independentFrameSelection; 770 } 771 // Otherwise, even if we're in a native anonymous subtree, our selection 772 // should be managed by the document selection. 773 } 774 PresShell* const presShell = OwnerDoc()->GetPresShell(); 775 if (!presShell) { 776 return nullptr; 777 } 778 // FIXME: PresShell::FrameSelection() returns 779 // already_AddRefed<nsFrameSelection> for making the users work safer. 780 // However, in these days, it should be managed with MOZ_CAN_RUN_SCRIPT. 781 // Therefore, for now, we should use ConstFrameSelection() and cost_cast 782 // here to avoid to AddRef/Release in unnecessary cases. 783 return const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); 784 } 785 786 nsINodeList* nsINode::ChildNodes() { 787 nsSlots* slots = Slots(); 788 if (!slots->mChildNodes) { 789 slots->mChildNodes = IsAttr() ? new nsAttrChildContentList(this) 790 : new nsParentNodeChildContentList(this); 791 } 792 793 return slots->mChildNodes; 794 } 795 796 nsIContent* nsINode::GetLastChild() const { 797 return mFirstChild ? mFirstChild->mPreviousOrLastSibling : nullptr; 798 } 799 800 void nsINode::InvalidateChildNodes() { 801 MOZ_ASSERT(!IsAttr()); 802 803 nsSlots* slots = GetExistingSlots(); 804 if (!slots || !slots->mChildNodes) { 805 return; 806 } 807 808 auto childNodes = 809 static_cast<nsParentNodeChildContentList*>(slots->mChildNodes.get()); 810 childNodes->InvalidateCache(); 811 } 812 813 void nsINode::GetTextContentInternal(nsAString& aTextContent, 814 OOMReporter& aError) { 815 SetDOMStringToNull(aTextContent); 816 } 817 818 DocumentOrShadowRoot* nsINode::GetContainingDocumentOrShadowRoot() const { 819 if (IsInUncomposedDoc()) { 820 return OwnerDoc(); 821 } 822 823 if (IsInShadowTree()) { 824 return AsContent()->GetContainingShadow(); 825 } 826 827 return nullptr; 828 } 829 830 DocumentOrShadowRoot* nsINode::GetUncomposedDocOrConnectedShadowRoot() const { 831 if (IsInUncomposedDoc()) { 832 return OwnerDoc(); 833 } 834 835 if (IsInComposedDoc() && IsInShadowTree()) { 836 return AsContent()->GetContainingShadow(); 837 } 838 839 return nullptr; 840 } 841 842 SafeDoublyLinkedList<nsIMutationObserver>* nsINode::GetMutationObservers() { 843 if (auto* slots = GetExistingSlots()) { 844 if (!slots->mMutationObservers.isEmpty()) { 845 return &slots->mMutationObservers; 846 } 847 } 848 return nullptr; 849 } 850 851 void nsINode::LastRelease() { 852 if (nsSlots* slots = GetExistingSlots()) { 853 if (!slots->mMutationObservers.isEmpty()) { 854 for (auto iter = slots->mMutationObservers.begin(); 855 iter != slots->mMutationObservers.end(); ++iter) { 856 iter->NodeWillBeDestroyed(this); 857 } 858 } 859 ClearBoundObjects(*slots, *this); 860 if (IsContent()) { 861 nsIContent* content = AsContent(); 862 if (HTMLSlotElement* slot = content->GetManualSlotAssignment()) { 863 content->SetManualSlotAssignment(nullptr); 864 slot->RemoveManuallyAssignedNode(*content); 865 } 866 } 867 868 if (Element* element = Element::FromNode(this)) { 869 if (CustomElementData* data = element->GetCustomElementData()) { 870 data->Unlink(); 871 } 872 } 873 874 delete slots; 875 mSlots = nullptr; 876 } 877 878 // Kill properties first since that may run external code, so we want to 879 // be in as complete state as possible at that time. 880 if (IsDocument()) { 881 // Delete all properties before tearing down the document. Some of the 882 // properties are bound to nsINode objects and the destructor functions of 883 // the properties may want to use the owner document of the nsINode. 884 AsDocument()->RemoveAllProperties(); 885 AsDocument()->DropStyleSet(); 886 } else { 887 if (HasProperties()) { 888 // Strong reference to the document so that deleting properties can't 889 // delete the document. 890 nsCOMPtr<Document> document = OwnerDoc(); 891 document->RemoveAllPropertiesFor(this); 892 } 893 894 if (HasFlag(ADDED_TO_FORM)) { 895 if (auto* formControl = nsGenericHTMLFormControlElement::FromNode(this)) { 896 // Tell the form (if any) this node is going away. Don't 897 // notify, since we're being destroyed in any case. 898 formControl->ClearForm(true, true); 899 } else if (auto* imageElem = HTMLImageElement::FromNode(this)) { 900 imageElem->ClearForm(true); 901 } 902 } 903 if (HasFlag(NODE_HAS_LISTENERMANAGER)) { 904 #ifdef DEBUG 905 if (nsContentUtils::IsInitialized()) { 906 EventListenerManager* manager = 907 nsContentUtils::GetExistingListenerManagerForNode(this); 908 if (!manager) { 909 NS_ERROR( 910 "Huh, our bit says we have a listener manager list, " 911 "but there's nothing in the hash!?!!"); 912 } 913 } 914 #endif 915 916 nsContentUtils::RemoveListenerManager(this); 917 UnsetFlags(NODE_HAS_LISTENERMANAGER); 918 } 919 920 if (Element* element = Element::FromNode(this)) { 921 element->ClearAttributes(); 922 } 923 } 924 925 UnsetFlags(NODE_HAS_PROPERTIES); 926 ReleaseWrapper(this); 927 928 FragmentOrElement::RemoveBlackMarkedNode(this); 929 } 930 931 std::ostream& operator<<(std::ostream& aStream, const nsINode& aNode) { 932 nsAutoString elemDesc; 933 const nsINode* curr = &aNode; 934 while (curr) { 935 nsString id, cls; 936 if (curr->IsElement()) { 937 curr->AsElement()->GetId(id); 938 if (const nsAttrValue* attrValue = curr->AsElement()->GetClasses()) { 939 attrValue->ToString(cls); 940 } 941 } 942 943 if (!elemDesc.IsEmpty()) { 944 elemDesc = elemDesc + u"."_ns; 945 } 946 947 if (!curr->LocalName().IsEmpty()) { 948 elemDesc.Append(curr->LocalName()); 949 } else { 950 elemDesc.Append(curr->NodeName()); 951 } 952 953 if (!id.IsEmpty()) { 954 elemDesc = elemDesc + u"['"_ns + id + u"']"_ns; 955 } else if (!cls.IsEmpty()) { 956 elemDesc = elemDesc + u"[class=\""_ns + cls + u"\"]"_ns; 957 } 958 959 if (curr->IsElement() && 960 curr->AsElement()->HasAttr(nsGkAtoms::contenteditable)) { 961 nsAutoString val; 962 curr->AsElement()->GetAttr(nsGkAtoms::contenteditable, val); 963 elemDesc = elemDesc + u"[contenteditable=\""_ns + val + u"\"]"_ns; 964 } 965 if (curr->IsDocument() && curr->IsInDesignMode()) { 966 elemDesc.Append(u"[designMode=\"on\"]"_ns); 967 } 968 969 curr = curr->GetParentNode(); 970 } 971 972 NS_ConvertUTF16toUTF8 str(elemDesc); 973 return aStream << str.get(); 974 } 975 976 nsIContent* nsINode::DoGetShadowHost() const { 977 MOZ_ASSERT(IsShadowRoot()); 978 return static_cast<const ShadowRoot*>(this)->GetHost(); 979 } 980 981 ShadowRoot* nsINode::GetContainingShadow() const { 982 if (!IsInShadowTree()) { 983 return nullptr; 984 } 985 return AsContent()->GetContainingShadow(); 986 } 987 988 Element* nsINode::GetContainingShadowHost() const { 989 if (ShadowRoot* shadow = GetContainingShadow()) { 990 return shadow->GetHost(); 991 } 992 return nullptr; 993 } 994 995 SVGUseElement* nsINode::DoGetContainingSVGUseShadowHost() const { 996 MOZ_ASSERT(IsInShadowTree()); 997 return SVGUseElement::FromNodeOrNull(GetContainingShadowHost()); 998 } 999 1000 void nsINode::GetNodeValueInternal(nsAString& aNodeValue) { 1001 SetDOMStringToNull(aNodeValue); 1002 } 1003 1004 static const char* NodeTypeAsString(nsINode* aNode) { 1005 static const char* NodeTypeStrings[] = { 1006 "", // No nodes of type 0 1007 "an Element", 1008 "an Attribute", 1009 "a Text", 1010 "a CDATASection", 1011 "an EntityReference", 1012 "an Entity", 1013 "a ProcessingInstruction", 1014 "a Comment", 1015 "a Document", 1016 "a DocumentType", 1017 "a DocumentFragment", 1018 "a Notation", 1019 }; 1020 static_assert(std::size(NodeTypeStrings) == nsINode::MAX_NODE_TYPE + 1, 1021 "Max node type out of range for our array"); 1022 1023 uint16_t nodeType = aNode->NodeType(); 1024 MOZ_RELEASE_ASSERT(nodeType < std::size(NodeTypeStrings), 1025 "Uknown out-of-range node type"); 1026 return NodeTypeStrings[nodeType]; 1027 } 1028 1029 nsINode* nsINode::RemoveChildInternal( 1030 nsINode& aOldChild, MutationEffectOnScript aMutationEffectOnScript, 1031 ErrorResult& aError) { 1032 if (!aOldChild.IsContent()) { 1033 // aOldChild can't be one of our children. 1034 aError.ThrowNotFoundError( 1035 "The node to be removed is not a child of this node"); 1036 return nullptr; 1037 } 1038 1039 if (aOldChild.GetParentNode() == this) { 1040 nsContentUtils::NotifyDevToolsOfNodeRemoval(aOldChild); 1041 } 1042 1043 // Check again, we may not be the child's parent anymore. 1044 // Can be triggered by dom/base/crashtests/293388-1.html 1045 if (aOldChild.IsRootOfNativeAnonymousSubtree() || 1046 aOldChild.GetParentNode() != this) { 1047 // aOldChild isn't one of our children. 1048 aError.ThrowNotFoundError( 1049 "The node to be removed is not a child of this node"); 1050 return nullptr; 1051 } 1052 1053 RemoveChildNode(aOldChild.AsContent(), true, nullptr, nullptr, 1054 aMutationEffectOnScript); 1055 return &aOldChild; 1056 } 1057 1058 void nsINode::Normalize() { 1059 // First collect list of nodes to be removed 1060 AutoTArray<nsCOMPtr<nsIContent>, 50> nodes; 1061 1062 bool canMerge = false; 1063 for (nsIContent* node = this->GetFirstChild(); node; 1064 node = node->GetNextNode(this)) { 1065 if (node->NodeType() != TEXT_NODE) { 1066 canMerge = false; 1067 continue; 1068 } 1069 1070 if (canMerge || node->TextLength() == 0) { 1071 // No need to touch canMerge. That way we can merge across empty 1072 // textnodes if and only if the node before is a textnode 1073 nodes.AppendElement(node); 1074 } else { 1075 canMerge = true; 1076 } 1077 1078 // If there's no following sibling, then we need to ensure that we don't 1079 // collect following siblings of our (grand)parent as to-be-removed 1080 canMerge = canMerge && !!node->GetNextSibling(); 1081 } 1082 1083 if (nodes.IsEmpty()) { 1084 return; 1085 } 1086 1087 const RefPtr<Document> doc = OwnerDoc(); 1088 1089 // Let DevTools know the node removals if and only if DevTools is observing 1090 // the mutations. 1091 const bool notifyDevToolsOfNodeRemovals = 1092 MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc(); 1093 if (MOZ_UNLIKELY(notifyDevToolsOfNodeRemovals)) { 1094 for (const nsCOMPtr<nsIContent>& node : nodes) { 1095 // Node may have already been removed. 1096 if (node->GetParentNode()) { 1097 // TODO: MOZ_KnownLive because of Bug 1620312 1098 nsContentUtils::NotifyDevToolsOfNodeRemoval(MOZ_KnownLive(*node)); 1099 } 1100 } 1101 } 1102 1103 mozAutoDocUpdate batch(doc, true); 1104 1105 // Merge and remove all nodes 1106 nsAutoString tmpStr; 1107 for (uint32_t i = 0; i < nodes.Length(); ++i) { 1108 nsIContent* node = nodes[i]; 1109 // Merge with previous node unless empty 1110 const CharacterDataBuffer* characterDataBuffer = 1111 node->GetCharacterDataBuffer(); 1112 if (characterDataBuffer->GetLength()) { 1113 nsIContent* target = node->GetPreviousSibling(); 1114 NS_ASSERTION((target && target->NodeType() == TEXT_NODE) || 1115 notifyDevToolsOfNodeRemovals, 1116 "Should always have a previous text sibling unless " 1117 "mutation events messed us up"); 1118 if (MOZ_LIKELY(!notifyDevToolsOfNodeRemovals) || 1119 (target && target->NodeType() == TEXT_NODE)) { 1120 nsTextNode* t = static_cast<nsTextNode*>(target); 1121 if (characterDataBuffer->Is2b()) { 1122 t->AppendTextForNormalize(characterDataBuffer->Get2b(), 1123 characterDataBuffer->GetLength(), true, 1124 node); 1125 } else { 1126 tmpStr.Truncate(); 1127 characterDataBuffer->AppendTo(tmpStr); 1128 t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), true, node); 1129 } 1130 } 1131 } 1132 1133 // Remove node 1134 nsCOMPtr<nsINode> parent = node->GetParentNode(); 1135 NS_ASSERTION(parent || notifyDevToolsOfNodeRemovals, 1136 "Should always have a parent unless " 1137 "mutation events messed us up"); 1138 if (parent) { 1139 parent->RemoveChildNode(node, true, nullptr, nullptr, 1140 MutationEffectOnScript::KeepTrustWorthiness); 1141 } 1142 } 1143 } 1144 1145 nsresult nsINode::GetBaseURI(nsAString& aURI) const { 1146 nsIURI* baseURI = GetBaseURI(); 1147 1148 nsAutoCString spec; 1149 if (baseURI) { 1150 nsresult rv = baseURI->GetSpec(spec); 1151 NS_ENSURE_SUCCESS(rv, rv); 1152 } 1153 1154 CopyUTF8toUTF16(spec, aURI); 1155 return NS_OK; 1156 } 1157 1158 void nsINode::GetBaseURIFromJS(nsAString& aURI, CallerType aCallerType, 1159 ErrorResult& aRv) const { 1160 nsIURI* baseURI = GetBaseURI(aCallerType == CallerType::System); 1161 nsAutoCString spec; 1162 if (baseURI) { 1163 nsresult res = baseURI->GetSpec(spec); 1164 if (NS_FAILED(res)) { 1165 aRv.Throw(res); 1166 return; 1167 } 1168 } 1169 CopyUTF8toUTF16(spec, aURI); 1170 } 1171 1172 nsIURI* nsINode::GetBaseURIObject() const { return GetBaseURI(true); } 1173 1174 void nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix) { 1175 if (Element* nsElement = GetNameSpaceElement()) { 1176 // XXX Waiting for DOM spec to list error codes. 1177 1178 // Trace up the content parent chain looking for the namespace 1179 // declaration that defines the aNamespaceURI namespace. Once found, 1180 // return the prefix (i.e. the attribute localName). 1181 for (Element* element : nsElement->InclusiveAncestorsOfType<Element>()) { 1182 uint32_t attrCount = element->GetAttrCount(); 1183 1184 for (uint32_t i = 0; i < attrCount; ++i) { 1185 const nsAttrName* name = element->GetAttrNameAt(i); 1186 1187 if (name->NamespaceEquals(kNameSpaceID_XMLNS) && 1188 element->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(), 1189 aNamespaceURI, eCaseMatters)) { 1190 // If the localName is "xmlns", the prefix we output should be 1191 // null. 1192 nsAtom* localName = name->LocalName(); 1193 1194 if (localName != nsGkAtoms::xmlns) { 1195 localName->ToString(aPrefix); 1196 } else { 1197 SetDOMStringToNull(aPrefix); 1198 } 1199 return; 1200 } 1201 } 1202 } 1203 } 1204 1205 SetDOMStringToNull(aPrefix); 1206 } 1207 1208 uint16_t nsINode::CompareDocumentPosition(const nsINode& aOtherNode) const { 1209 if (this == &aOtherNode) { 1210 return 0; 1211 } 1212 if (GetPreviousSibling() == &aOtherNode) { 1213 MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode()); 1214 return Node_Binding::DOCUMENT_POSITION_PRECEDING; 1215 } 1216 if (GetNextSibling() == &aOtherNode) { 1217 MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode()); 1218 return Node_Binding::DOCUMENT_POSITION_FOLLOWING; 1219 } 1220 1221 AutoTArray<const nsINode*, 32> parents1, parents2; 1222 1223 const nsINode* node1 = &aOtherNode; 1224 const nsINode* node2 = this; 1225 1226 // Check if either node is an attribute 1227 const Attr* attr1 = Attr::FromNode(node1); 1228 if (attr1) { 1229 const Element* elem = attr1->GetElement(); 1230 // If there is an owner element add the attribute 1231 // to the chain and walk up to the element 1232 if (elem) { 1233 node1 = elem; 1234 parents1.AppendElement(attr1); 1235 } 1236 } 1237 if (auto* attr2 = Attr::FromNode(node2)) { 1238 const Element* elem = attr2->GetElement(); 1239 if (elem == node1 && attr1) { 1240 // Both nodes are attributes on the same element. 1241 // Compare position between the attributes. 1242 1243 uint32_t i; 1244 const nsAttrName* attrName; 1245 for (i = 0; elem->GetAttrNameAt(i, &attrName); ++i) { 1246 if (attrName->Equals(attr1->NodeInfo())) { 1247 NS_ASSERTION(!attrName->Equals(attr2->NodeInfo()), 1248 "Different attrs at same position"); 1249 return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | 1250 Node_Binding::DOCUMENT_POSITION_PRECEDING; 1251 } 1252 if (attrName->Equals(attr2->NodeInfo())) { 1253 return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | 1254 Node_Binding::DOCUMENT_POSITION_FOLLOWING; 1255 } 1256 } 1257 MOZ_ASSERT_UNREACHABLE("neither attribute in the element"); 1258 return Node_Binding::DOCUMENT_POSITION_DISCONNECTED; 1259 } 1260 1261 if (elem) { 1262 node2 = elem; 1263 parents2.AppendElement(attr2); 1264 } 1265 } 1266 1267 // We now know that both nodes are either nsIContents or Documents. 1268 // If either node started out as an attribute, that attribute will have 1269 // the same relative position as its ownerElement, except if the 1270 // ownerElement ends up being the container for the other node 1271 1272 // Build the chain of parents 1273 do { 1274 parents1.AppendElement(node1); 1275 node1 = node1->GetParentNode(); 1276 } while (node1); 1277 do { 1278 parents2.AppendElement(node2); 1279 node2 = node2->GetParentNode(); 1280 } while (node2); 1281 1282 // Check if the nodes are disconnected. 1283 uint32_t pos1 = parents1.Length(); 1284 uint32_t pos2 = parents2.Length(); 1285 const nsINode* top1 = parents1.ElementAt(--pos1); 1286 const nsINode* top2 = parents2.ElementAt(--pos2); 1287 if (top1 != top2) { 1288 return top1 < top2 1289 ? (Node_Binding::DOCUMENT_POSITION_PRECEDING | 1290 Node_Binding::DOCUMENT_POSITION_DISCONNECTED | 1291 Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC) 1292 : (Node_Binding::DOCUMENT_POSITION_FOLLOWING | 1293 Node_Binding::DOCUMENT_POSITION_DISCONNECTED | 1294 Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); 1295 } 1296 1297 // Find where the parent chain differs and check indices in the parent. 1298 const nsINode* parent = top1; 1299 uint32_t len; 1300 for (len = std::min(pos1, pos2); len > 0; --len) { 1301 const nsINode* child1 = parents1.ElementAt(--pos1); 1302 const nsINode* child2 = parents2.ElementAt(--pos2); 1303 if (child1 != child2) { 1304 // child1 or child2 can be an attribute here. This will work fine since 1305 // ComputeIndexOf will return Nothing for the attribute making the 1306 // attribute be considered before any child. 1307 Maybe<uint32_t> child1Index = parent->ComputeIndexOf(child1); 1308 Maybe<uint32_t> child2Index = parent->ComputeIndexOf(child2); 1309 return child1Index < child2Index 1310 ? Node_Binding::DOCUMENT_POSITION_PRECEDING 1311 : Node_Binding::DOCUMENT_POSITION_FOLLOWING; 1312 } 1313 parent = child1; 1314 } 1315 1316 // We hit the end of one of the parent chains without finding a difference 1317 // between the chains. That must mean that one node is an ancestor of the 1318 // other. The one with the shortest chain must be the ancestor. 1319 return pos1 < pos2 ? (Node_Binding::DOCUMENT_POSITION_PRECEDING | 1320 Node_Binding::DOCUMENT_POSITION_CONTAINS) 1321 : (Node_Binding::DOCUMENT_POSITION_FOLLOWING | 1322 Node_Binding::DOCUMENT_POSITION_CONTAINED_BY); 1323 } 1324 1325 bool nsINode::IsSameNode(nsINode* other) { return other == this; } 1326 1327 bool nsINode::IsEqualNode(nsINode* aOther) { 1328 if (!aOther) { 1329 return false; 1330 } 1331 1332 // Might as well do a quick check to avoid walking our kids if we're 1333 // obviously the same. 1334 if (aOther == this) { 1335 return true; 1336 } 1337 1338 nsAutoString string1, string2; 1339 1340 nsINode* node1 = this; 1341 nsINode* node2 = aOther; 1342 do { 1343 uint16_t nodeType = node1->NodeType(); 1344 if (nodeType != node2->NodeType()) { 1345 return false; 1346 } 1347 1348 mozilla::dom::NodeInfo* nodeInfo1 = node1->mNodeInfo; 1349 mozilla::dom::NodeInfo* nodeInfo2 = node2->mNodeInfo; 1350 if (!nodeInfo1->Equals(nodeInfo2) || 1351 nodeInfo1->GetExtraName() != nodeInfo2->GetExtraName()) { 1352 return false; 1353 } 1354 1355 switch (nodeType) { 1356 case ELEMENT_NODE: { 1357 // Both are elements (we checked that their nodeinfos are equal). Do the 1358 // check on attributes. 1359 Element* element1 = node1->AsElement(); 1360 Element* element2 = node2->AsElement(); 1361 uint32_t attrCount = element1->GetAttrCount(); 1362 if (attrCount != element2->GetAttrCount()) { 1363 return false; 1364 } 1365 1366 // Iterate over attributes. 1367 for (uint32_t i = 0; i < attrCount; ++i) { 1368 const nsAttrName* attrName = element1->GetAttrNameAt(i); 1369 #ifdef DEBUG 1370 bool hasAttr = 1371 #endif 1372 element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(), 1373 string1); 1374 NS_ASSERTION(hasAttr, "Why don't we have an attr?"); 1375 1376 if (!element2->AttrValueIs(attrName->NamespaceID(), 1377 attrName->LocalName(), string1, 1378 eCaseMatters)) { 1379 return false; 1380 } 1381 } 1382 break; 1383 } 1384 case TEXT_NODE: 1385 case COMMENT_NODE: 1386 case CDATA_SECTION_NODE: 1387 case PROCESSING_INSTRUCTION_NODE: { 1388 MOZ_ASSERT(node1->IsCharacterData()); 1389 MOZ_ASSERT(node2->IsCharacterData()); 1390 auto* data1 = static_cast<CharacterData*>(node1); 1391 auto* data2 = static_cast<CharacterData*>(node2); 1392 1393 if (!data1->TextEquals(data2)) { 1394 return false; 1395 } 1396 1397 break; 1398 } 1399 case DOCUMENT_NODE: 1400 case DOCUMENT_FRAGMENT_NODE: 1401 break; 1402 case ATTRIBUTE_NODE: { 1403 NS_ASSERTION(node1 == this && node2 == aOther, 1404 "Did we come upon an attribute node while walking a " 1405 "subtree?"); 1406 node1->GetNodeValue(string1); 1407 node2->GetNodeValue(string2); 1408 1409 // Returning here as to not bother walking subtree. And there is no 1410 // risk that we're half way through walking some other subtree since 1411 // attribute nodes doesn't appear in subtrees. 1412 return string1.Equals(string2); 1413 } 1414 case DOCUMENT_TYPE_NODE: { 1415 DocumentType* docType1 = static_cast<DocumentType*>(node1); 1416 DocumentType* docType2 = static_cast<DocumentType*>(node2); 1417 1418 // Public ID 1419 docType1->GetPublicId(string1); 1420 docType2->GetPublicId(string2); 1421 if (!string1.Equals(string2)) { 1422 return false; 1423 } 1424 1425 // System ID 1426 docType1->GetSystemId(string1); 1427 docType2->GetSystemId(string2); 1428 if (!string1.Equals(string2)) { 1429 return false; 1430 } 1431 1432 break; 1433 } 1434 default: 1435 MOZ_ASSERT(false, "Unknown node type"); 1436 } 1437 1438 nsINode* nextNode = node1->GetFirstChild(); 1439 if (nextNode) { 1440 node1 = nextNode; 1441 node2 = node2->GetFirstChild(); 1442 } else { 1443 if (node2->GetFirstChild()) { 1444 // node2 has a firstChild, but node1 doesn't 1445 return false; 1446 } 1447 1448 // Find next sibling, possibly walking parent chain. 1449 while (1) { 1450 if (node1 == this) { 1451 NS_ASSERTION(node2 == aOther, 1452 "Should have reached the start node " 1453 "for both trees at the same time"); 1454 return true; 1455 } 1456 1457 nextNode = node1->GetNextSibling(); 1458 if (nextNode) { 1459 node1 = nextNode; 1460 node2 = node2->GetNextSibling(); 1461 break; 1462 } 1463 1464 if (node2->GetNextSibling()) { 1465 // node2 has a nextSibling, but node1 doesn't 1466 return false; 1467 } 1468 1469 node1 = node1->GetParentNode(); 1470 node2 = node2->GetParentNode(); 1471 NS_ASSERTION(node1 && node2, "no parent while walking subtree"); 1472 } 1473 } 1474 } while (node2); 1475 1476 return false; 1477 } 1478 1479 void nsINode::LookupNamespaceURI(const nsAString& aNamespacePrefix, 1480 nsAString& aNamespaceURI) { 1481 Element* element = GetNameSpaceElement(); 1482 if (!element || NS_FAILED(element->LookupNamespaceURIInternal( 1483 aNamespacePrefix, aNamespaceURI))) { 1484 SetDOMStringToNull(aNamespaceURI); 1485 } 1486 } 1487 1488 mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType> 1489 nsINode::GetDebuggerNotificationType() const { 1490 return mozilla::Some( 1491 mozilla::dom::EventCallbackDebuggerNotificationType::Node); 1492 } 1493 1494 bool nsINode::ComputeDefaultWantsUntrusted(ErrorResult& aRv) { 1495 return !nsContentUtils::IsChromeDoc(OwnerDoc()); 1496 } 1497 1498 void nsINode::GetBoxQuads(const BoxQuadOptions& aOptions, 1499 nsTArray<RefPtr<DOMQuad>>& aResult, 1500 CallerType aCallerType, mozilla::ErrorResult& aRv) { 1501 mozilla::GetBoxQuads(this, aOptions, aResult, aCallerType, aRv); 1502 } 1503 1504 void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, 1505 nsTArray<RefPtr<DOMQuad>>& aResult, 1506 mozilla::ErrorResult& aRv) { 1507 mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv); 1508 } 1509 1510 already_AddRefed<DOMQuad> nsINode::ConvertQuadFromNode( 1511 DOMQuad& aQuad, const GeometryNode& aFrom, 1512 const ConvertCoordinateOptions& aOptions, CallerType aCallerType, 1513 ErrorResult& aRv) { 1514 return mozilla::ConvertQuadFromNode(this, aQuad, aFrom, aOptions, aCallerType, 1515 aRv); 1516 } 1517 1518 already_AddRefed<DOMQuad> nsINode::ConvertRectFromNode( 1519 DOMRectReadOnly& aRect, const GeometryNode& aFrom, 1520 const ConvertCoordinateOptions& aOptions, CallerType aCallerType, 1521 ErrorResult& aRv) { 1522 return mozilla::ConvertRectFromNode(this, aRect, aFrom, aOptions, aCallerType, 1523 aRv); 1524 } 1525 1526 already_AddRefed<DOMPoint> nsINode::ConvertPointFromNode( 1527 const DOMPointInit& aPoint, const GeometryNode& aFrom, 1528 const ConvertCoordinateOptions& aOptions, CallerType aCallerType, 1529 ErrorResult& aRv) { 1530 return mozilla::ConvertPointFromNode(this, aPoint, aFrom, aOptions, 1531 aCallerType, aRv); 1532 } 1533 1534 bool nsINode::DispatchEvent(Event& aEvent, CallerType aCallerType, 1535 ErrorResult& aRv) { 1536 // XXX sXBL/XBL2 issue -- do we really want the owner here? What 1537 // if that's the XBL document? Would we want its presshell? Or what? 1538 nsCOMPtr<Document> document = OwnerDoc(); 1539 1540 // Do nothing if the element does not belong to a document 1541 if (!document) { 1542 return true; 1543 } 1544 1545 // Obtain a presentation shell 1546 RefPtr<nsPresContext> context = document->GetPresContext(); 1547 1548 nsEventStatus status = nsEventStatus_eIgnore; 1549 nsresult rv = EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent, 1550 context, &status); 1551 bool retval = !aEvent.DefaultPrevented(aCallerType); 1552 if (NS_FAILED(rv)) { 1553 aRv.Throw(rv); 1554 } 1555 return retval; 1556 } 1557 1558 nsresult nsINode::PostHandleEvent(EventChainPostVisitor& /*aVisitor*/) { 1559 return NS_OK; 1560 } 1561 1562 EventListenerManager* nsINode::GetOrCreateListenerManager() { 1563 return nsContentUtils::GetListenerManagerForNode(this); 1564 } 1565 1566 EventListenerManager* nsINode::GetExistingListenerManager() const { 1567 return nsContentUtils::GetExistingListenerManagerForNode(this); 1568 } 1569 1570 nsPIDOMWindowOuter* nsINode::GetOwnerGlobalForBindingsInternal() { 1571 bool dummy; 1572 // FIXME(bz): This cast is a bit bogus. See 1573 // https://bugzilla.mozilla.org/show_bug.cgi?id=1515709 1574 auto* window = static_cast<nsGlobalWindowInner*>( 1575 OwnerDoc()->GetScriptHandlingObject(dummy)); 1576 return window ? nsPIDOMWindowOuter::GetFromCurrentInner(window) : nullptr; 1577 } 1578 1579 nsIGlobalObject* nsINode::GetOwnerGlobal() const { 1580 bool dummy; 1581 return OwnerDoc()->GetScriptHandlingObject(dummy); 1582 } 1583 1584 bool nsINode::UnoptimizableCCNode() const { 1585 return IsInNativeAnonymousSubtree() || IsAttr(); 1586 } 1587 1588 /* static */ 1589 bool nsINode::Traverse(nsINode* tmp, nsCycleCollectionTraversalCallback& cb) { 1590 if (MOZ_LIKELY(!cb.WantAllTraces())) { 1591 Document* currentDoc = tmp->GetComposedDoc(); 1592 if (currentDoc && nsCCUncollectableMarker::InGeneration( 1593 currentDoc->GetMarkedCCGeneration())) { 1594 return false; 1595 } 1596 1597 if (nsCCUncollectableMarker::sGeneration) { 1598 // If we're black no need to traverse. 1599 if (tmp->HasKnownLiveWrapper() || tmp->InCCBlackTree()) { 1600 return false; 1601 } 1602 1603 if (!tmp->UnoptimizableCCNode()) { 1604 // If we're in a black document, return early. 1605 if ((currentDoc && currentDoc->HasKnownLiveWrapper())) { 1606 return false; 1607 } 1608 // If we're not in anonymous content and we have a black parent, 1609 // return early. 1610 nsIContent* parent = tmp->GetParent(); 1611 if (parent && !parent->UnoptimizableCCNode() && 1612 parent->HasKnownLiveWrapper()) { 1613 MOZ_ASSERT(parent->ComputeIndexOf(tmp).isSome(), 1614 "Parent doesn't own us?"); 1615 return false; 1616 } 1617 } 1618 } 1619 } 1620 1621 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo) 1622 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstChild) 1623 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextSibling) 1624 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent()) 1625 1626 if (nsSlots* slots = tmp->GetExistingSlots()) { 1627 slots->Traverse(cb); 1628 } 1629 1630 if (tmp->HasProperties()) { 1631 #ifdef ACCESSIBILITY 1632 auto* anode = static_cast<AccessibleNode*>( 1633 tmp->GetProperty(nsGkAtoms::accessiblenode)); 1634 if (anode) { 1635 cb.NoteXPCOMChild(anode); 1636 } 1637 #endif 1638 } 1639 1640 if (tmp->NodeType() != DOCUMENT_NODE && 1641 tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) { 1642 nsContentUtils::TraverseListenerManager(tmp, cb); 1643 } 1644 1645 return true; 1646 } 1647 1648 /* static */ 1649 void nsINode::Unlink(nsINode* tmp) { 1650 tmp->ReleaseWrapper(tmp); 1651 1652 if (nsSlots* slots = tmp->GetExistingSlots()) { 1653 slots->Unlink(*tmp); 1654 } 1655 1656 if (tmp->NodeType() != DOCUMENT_NODE && 1657 tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) { 1658 nsContentUtils::RemoveListenerManager(tmp); 1659 tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); 1660 } 1661 1662 if (tmp->HasProperties()) { 1663 tmp->RemoveProperty(nsGkAtoms::accessiblenode); 1664 } 1665 } 1666 1667 static void AdoptNodeIntoOwnerDoc(nsINode* aParent, nsINode* aNode, 1668 ErrorResult& aError) { 1669 NS_ASSERTION(!aNode->GetParentNode(), 1670 "Should have removed from parent already"); 1671 1672 Document* doc = aParent->OwnerDoc(); 1673 1674 DebugOnly<nsINode*> adoptedNode = doc->AdoptNode(*aNode, aError, true); 1675 1676 #ifdef DEBUG 1677 if (!aError.Failed()) { 1678 MOZ_ASSERT(aParent->OwnerDoc() == doc, "ownerDoc chainged while adopting"); 1679 MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?"); 1680 MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(), 1681 "ownerDocument changed again after adopting!"); 1682 } 1683 #endif // DEBUG 1684 } 1685 1686 static nsresult UpdateGlobalsInSubtree(nsIContent* aRoot) { 1687 MOZ_ASSERT(ShouldUseNACScope(aRoot)); 1688 // Start off with no global so we don't fire any error events on failure. 1689 AutoJSAPI jsapi; 1690 jsapi.Init(); 1691 1692 JSContext* cx = jsapi.cx(); 1693 1694 ErrorResult rv; 1695 JS::Rooted<JSObject*> reflector(cx); 1696 for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) { 1697 if ((reflector = cur->GetWrapper())) { 1698 JSAutoRealm ar(cx, reflector); 1699 UpdateReflectorGlobal(cx, reflector, rv); 1700 rv.WouldReportJSException(); 1701 if (rv.Failed()) { 1702 // We _could_ consider BlastSubtreeToPieces here, but it's not really 1703 // needed. Having some nodes in here accessible to content while others 1704 // are not is probably OK. We just need to fail out of the actual 1705 // insertion, so they're not in the DOM. Returning a failure here will 1706 // do that. 1707 return rv.StealNSResult(); 1708 } 1709 } 1710 } 1711 1712 return NS_OK; 1713 } 1714 1715 void nsINode::InsertChildBefore( 1716 nsIContent* aKid, nsIContent* aBeforeThis, bool aNotify, ErrorResult& aRv, 1717 nsINode* aOldParent, MutationEffectOnScript aMutationEffectOnScript) { 1718 if (!IsContainerNode()) { 1719 aRv.ThrowHierarchyRequestError( 1720 "Parent is not a Document, DocumentFragment, or Element node."); 1721 return; 1722 } 1723 1724 MOZ_ASSERT(!aKid->GetParentNode(), "Inserting node that already has parent"); 1725 MOZ_ASSERT(!IsAttr()); 1726 1727 // The id-handling code, and in the future possibly other code, need to 1728 // react to unexpected attribute changes. 1729 nsMutationGuard::DidMutate(); 1730 1731 // Do this before checking the child-count since this could cause mutations 1732 mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify); 1733 1734 if (OwnerDoc() != aKid->OwnerDoc()) { 1735 AdoptNodeIntoOwnerDoc(this, aKid, aRv); 1736 if (NS_WARN_IF(aRv.Failed())) { 1737 return; 1738 } 1739 } 1740 1741 if (!aBeforeThis) { 1742 AppendChildToChildList(aKid); 1743 } else { 1744 InsertChildToChildList(aKid, aBeforeThis); 1745 } 1746 1747 nsIContent* parent = IsContent() ? AsContent() : nullptr; 1748 1749 // XXXbz Do we even need this code anymore? 1750 bool wasInNACScope = ShouldUseNACScope(aKid); 1751 BindContext context(*this); 1752 context.SetIsMove(aOldParent != nullptr); 1753 aRv = aKid->BindToTree(context, *this); 1754 if (!aRv.Failed() && !wasInNACScope && ShouldUseNACScope(aKid)) { 1755 MOZ_ASSERT(ShouldUseNACScope(this), 1756 "Why does the kid need to use an the anonymous content scope?"); 1757 aRv = UpdateGlobalsInSubtree(aKid); 1758 } 1759 if (aRv.Failed()) { 1760 DisconnectChild(aKid); 1761 aKid->UnbindFromTree(); 1762 return; 1763 } 1764 1765 // Invalidate cached array of child nodes 1766 InvalidateChildNodes(); 1767 1768 NS_ASSERTION(aKid->GetParentNode() == this, 1769 "Did we run script inappropriately?"); 1770 1771 if (aNotify) { 1772 // Note that we always want to call ContentInserted when things are added 1773 // as kids to documents 1774 if (parent && !aBeforeThis) { 1775 ContentAppendInfo info; 1776 info.mOldParent = aOldParent; 1777 info.mMutationEffectOnScript = aMutationEffectOnScript; 1778 MutationObservers::NotifyContentAppended(parent, aKid, info); 1779 } else { 1780 ContentInsertInfo info; 1781 info.mOldParent = aOldParent; 1782 info.mMutationEffectOnScript = aMutationEffectOnScript; 1783 MutationObservers::NotifyContentInserted(this, aKid, info); 1784 } 1785 } 1786 } 1787 1788 nsIContent* nsINode::GetPreviousSibling() const { 1789 // Do not expose circular linked list 1790 if (mPreviousOrLastSibling && !mPreviousOrLastSibling->mNextSibling) { 1791 return nullptr; 1792 } 1793 return mPreviousOrLastSibling; 1794 } 1795 1796 // CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer. 1797 // It should be small enough to not cause collisions between adjecent objects, 1798 // and large enough to make sure that all indexes are used. 1799 #define CACHE_POINTER_SHIFT 6 1800 #define CACHE_NUM_SLOTS 128 1801 #define CACHE_CHILD_LIMIT 10 1802 1803 #define CACHE_GET_INDEX(_parent) \ 1804 ((NS_PTR_TO_INT32(_parent) >> CACHE_POINTER_SHIFT) & (CACHE_NUM_SLOTS - 1)) 1805 1806 struct IndexCacheSlot { 1807 const nsINode* mParent; 1808 const nsINode* mChild; 1809 uint32_t mChildIndex; 1810 }; 1811 1812 static IndexCacheSlot sIndexCache[CACHE_NUM_SLOTS]; 1813 1814 static inline void AddChildAndIndexToCache(const nsINode* aParent, 1815 const nsINode* aChild, 1816 uint32_t aChildIndex) { 1817 uint32_t index = CACHE_GET_INDEX(aParent); 1818 sIndexCache[index].mParent = aParent; 1819 sIndexCache[index].mChild = aChild; 1820 sIndexCache[index].mChildIndex = aChildIndex; 1821 } 1822 1823 static inline void GetChildAndIndexFromCache(const nsINode* aParent, 1824 const nsINode** aChild, 1825 Maybe<uint32_t>* aChildIndex) { 1826 uint32_t index = CACHE_GET_INDEX(aParent); 1827 if (sIndexCache[index].mParent == aParent) { 1828 *aChild = sIndexCache[index].mChild; 1829 *aChildIndex = Some(sIndexCache[index].mChildIndex); 1830 } else { 1831 *aChild = nullptr; 1832 *aChildIndex = Nothing(); 1833 } 1834 } 1835 1836 static inline void RemoveFromCache(const nsINode* aParent) { 1837 uint32_t index = CACHE_GET_INDEX(aParent); 1838 if (sIndexCache[index].mParent == aParent) { 1839 sIndexCache[index] = {nullptr, nullptr, UINT32_MAX}; 1840 } 1841 } 1842 1843 void nsINode::AppendChildToChildList(nsIContent* aKid) { 1844 MOZ_ASSERT(aKid); 1845 MOZ_ASSERT(!aKid->mNextSibling); 1846 1847 RemoveFromCache(this); 1848 1849 if (mFirstChild) { 1850 nsIContent* lastChild = GetLastChild(); 1851 lastChild->mNextSibling = aKid; 1852 aKid->mPreviousOrLastSibling = lastChild; 1853 } else { 1854 mFirstChild = aKid; 1855 } 1856 1857 // Maintain link to the last child 1858 mFirstChild->mPreviousOrLastSibling = aKid; 1859 ++mChildCount; 1860 } 1861 1862 void nsINode::InsertChildToChildList(nsIContent* aKid, 1863 nsIContent* aNextSibling) { 1864 MOZ_ASSERT(aKid); 1865 MOZ_ASSERT(aNextSibling); 1866 1867 RemoveFromCache(this); 1868 1869 nsIContent* previousSibling = aNextSibling->mPreviousOrLastSibling; 1870 aNextSibling->mPreviousOrLastSibling = aKid; 1871 aKid->mPreviousOrLastSibling = previousSibling; 1872 aKid->mNextSibling = aNextSibling; 1873 1874 if (aNextSibling == mFirstChild) { 1875 MOZ_ASSERT(!previousSibling->mNextSibling); 1876 mFirstChild = aKid; 1877 } else { 1878 previousSibling->mNextSibling = aKid; 1879 } 1880 1881 ++mChildCount; 1882 } 1883 1884 void nsINode::DisconnectChild(nsIContent* aKid) { 1885 MOZ_ASSERT(aKid); 1886 MOZ_ASSERT(GetChildCount() > 0); 1887 1888 RemoveFromCache(this); 1889 1890 nsIContent* previousSibling = aKid->GetPreviousSibling(); 1891 nsCOMPtr<nsIContent> ref = aKid; 1892 1893 if (aKid->mNextSibling) { 1894 aKid->mNextSibling->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling; 1895 } else { 1896 // aKid is the last child in the list 1897 mFirstChild->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling; 1898 } 1899 aKid->mPreviousOrLastSibling = nullptr; 1900 1901 if (previousSibling) { 1902 previousSibling->mNextSibling = std::move(aKid->mNextSibling); 1903 } else { 1904 // aKid is the first child in the list 1905 mFirstChild = std::move(aKid->mNextSibling); 1906 } 1907 1908 --mChildCount; 1909 } 1910 1911 nsIContent* nsINode::GetChildAt_Deprecated(uint32_t aIndex) const { 1912 if (aIndex >= GetChildCount()) { 1913 return nullptr; 1914 } 1915 1916 nsIContent* child = mFirstChild; 1917 while (aIndex--) { 1918 child = child->GetNextSibling(); 1919 } 1920 1921 return child; 1922 } 1923 1924 nsINode* nsINode::GetChildAtInFlatTree(uint32_t aIndex) const { 1925 if (const auto* slot = HTMLSlotElement::FromNode(this)) { 1926 const auto& assignedNodes = slot->AssignedNodes(); 1927 if (!assignedNodes.IsEmpty()) { 1928 if (aIndex >= assignedNodes.Length()) { 1929 return nullptr; 1930 } 1931 return assignedNodes[aIndex]; 1932 } 1933 } else if (auto* shadowRoot = GetShadowRoot()) { 1934 return shadowRoot->GetChildAtInFlatTree(aIndex); 1935 } 1936 return GetChildAt_Deprecated(aIndex); 1937 } 1938 1939 int32_t nsINode::ComputeIndexOf_Deprecated( 1940 const nsINode* aPossibleChild) const { 1941 Maybe<uint32_t> maybeIndex = ComputeIndexOf(aPossibleChild); 1942 if (!maybeIndex) { 1943 return -1; 1944 } 1945 MOZ_ASSERT(*maybeIndex <= INT32_MAX, 1946 "ComputeIndexOf_Deprecated() returns unsupported index value, use " 1947 "ComputeIndex() instead"); 1948 return static_cast<int32_t>(*maybeIndex); 1949 } 1950 1951 Maybe<uint32_t> nsINode::ComputeIndexOf(const nsINode* aPossibleChild) const { 1952 if (!aPossibleChild) { 1953 return Nothing(); 1954 } 1955 1956 if (aPossibleChild->GetParentNode() != this) { 1957 return Nothing(); 1958 } 1959 1960 if (aPossibleChild == GetFirstChild()) { 1961 return Some(0); 1962 } 1963 1964 if (aPossibleChild == GetLastChild()) { 1965 MOZ_ASSERT(GetChildCount()); 1966 return Some(GetChildCount() - 1); 1967 } 1968 1969 if (MaybeCachesComputedIndex()) { 1970 const nsINode* child; 1971 Maybe<uint32_t> maybeChildIndex; 1972 GetChildAndIndexFromCache(this, &child, &maybeChildIndex); 1973 if (child) { 1974 if (child == aPossibleChild) { 1975 return maybeChildIndex; 1976 } 1977 1978 uint32_t nextIndex = *maybeChildIndex; 1979 uint32_t prevIndex = *maybeChildIndex; 1980 nsINode* prev = child->GetPreviousSibling(); 1981 nsINode* next = child->GetNextSibling(); 1982 do { 1983 if (next) { 1984 MOZ_ASSERT(nextIndex < UINT32_MAX); 1985 ++nextIndex; 1986 if (next == aPossibleChild) { 1987 AddChildAndIndexToCache(this, aPossibleChild, nextIndex); 1988 return Some(nextIndex); 1989 } 1990 next = next->GetNextSibling(); 1991 } 1992 if (prev) { 1993 MOZ_ASSERT(prevIndex > 0); 1994 --prevIndex; 1995 if (prev == aPossibleChild) { 1996 AddChildAndIndexToCache(this, aPossibleChild, prevIndex); 1997 return Some(prevIndex); 1998 } 1999 prev = prev->GetPreviousSibling(); 2000 } 2001 } while (prev || next); 2002 } 2003 } 2004 2005 uint32_t index = 0u; 2006 nsINode* current = mFirstChild; 2007 while (current) { 2008 MOZ_ASSERT(current->GetParentNode() == this); 2009 if (current == aPossibleChild) { 2010 if (MaybeCachesComputedIndex()) { 2011 AddChildAndIndexToCache(this, current, index); 2012 } 2013 return Some(index); 2014 } 2015 current = current->GetNextSibling(); 2016 MOZ_ASSERT(index < UINT32_MAX); 2017 ++index; 2018 } 2019 2020 return Nothing(); 2021 } 2022 2023 bool nsINode::MaybeCachesComputedIndex() const { 2024 return mChildCount >= CACHE_CHILD_LIMIT; 2025 } 2026 2027 Maybe<uint32_t> nsINode::ComputeIndexInParentNode() const { 2028 nsINode* parent = GetParentNode(); 2029 if (MOZ_UNLIKELY(!parent)) { 2030 return Nothing(); 2031 } 2032 return parent->ComputeIndexOf(this); 2033 } 2034 2035 Maybe<uint32_t> nsINode::ComputeIndexInParentContent() const { 2036 nsIContent* parent = GetParent(); 2037 if (MOZ_UNLIKELY(!parent)) { 2038 return Nothing(); 2039 } 2040 return parent->ComputeIndexOf(this); 2041 } 2042 2043 bool nsINode::MaybeParentCachesComputedIndex() const { 2044 nsINode* parent = GetParentNode(); 2045 return parent && parent->MaybeCachesComputedIndex(); 2046 } 2047 2048 uint32_t nsINode::GetFlatTreeChildCount() const { 2049 return FlattenedChildIterator::GetLength(this); 2050 } 2051 2052 Maybe<uint32_t> nsINode::ComputeFlatTreeIndexOf( 2053 const nsINode* aPossibleChild) const { 2054 return FlattenedChildIterator::GetIndexOf(this, aPossibleChild); 2055 } 2056 2057 static already_AddRefed<nsINode> GetNodeFromNodeOrString( 2058 const OwningNodeOrString& aNode, Document* aDocument) { 2059 if (aNode.IsNode()) { 2060 nsCOMPtr<nsINode> node = aNode.GetAsNode(); 2061 return node.forget(); 2062 } 2063 2064 if (aNode.IsString()) { 2065 RefPtr<nsTextNode> textNode = 2066 aDocument->CreateTextNode(aNode.GetAsString()); 2067 return textNode.forget(); 2068 } 2069 2070 MOZ_CRASH("Impossible type"); 2071 } 2072 2073 /** 2074 * Implement the algorithm specified at 2075 * https://dom.spec.whatwg.org/#converting-nodes-into-a-node for |prepend()|, 2076 * |append()|, |before()|, |after()|, and |replaceWith()| APIs. 2077 */ 2078 MOZ_CAN_RUN_SCRIPT static already_AddRefed<nsINode> 2079 ConvertNodesOrStringsIntoNode(const Sequence<OwningNodeOrString>& aNodes, 2080 Document* aDocument, ErrorResult& aRv) { 2081 if (aNodes.Length() == 1) { 2082 return GetNodeFromNodeOrString(aNodes[0], aDocument); 2083 } 2084 2085 nsCOMPtr<nsINode> fragment = aDocument->CreateDocumentFragment(); 2086 2087 for (const auto& node : aNodes) { 2088 nsCOMPtr<nsINode> childNode = GetNodeFromNodeOrString(node, aDocument); 2089 fragment->AppendChild(*childNode, aRv); 2090 if (aRv.Failed()) { 2091 return nullptr; 2092 } 2093 } 2094 2095 return fragment.forget(); 2096 } 2097 2098 static void InsertNodesIntoHashset(const Sequence<OwningNodeOrString>& aNodes, 2099 nsTHashSet<nsINode*>& aHashset) { 2100 for (const auto& node : aNodes) { 2101 if (node.IsNode()) { 2102 aHashset.Insert(node.GetAsNode()); 2103 } 2104 } 2105 } 2106 2107 static nsINode* FindViablePreviousSibling( 2108 const nsINode& aNode, const Sequence<OwningNodeOrString>& aNodes) { 2109 nsTHashSet<nsINode*> nodeSet(16); 2110 InsertNodesIntoHashset(aNodes, nodeSet); 2111 2112 nsINode* viablePreviousSibling = nullptr; 2113 for (nsINode* sibling = aNode.GetPreviousSibling(); sibling; 2114 sibling = sibling->GetPreviousSibling()) { 2115 if (!nodeSet.Contains(sibling)) { 2116 viablePreviousSibling = sibling; 2117 break; 2118 } 2119 } 2120 2121 return viablePreviousSibling; 2122 } 2123 2124 static nsINode* FindViableNextSibling( 2125 const nsINode& aNode, const Sequence<OwningNodeOrString>& aNodes) { 2126 nsTHashSet<nsINode*> nodeSet(16); 2127 InsertNodesIntoHashset(aNodes, nodeSet); 2128 2129 nsINode* viableNextSibling = nullptr; 2130 for (nsINode* sibling = aNode.GetNextSibling(); sibling; 2131 sibling = sibling->GetNextSibling()) { 2132 if (!nodeSet.Contains(sibling)) { 2133 viableNextSibling = sibling; 2134 break; 2135 } 2136 } 2137 2138 return viableNextSibling; 2139 } 2140 2141 void nsINode::Before(const Sequence<OwningNodeOrString>& aNodes, 2142 ErrorResult& aRv) { 2143 nsCOMPtr<nsINode> parent = GetParentNode(); 2144 if (!parent) { 2145 return; 2146 } 2147 2148 nsCOMPtr<nsINode> viablePreviousSibling = 2149 FindViablePreviousSibling(*this, aNodes); 2150 2151 nsCOMPtr<Document> doc = OwnerDoc(); 2152 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); 2153 if (aRv.Failed()) { 2154 return; 2155 } 2156 2157 viablePreviousSibling = viablePreviousSibling 2158 ? viablePreviousSibling->GetNextSibling() 2159 : parent->GetFirstChild(); 2160 2161 parent->InsertBefore(*node, viablePreviousSibling, aRv); 2162 } 2163 2164 void nsINode::After(const Sequence<OwningNodeOrString>& aNodes, 2165 ErrorResult& aRv) { 2166 nsCOMPtr<nsINode> parent = GetParentNode(); 2167 if (!parent) { 2168 return; 2169 } 2170 2171 nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes); 2172 2173 nsCOMPtr<Document> doc = OwnerDoc(); 2174 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); 2175 if (aRv.Failed()) { 2176 return; 2177 } 2178 2179 parent->InsertBefore(*node, viableNextSibling, aRv); 2180 } 2181 2182 void nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes, 2183 ErrorResult& aRv) { 2184 nsCOMPtr<nsINode> parent = GetParentNode(); 2185 if (!parent) { 2186 return; 2187 } 2188 2189 nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes); 2190 2191 nsCOMPtr<Document> doc = OwnerDoc(); 2192 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); 2193 if (aRv.Failed()) { 2194 return; 2195 } 2196 2197 if (parent == GetParentNode()) { 2198 parent->ReplaceChild(*node, *this, aRv); 2199 } else { 2200 parent->InsertBefore(*node, viableNextSibling, aRv); 2201 } 2202 } 2203 2204 void nsINode::Remove() { 2205 nsCOMPtr<nsINode> parent = GetParentNode(); 2206 if (!parent) { 2207 return; 2208 } 2209 2210 parent->RemoveChild(*this, IgnoreErrors()); 2211 } 2212 2213 Element* nsINode::GetFirstElementChild() const { 2214 for (nsIContent* child = GetFirstChild(); child; 2215 child = child->GetNextSibling()) { 2216 if (child->IsElement()) { 2217 return child->AsElement(); 2218 } 2219 } 2220 2221 return nullptr; 2222 } 2223 2224 Element* nsINode::GetLastElementChild() const { 2225 for (nsIContent* child = GetLastChild(); child; 2226 child = child->GetPreviousSibling()) { 2227 if (child->IsElement()) { 2228 return child->AsElement(); 2229 } 2230 } 2231 2232 return nullptr; 2233 } 2234 2235 static bool MatchAttribute(Element* aElement, int32_t aNamespaceID, 2236 nsAtom* aAttrName, void* aData) { 2237 MOZ_ASSERT(aElement, "Must have content node to work with!"); 2238 nsString* attrValue = static_cast<nsString*>(aData); 2239 if (aNamespaceID != kNameSpaceID_Unknown && 2240 aNamespaceID != kNameSpaceID_Wildcard) { 2241 return attrValue->EqualsLiteral("*") 2242 ? aElement->HasAttr(aNamespaceID, aAttrName) 2243 : aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue, 2244 eCaseMatters); 2245 } 2246 2247 // Qualified name match. This takes more work. 2248 uint32_t count = aElement->GetAttrCount(); 2249 for (uint32_t i = 0; i < count; ++i) { 2250 const nsAttrName* name = aElement->GetAttrNameAt(i); 2251 bool nameMatch; 2252 if (name->IsAtom()) { 2253 nameMatch = name->Atom() == aAttrName; 2254 } else if (aNamespaceID == kNameSpaceID_Wildcard) { 2255 nameMatch = name->NodeInfo()->Equals(aAttrName); 2256 } else { 2257 nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName); 2258 } 2259 2260 if (nameMatch) { 2261 return attrValue->EqualsLiteral("*") || 2262 aElement->AttrValueIs(name->NamespaceID(), name->LocalName(), 2263 *attrValue, eCaseMatters); 2264 } 2265 } 2266 2267 return false; 2268 } 2269 2270 already_AddRefed<nsIHTMLCollection> nsINode::GetElementsByAttribute( 2271 const nsAString& aAttribute, const nsAString& aValue) { 2272 RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute)); 2273 RefPtr<nsContentList> list = new nsContentList( 2274 this, MatchAttribute, nsContentUtils::DestroyMatchString, 2275 new nsString(aValue), true, attrAtom, kNameSpaceID_Unknown); 2276 2277 return list.forget(); 2278 } 2279 2280 already_AddRefed<nsIHTMLCollection> nsINode::GetElementsByAttributeNS( 2281 const nsAString& aNamespaceURI, const nsAString& aAttribute, 2282 const nsAString& aValue, ErrorResult& aRv) { 2283 RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute)); 2284 2285 int32_t nameSpaceId = kNameSpaceID_Wildcard; 2286 if (!aNamespaceURI.EqualsLiteral("*")) { 2287 nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace( 2288 aNamespaceURI, nameSpaceId); 2289 if (NS_FAILED(rv)) { 2290 aRv.Throw(rv); 2291 return nullptr; 2292 } 2293 } 2294 2295 RefPtr<nsContentList> list = new nsContentList( 2296 this, MatchAttribute, nsContentUtils::DestroyMatchString, 2297 new nsString(aValue), true, attrAtom, nameSpaceId); 2298 return list.forget(); 2299 } 2300 2301 void nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes, 2302 ErrorResult& aRv) { 2303 nsCOMPtr<Document> doc = OwnerDoc(); 2304 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); 2305 if (aRv.Failed()) { 2306 return; 2307 } 2308 2309 nsCOMPtr<nsIContent> refNode = mFirstChild; 2310 InsertBefore(*node, refNode, aRv); 2311 } 2312 2313 void nsINode::Append(const Sequence<OwningNodeOrString>& aNodes, 2314 ErrorResult& aRv) { 2315 nsCOMPtr<Document> doc = OwnerDoc(); 2316 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); 2317 if (aRv.Failed()) { 2318 return; 2319 } 2320 2321 AppendChild(*node, aRv); 2322 } 2323 2324 // https://dom.spec.whatwg.org/#dom-parentnode-replacechildren 2325 void nsINode::ReplaceChildren(const Sequence<OwningNodeOrString>& aNodes, 2326 ErrorResult& aRv) { 2327 nsCOMPtr<Document> doc = OwnerDoc(); 2328 nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); 2329 if (aRv.Failed()) { 2330 return; 2331 } 2332 MOZ_ASSERT(node); 2333 return ReplaceChildren(node, aRv); 2334 } 2335 2336 void nsINode::ReplaceChildren(nsINode* aNode, ErrorResult& aRv, 2337 MutationEffectOnScript aMutationEffectOnScript) { 2338 if (aNode) { 2339 EnsurePreInsertionValidity(*aNode, nullptr, aRv); 2340 if (aRv.Failed()) { 2341 return; 2342 } 2343 } 2344 nsCOMPtr<nsINode> node = aNode; 2345 const RefPtr<Document> doc = OwnerDoc(); 2346 2347 if (MOZ_UNLIKELY(MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc())) { 2348 NotifyDevToolsOfRemovalsOfChildren(); 2349 // FIXME: There is no guarantee that node->OwnerDoc() == OwnerDoc(). 2350 // Thus, we may not require to notify DevTools of any node removals 2351 // in the same document, but the node or its children may be in different 2352 // document and its removal may be observed by the DevTools. 2353 if (node) { 2354 if (node->NodeType() == DOCUMENT_FRAGMENT_NODE) { 2355 node->NotifyDevToolsOfRemovalsOfChildren(); 2356 } else if (node->GetParentNode()) { 2357 nsContentUtils::NotifyDevToolsOfNodeRemoval(*node); 2358 } 2359 } 2360 } 2361 2362 // Needed when used in combination with contenteditable (maybe) 2363 mozAutoDocUpdate updateBatch(doc, true); 2364 2365 nsAutoMutationBatch mb(this, true, true); 2366 2367 // The code above explicitly dispatched DOMNodeRemoved events if needed. 2368 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker; 2369 2370 // Replace all with node within this. 2371 RemoveAllChildren(true); 2372 mb.RemovalDone(); 2373 2374 if (aNode) { 2375 AppendChildInternal(*aNode, aMutationEffectOnScript, aRv); 2376 mb.NodesAdded(); 2377 } 2378 } 2379 2380 static bool IsDoctypeOrHasFollowingDoctype(nsINode* aNode) { 2381 for (; aNode; aNode = aNode->GetNextSibling()) { 2382 if (aNode->NodeType() == nsINode::DOCUMENT_TYPE_NODE) { 2383 return true; 2384 } 2385 } 2386 2387 return false; 2388 } 2389 2390 // https://dom.spec.whatwg.org/#dom-parentnode-movebefore 2391 void nsINode::MoveBefore(nsINode& aNode, nsINode* aChild, ErrorResult& aRv) { 2392 const auto ComputeReferenceChild = [&]() -> nsINode* { 2393 return &aNode == aChild ? aNode.GetNextSibling() : aChild; 2394 }; 2395 nsINode* referenceChild = ComputeReferenceChild(); 2396 2397 // Move algorithm 2398 // https://dom.spec.whatwg.org/#move 2399 nsINode& newParent = *this; 2400 const auto EnsureValidMoveRequest = [&newParent](nsINode& aNode, 2401 nsINode* aReferenceChild, 2402 ErrorResult& aRv) -> void { 2403 // Step 1. 2404 GetRootNodeOptions options; 2405 options.mComposed = true; 2406 if (newParent.GetRootNode(options) != aNode.GetRootNode(options)) { 2407 aRv.ThrowHierarchyRequestError("Different root node."); 2408 return; 2409 } 2410 2411 // Step 2. 2412 if (nsContentUtils::ContentIsHostIncludingDescendantOf(&newParent, 2413 &aNode)) { 2414 aRv.ThrowHierarchyRequestError("Node is an ancestor of the new parent."); 2415 return; 2416 } 2417 2418 // Step 3. 2419 if (aReferenceChild && aReferenceChild->GetParentNode() != &newParent) { 2420 aRv.ThrowNotFoundError("Wrong reference child."); 2421 return; 2422 } 2423 2424 // Step 4. 2425 if (!aNode.IsElement() && !aNode.IsCharacterData()) { 2426 aRv.ThrowHierarchyRequestError("Wrong type of node."); 2427 return; 2428 } 2429 2430 // Step 5. 2431 if (aNode.IsText() && newParent.IsDocument()) { 2432 aRv.ThrowHierarchyRequestError( 2433 "Can't move a text node to be a child of a document."); 2434 return; 2435 } 2436 2437 // Step 6. 2438 if (newParent.IsDocument() && aNode.IsElement() && 2439 (newParent.AsDocument()->GetRootElement() || 2440 IsDoctypeOrHasFollowingDoctype(aReferenceChild))) { 2441 aRv.ThrowHierarchyRequestError( 2442 "Can't move an element to be a child of the document."); 2443 return; 2444 } 2445 }; 2446 EnsureValidMoveRequest(aNode, referenceChild, aRv); 2447 if (MOZ_UNLIKELY(aRv.Failed())) { 2448 return; 2449 } 2450 2451 // Step 7. 2452 nsINode* oldParent = aNode.GetParentNode(); 2453 2454 // Step 8. 2455 MOZ_ASSERT(oldParent); 2456 2457 // For consistency with ReplaceOrInsertBefore(), we should allow DevTools to 2458 // break on the removal of aNode. 2459 if (MOZ_UNLIKELY( 2460 aNode.MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc())) { 2461 nsMutationGuard guard; 2462 nsContentUtils::NotifyDevToolsOfNodeRemoval(aNode); 2463 // If the user modifies the DOM tree, let's check same things again. 2464 if (MOZ_UNLIKELY(guard.Mutated(0))) { 2465 referenceChild = ComputeReferenceChild(); 2466 // Step 1-6. 2467 EnsureValidMoveRequest(aNode, referenceChild, aRv); 2468 if (aRv.Failed()) { 2469 return; 2470 } 2471 // Step 7. 2472 oldParent = aNode.GetParentNode(); 2473 // Step 8. 2474 MOZ_ASSERT(oldParent); 2475 } 2476 } 2477 2478 // Steps 9-12 happen implicitly in when triggering 2479 // nsIMutationObserver notifications. 2480 // Step 13, and UnbindFromTree runs step 14 and step 15 and step 16, 2481 // and also Step 25.. 2482 mozAutoDocUpdate updateBatch(GetComposedDoc(), true); 2483 { // Scope for AutoSuppressNotifyingDevToolsOfNodeRemovals 2484 // XXX Do we really need to suppress notifying DevTools of this node 2485 // removal? If we stop suppressing that, we need to check whether 2486 // `referenceChild` is still in `oldParent` after that because the user 2487 // can change the DOM with Inspector or Console. 2488 AutoSuppressNotifyingDevToolsOfNodeRemovals suppressNotifyingDevTools( 2489 *OwnerDoc()); 2490 oldParent->RemoveChildNode(aNode.AsContent(), true, nullptr, &newParent); 2491 2492 // Steps 17-24 and Step 26. 2493 // FIXME: I think this InsertChildBefore() call can be moved outside the 2494 // scope of AutoSuppressNotifyingDevToolsOfNodeRemovals. 2495 InsertChildBefore(aNode.AsContent(), 2496 referenceChild ? referenceChild->AsContent() : nullptr, 2497 true, aRv, oldParent); 2498 } 2499 } 2500 2501 void nsINode::RemoveChildNode(nsIContent* aKid, bool aNotify, 2502 const BatchRemovalState* aState, 2503 nsINode* aNewParent, 2504 MutationEffectOnScript aMutationEffectOnScript) { 2505 // NOTE: This function must not trigger any calls to 2506 // Document::GetRootElement() calls until *after* it has removed aKid from 2507 // aChildArray. Any calls before then could potentially restore a stale 2508 // value for our cached root element, per note in 2509 // Document::RemoveChildNode(). 2510 MOZ_ASSERT(aKid && aKid->GetParentNode() == this, "Bogus aKid"); 2511 MOZ_ASSERT(!IsAttr()); 2512 2513 nsMutationGuard::DidMutate(); 2514 mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify); 2515 2516 if (aNotify) { 2517 ContentRemoveInfo info; 2518 info.mBatchRemovalState = aState; 2519 info.mNewParent = aNewParent; 2520 info.mMutationEffectOnScript = aMutationEffectOnScript; 2521 MutationObservers::NotifyContentWillBeRemoved(this, aKid, info); 2522 } 2523 2524 // Since aKid is use also after DisconnectChild, ensure it stays alive. 2525 nsCOMPtr<nsIContent> kungfuDeathGrip = aKid; 2526 DisconnectChild(aKid); 2527 2528 // Invalidate cached array of child nodes 2529 InvalidateChildNodes(); 2530 aKid->UnbindFromTree(aNewParent, aState); 2531 } 2532 2533 // When replacing, aRefChild is the content being replaced; when 2534 // inserting it's the content before which we're inserting. In the 2535 // latter case it may be null. 2536 // 2537 // If aRv is a failure after this call, the insertion should not happen. 2538 // 2539 // This implements the parts of 2540 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and 2541 // the checks in https://dom.spec.whatwg.org/#concept-node-replace that 2542 // depend on the child nodes or come after steps that depend on the child nodes 2543 // (steps 2-6 in both cases). 2544 static void EnsureAllowedAsChild(nsINode* aNewChild, nsINode* aParent, 2545 bool aIsReplace, nsINode* aRefChild, 2546 ErrorResult& aRv) { 2547 MOZ_ASSERT(aNewChild, "Must have new child"); 2548 MOZ_ASSERT_IF(aIsReplace, aRefChild); 2549 MOZ_ASSERT(aParent); 2550 MOZ_ASSERT(aParent->IsDocument() || aParent->IsDocumentFragment() || 2551 aParent->IsElement(), 2552 "Nodes that are not documents, document fragments or elements " 2553 "can't be parents!"); 2554 2555 // Step 2. 2556 // A common case is that aNewChild has no element kids, in which case 2557 // aParent can't be a descendant of aNewChild unless they're 2558 // actually equal to each other. Fast-path that case, since aParent 2559 // could be pretty deep in the DOM tree. 2560 if (aNewChild == aParent || 2561 (((aNewChild->HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN) && 2562 aNewChild->GetFirstChild()) || 2563 // HTML template elements and ShadowRoot hosts need 2564 // to be checked to ensure that they are not inserted into 2565 // the hosted content. 2566 aNewChild->NodeInfo()->NameAtom() == nsGkAtoms::_template || 2567 (aNewChild->IsElement() && aNewChild->AsElement()->GetShadowRoot())) && 2568 nsContentUtils::ContentIsHostIncludingDescendantOf(aParent, 2569 aNewChild))) { 2570 aRv.ThrowHierarchyRequestError( 2571 "The new child is an ancestor of the parent"); 2572 return; 2573 } 2574 2575 // Step 3. 2576 if (aRefChild && aRefChild->GetParentNode() != aParent) { 2577 if (aIsReplace) { 2578 if (aNewChild->GetParentNode() == aParent) { 2579 aRv.ThrowNotFoundError( 2580 "New child already has this parent and old child does not. Please " 2581 "check the order of replaceChild's arguments."); 2582 } else { 2583 aRv.ThrowNotFoundError( 2584 "Child to be replaced is not a child of this node"); 2585 } 2586 } else { 2587 aRv.ThrowNotFoundError( 2588 "Child to insert before is not a child of this node"); 2589 } 2590 return; 2591 } 2592 2593 // Step 4. 2594 if (!aNewChild->IsContent()) { 2595 aRv.ThrowHierarchyRequestError(nsPrintfCString( 2596 "May not add %s as a child", NodeTypeAsString(aNewChild))); 2597 return; 2598 } 2599 2600 // Steps 5 and 6 combined. 2601 // The allowed child nodes differ for documents and elements 2602 switch (aNewChild->NodeType()) { 2603 case nsINode::COMMENT_NODE: 2604 case nsINode::PROCESSING_INSTRUCTION_NODE: 2605 // OK in both cases 2606 return; 2607 case nsINode::TEXT_NODE: 2608 case nsINode::CDATA_SECTION_NODE: 2609 case nsINode::ENTITY_REFERENCE_NODE: 2610 // Allowed under Elements and DocumentFragments 2611 if (aParent->NodeType() == nsINode::DOCUMENT_NODE) { 2612 aRv.ThrowHierarchyRequestError( 2613 nsPrintfCString("Cannot insert %s as a child of a Document", 2614 NodeTypeAsString(aNewChild))); 2615 } 2616 return; 2617 case nsINode::ELEMENT_NODE: { 2618 if (!aParent->IsDocument()) { 2619 // Always ok to have elements under other elements or document fragments 2620 return; 2621 } 2622 2623 Document* parentDocument = aParent->AsDocument(); 2624 Element* rootElement = parentDocument->GetRootElement(); 2625 if (rootElement) { 2626 // Already have a documentElement, so this is only OK if we're 2627 // replacing it. 2628 if (!aIsReplace || rootElement != aRefChild) { 2629 aRv.ThrowHierarchyRequestError( 2630 "Cannot have more than one Element child of a Document"); 2631 } 2632 return; 2633 } 2634 2635 // We don't have a documentElement yet. Our one remaining constraint is 2636 // that the documentElement must come after the doctype. 2637 if (!aRefChild) { 2638 // Appending is just fine. 2639 return; 2640 } 2641 2642 nsIContent* docTypeContent = parentDocument->GetDoctype(); 2643 if (!docTypeContent) { 2644 // It's all good. 2645 return; 2646 } 2647 2648 // The docTypeContent is retrived from the child list of the Document 2649 // node so that doctypeIndex is never Nothing. 2650 const Maybe<uint32_t> doctypeIndex = 2651 aParent->ComputeIndexOf(docTypeContent); 2652 MOZ_ASSERT(doctypeIndex.isSome()); 2653 // If aRefChild is an NAC, its index can be Nothing. 2654 const Maybe<uint32_t> insertIndex = aParent->ComputeIndexOf(aRefChild); 2655 2656 // Now we're OK in the following two cases only: 2657 // 1) We're replacing something that's not before the doctype 2658 // 2) We're inserting before something that comes after the doctype 2659 const bool ok = MOZ_LIKELY(insertIndex.isSome()) && 2660 (aIsReplace ? *insertIndex >= *doctypeIndex 2661 : *insertIndex > *doctypeIndex); 2662 if (!ok) { 2663 aRv.ThrowHierarchyRequestError( 2664 "Cannot insert a root element before the doctype"); 2665 } 2666 return; 2667 } 2668 case nsINode::DOCUMENT_TYPE_NODE: { 2669 if (!aParent->IsDocument()) { 2670 // doctypes only allowed under documents 2671 aRv.ThrowHierarchyRequestError( 2672 nsPrintfCString("Cannot insert a DocumentType as a child of %s", 2673 NodeTypeAsString(aParent))); 2674 return; 2675 } 2676 2677 Document* parentDocument = aParent->AsDocument(); 2678 nsIContent* docTypeContent = parentDocument->GetDoctype(); 2679 if (docTypeContent) { 2680 // Already have a doctype, so this is only OK if we're replacing it 2681 if (!aIsReplace || docTypeContent != aRefChild) { 2682 aRv.ThrowHierarchyRequestError( 2683 "Cannot have more than one DocumentType child of a Document"); 2684 } 2685 return; 2686 } 2687 2688 // We don't have a doctype yet. Our one remaining constraint is 2689 // that the doctype must come before the documentElement. 2690 Element* rootElement = parentDocument->GetRootElement(); 2691 if (!rootElement) { 2692 // It's all good 2693 return; 2694 } 2695 2696 if (!aRefChild) { 2697 // Trying to append a doctype, but have a documentElement 2698 aRv.ThrowHierarchyRequestError( 2699 "Cannot have a DocumentType node after the root element"); 2700 return; 2701 } 2702 2703 // rootElement is now in the child list of the Document node so that 2704 // ComputeIndexOf must success to find it. 2705 const Maybe<uint32_t> rootIndex = aParent->ComputeIndexOf(rootElement); 2706 MOZ_ASSERT(rootIndex.isSome()); 2707 const Maybe<uint32_t> insertIndex = aParent->ComputeIndexOf(aRefChild); 2708 2709 // Now we're OK if and only if insertIndex <= rootIndex. Indeed, either 2710 // we end up replacing aRefChild or we end up before it. Either one is 2711 // ok as long as aRefChild is not after rootElement. 2712 if (MOZ_LIKELY(insertIndex.isSome()) && *insertIndex > *rootIndex) { 2713 aRv.ThrowHierarchyRequestError( 2714 "Cannot have a DocumentType node after the root element"); 2715 } 2716 return; 2717 } 2718 case nsINode::DOCUMENT_FRAGMENT_NODE: { 2719 // Note that for now we only allow nodes inside document fragments if 2720 // they're allowed inside elements. If we ever change this to allow 2721 // doctype nodes in document fragments, we'll need to update this code. 2722 // Also, there's a version of this code in ReplaceOrInsertBefore. If you 2723 // change this code, change that too. 2724 if (!aParent->IsDocument()) { 2725 // All good here 2726 return; 2727 } 2728 2729 bool sawElement = false; 2730 for (nsIContent* child = aNewChild->GetFirstChild(); child; 2731 child = child->GetNextSibling()) { 2732 if (child->IsElement()) { 2733 if (sawElement) { 2734 // Can't put two elements into a document 2735 aRv.ThrowHierarchyRequestError( 2736 "Cannot have more than one Element child of a Document"); 2737 return; 2738 } 2739 sawElement = true; 2740 } 2741 // If we can put this content at the right place, we might be ok; 2742 // if not, we bail out. 2743 EnsureAllowedAsChild(child, aParent, aIsReplace, aRefChild, aRv); 2744 if (aRv.Failed()) { 2745 return; 2746 } 2747 } 2748 2749 // Everything in the fragment checked out ok, so we can stick it in here 2750 return; 2751 } 2752 default: 2753 /* 2754 * aNewChild is of invalid type. 2755 */ 2756 break; 2757 } 2758 2759 // XXXbz when can we reach this? 2760 aRv.ThrowHierarchyRequestError(nsPrintfCString("Cannot insert %s inside %s", 2761 NodeTypeAsString(aNewChild), 2762 NodeTypeAsString(aParent))); 2763 } 2764 2765 // Implements 2766 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity 2767 void nsINode::EnsurePreInsertionValidity(nsINode& aNewChild, nsINode* aRefChild, 2768 ErrorResult& aError) { 2769 EnsurePreInsertionValidity1(aError); 2770 if (aError.Failed()) { 2771 return; 2772 } 2773 EnsurePreInsertionValidity2(false, aNewChild, aRefChild, aError); 2774 } 2775 2776 // Implements the parts of 2777 // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity and 2778 // the checks in https://dom.spec.whatwg.org/#concept-node-replace that can be 2779 // evaluated before ever looking at the child nodes (step 1 in both cases). 2780 void nsINode::EnsurePreInsertionValidity1(ErrorResult& aError) { 2781 if (!IsDocument() && !IsDocumentFragment() && !IsElement()) { 2782 aError.ThrowHierarchyRequestError( 2783 nsPrintfCString("Cannot add children to %s", NodeTypeAsString(this))); 2784 return; 2785 } 2786 } 2787 2788 void nsINode::EnsurePreInsertionValidity2(bool aReplace, nsINode& aNewChild, 2789 nsINode* aRefChild, 2790 ErrorResult& aError) { 2791 if (aNewChild.IsRootOfNativeAnonymousSubtree()) { 2792 // This is anonymous content. Don't allow its insertion 2793 // anywhere, since it might have UnbindFromTree calls coming 2794 // its way. 2795 aError.ThrowNotSupportedError( 2796 "Inserting anonymous content manually is not supported"); 2797 return; 2798 } 2799 2800 // Make sure that the inserted node is allowed as a child of its new parent. 2801 EnsureAllowedAsChild(&aNewChild, this, aReplace, aRefChild, aError); 2802 } 2803 2804 nsINode* nsINode::ReplaceOrInsertBefore( 2805 bool aReplace, nsINode* aNewChild, nsINode* aRefChild, 2806 MutationEffectOnScript aMutationEffectOnScript, ErrorResult& aError) { 2807 // XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we 2808 // could rely on scriptblockers going out of scope to actually run XBL 2809 // teardown, but various crud adds nodes under scriptblockers (e.g. native 2810 // anonymous content). The only good news is those insertions can't trigger 2811 // the bad XBL cases. 2812 MOZ_ASSERT_IF(aReplace, aRefChild); 2813 2814 // Before firing DOMNodeRemoved events, make sure this is actually an insert 2815 // we plan to do. 2816 EnsurePreInsertionValidity1(aError); 2817 if (aError.Failed()) { 2818 return nullptr; 2819 } 2820 2821 EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError); 2822 if (aError.Failed()) { 2823 return nullptr; 2824 } 2825 2826 uint16_t nodeType = aNewChild->NodeType(); 2827 2828 // Before we do anything else, fire all DOMNodeRemoved mutation events 2829 // We do this up front as to avoid having to deal with script running 2830 // at random places further down. 2831 // Scope firing mutation events so that we don't carry any state that 2832 // might be stale 2833 { 2834 nsMutationGuard guard; 2835 2836 // If we're replacing, fire for node-to-be-replaced. 2837 // If aRefChild == aNewChild then we'll fire for it in check below 2838 if (aReplace && aRefChild != aNewChild) { 2839 nsContentUtils::NotifyDevToolsOfNodeRemoval(*aRefChild); 2840 } 2841 2842 // If the new node already has a parent, fire for removing from old 2843 // parent 2844 if (aNewChild->GetParentNode()) { 2845 nsContentUtils::NotifyDevToolsOfNodeRemoval(*aNewChild); 2846 } 2847 2848 // If we're inserting a fragment, fire for all the children of the 2849 // fragment 2850 if (nodeType == DOCUMENT_FRAGMENT_NODE) { 2851 static_cast<FragmentOrElement*>(aNewChild) 2852 ->NotifyDevToolsOfRemovalsOfChildren(); 2853 } 2854 2855 if (guard.Mutated(0)) { 2856 // Re-check the parts of our pre-insertion validity that might depend on 2857 // the tree shape. 2858 EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError); 2859 if (aError.Failed()) { 2860 return nullptr; 2861 } 2862 } 2863 } 2864 2865 // Record the node to insert before, if any 2866 nsIContent* nodeToInsertBefore; 2867 if (aReplace) { 2868 nodeToInsertBefore = aRefChild->GetNextSibling(); 2869 } else { 2870 // Since aRefChild is our child, it must be an nsIContent object. 2871 nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr; 2872 } 2873 if (nodeToInsertBefore == aNewChild) { 2874 // We're going to remove aNewChild from its parent, so use its next sibling 2875 // as the node to insert before. 2876 nodeToInsertBefore = nodeToInsertBefore->GetNextSibling(); 2877 } 2878 2879 Maybe<AutoTArray<nsCOMPtr<nsIContent>, 50>> fragChildren; 2880 2881 // Remove the new child from the old parent if one exists 2882 nsIContent* newContent = aNewChild->AsContent(); 2883 nsCOMPtr<nsINode> oldParent = newContent->GetParentNode(); 2884 if (oldParent) { 2885 // Hold a strong ref to nodeToInsertBefore across the removal of newContent 2886 nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore; 2887 2888 // Removing a child can run script, via XBL destructors. 2889 nsMutationGuard guard; 2890 2891 // Scope for the mutation batch and scriptblocker, so they go away 2892 // while kungFuDeathGrip is still alive. 2893 { 2894 mozAutoDocUpdate batch(newContent->GetComposedDoc(), true); 2895 nsAutoMutationBatch mb(oldParent, true, true); 2896 // ScriptBlocker ensures previous and next stay alive. 2897 nsIContent* previous = aNewChild->GetPreviousSibling(); 2898 nsIContent* next = aNewChild->GetNextSibling(); 2899 oldParent->RemoveChildNode(aNewChild->AsContent(), true, nullptr, nullptr, 2900 aMutationEffectOnScript); 2901 if (nsAutoMutationBatch::GetCurrentBatch() == &mb) { 2902 mb.RemovalDone(); 2903 mb.SetPrevSibling(previous); 2904 mb.SetNextSibling(next); 2905 } 2906 } 2907 2908 // We expect one mutation (the removal) to have happened. 2909 if (guard.Mutated(1)) { 2910 // XBL destructors, yuck. 2911 2912 // Verify that newContent has no parent. 2913 if (newContent->GetParentNode()) { 2914 aError.ThrowHierarchyRequestError( 2915 "New child was inserted somewhere else"); 2916 return nullptr; 2917 } 2918 2919 // And verify that newContent is still allowed as our child. 2920 if (aNewChild == aRefChild) { 2921 // We've already removed aRefChild. So even if we were doing a replace, 2922 // now we're doing a simple insert before nodeToInsertBefore. 2923 EnsureAllowedAsChild(newContent, this, false, nodeToInsertBefore, 2924 aError); 2925 if (aError.Failed()) { 2926 return nullptr; 2927 } 2928 } else { 2929 EnsureAllowedAsChild(newContent, this, aReplace, aRefChild, aError); 2930 if (aError.Failed()) { 2931 return nullptr; 2932 } 2933 2934 // And recompute nodeToInsertBefore, just in case. 2935 if (aReplace) { 2936 nodeToInsertBefore = aRefChild->GetNextSibling(); 2937 } else { 2938 nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr; 2939 } 2940 } 2941 } 2942 } else if (nodeType == DOCUMENT_FRAGMENT_NODE) { 2943 // Make sure to remove all the fragment's kids. We need to do this before 2944 // we start inserting anything, so we will run out XBL destructors and 2945 // binding teardown (GOD, I HATE THESE THINGS) before we insert anything 2946 // into the DOM. 2947 uint32_t count = newContent->GetChildCount(); 2948 2949 fragChildren.emplace(); 2950 2951 // Copy the children into a separate array to avoid having to deal with 2952 // mutations to the fragment later on here. 2953 fragChildren->SetCapacity(count); 2954 for (nsIContent* child = newContent->GetFirstChild(); child; 2955 child = child->GetNextSibling()) { 2956 NS_ASSERTION(!child->GetUncomposedDoc(), 2957 "How did we get a child with a current doc?"); 2958 fragChildren->AppendElement(child); 2959 } 2960 2961 // Hold a strong ref to nodeToInsertBefore across the removals 2962 nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore; 2963 2964 nsMutationGuard guard; 2965 2966 // Scope for the mutation batch and scriptblocker, so they go away 2967 // while kungFuDeathGrip is still alive. 2968 { 2969 mozAutoDocUpdate batch(newContent->GetComposedDoc(), true); 2970 nsAutoMutationBatch mb(newContent, false, true); 2971 2972 newContent->RemoveAllChildren<BatchRemovalOrder::BackToFront>(true); 2973 } 2974 2975 // We expect |count| removals 2976 if (guard.Mutated(count)) { 2977 // XBL destructors, yuck. 2978 2979 // Verify that nodeToInsertBefore, if non-null, is still our child. If 2980 // it's not, there's no way we can do this insert sanely; just bail out. 2981 if (nodeToInsertBefore && nodeToInsertBefore->GetParent() != this) { 2982 aError.ThrowHierarchyRequestError("Don't know where to insert child"); 2983 return nullptr; 2984 } 2985 2986 // Verify that all the things in fragChildren have no parent. 2987 for (uint32_t i = 0; i < count; ++i) { 2988 if (fragChildren->ElementAt(i)->GetParentNode()) { 2989 aError.ThrowHierarchyRequestError( 2990 "New child was inserted somewhere else"); 2991 return nullptr; 2992 } 2993 } 2994 2995 // Note that unlike the single-element case above, none of our kids can 2996 // be aRefChild, so we can always pass through aReplace in the 2997 // EnsureAllowedAsChild checks below and don't have to worry about whether 2998 // recomputing nodeToInsertBefore is OK. 2999 3000 // Verify that our aRefChild is still sensible 3001 if (aRefChild && aRefChild->GetParent() != this) { 3002 aError.ThrowHierarchyRequestError("Don't know where to insert child"); 3003 return nullptr; 3004 } 3005 3006 // Recompute nodeToInsertBefore, just in case. 3007 if (aReplace) { 3008 nodeToInsertBefore = aRefChild->GetNextSibling(); 3009 } else { 3010 // If aRefChild has 'this' as a parent, it must be an nsIContent. 3011 nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr; 3012 } 3013 3014 // And verify that newContent is still allowed as our child. Sadly, we 3015 // need to reimplement the relevant part of EnsureAllowedAsChild() because 3016 // now our nodes are in an array and all. If you change this code, 3017 // change the code there. 3018 if (IsDocument()) { 3019 bool sawElement = false; 3020 for (uint32_t i = 0; i < count; ++i) { 3021 nsIContent* child = fragChildren->ElementAt(i); 3022 if (child->IsElement()) { 3023 if (sawElement) { 3024 // No good 3025 aError.ThrowHierarchyRequestError( 3026 "Cannot have more than one Element child of a Document"); 3027 return nullptr; 3028 } 3029 sawElement = true; 3030 } 3031 EnsureAllowedAsChild(child, this, aReplace, aRefChild, aError); 3032 if (aError.Failed()) { 3033 return nullptr; 3034 } 3035 } 3036 } 3037 } 3038 } 3039 3040 mozAutoDocUpdate batch(GetComposedDoc(), true); 3041 nsAutoMutationBatch mb; 3042 3043 // If we're replacing and we haven't removed aRefChild yet, do so now 3044 if (aReplace && aRefChild != aNewChild) { 3045 mb.Init(this, true, true); 3046 3047 // Since aRefChild is never null in the aReplace case, we know that at 3048 // this point nodeToInsertBefore is the next sibling of aRefChild. 3049 NS_ASSERTION(aRefChild->GetNextSibling() == nodeToInsertBefore, 3050 "Unexpected nodeToInsertBefore"); 3051 3052 nsIContent* toBeRemoved = nodeToInsertBefore 3053 ? nodeToInsertBefore->GetPreviousSibling() 3054 : GetLastChild(); 3055 MOZ_ASSERT(toBeRemoved); 3056 3057 RemoveChildNode(toBeRemoved, true, nullptr, nullptr, 3058 aMutationEffectOnScript); 3059 } 3060 3061 // Move new child over to our document if needed. Do this after removing 3062 // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved 3063 // DocumentType nodes are the only nodes that can have a null 3064 // ownerDocument according to the DOM spec, and we need to allow 3065 // inserting them w/o calling AdoptNode(). 3066 Document* doc = OwnerDoc(); 3067 if (doc != newContent->OwnerDoc() && nodeType != DOCUMENT_FRAGMENT_NODE) { 3068 AdoptNodeIntoOwnerDoc(this, aNewChild, aError); 3069 if (aError.Failed()) { 3070 return nullptr; 3071 } 3072 } 3073 3074 /* 3075 * Check if we're inserting a document fragment. If we are, we need 3076 * to actually add its children individually (i.e. we don't add the 3077 * actual document fragment). 3078 */ 3079 nsINode* result = aReplace ? aRefChild : aNewChild; 3080 if (nodeType == DOCUMENT_FRAGMENT_NODE) { 3081 nsAutoMutationBatch* mutationBatch = nsAutoMutationBatch::GetCurrentBatch(); 3082 if (mutationBatch && mutationBatch != &mb) { 3083 mutationBatch = nullptr; 3084 } else if (!aReplace) { 3085 mb.Init(this, true, true); 3086 mutationBatch = nsAutoMutationBatch::GetCurrentBatch(); 3087 } 3088 3089 if (mutationBatch) { 3090 mutationBatch->RemovalDone(); 3091 mutationBatch->SetPrevSibling( 3092 nodeToInsertBefore ? nodeToInsertBefore->GetPreviousSibling() 3093 : GetLastChild()); 3094 mutationBatch->SetNextSibling(nodeToInsertBefore); 3095 } 3096 3097 uint32_t count = fragChildren->Length(); 3098 if (!count) { 3099 return result; 3100 } 3101 3102 bool appending = !IsDocument() && !nodeToInsertBefore; 3103 nsIContent* firstInsertedContent = fragChildren->ElementAt(0); 3104 3105 // Iterate through the fragment's children, and insert them in the new 3106 // parent 3107 for (uint32_t i = 0; i < count; ++i) { 3108 // XXXbz how come no reparenting here? That seems odd... 3109 // Insert the child. 3110 InsertChildBefore(fragChildren->ElementAt(i), nodeToInsertBefore, 3111 !appending, aError); 3112 if (aError.Failed()) { 3113 // Make sure to notify on any children that we did succeed to insert 3114 if (appending && i != 0) { 3115 ContentAppendInfo info; 3116 info.mMutationEffectOnScript = aMutationEffectOnScript; 3117 MutationObservers::NotifyContentAppended( 3118 static_cast<nsIContent*>(this), firstInsertedContent, info); 3119 } 3120 return nullptr; 3121 } 3122 } 3123 3124 if (mutationBatch && !appending) { 3125 mutationBatch->NodesAdded(); 3126 } 3127 3128 // Notify and fire mutation events when appending 3129 if (appending) { 3130 ContentAppendInfo info; 3131 info.mMutationEffectOnScript = aMutationEffectOnScript; 3132 MutationObservers::NotifyContentAppended(static_cast<nsIContent*>(this), 3133 firstInsertedContent, info); 3134 if (mutationBatch) { 3135 mutationBatch->NodesAdded(); 3136 } 3137 } 3138 } else { 3139 // Not inserting a fragment but rather a single node. 3140 3141 // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654 3142 // We need to reparent here for nodes for which the parent of their 3143 // wrapper is not the wrapper for their ownerDocument (XUL elements, 3144 // form controls, ...). Also applies in the fragment code above. 3145 if (nsAutoMutationBatch::GetCurrentBatch() == &mb) { 3146 mb.RemovalDone(); 3147 mb.SetPrevSibling(nodeToInsertBefore 3148 ? nodeToInsertBefore->GetPreviousSibling() 3149 : GetLastChild()); 3150 mb.SetNextSibling(nodeToInsertBefore); 3151 } 3152 InsertChildBefore(newContent, nodeToInsertBefore, true, aError, nullptr, 3153 aMutationEffectOnScript); 3154 if (aError.Failed()) { 3155 return nullptr; 3156 } 3157 } 3158 3159 return result; 3160 } 3161 3162 void nsINode::BindObject(nsISupports* aObject, UnbindCallback aDtor) { 3163 Slots()->mBoundObjects.EmplaceBack(aObject, aDtor); 3164 } 3165 3166 void nsINode::UnbindObject(nsISupports* aObject) { 3167 if (auto* slots = GetExistingSlots()) { 3168 slots->mBoundObjects.RemoveElement(aObject); 3169 } 3170 } 3171 3172 already_AddRefed<AccessibleNode> nsINode::GetAccessibleNode() { 3173 #ifdef ACCESSIBILITY 3174 nsresult rv = NS_OK; 3175 3176 RefPtr<AccessibleNode> anode = 3177 static_cast<AccessibleNode*>(GetProperty(nsGkAtoms::accessiblenode, &rv)); 3178 if (NS_FAILED(rv)) { 3179 anode = new AccessibleNode(this); 3180 RefPtr<AccessibleNode> temp = anode; 3181 rv = SetProperty(nsGkAtoms::accessiblenode, temp.forget().take(), 3182 nsPropertyTable::SupportsDtorFunc, true); 3183 if (NS_FAILED(rv)) { 3184 NS_WARNING("SetProperty failed"); 3185 return nullptr; 3186 } 3187 } 3188 return anode.forget(); 3189 #else 3190 return nullptr; 3191 #endif 3192 } 3193 3194 void nsINode::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 3195 size_t* aNodeSize) const { 3196 EventListenerManager* elm = GetExistingListenerManager(); 3197 if (elm) { 3198 *aNodeSize += elm->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf); 3199 } 3200 3201 // Measurement of the following members may be added later if DMD finds it is 3202 // worthwhile: 3203 // - mNodeInfo 3204 // - mSlots 3205 // 3206 // The following members are not measured: 3207 // - mParent, mNextSibling, mPreviousOrLastSibling, mFirstChild: because 3208 // they're non-owning, from "exclusive ownership" point of view. 3209 } 3210 3211 void nsINode::AddSizeOfIncludingThis(nsWindowSizes& aSizes, 3212 size_t* aNodeSize) const { 3213 *aNodeSize += aSizes.mState.mMallocSizeOf(this); 3214 AddSizeOfExcludingThis(aSizes, aNodeSize); 3215 } 3216 3217 bool nsINode::Contains(const nsINode* aOther) const { 3218 if (aOther == this) { 3219 return true; 3220 } 3221 3222 if (!aOther || OwnerDoc() != aOther->OwnerDoc() || 3223 IsInUncomposedDoc() != aOther->IsInUncomposedDoc() || 3224 !aOther->IsContent() || !HasChildren()) { 3225 return false; 3226 } 3227 3228 if (IsDocument()) { 3229 // document.contains(aOther) returns true if aOther is in the document, 3230 // but is not in any anonymous subtree. 3231 // IsInUncomposedDoc() check is done already before this. 3232 return !aOther->IsInNativeAnonymousSubtree(); 3233 } 3234 3235 if (!IsElement() && !IsDocumentFragment()) { 3236 return false; 3237 } 3238 3239 if (IsInShadowTree() != aOther->IsInShadowTree() || 3240 IsInNativeAnonymousSubtree() != aOther->IsInNativeAnonymousSubtree()) { 3241 return false; 3242 } 3243 3244 if (IsInNativeAnonymousSubtree()) { 3245 if (GetClosestNativeAnonymousSubtreeRoot() != 3246 aOther->GetClosestNativeAnonymousSubtreeRoot()) { 3247 return false; 3248 } 3249 } 3250 3251 if (IsInShadowTree()) { 3252 ShadowRoot* otherRoot = aOther->GetContainingShadow(); 3253 if (IsShadowRoot()) { 3254 return otherRoot == this; 3255 } 3256 if (otherRoot != GetContainingShadow()) { 3257 return false; 3258 } 3259 } 3260 3261 return aOther->IsInclusiveDescendantOf(this); 3262 } 3263 3264 uint32_t nsINode::Length() const { 3265 switch (NodeType()) { 3266 case DOCUMENT_TYPE_NODE: 3267 return 0; 3268 3269 case TEXT_NODE: 3270 case CDATA_SECTION_NODE: 3271 case PROCESSING_INSTRUCTION_NODE: 3272 case COMMENT_NODE: 3273 MOZ_ASSERT(IsContent()); 3274 return AsContent()->TextLength(); 3275 3276 default: 3277 return GetChildCount(); 3278 } 3279 } 3280 3281 namespace { 3282 class SelectorCacheKey { 3283 public: 3284 explicit SelectorCacheKey(const nsACString& aString) : mKey(aString) { 3285 MOZ_COUNT_CTOR(SelectorCacheKey); 3286 } 3287 3288 nsCString mKey; 3289 nsExpirationState mState; 3290 3291 nsExpirationState* GetExpirationState() { return &mState; } 3292 3293 MOZ_COUNTED_DTOR(SelectorCacheKey) 3294 }; 3295 3296 class SelectorCache final : public nsExpirationTracker<SelectorCacheKey, 4> { 3297 public: 3298 using SelectorList = UniquePtr<StyleSelectorList>; 3299 using Table = nsTHashMap<nsCStringHashKey, SelectorList>; 3300 3301 SelectorCache() 3302 : nsExpirationTracker<SelectorCacheKey, 4>( 3303 1000, "SelectorCache"_ns, GetMainThreadSerialEventTarget()) {} 3304 3305 void NotifyExpired(SelectorCacheKey* aSelector) final { 3306 MOZ_ASSERT(NS_IsMainThread()); 3307 MOZ_ASSERT(aSelector); 3308 3309 // There is no guarantee that this method won't be re-entered when selector 3310 // matching is ongoing because "memory-pressure" could be notified 3311 // immediately when OOM happens according to the design of 3312 // nsExpirationTracker. The perfect solution is to delete the |aSelector| 3313 // and its StyleSelectorList in mTable asynchronously. We remove these 3314 // objects synchronously for now because NotifyExpired() will never be 3315 // triggered by "memory-pressure" which is not implemented yet in the stage 3316 // 2 of mozalloc_handle_oom(). Once these objects are removed 3317 // asynchronously, we should update the warning added in 3318 // mozalloc_handle_oom() as well. 3319 RemoveObject(aSelector); 3320 mTable.Remove(aSelector->mKey); 3321 delete aSelector; 3322 } 3323 3324 // We do not call MarkUsed because it would just slow down lookups and 3325 // because we're OK expiring things after a few seconds even if they're 3326 // being used. Returns whether we actually had an entry for aSelector. 3327 // 3328 // If we have an entry and the selector list returned has a null 3329 // StyleSelectorList*, that indicates that aSelector has already been 3330 // parsed and is not a syntactically valid selector. 3331 template <typename F> 3332 StyleSelectorList* GetListOrInsertFrom(const nsACString& aSelector, 3333 F&& aFrom) { 3334 MOZ_ASSERT(NS_IsMainThread()); 3335 return mTable.LookupOrInsertWith(aSelector, std::forward<F>(aFrom)).get(); 3336 } 3337 3338 ~SelectorCache() { AgeAllGenerations(); } 3339 3340 private: 3341 Table mTable; 3342 }; 3343 3344 SelectorCache& GetSelectorCache(bool aChromeRulesEnabled) { 3345 static StaticAutoPtr<SelectorCache> sSelectorCache; 3346 static StaticAutoPtr<SelectorCache> sChromeSelectorCache; 3347 auto& cache = aChromeRulesEnabled ? sChromeSelectorCache : sSelectorCache; 3348 if (!cache) { 3349 cache = new SelectorCache(); 3350 ClearOnShutdown(&cache); 3351 } 3352 return *cache; 3353 } 3354 } // namespace 3355 3356 const StyleSelectorList* nsINode::ParseSelectorList( 3357 const nsACString& aSelectorString, ErrorResult& aRv) { 3358 Document* doc = OwnerDoc(); 3359 const bool chromeRulesEnabled = doc->ChromeRulesEnabled(); 3360 3361 SelectorCache& cache = GetSelectorCache(chromeRulesEnabled); 3362 StyleSelectorList* list = cache.GetListOrInsertFrom(aSelectorString, [&] { 3363 // Note that we want to cache even if null was returned, because we 3364 // want to cache the "This is not a valid selector" result. 3365 return WrapUnique( 3366 Servo_SelectorList_Parse(&aSelectorString, chromeRulesEnabled)); 3367 }); 3368 3369 if (!list) { 3370 // Invalid selector. 3371 aRv.ThrowSyntaxError("'"_ns + aSelectorString + 3372 "' is not a valid selector"_ns); 3373 } 3374 3375 return list; 3376 } 3377 3378 // Given an id, find first element with that id under aRoot. 3379 // If none found, return nullptr. aRoot must be in the document. 3380 inline static Element* FindMatchingElementWithId( 3381 const nsAString& aId, const Element& aRoot, 3382 const DocumentOrShadowRoot& aContainingDocOrShadowRoot) { 3383 MOZ_ASSERT(aRoot.SubtreeRoot() == &aContainingDocOrShadowRoot.AsNode()); 3384 MOZ_ASSERT( 3385 aRoot.IsInUncomposedDoc() || aRoot.IsInShadowTree(), 3386 "Don't call me if the root is not in the document or in a shadow tree"); 3387 3388 Span elements = aContainingDocOrShadowRoot.GetAllElementsForId(aId); 3389 3390 // XXXbz: Should we fall back to the tree walk if |elements| is long, 3391 // for some value of "long"? 3392 for (Element* element : elements) { 3393 if (MOZ_UNLIKELY(element == &aRoot)) { 3394 continue; 3395 } 3396 3397 if (!element->IsInclusiveDescendantOf(&aRoot)) { 3398 continue; 3399 } 3400 3401 // We have an element with the right id and it's a strict descendant 3402 // of aRoot. 3403 return element; 3404 } 3405 3406 return nullptr; 3407 } 3408 3409 Element* nsINode::QuerySelector(const nsACString& aSelector, 3410 ErrorResult& aResult) { 3411 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS( 3412 "querySelector", LAYOUT_SelectorQuery, aSelector); 3413 3414 const StyleSelectorList* list = ParseSelectorList(aSelector, aResult); 3415 if (!list) { 3416 return nullptr; 3417 } 3418 const bool useInvalidation = false; 3419 return const_cast<Element*>( 3420 Servo_SelectorList_QueryFirst(this, list, useInvalidation)); 3421 } 3422 3423 already_AddRefed<nsINodeList> nsINode::QuerySelectorAll( 3424 const nsACString& aSelector, ErrorResult& aResult) { 3425 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS( 3426 "querySelectorAll", LAYOUT_SelectorQuery, aSelector); 3427 3428 RefPtr<nsSimpleContentList> contentList = new nsSimpleContentList(this); 3429 const StyleSelectorList* list = ParseSelectorList(aSelector, aResult); 3430 if (!list) { 3431 return contentList.forget(); 3432 } 3433 3434 const bool useInvalidation = false; 3435 Servo_SelectorList_QueryAll(this, list, contentList.get(), useInvalidation); 3436 return contentList.forget(); 3437 } 3438 3439 Element* nsINode::GetElementById(const nsAString& aId) { 3440 MOZ_ASSERT(!IsShadowRoot(), "Should use the faster version"); 3441 MOZ_ASSERT(IsElement() || IsDocumentFragment(), 3442 "Bogus this object for GetElementById call"); 3443 if (IsInUncomposedDoc()) { 3444 MOZ_ASSERT(IsElement(), "Huh? A fragment in a document?"); 3445 return FindMatchingElementWithId(aId, *AsElement(), *OwnerDoc()); 3446 } 3447 3448 if (ShadowRoot* containingShadow = AsContent()->GetContainingShadow()) { 3449 MOZ_ASSERT(IsElement(), "Huh? A fragment in a ShadowRoot?"); 3450 return FindMatchingElementWithId(aId, *AsElement(), *containingShadow); 3451 } 3452 3453 for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextNode(this)) { 3454 if (!kid->IsElement()) { 3455 continue; 3456 } 3457 nsAtom* id = kid->AsElement()->GetID(); 3458 if (id && id->Equals(aId)) { 3459 return kid->AsElement(); 3460 } 3461 } 3462 return nullptr; 3463 } 3464 3465 JSObject* nsINode::WrapObject(JSContext* aCx, 3466 JS::Handle<JSObject*> aGivenProto) { 3467 // Make sure one of these is true 3468 // (1) our owner document has a script handling object, 3469 // (2) Our owner document has had a script handling object, or has been marked 3470 // to have had one, 3471 // (3) we are running a privileged script. 3472 // Event handling is possible only if (1). If (2) event handling is 3473 // prevented. 3474 // If the document has never had a script handling object, untrusted 3475 // scripts (3) shouldn't touch it! 3476 bool hasHadScriptHandlingObject = false; 3477 if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject) && 3478 !hasHadScriptHandlingObject && !nsContentUtils::IsSystemCaller(aCx)) { 3479 Throw(aCx, NS_ERROR_UNEXPECTED); 3480 return nullptr; 3481 } 3482 3483 JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx, aGivenProto)); 3484 if (obj && ChromeOnlyAccess()) { 3485 MOZ_RELEASE_ASSERT( 3486 xpc::IsUnprivilegedJunkScope(JS::GetNonCCWObjectGlobal(obj)) || 3487 xpc::IsInUAWidgetScope(obj) || xpc::AccessCheck::isChrome(obj)); 3488 } 3489 return obj; 3490 } 3491 3492 already_AddRefed<nsINode> nsINode::CloneNode(bool aDeep, ErrorResult& aError) { 3493 return Clone(aDeep, nullptr, aError); 3494 } 3495 3496 nsDOMAttributeMap* nsINode::GetAttributes() { 3497 if (!IsElement()) { 3498 return nullptr; 3499 } 3500 return AsElement()->Attributes(); 3501 } 3502 3503 Element* nsINode::GetParentElementCrossingShadowRoot() const { 3504 if (!mParent) { 3505 return nullptr; 3506 } 3507 3508 if (mParent->IsElement()) { 3509 return mParent->AsElement(); 3510 } 3511 3512 if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent)) { 3513 MOZ_ASSERT(shadowRoot->GetHost(), "ShowRoots should always have a host"); 3514 return shadowRoot->GetHost(); 3515 } 3516 3517 return nullptr; 3518 } 3519 3520 bool nsINode::HasBoxQuadsSupport(JSContext* aCx, JSObject* /* unused */) { 3521 return xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) || 3522 StaticPrefs::layout_css_getBoxQuads_enabled(); 3523 } 3524 3525 nsINode* nsINode::GetScopeChainParent() const { return nullptr; } 3526 3527 Element* nsINode::GetParentFlexElement() { 3528 if (!IsContent()) { 3529 return nullptr; 3530 } 3531 3532 nsIFrame* primaryFrame = AsContent()->GetPrimaryFrame(FlushType::Frames); 3533 3534 // Walk up the parent chain and pierce through any anonymous boxes 3535 // that might be between this frame and a possible flex parent. 3536 for (nsIFrame* f = primaryFrame; f; f = f->GetParent()) { 3537 if (f != primaryFrame && !f->Style()->IsAnonBox()) { 3538 // We hit a non-anonymous ancestor before finding a flex item. 3539 // Bail out. 3540 break; 3541 } 3542 if (f->IsFlexItem()) { 3543 return f->GetParent()->GetContent()->AsElement(); 3544 } 3545 } 3546 3547 return nullptr; 3548 } 3549 3550 Element* nsINode::GetNearestInclusiveOpenPopover() const { 3551 for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) { 3552 if (el->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) { 3553 return el; 3554 } 3555 } 3556 return nullptr; 3557 } 3558 3559 Element* nsINode::GetNearestInclusiveTargetPopoverForInvoker() const { 3560 for (auto* el : InclusiveFlatTreeAncestorsOfType<Element>()) { 3561 if (auto* popover = el->GetEffectiveCommandForElement()) { 3562 if (popover->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) { 3563 return popover; 3564 } 3565 } 3566 if (auto* popover = el->GetEffectivePopoverTargetElement()) { 3567 if (popover->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) { 3568 return popover; 3569 } 3570 } 3571 } 3572 return nullptr; 3573 } 3574 3575 nsGenericHTMLElement* nsINode::GetEffectiveCommandForElement() const { 3576 if (!StaticPrefs::dom_element_commandfor_enabled()) { 3577 return nullptr; 3578 } 3579 3580 const auto* formControl = 3581 nsGenericHTMLFormControlElementWithState::FromNode(this); 3582 if (!formControl || formControl->IsDisabled() || 3583 !formControl->IsButtonControl()) { 3584 return nullptr; 3585 } 3586 3587 if (const auto* buttonControl = HTMLButtonElement::FromNodeOrNull(this)) { 3588 if (auto* popover = nsGenericHTMLElement::FromNodeOrNull( 3589 buttonControl->GetCommandForElement())) { 3590 if (popover->GetPopoverAttributeState() != PopoverAttributeState::None) { 3591 return popover; 3592 } 3593 } 3594 } 3595 return nullptr; 3596 } 3597 3598 nsGenericHTMLElement* nsINode::GetEffectivePopoverTargetElement() const { 3599 const auto* formControl = 3600 nsGenericHTMLFormControlElementWithState::FromNode(this); 3601 if (!formControl || formControl->IsDisabled() || 3602 !formControl->IsButtonControl()) { 3603 return nullptr; 3604 } 3605 if (auto* popover = nsGenericHTMLElement::FromNodeOrNull( 3606 formControl->GetPopoverTargetElement())) { 3607 if (popover->GetPopoverAttributeState() != PopoverAttributeState::None) { 3608 return popover; 3609 } 3610 } 3611 return nullptr; 3612 } 3613 3614 Element* nsINode::GetTopmostClickedPopover() const { 3615 Element* clickedPopover = GetNearestInclusiveOpenPopover(); 3616 Element* invokedPopover = GetNearestInclusiveTargetPopoverForInvoker(); 3617 if (!clickedPopover) { 3618 return invokedPopover; 3619 } 3620 auto autoPopoverList = 3621 clickedPopover->OwnerDoc()->PopoverListOf(PopoverAttributeState::Auto); 3622 for (Element* el : Reversed(autoPopoverList)) { 3623 if (el == clickedPopover || el == invokedPopover) { 3624 return el; 3625 } 3626 } 3627 return nullptr; 3628 } 3629 3630 // https://html.spec.whatwg.org/multipage/interactive-elements.html#nearest-clicked-dialog 3631 HTMLDialogElement* nsINode::NearestClickedDialog(mozilla::WidgetEvent* aEvent) { 3632 // 1. Let target be event's target. 3633 // (Skipped - `this`). 3634 3635 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent(); 3636 if (!pointerEvent) { 3637 return nullptr; 3638 } 3639 3640 // 2. If target is a dialog element, target has an open attribute, target's is 3641 // modal is true... 3642 RefPtr dialogElement = HTMLDialogElement::FromNode(this); 3643 if (dialogElement && dialogElement->IsInTopLayer()) { 3644 // ... , and event's clientX and clientY are outside the bounds of target, 3645 // then return null. 3646 auto* frame = dialogElement->GetPrimaryFrame(); 3647 if (!frame) { 3648 return nullptr; 3649 } 3650 nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo( 3651 aEvent, pointerEvent->mRefPoint, RelativeTo{frame}); 3652 nsRect frameRect = frame->GetRectRelativeToSelf(); 3653 if (!frameRect.Contains(point)) { 3654 return nullptr; 3655 } 3656 } 3657 3658 // 3. Let currentNode be target. 3659 // 4. While currentNode is not null: 3660 // 4.2 Set currentNode to currentNode's parent in the flat tree. 3661 for (auto* currentNode : 3662 InclusiveFlatTreeAncestorsOfType<HTMLDialogElement>()) { 3663 // 4.1 If currentNode is a dialog element and currentNode has an open 3664 // attribute, then return currentNode. 3665 if (currentNode->Open()) { 3666 return currentNode; 3667 } 3668 } 3669 3670 // 5. Return null. 3671 return nullptr; 3672 } 3673 3674 void nsINode::AddAnimationObserver(nsIAnimationObserver* aAnimationObserver) { 3675 AddMutationObserver(aAnimationObserver); 3676 OwnerDoc()->SetMayHaveAnimationObservers(); 3677 } 3678 3679 void nsINode::AddAnimationObserverUnlessExists( 3680 nsIAnimationObserver* aAnimationObserver) { 3681 AddMutationObserverUnlessExists(aAnimationObserver); 3682 OwnerDoc()->SetMayHaveAnimationObservers(); 3683 } 3684 3685 already_AddRefed<nsINode> nsINode::CloneAndAdopt( 3686 nsINode* aNode, bool aClone, bool aDeep, 3687 nsNodeInfoManager* aNewNodeInfoManager, 3688 JS::Handle<JSObject*> aReparentScope, nsINode* aParent, 3689 ErrorResult& aError) { 3690 MOZ_ASSERT((!aClone && aNewNodeInfoManager) || !aReparentScope, 3691 "If cloning or not getting a new nodeinfo we shouldn't rewrap"); 3692 MOZ_ASSERT(!aParent || aNode->IsContent(), 3693 "Can't insert document or attribute nodes into a parent"); 3694 3695 // First deal with aNode and walk its attributes (and their children). Then, 3696 // if aDeep is true, deal with aNode's children (and recurse into their 3697 // attributes and children). 3698 3699 nsAutoScriptBlocker scriptBlocker; 3700 3701 nsNodeInfoManager* nodeInfoManager = aNewNodeInfoManager; 3702 3703 // aNode. 3704 class NodeInfo* nodeInfo = aNode->mNodeInfo; 3705 RefPtr<class NodeInfo> newNodeInfo; 3706 if (nodeInfoManager) { 3707 // Don't allow importing/adopting nodes from non-privileged "scriptable" 3708 // documents to "non-scriptable" documents. 3709 Document* newDoc = nodeInfoManager->GetDocument(); 3710 if (NS_WARN_IF(!newDoc)) { 3711 aError.Throw(NS_ERROR_UNEXPECTED); 3712 return nullptr; 3713 } 3714 bool hasHadScriptHandlingObject = false; 3715 if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) && 3716 !hasHadScriptHandlingObject) { 3717 Document* currentDoc = aNode->OwnerDoc(); 3718 if (NS_WARN_IF(!nsContentUtils::IsChromeDoc(currentDoc) && 3719 (currentDoc->GetScriptHandlingObject( 3720 hasHadScriptHandlingObject) || 3721 hasHadScriptHandlingObject))) { 3722 aError.Throw(NS_ERROR_UNEXPECTED); 3723 return nullptr; 3724 } 3725 } 3726 3727 newNodeInfo = nodeInfoManager->GetNodeInfo( 3728 nodeInfo->NameAtom(), nodeInfo->GetPrefixAtom(), 3729 nodeInfo->NamespaceID(), nodeInfo->NodeType(), 3730 nodeInfo->GetExtraName()); 3731 3732 nodeInfo = newNodeInfo; 3733 } 3734 3735 Element* elem = Element::FromNode(aNode); 3736 3737 nsCOMPtr<nsINode> clone; 3738 if (aClone) { 3739 nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone)); 3740 if (NS_WARN_IF(NS_FAILED(rv))) { 3741 aError.Throw(rv); 3742 return nullptr; 3743 } 3744 3745 if (aParent) { 3746 // If we're cloning we need to insert the cloned children into the cloned 3747 // parent. 3748 aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()), 3749 /* aNotify = */ true, aError); 3750 if (NS_WARN_IF(aError.Failed())) { 3751 return nullptr; 3752 } 3753 } else if (aDeep && clone->IsDocument()) { 3754 // After cloning the document itself, we want to clone the children into 3755 // the cloned document (somewhat like cloning and importing them into the 3756 // cloned document). 3757 nodeInfoManager = clone->mNodeInfo->NodeInfoManager(); 3758 } 3759 } else if (nodeInfoManager) { 3760 Document* oldDoc = aNode->OwnerDoc(); 3761 3762 DOMArena* domArenaToStore = 3763 !aNode->HasFlag(NODE_KEEPS_DOMARENA) 3764 ? aNode->NodeInfo()->NodeInfoManager()->GetArenaAllocator() 3765 : nullptr; 3766 3767 Document* newDoc = nodeInfoManager->GetDocument(); 3768 MOZ_ASSERT(newDoc); 3769 3770 bool wasRegistered = false; 3771 if (elem) { 3772 wasRegistered = oldDoc->UnregisterActivityObserver(elem); 3773 } 3774 3775 const bool hadProperties = aNode->HasProperties(); 3776 if (hadProperties) { 3777 // NOTE: We want this to happen before NodeInfoChanged so that 3778 // NodeInfoChanged can use node properties normally. 3779 // 3780 // When this fails, it removes all properties for the node anyway, so no 3781 // extra error handling needed. 3782 (void)oldDoc->PropertyTable().TransferOrRemoveAllPropertiesFor( 3783 aNode, newDoc->PropertyTable()); 3784 } 3785 3786 aNode->mNodeInfo.swap(newNodeInfo); 3787 aNode->NodeInfoChanged(oldDoc); 3788 3789 MOZ_ASSERT(newDoc != oldDoc); 3790 if (elem) { 3791 // Adopted callback must be enqueued whenever a node’s 3792 // shadow-including inclusive descendants that is custom. 3793 CustomElementData* data = elem->GetCustomElementData(); 3794 if (data && data->mState == CustomElementData::State::eCustom) { 3795 LifecycleCallbackArgs args; 3796 args.mOldDocument = oldDoc; 3797 args.mNewDocument = newDoc; 3798 3799 nsContentUtils::EnqueueLifecycleCallback(ElementCallbackType::eAdopted, 3800 elem, args); 3801 } 3802 } 3803 3804 // XXX what if oldDoc is null, we don't know if this should be 3805 // registered or not! Can that really happen? 3806 if (wasRegistered) { 3807 newDoc->RegisterActivityObserver(aNode->AsElement()); 3808 } 3809 3810 if (nsPIDOMWindowInner* window = newDoc->GetInnerWindow()) { 3811 EventListenerManager* elm = aNode->GetExistingListenerManager(); 3812 if (elm) { 3813 if (elm->MayHaveDOMActivateListeners()) { 3814 window->SetHasDOMActivateEventListeners(); 3815 } 3816 if (elm->MayHaveTouchEventListener()) { 3817 window->SetHasTouchEventListeners(); 3818 } 3819 if (elm->MayHaveMouseEnterLeaveEventListener()) { 3820 window->SetHasMouseEnterLeaveEventListeners(); 3821 } 3822 if (elm->MayHavePointerEnterLeaveEventListener()) { 3823 window->SetHasPointerEnterLeaveEventListeners(); 3824 } 3825 if (elm->MayHavePointerRawUpdateEventListener()) { 3826 window->MaybeSetHasPointerRawUpdateEventListeners(); 3827 } 3828 if (elm->MayHaveSelectionChangeEventListener()) { 3829 window->SetHasSelectionChangeEventListeners(); 3830 } 3831 if (elm->MayHaveFormSelectEventListener()) { 3832 window->SetHasFormSelectEventListeners(); 3833 } 3834 if (elm->MayHaveTransitionEventListener()) { 3835 window->SetHasTransitionEventListeners(); 3836 } 3837 if (elm->MayHaveSMILTimeEventListener()) { 3838 window->SetHasSMILTimeEventListeners(); 3839 } 3840 } 3841 } 3842 if (wasRegistered) { 3843 nsIContent* content = aNode->AsContent(); 3844 if (auto* mediaElem = HTMLMediaElement::FromNodeOrNull(content)) { 3845 mediaElem->NotifyOwnerDocumentActivityChanged(); 3846 } 3847 // HTMLImageElement::FromNode is insufficient since we need this for 3848 // <svg:image> as well. 3849 nsCOMPtr<nsIImageLoadingContent> imageLoadingContent = 3850 do_QueryInterface(aNode); 3851 if (imageLoadingContent) { 3852 auto* ilc = 3853 static_cast<nsImageLoadingContent*>(imageLoadingContent.get()); 3854 ilc->NotifyOwnerDocumentActivityChanged(); 3855 } 3856 } 3857 3858 if (oldDoc->MayHaveDOMMutationObservers()) { 3859 newDoc->SetMayHaveDOMMutationObservers(); 3860 } 3861 3862 if (oldDoc->MayHaveAnimationObservers()) { 3863 newDoc->SetMayHaveAnimationObservers(); 3864 } 3865 3866 if (elem) { 3867 elem->RecompileScriptEventListeners(); 3868 } 3869 3870 if (aReparentScope) { 3871 AutoJSContext cx; 3872 JS::Rooted<JSObject*> wrapper(cx); 3873 if ((wrapper = aNode->GetWrapper())) { 3874 MOZ_ASSERT(IsDOMObject(wrapper)); 3875 JSAutoRealm ar(cx, wrapper); 3876 UpdateReflectorGlobal(cx, wrapper, aError); 3877 if (aError.Failed()) { 3878 if (wasRegistered) { 3879 newDoc->UnregisterActivityObserver(aNode->AsElement()); 3880 } 3881 if (hadProperties) { 3882 // NOTE: When it fails it removes all properties for the node 3883 // anyway, so no extra error handling needed. 3884 (void)newDoc->PropertyTable().TransferOrRemoveAllPropertiesFor( 3885 aNode, oldDoc->PropertyTable()); 3886 } 3887 aNode->mNodeInfo.swap(newNodeInfo); 3888 aNode->NodeInfoChanged(newDoc); 3889 if (wasRegistered) { 3890 oldDoc->RegisterActivityObserver(aNode->AsElement()); 3891 } 3892 return nullptr; 3893 } 3894 } 3895 } 3896 3897 // At this point, a new node is added to the document, and this 3898 // node isn't allocated by the NodeInfoManager of this document, 3899 // so we need to do this SetArenaAllocator logic to bypass 3900 // the !HasChildren() check in NodeInfoManager::Allocate. 3901 if (mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) { 3902 if (!newDoc->NodeInfoManager()->HasAllocated()) { 3903 if (DocGroup* docGroup = newDoc->GetDocGroup()) { 3904 newDoc->NodeInfoManager()->SetArenaAllocator( 3905 docGroup->ArenaAllocator()); 3906 } 3907 } 3908 3909 if (domArenaToStore && newDoc->GetDocGroup() != oldDoc->GetDocGroup()) { 3910 nsContentUtils::AddEntryToDOMArenaTable(aNode, domArenaToStore); 3911 } 3912 } 3913 } 3914 3915 if (aDeep && (!aClone || !aNode->IsAttr())) { 3916 // aNode's children. 3917 for (nsIContent* cloneChild = aNode->GetFirstChild(); cloneChild; 3918 cloneChild = cloneChild->GetNextSibling()) { 3919 nsCOMPtr<nsINode> child = 3920 CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager, 3921 aReparentScope, clone, aError); 3922 if (NS_WARN_IF(aError.Failed())) { 3923 return nullptr; 3924 } 3925 } 3926 } 3927 3928 if (aDeep && aNode->IsElement()) { 3929 if (aClone) { 3930 if (nodeInfo->GetDocument()->IsStaticDocument()) { 3931 // Clone any animations to the node in the static document, including 3932 // the current timing. They will need to be paused later after the new 3933 // document's pres shell gets initialized. 3934 // 3935 // This needs to be done here rather than in Element::CopyInnerTo 3936 // because the animations clone code relies on the target (that is, 3937 // `clone`) being connected already. 3938 clone->AsElement()->CloneAnimationsFrom(*aNode->AsElement()); 3939 3940 // Clone the Shadow DOM 3941 ShadowRoot* originalShadowRoot = aNode->AsElement()->GetShadowRoot(); 3942 if (originalShadowRoot) { 3943 RefPtr<ShadowRoot> newShadowRoot = 3944 clone->AsElement()->AttachShadowWithoutNameChecks( 3945 originalShadowRoot->Mode()); 3946 3947 newShadowRoot->CloneInternalDataFrom(originalShadowRoot); 3948 for (nsIContent* origChild = originalShadowRoot->GetFirstChild(); 3949 origChild; origChild = origChild->GetNextSibling()) { 3950 nsCOMPtr<nsINode> child = 3951 CloneAndAdopt(origChild, aClone, aDeep, nodeInfoManager, 3952 aReparentScope, newShadowRoot, aError); 3953 if (NS_WARN_IF(aError.Failed())) { 3954 return nullptr; 3955 } 3956 } 3957 } 3958 } 3959 } else { 3960 if (ShadowRoot* shadowRoot = aNode->AsElement()->GetShadowRoot()) { 3961 nsCOMPtr<nsINode> child = 3962 CloneAndAdopt(shadowRoot, aClone, aDeep, nodeInfoManager, 3963 aReparentScope, clone, aError); 3964 if (NS_WARN_IF(aError.Failed())) { 3965 return nullptr; 3966 } 3967 } 3968 } 3969 } 3970 3971 if (aClone && aNode->IsElement() && 3972 !nodeInfo->GetDocument()->IsStaticDocument()) { 3973 // Clone the Shadow DOM 3974 ShadowRoot* originalShadowRoot = aNode->AsElement()->GetShadowRoot(); 3975 if (originalShadowRoot && originalShadowRoot->Clonable()) { 3976 ShadowRootInit init; 3977 init.mMode = originalShadowRoot->Mode(); 3978 init.mDelegatesFocus = originalShadowRoot->DelegatesFocus(); 3979 init.mSlotAssignment = originalShadowRoot->SlotAssignment(); 3980 init.mClonable = true; 3981 3982 RefPtr<ShadowRoot> newShadowRoot = 3983 clone->AsElement()->AttachShadow(init, aError); 3984 if (NS_WARN_IF(aError.Failed())) { 3985 return nullptr; 3986 } 3987 newShadowRoot->SetIsDeclarative(originalShadowRoot->IsDeclarative()); 3988 nsAtom* referenceTarget = originalShadowRoot->ReferenceTarget(); 3989 newShadowRoot->SetReferenceTarget(referenceTarget); 3990 3991 for (nsIContent* origChild = originalShadowRoot->GetFirstChild(); 3992 origChild; origChild = origChild->GetNextSibling()) { 3993 nsCOMPtr<nsINode> child = 3994 CloneAndAdopt(origChild, aClone, true, nodeInfoManager, 3995 aReparentScope, newShadowRoot, aError); 3996 if (NS_WARN_IF(aError.Failed())) { 3997 return nullptr; 3998 } 3999 } 4000 } 4001 } 4002 4003 // Cloning template element. 4004 if (aDeep && aClone && aNode->IsTemplateElement()) { 4005 DocumentFragment* origContent = 4006 static_cast<HTMLTemplateElement*>(aNode)->Content(); 4007 DocumentFragment* cloneContent = 4008 static_cast<HTMLTemplateElement*>(clone.get())->Content(); 4009 4010 // Clone the children into the clone's template content owner 4011 // document's nodeinfo manager. 4012 nsNodeInfoManager* ownerNodeInfoManager = 4013 cloneContent->mNodeInfo->NodeInfoManager(); 4014 4015 for (nsIContent* cloneChild = origContent->GetFirstChild(); cloneChild; 4016 cloneChild = cloneChild->GetNextSibling()) { 4017 nsCOMPtr<nsINode> child = 4018 CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager, 4019 aReparentScope, cloneContent, aError); 4020 if (NS_WARN_IF(aError.Failed())) { 4021 return nullptr; 4022 } 4023 } 4024 } 4025 4026 return clone.forget(); 4027 } 4028 4029 void nsINode::Adopt(nsNodeInfoManager* aNewNodeInfoManager, 4030 JS::Handle<JSObject*> aReparentScope, 4031 mozilla::ErrorResult& aError) { 4032 if (aNewNodeInfoManager) { 4033 Document* beforeAdoptDoc = OwnerDoc(); 4034 Document* afterAdoptDoc = aNewNodeInfoManager->GetDocument(); 4035 4036 MOZ_ASSERT(beforeAdoptDoc); 4037 MOZ_ASSERT(afterAdoptDoc); 4038 MOZ_ASSERT(beforeAdoptDoc != afterAdoptDoc); 4039 4040 if (afterAdoptDoc->GetDocGroup() != beforeAdoptDoc->GetDocGroup()) { 4041 // This is a temporary solution for Bug 1590526 to only limit 4042 // the restriction to chrome level documents because web extensions 4043 // rely on content to content node adoption. 4044 if (nsContentUtils::IsChromeDoc(afterAdoptDoc) || 4045 nsContentUtils::IsChromeDoc(beforeAdoptDoc)) { 4046 return aError.ThrowSecurityError( 4047 "Adopting nodes across docgroups in chrome documents " 4048 "is unsupported"); 4049 } 4050 } 4051 } 4052 4053 // Just need to store the return value of CloneAndAdopt in a 4054 // temporary nsCOMPtr to make sure we release it. 4055 nsCOMPtr<nsINode> node = CloneAndAdopt(this, false, true, aNewNodeInfoManager, 4056 aReparentScope, nullptr, aError); 4057 4058 nsMutationGuard::DidMutate(); 4059 } 4060 4061 already_AddRefed<nsINode> nsINode::Clone(bool aDeep, 4062 nsNodeInfoManager* aNewNodeInfoManager, 4063 ErrorResult& aError) { 4064 return CloneAndAdopt(this, true, aDeep, aNewNodeInfoManager, nullptr, nullptr, 4065 aError); 4066 } 4067 4068 void nsINode::GenerateXPath(nsAString& aResult) { 4069 XPathGenerator::Generate(this, aResult); 4070 } 4071 4072 bool nsINode::IsApzAware() const { return IsNodeApzAware(); } 4073 4074 bool nsINode::IsNodeApzAwareInternal() const { 4075 return EventTarget::IsApzAware(); 4076 } 4077 4078 DocGroup* nsINode::GetDocGroup() const { return OwnerDoc()->GetDocGroup(); } 4079 4080 nsINode* nsINode::GetFlattenedTreeParentNodeNonInline() const { 4081 return GetFlattenedTreeParentNode(); 4082 } 4083 4084 ParentObject nsINode::GetParentObject() const { 4085 ParentObject p(OwnerDoc()); 4086 // Note that mReflectionScope is a no-op for chrome, and other places where we 4087 // don't check this value. 4088 if (IsInNativeAnonymousSubtree()) { 4089 if (ShouldUseUAWidgetScope(this)) { 4090 p.mReflectionScope = ReflectionScope::UAWidget; 4091 } else { 4092 MOZ_ASSERT(ShouldUseNACScope(this)); 4093 p.mReflectionScope = ReflectionScope::NAC; 4094 } 4095 } else { 4096 MOZ_ASSERT(!ShouldUseNACScope(this)); 4097 MOZ_ASSERT(!ShouldUseUAWidgetScope(this)); 4098 } 4099 return p; 4100 } 4101 4102 void nsINode::AddMutationObserver( 4103 nsMultiMutationObserver* aMultiMutationObserver) { 4104 if (aMultiMutationObserver) { 4105 NS_ASSERTION(!aMultiMutationObserver->ContainsNode(this), 4106 "Observer already in the list"); 4107 aMultiMutationObserver->AddMutationObserverToNode(this); 4108 } 4109 } 4110 4111 void nsINode::AddMutationObserverUnlessExists( 4112 nsMultiMutationObserver* aMultiMutationObserver) { 4113 if (aMultiMutationObserver && !aMultiMutationObserver->ContainsNode(this)) { 4114 aMultiMutationObserver->AddMutationObserverToNode(this); 4115 } 4116 } 4117 4118 void nsINode::RemoveMutationObserver( 4119 nsMultiMutationObserver* aMultiMutationObserver) { 4120 if (aMultiMutationObserver) { 4121 aMultiMutationObserver->RemoveMutationObserverFromNode(this); 4122 } 4123 } 4124 4125 bool nsINode::MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc() const { 4126 // XXX Should we check SuppressedNotifyingDevToolsOfNodeRemovals() here too? 4127 // Then, we could skip to handle some node removals while we're handling some 4128 // APIs. 4129 return OwnerDoc()->DevToolsWatchingDOMMutations(); 4130 } 4131 4132 bool nsINode::DevToolsShouldBeNotifiedOfThisRemoval() const { 4133 return MOZ_UNLIKELY(MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc()) && 4134 IsInComposedDoc() && 4135 !OwnerDoc()->SuppressedNotifyingDevToolsOfNodeRemovals() && 4136 !ChromeOnlyAccess(); 4137 } 4138 4139 void nsINode::NotifyDevToolsOfRemovalsOfChildren() { 4140 // Optimize the common case 4141 if (MOZ_LIKELY(!MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc())) { 4142 return; 4143 } 4144 4145 for (nsCOMPtr<nsIContent> child = GetFirstChild(); 4146 child && child->GetParentNode() == this; 4147 child = child->GetNextSibling()) { 4148 nsContentUtils::NotifyDevToolsOfNodeRemoval(*child); 4149 } 4150 } 4151 4152 ShadowRoot* nsINode::GetShadowRootForSelection() const { 4153 if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { 4154 return nullptr; 4155 } 4156 4157 ShadowRoot* shadowRoot = GetShadowRoot(); 4158 if (!shadowRoot) { 4159 return nullptr; 4160 } 4161 4162 // ie. <details> and <video> 4163 if (shadowRoot->IsUAWidget()) { 4164 return nullptr; 4165 } 4166 4167 // ie. <use> element 4168 if (IsElement() && !AsElement()->CanAttachShadowDOM()) { 4169 return nullptr; 4170 } 4171 4172 return shadowRoot; 4173 } 4174 4175 void nsINode::QueueAncestorRevealingAlgorithm() { 4176 NS_DispatchToMainThread(NS_NewRunnableFunction( 4177 "RevealAncestors", 4178 [self = RefPtr{this}]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { 4179 self->AncestorRevealingAlgorithm(IgnoreErrors()); 4180 })); 4181 } 4182 4183 enum class RevealType : uint8_t { 4184 UntilFound, 4185 Details, 4186 }; 4187 // https://html.spec.whatwg.org/#ancestor-revealing-algorithm 4188 void nsINode::AncestorRevealingAlgorithm(ErrorResult& aRv) { 4189 // 1. Let ancestorsToReveal be an empty list. 4190 AutoTArray<std::pair<RefPtr<nsINode>, RevealType>, 16> ancestorsToReveal; 4191 // 2. Let ancestor be target. 4192 // 3. While ancestor has a parent node within the flat tree: 4193 for (nsINode* ancestor : InclusiveFlatTreeAncestors(*this)) { 4194 // 3.1 If ancestor has a hidden attribute in the hidden until found state, 4195 // then append (ancestor, "until-found") to ancestorsToReveal. 4196 if (Element* currentAsElement = Element::FromNode(ancestor); 4197 currentAsElement && 4198 currentAsElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, 4199 nsGkAtoms::untilFound, eIgnoreCase)) { 4200 ancestorsToReveal.AppendElement( 4201 std::make_pair(ancestor, RevealType::UntilFound)); 4202 } 4203 4204 // 3.2 If ancestor is slotted into the second slot of a details element 4205 // which does not have an open attribute, then append (ancestor's 4206 // parent node, "details") to ancestorsToReveal. 4207 if (HTMLSlotElement* slot = HTMLSlotElement::FromNode(ancestor)) { 4208 // Note: There are two slots in the details element. Gecko names the 4209 // summary, and leaves the content slot unnamed. 4210 if (HTMLDetailsElement* details = HTMLDetailsElement::FromNodeOrNull( 4211 slot->GetContainingShadowHost()); 4212 details && !details->Open() && !slot->HasName()) { 4213 ancestorsToReveal.AppendElement( 4214 std::make_pair(details, RevealType::Details)); 4215 } 4216 } 4217 4218 // 3.3 Set ancestor to ancestor's parent node within the flat tree. 4219 } 4220 4221 // 4. For each (ancestor, type) in ancestorsToReveal: 4222 for (const auto& [ancestor, revealType] : ancestorsToReveal) { 4223 // 4.1 If ancestorToReveal is not connected, then return. 4224 if (!ancestor->IsInComposedDoc()) { 4225 return; 4226 } 4227 4228 // 4.2 If type is "until-found", then: 4229 if (revealType == RevealType::UntilFound) { 4230 // 4.2.1 If ancestorToReveal's hidden attribute is not in the Hidden Until 4231 // Found state, then return. 4232 RefPtr ancestorAsElement = Element::FromNode(ancestor); 4233 if (!ancestorAsElement || 4234 !ancestorAsElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, 4235 nsGkAtoms::untilFound, eIgnoreCase)) { 4236 return; 4237 } 4238 // 4.2.2 Fire an event named beforematch at ancestorToReveal with the 4239 // bubbles attribute initialized to true. 4240 ancestorAsElement->FireBeforematchEvent(aRv); 4241 if (MOZ_UNLIKELY(aRv.Failed())) { 4242 return; 4243 } 4244 // 4.2.3 If ancestorToReveal is not connected, then return. 4245 if (!ancestor->IsInComposedDoc()) { 4246 return; 4247 } 4248 // 4.2.4 Remove the hidden attribute from ancestorToReveal. 4249 ancestorAsElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, 4250 /*aNotify=*/true); 4251 } else { // 4.3 Otherwise 4252 // 4.3.1 Assert: revealType is "details". 4253 MOZ_ASSERT(revealType == RevealType::Details); 4254 // 4.3.2 If ancestorToReveal has an open attribute, then return. 4255 RefPtr details = HTMLDetailsElement::FromNode(ancestor); 4256 MOZ_ASSERT(details); 4257 if (details->Open()) { 4258 return; 4259 } 4260 // 4.3.3 Set the open attribute on ancestorToReveal to the empty string. 4261 details->SetOpen(true, aRv); 4262 if (MOZ_UNLIKELY(aRv.Failed())) { 4263 return; 4264 } 4265 } 4266 } 4267 } 4268 4269 NS_IMPL_ISUPPORTS(nsNodeWeakReference, nsIWeakReference) 4270 4271 nsNodeWeakReference::nsNodeWeakReference(nsINode* aNode) 4272 : nsIWeakReference(aNode) {} 4273 4274 nsNodeWeakReference::~nsNodeWeakReference() { 4275 nsINode* node = static_cast<nsINode*>(mObject); 4276 4277 if (node) { 4278 NS_ASSERTION(node->Slots()->mWeakReference == this, 4279 "Weak reference has wrong value"); 4280 node->Slots()->mWeakReference = nullptr; 4281 } 4282 } 4283 4284 NS_IMETHODIMP 4285 nsNodeWeakReference::QueryReferentFromScript(const nsIID& aIID, 4286 void** aInstancePtr) { 4287 return QueryReferent(aIID, aInstancePtr); 4288 } 4289 4290 size_t nsNodeWeakReference::SizeOfOnlyThis( 4291 mozilla::MallocSizeOf aMallocSizeOf) { 4292 return aMallocSizeOf(this); 4293 }