FragmentOrElement.cpp (68935B)
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 element classes and DocumentFragment. 9 */ 10 11 #include "mozilla/dom/FragmentOrElement.h" 12 13 #include "DOMIntersectionObserver.h" 14 #include "mozilla/AsyncEventDispatcher.h" 15 #include "mozilla/DeclarationBlock.h" 16 #include "mozilla/EffectSet.h" 17 #include "mozilla/ElementAnimationData.h" 18 #include "mozilla/EventDispatcher.h" 19 #include "mozilla/EventListenerManager.h" 20 #include "mozilla/HTMLEditor.h" 21 #include "mozilla/Likely.h" 22 #include "mozilla/MemoryReporting.h" 23 #include "mozilla/MouseEvents.h" 24 #include "mozilla/PresShell.h" 25 #include "mozilla/RestyleManager.h" 26 #include "mozilla/StaticPtr.h" 27 #include "mozilla/TextEditor.h" 28 #include "mozilla/TouchEvents.h" 29 #include "mozilla/URLExtraData.h" 30 #include "mozilla/dom/AncestorIterator.h" 31 #include "mozilla/dom/Attr.h" 32 #include "mozilla/dom/CharacterDataBuffer.h" 33 #include "mozilla/dom/CloseWatcher.h" 34 #include "mozilla/dom/CustomElementRegistry.h" 35 #include "mozilla/dom/Document.h" 36 #include "mozilla/dom/DocumentInlines.h" 37 #include "mozilla/dom/Event.h" 38 #include "mozilla/dom/NodeInfo.h" 39 #include "mozilla/dom/RadioGroupContainer.h" 40 #include "mozilla/dom/ScriptLoader.h" 41 #include "mozilla/dom/StylePropertyMap.h" 42 #include "mozilla/dom/StylePropertyMapReadOnly.h" 43 #include "mozilla/dom/UnbindContext.h" 44 #include "mozilla/mozInlineSpellChecker.h" 45 #include "nsAtom.h" 46 #include "nsContentList.h" 47 #include "nsDOMAttributeMap.h" 48 #include "nsDOMCSSAttrDeclaration.h" 49 #include "nsDOMTokenList.h" 50 #include "nsError.h" 51 #include "nsFocusManager.h" 52 #include "nsIAnonymousContentCreator.h" 53 #include "nsIControllers.h" 54 #include "nsIDocumentEncoder.h" 55 #include "nsIFrame.h" 56 #include "nsNameSpaceManager.h" 57 #include "nsNetUtil.h" 58 #include "nsPresContext.h" 59 #include "nsString.h" 60 #include "nsXULElement.h" 61 #ifdef DEBUG 62 # include "nsRange.h" 63 #endif 64 65 #include "ChildIterator.h" 66 #include "NodeUbiReporting.h" 67 #include "mozAutoDocUpdate.h" 68 #include "mozilla/BloomFilter.h" 69 #include "mozilla/Sprintf.h" 70 #include "mozilla/dom/HTMLSlotElement.h" 71 #include "mozilla/dom/HTMLTemplateElement.h" 72 #include "mozilla/dom/MutationObservers.h" 73 #include "mozilla/dom/NodeListBinding.h" 74 #include "mozilla/dom/SVGUseElement.h" 75 #include "mozilla/dom/ShadowRoot.h" 76 #include "mozilla/htmlaccel/htmlaccelEnabled.h" 77 #ifdef MOZ_MAY_HAVE_HTMLACCEL 78 # include "mozilla/htmlaccel/htmlaccelNotInline.h" 79 #endif 80 #include "nsCCUncollectableMarker.h" 81 #include "nsChildContentList.h" 82 #include "nsContentCreatorFunctions.h" 83 #include "nsContentUtils.h" 84 #include "nsCycleCollector.h" 85 #include "nsDOMMutationObserver.h" 86 #include "nsFrameLoader.h" 87 #include "nsGenericHTMLElement.h" 88 #include "nsGkAtoms.h" 89 #include "nsIWidget.h" 90 #include "nsLayoutUtils.h" 91 #include "nsNodeInfoManager.h" 92 #include "nsPIDOMWindow.h" 93 #include "nsWindowSizes.h" 94 #include "nsWrapperCacheInlines.h" 95 #include "xpcpublic.h" 96 97 #ifdef ACCESSIBILITY 98 # include "nsAccessibilityService.h" 99 #endif 100 101 using namespace mozilla; 102 using namespace mozilla::dom; 103 104 uint64_t nsMutationGuard::sGeneration = 0; 105 106 NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent) 107 108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent) 109 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us"); 110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 111 112 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent) 113 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us"); 114 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 115 116 NS_INTERFACE_MAP_BEGIN(nsIContent) 117 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 118 // Don't bother to QI to cycle collection, because our CC impl is 119 // not doing anything anyway. 120 NS_INTERFACE_MAP_ENTRY(nsIContent) 121 NS_INTERFACE_MAP_ENTRY(nsINode) 122 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) 123 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference, 124 new nsNodeSupportsWeakRefTearoff(this)) 125 // DOM bindings depend on the identity pointer being the 126 // same as nsINode (which nsIContent inherits). 127 NS_INTERFACE_MAP_ENTRY(nsISupports) 128 NS_INTERFACE_MAP_END 129 130 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsIContent) 131 132 NS_IMPL_DOMARENA_DESTROY(nsIContent) 133 134 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(nsIContent, 135 LastRelease(), 136 Destroy()) 137 138 nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const { 139 // This handles also nested native anonymous content. 140 // Oops, this function signature allows casting const to non-const. (Then 141 // again, so does GetFirstChild()->GetParent().) 142 for (nsIContent* content = const_cast<nsIContent*>(this); content; 143 content = content->GetClosestNativeAnonymousSubtreeRootParentOrHost()) { 144 if (!content->ChromeOnlyAccess()) { 145 return content; 146 } 147 } 148 return nullptr; 149 } 150 151 void nsIContent::UnbindFromTree(nsINode* aNewParent, 152 const BatchRemovalState* aBatchState) { 153 UnbindContext context(*this, aBatchState); 154 context.SetIsMove(aNewParent != nullptr); 155 UnbindFromTree(context); 156 } 157 158 // https://dom.spec.whatwg.org/#dom-slotable-assignedslot 159 HTMLSlotElement* nsIContent::GetAssignedSlotByMode() const { 160 /** 161 * Get slotable's assigned slot for the result of 162 * find a slot with open flag UNSET [1]. 163 * 164 * [1] https://dom.spec.whatwg.org/#assign-a-slot 165 */ 166 HTMLSlotElement* slot = GetAssignedSlot(); 167 if (!slot) { 168 return nullptr; 169 } 170 171 MOZ_ASSERT(GetParent()); 172 MOZ_ASSERT(GetParent()->GetShadowRoot()); 173 174 /** 175 * Additional check for open flag SET: 176 * If slotable’s parent’s shadow root's mode is not "open", 177 * then return null. 178 */ 179 if (GetParent()->GetShadowRoot()->IsClosed()) { 180 return nullptr; 181 } 182 183 return slot; 184 } 185 186 nsIContent::IMEState nsIContent::GetDesiredIMEState() { 187 if (!IsEditable() || !IsInComposedDoc()) { 188 // Check for the special case where we're dealing with elements which don't 189 // have the editable flag set, but are readwrite (such as text controls). 190 if (!IsElement() || 191 !AsElement()->State().HasState(ElementState::READWRITE)) { 192 return IMEState(IMEEnabled::Disabled); 193 } 194 } 195 // NOTE: The content for independent editors (e.g., input[type=text], 196 // textarea) must override this method, so, we don't need to worry about 197 // that here. 198 nsIContent* editableAncestor = GetEditingHost(); 199 200 // This is in another editable content, use the result of it. 201 if (editableAncestor && editableAncestor != this) { 202 return editableAncestor->GetDesiredIMEState(); 203 } 204 Document* doc = GetComposedDoc(); 205 if (!doc) { 206 return IMEState(IMEEnabled::Disabled); 207 } 208 nsPresContext* pc = doc->GetPresContext(); 209 if (!pc) { 210 return IMEState(IMEEnabled::Disabled); 211 } 212 HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(pc); 213 if (!htmlEditor) { 214 return IMEState(IMEEnabled::Disabled); 215 } 216 // FYI: HTMLEditor::GetPreferredIMEState() is infallible. 217 return htmlEditor->GetPreferredIMEState().unwrap(); 218 } 219 220 bool nsIContent::HasIndependentSelection() const { 221 nsIFrame* frame = GetPrimaryFrame(); 222 return frame && frame->IsInsideTextControl(); 223 } 224 225 dom::Element* nsIContent::GetEditingHost() const { 226 // If this isn't editable, return nullptr. 227 if (!IsEditable()) { 228 return nullptr; 229 } 230 231 Document* doc = GetComposedDoc(); 232 if (!doc) { 233 return nullptr; 234 } 235 236 // If this is in designMode, we should return <body> 237 if (IsInDesignMode() && !IsInShadowTree()) { 238 // FIXME: There may be no <body>. In such case and aLimitInBodyElement is 239 // "No", we should use root element instead. 240 return doc->GetBodyElement(); 241 } 242 243 dom::Element* editableParentElement = nullptr; 244 for (dom::Element* parent = GetParentElement(); 245 parent && parent->HasFlag(NODE_IS_EDITABLE); 246 parent = editableParentElement->GetParentElement()) { 247 editableParentElement = parent; 248 } 249 return editableParentElement 250 ? editableParentElement 251 : dom::Element::FromNode(const_cast<nsIContent*>(this)); 252 } 253 254 nsresult nsIContent::LookupNamespaceURIInternal( 255 const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) const { 256 if (aNamespacePrefix.EqualsLiteral("xml")) { 257 // Special-case for xml prefix 258 aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace"); 259 return NS_OK; 260 } 261 262 if (aNamespacePrefix.EqualsLiteral("xmlns")) { 263 // Special-case for xmlns prefix 264 aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/"); 265 return NS_OK; 266 } 267 268 RefPtr<nsAtom> name; 269 if (!aNamespacePrefix.IsEmpty()) { 270 name = NS_Atomize(aNamespacePrefix); 271 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY); 272 } else { 273 name = nsGkAtoms::xmlns; 274 } 275 // Trace up the content parent chain looking for the namespace 276 // declaration that declares aNamespacePrefix. 277 for (Element* element = GetAsElementOrParentElement(); element; 278 element = element->GetParentElement()) { 279 if (element->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) { 280 return NS_OK; 281 } 282 } 283 return NS_ERROR_FAILURE; 284 } 285 286 nsAtom* nsIContent::GetLang() const { 287 for (const Element* element = GetAsElementOrParentElement(); element; 288 element = element->GetParentElement()) { 289 if (!element->GetAttrCount()) { 290 continue; 291 } 292 293 // xml:lang has precedence over lang on HTML elements (see 294 // XHTML1 section C.7). 295 const nsAttrValue* attr = 296 element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML); 297 if (!attr && element->SupportsLangAttr()) { 298 attr = element->GetParsedAttr(nsGkAtoms::lang); 299 } 300 if (attr) { 301 MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom); 302 MOZ_ASSERT(attr->GetAtomValue()); 303 return attr->GetAtomValue(); 304 } 305 } 306 307 return nullptr; 308 } 309 310 nsIURI* nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const { 311 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) { 312 if (URLExtraData* data = use->GetContentURLData()) { 313 return data->BaseURI(); 314 } 315 } 316 317 return OwnerDoc()->GetBaseURI(aTryUseXHRDocBaseURI); 318 } 319 320 nsIURI* nsIContent::GetBaseURIForStyleAttr() const { 321 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) { 322 if (URLExtraData* data = use->GetContentURLData()) { 323 return data->BaseURI(); 324 } 325 } 326 // This also ignores the case that SVG inside XBL binding. 327 // But it is probably fine. 328 return OwnerDoc()->GetDocBaseURI(); 329 } 330 331 already_AddRefed<URLExtraData> nsIContent::GetURLDataForStyleAttr( 332 nsIPrincipal* aSubjectPrincipal) const { 333 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) { 334 if (URLExtraData* data = use->GetContentURLData()) { 335 return do_AddRef(data); 336 } 337 } 338 auto* doc = OwnerDoc(); 339 if (aSubjectPrincipal && aSubjectPrincipal != NodePrincipal()) { 340 nsCOMPtr<nsIReferrerInfo> referrerInfo = 341 doc->ReferrerInfoForInternalCSSAndSVGResources(); 342 // TODO: Cache this? 343 return MakeAndAddRef<URLExtraData>(doc->GetDocBaseURI(), referrerInfo, 344 aSubjectPrincipal); 345 } 346 return do_AddRef(doc->DefaultStyleAttrURLData()); 347 } 348 349 void nsIContent::ConstructUbiNode(void* storage) { 350 JS::ubi::Concrete<nsIContent>::construct(storage, this); 351 } 352 353 bool nsIContent::InclusiveDescendantMayNeedSpellchecking(HTMLEditor* aEditor) { 354 // Return true if the node may have elements as children, since those or their 355 // descendants may have spellcheck attributes. 356 return HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN) || 357 mozInlineSpellChecker::ShouldSpellCheckNode(aEditor, this); 358 } 359 360 //---------------------------------------------------------------------- 361 362 static inline JSObject* GetJSObjectChild(nsWrapperCache* aCache) { 363 return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() 364 : nullptr; 365 } 366 367 static bool NeedsScriptTraverse(nsINode* aNode) { 368 return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() && 369 !aNode->HasKnownLiveWrapperAndDoesNotNeedTracing(aNode); 370 } 371 372 //---------------------------------------------------------------------- 373 374 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList) 375 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList) 376 377 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAttrChildContentList, mNode) 378 379 // If the wrapper is known-live, the list can't be part of a garbage cycle. 380 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList) 381 return tmp->HasKnownLiveWrapper(); 382 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 383 384 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList) 385 return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp); 386 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 387 388 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList) 389 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 390 391 NS_INTERFACE_TABLE_HEAD(nsAttrChildContentList) 392 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY 393 NS_INTERFACE_TABLE(nsAttrChildContentList, nsINodeList) 394 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAttrChildContentList) 395 NS_INTERFACE_MAP_END 396 397 JSObject* nsAttrChildContentList::WrapObject( 398 JSContext* cx, JS::Handle<JSObject*> aGivenProto) { 399 return NodeList_Binding::Wrap(cx, this, aGivenProto); 400 } 401 402 uint32_t nsAttrChildContentList::Length() { 403 return mNode ? mNode->GetChildCount() : 0; 404 } 405 406 nsIContent* nsAttrChildContentList::Item(uint32_t aIndex) { 407 if (mNode) { 408 return mNode->GetChildAt_Deprecated(aIndex); 409 } 410 411 return nullptr; 412 } 413 414 int32_t nsAttrChildContentList::IndexOf(nsIContent* aContent) { 415 if (mNode) { 416 return mNode->ComputeIndexOf_Deprecated(aContent); 417 } 418 419 return -1; 420 } 421 422 //---------------------------------------------------------------------- 423 uint32_t nsParentNodeChildContentList::Length() { 424 return mNode ? mNode->GetChildCount() : 0; 425 } 426 427 nsIContent* nsParentNodeChildContentList::Item(uint32_t aIndex) { 428 if (!mIsCacheValid) { 429 if (MOZ_UNLIKELY(!mNode)) { 430 return nullptr; 431 } 432 // Try to avoid the cache for some common cases, see bug 1917511. 433 if (aIndex == 0) { 434 return mNode->GetFirstChild(); 435 } 436 uint32_t childCount = mNode->GetChildCount(); 437 if (aIndex >= childCount) { 438 return nullptr; 439 } 440 if (aIndex + 1 == childCount) { 441 return mNode->GetLastChild(); 442 } 443 ValidateCache(); 444 MOZ_ASSERT(mIsCacheValid); 445 } 446 return mCachedChildArray.SafeElementAt(aIndex, nullptr); 447 } 448 449 int32_t nsParentNodeChildContentList::IndexOf(nsIContent* aContent) { 450 EnsureCacheValid(); 451 return mCachedChildArray.IndexOf(aContent); 452 } 453 454 void nsParentNodeChildContentList::ValidateCache() { 455 MOZ_ASSERT(!mIsCacheValid); 456 MOZ_ASSERT(mCachedChildArray.IsEmpty()); 457 458 if (MOZ_UNLIKELY(!mNode)) { 459 return; 460 } 461 462 for (nsIContent* node = mNode->GetFirstChild(); node; 463 node = node->GetNextSibling()) { 464 mCachedChildArray.AppendElement(node); 465 } 466 mIsCacheValid = true; 467 } 468 469 //---------------------------------------------------------------------- 470 471 nsIHTMLCollection* FragmentOrElement::Children() { 472 nsDOMSlots* slots = DOMSlots(); 473 474 if (!slots->mChildrenList) { 475 slots->mChildrenList = 476 new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk, 477 nsGkAtoms::_asterisk, false); 478 } 479 480 return slots->mChildrenList; 481 } 482 483 //---------------------------------------------------------------------- 484 485 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode) 486 487 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff) 488 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 489 NS_INTERFACE_MAP_END_AGGREGATED(mNode) 490 491 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff) 492 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff) 493 494 NS_IMETHODIMP 495 nsNodeSupportsWeakRefTearoff::GetWeakReference( 496 nsIWeakReference** aInstancePtr) { 497 nsINode::nsSlots* slots = mNode->Slots(); 498 if (!slots->mWeakReference) { 499 slots->mWeakReference = new nsNodeWeakReference(mNode); 500 } 501 502 NS_ADDREF(*aInstancePtr = slots->mWeakReference); 503 504 return NS_OK; 505 } 506 507 //---------------------------------------------------------------------- 508 509 static const size_t MaxDOMSlotSizeAllowed = 510 #ifdef HAVE_64BIT_BUILD 511 128; 512 #else 513 64; 514 #endif 515 516 static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed, 517 "DOM slots cannot be grown without consideration"); 518 static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed, 519 "DOM slots cannot be grown without consideration"); 520 521 void nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(nsIContent&) { 522 mContainingShadow = nullptr; 523 mAssignedSlot = nullptr; 524 } 525 526 void nsIContent::nsExtendedContentSlots::TraverseExtendedSlots( 527 nsCycleCollectionTraversalCallback& aCb) { 528 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mContainingShadow"); 529 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow)); 530 531 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mAssignedSlot"); 532 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get())); 533 } 534 535 nsIContent::nsExtendedContentSlots::nsExtendedContentSlots() = default; 536 537 nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() { 538 MOZ_ASSERT(!mManualSlotAssignment); 539 } 540 541 size_t nsIContent::nsExtendedContentSlots::SizeOfExcludingThis( 542 MallocSizeOf aMallocSizeOf) const { 543 // For now, nothing to measure here. We don't actually own any of our 544 // members. 545 return 0; 546 } 547 548 FragmentOrElement::nsDOMSlots::nsDOMSlots() { MOZ_COUNT_CTOR(nsDOMSlots); } 549 550 FragmentOrElement::nsDOMSlots::~nsDOMSlots() { 551 MOZ_COUNT_DTOR(nsDOMSlots); 552 553 if (mAttributeMap) { 554 mAttributeMap->DropReference(); 555 } 556 } 557 558 void FragmentOrElement::nsDOMSlots::Traverse( 559 nsCycleCollectionTraversalCallback& aCb) { 560 nsIContent::nsContentSlots::Traverse(aCb); 561 562 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mStyle"); 563 aCb.NoteXPCOMChild(mStyle.get()); 564 565 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeMap"); 566 aCb.NoteXPCOMChild(mAttributeMap.get()); 567 568 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mChildrenList"); 569 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mChildrenList)); 570 571 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mClassList"); 572 aCb.NoteXPCOMChild(mClassList.get()); 573 574 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mComputedStyleMap"); 575 aCb.NoteXPCOMChild(mComputedStyleMap.get()); 576 577 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeStyleMap"); 578 aCb.NoteXPCOMChild(mAttributeStyleMap.get()); 579 } 580 581 void FragmentOrElement::nsDOMSlots::Unlink(nsINode& aNode) { 582 nsIContent::nsContentSlots::Unlink(aNode); 583 mStyle = nullptr; 584 if (mAttributeMap) { 585 mAttributeMap->DropReference(); 586 mAttributeMap = nullptr; 587 } 588 mChildrenList = nullptr; 589 mClassList = nullptr; 590 mComputedStyleMap = nullptr; 591 mAttributeStyleMap = nullptr; 592 } 593 594 size_t FragmentOrElement::nsDOMSlots::SizeOfIncludingThis( 595 MallocSizeOf aMallocSizeOf) const { 596 size_t n = aMallocSizeOf(this); 597 598 nsExtendedContentSlots* extendedSlots = GetExtendedContentSlots(); 599 if (extendedSlots) { 600 if (OwnsExtendedSlots()) { 601 n += aMallocSizeOf(extendedSlots); 602 } 603 604 n += extendedSlots->SizeOfExcludingThis(aMallocSizeOf); 605 } 606 607 if (mAttributeMap) { 608 n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf); 609 } 610 611 if (mChildrenList) { 612 n += mChildrenList->SizeOfIncludingThis(aMallocSizeOf); 613 } 614 615 if (mComputedStyleMap) { 616 n += mComputedStyleMap->SizeOfIncludingThis(aMallocSizeOf); 617 } 618 619 if (mAttributeStyleMap) { 620 n += mAttributeStyleMap->SizeOfIncludingThis(aMallocSizeOf); 621 } 622 623 // Measurement of the following members may be added later if DMD finds it is 624 // worthwhile: 625 // - Superclass members (nsINode::nsSlots) 626 // - mStyle 627 // - mClassList 628 629 // The following member are not measured: 630 // - mControllers: because it is non-owning 631 return n; 632 } 633 634 FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default; 635 636 FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots() = default; 637 638 void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots( 639 nsIContent& aContent) { 640 nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(aContent); 641 642 // mShadowRoot will similarly be cleared explicitly from 643 // FragmentOrElement::Unlink. 644 mSMILOverrideStyle = nullptr; 645 mControllers = nullptr; 646 mLabelsList = nullptr; 647 mPopoverData = nullptr; 648 if (mCustomElementData) { 649 mCustomElementData->Unlink(); 650 mCustomElementData = nullptr; 651 } 652 if (mAnimations) { 653 mAnimations = nullptr; 654 aContent.ClearMayHaveAnimations(); 655 } 656 mExplicitlySetAttrElementMap.Clear(); 657 mAttrElementsMap.Clear(); 658 mRadioGroupContainer = nullptr; 659 mPart = nullptr; 660 } 661 662 void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots( 663 nsCycleCollectionTraversalCallback& aCb) { 664 nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(aCb); 665 666 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mSMILOverrideStyle"); 667 aCb.NoteXPCOMChild(mSMILOverrideStyle.get()); 668 669 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mControllers"); 670 aCb.NoteXPCOMChild(mControllers); 671 672 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mLabelsList"); 673 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mLabelsList)); 674 675 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mShadowRoot"); 676 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot)); 677 678 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mPart"); 679 aCb.NoteXPCOMChild(mPart.get()); 680 681 for (auto& tableEntry : mAttrElementsMap) { 682 auto& [explicitlySetElements, cachedAttrElements] = 683 *tableEntry.GetModifiableData(); 684 if (cachedAttrElements) { 685 ImplCycleCollectionTraverse(aCb, *cachedAttrElements, 686 "cached attribute elements entry", 0); 687 } 688 } 689 690 if (mCustomElementData) { 691 mCustomElementData->Traverse(aCb); 692 } 693 if (mAnimations) { 694 mAnimations->Traverse(aCb); 695 } 696 if (mRadioGroupContainer) { 697 RadioGroupContainer::Traverse(mRadioGroupContainer.get(), aCb); 698 } 699 } 700 701 size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis( 702 MallocSizeOf aMallocSizeOf) const { 703 size_t n = 704 nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(aMallocSizeOf); 705 706 // We own mSMILOverrideStyle but there seems to be no memory reporting on CSS 707 // declarations? At least report the memory the declaration takes up 708 // directly. 709 if (mSMILOverrideStyle) { 710 n += aMallocSizeOf(mSMILOverrideStyle); 711 } 712 713 // We don't really own mSMILOverrideStyleDeclaration. mSMILOverrideStyle owns 714 // it. 715 716 // We don't seem to have memory reporting for nsXULControllers. At least 717 // report the memory it's using directly. 718 if (mControllers) { 719 n += aMallocSizeOf(mControllers); 720 } 721 722 if (mLabelsList) { 723 n += mLabelsList->SizeOfIncludingThis(aMallocSizeOf); 724 } 725 726 // mShadowRoot should be handled during normal DOM tree memory reporting, just 727 // like kids, siblings, etc. 728 729 if (mCustomElementData) { 730 n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf); 731 } 732 733 if (mRadioGroupContainer) { 734 n += mRadioGroupContainer->SizeOfIncludingThis(aMallocSizeOf); 735 } 736 737 return n; 738 } 739 740 FragmentOrElement::FragmentOrElement( 741 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 742 : nsIContent(std::move(aNodeInfo)) {} 743 744 FragmentOrElement::~FragmentOrElement() { 745 MOZ_ASSERT(!IsInUncomposedDoc(), 746 "Please remove this from the document properly"); 747 if (GetParent()) { 748 NS_RELEASE(mParent); 749 } 750 } 751 752 static nsINode* FindChromeAccessOnlySubtreeOwnerForEvents(nsINode* aNode) { 753 if (!aNode->ChromeOnlyAccessForEvents()) { 754 return aNode; 755 } 756 return aNode->GetClosestNativeAnonymousSubtreeRootParentOrHost(); 757 } 758 759 nsINode* FindChromeAccessOnlySubtreeOwnerForEvents(EventTarget* aTarget) { 760 nsINode* node = nsINode::FromEventTargetOrNull(aTarget); 761 if (!node) { 762 return nullptr; 763 } 764 return FindChromeAccessOnlySubtreeOwnerForEvents(node); 765 } 766 767 void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) { 768 // FIXME! Document how this event retargeting works, Bug 329124. 769 aVisitor.mCanHandle = true; 770 aVisitor.mMayHaveListenerManager = HasListenerManager(); 771 772 if (IsInShadowTree()) { 773 aVisitor.mItemInShadowTree = true; 774 } 775 776 // Don't propagate mouseover and mouseout events when mouse is moving 777 // inside chrome access only content. 778 const bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree(); 779 aVisitor.mRootOfClosedTree = isAnonForEvents; 780 if ((aVisitor.mEvent->mMessage == eMouseOver || 781 aVisitor.mEvent->mMessage == eMouseOut || 782 aVisitor.mEvent->mMessage == ePointerOver || 783 aVisitor.mEvent->mMessage == ePointerOut) && 784 // Check if we should stop event propagation when event has just been 785 // dispatched or when we're about to propagate from 786 // chrome access only subtree or if we are about to propagate out of 787 // a shadow root to a shadow root host. 788 ((this == aVisitor.mEvent->mOriginalTarget && !ChromeOnlyAccess()) || 789 isAnonForEvents)) { 790 nsIContent* relatedTarget = nsIContent::FromEventTargetOrNull( 791 aVisitor.mEvent->AsMouseEvent()->mRelatedTarget); 792 if (relatedTarget && relatedTarget->OwnerDoc() == OwnerDoc()) { 793 // If current target is anonymous for events or we know that related 794 // target is descendant of an element which is anonymous for events, 795 // we may want to stop event propagation. 796 // If this is the original target, aVisitor.mRelatedTargetIsInAnon 797 // must be updated. 798 if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon || 799 (aVisitor.mEvent->mOriginalTarget == this && 800 (aVisitor.mRelatedTargetIsInAnon = 801 relatedTarget->ChromeOnlyAccessForEvents()))) { 802 nsINode* anonOwner = FindChromeAccessOnlySubtreeOwnerForEvents(this); 803 if (anonOwner) { 804 nsINode* anonOwnerRelated = 805 FindChromeAccessOnlySubtreeOwnerForEvents(relatedTarget); 806 if (anonOwnerRelated) { 807 // Note, anonOwnerRelated may still be inside some other 808 // native anonymous subtree. The case where anonOwner is still 809 // inside native anonymous subtree will be handled when event 810 // propagates up in the DOM tree. 811 while (anonOwner != anonOwnerRelated && 812 anonOwnerRelated->ChromeOnlyAccessForEvents()) { 813 anonOwnerRelated = 814 FindChromeAccessOnlySubtreeOwnerForEvents(anonOwnerRelated); 815 } 816 if (anonOwner == anonOwnerRelated) { 817 #ifdef DEBUG_smaug 818 nsIContent* originalTarget = nsIContent::FromEventTargetOrNull( 819 aVisitor.mEvent->mOriginalTarget); 820 nsAutoString ot, ct, rt; 821 if (originalTarget) { 822 originalTarget->NodeInfo()->NameAtom()->ToString(ot); 823 } 824 NodeInfo()->NameAtom()->ToString(ct); 825 relatedTarget->NodeInfo()->NameAtom()->ToString(rt); 826 printf( 827 "Stopping %s propagation:" 828 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s" 829 "\n\trelatedTarget=%s %s \n%s", 830 (aVisitor.mEvent->mMessage == eMouseOver) ? "mouseover" 831 : "mouseout", 832 NS_ConvertUTF16toUTF8(ot).get(), 833 NS_ConvertUTF16toUTF8(ct).get(), 834 isAnonForEvents 835 ? "(is native anonymous)" 836 : (ChromeOnlyAccess() ? "(is in native anonymous subtree)" 837 : ""), 838 NS_ConvertUTF16toUTF8(rt).get(), 839 relatedTarget->ChromeOnlyAccess() 840 ? "(is in native anonymous subtree)" 841 : "", 842 (originalTarget && 843 relatedTarget->FindFirstNonChromeOnlyAccessContent() == 844 originalTarget->FindFirstNonChromeOnlyAccessContent()) 845 ? "" 846 : "Wrong event propagation!?!\n"); 847 #endif 848 aVisitor.SetParentTarget(nullptr, false); 849 // Event should not propagate to non-anon content. 850 aVisitor.mCanHandle = isAnonForEvents; 851 return; 852 } 853 } 854 } 855 } 856 } 857 } 858 859 // Event parent is the assigned slot, if node is assigned, or node's parent 860 // otherwise. 861 HTMLSlotElement* slot = GetAssignedSlot(); 862 nsIContent* parent = slot ? slot : GetParent(); 863 864 // Event may need to be retargeted if this is the root of a native anonymous 865 // content subtree. 866 if (isAnonForEvents) { 867 aVisitor.mEventTargetAtParent = parent; 868 } else if (parent && aVisitor.mOriginalTargetIsInAnon) { 869 nsIContent* content = 870 nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget); 871 if (content && 872 content->GetClosestNativeAnonymousSubtreeRootParentOrHost() == parent) { 873 aVisitor.mEventTargetAtParent = parent; 874 } 875 } 876 877 if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent && 878 isAnonForEvents && OwnerDoc()->GetWindow()) { 879 aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true); 880 } else if (parent) { 881 aVisitor.SetParentTarget(parent, false); 882 if (slot) { 883 ShadowRoot* root = slot->GetContainingShadow(); 884 if (root && root->IsClosed()) { 885 aVisitor.mParentIsSlotInClosedTree = true; 886 } 887 } 888 } else { 889 aVisitor.SetParentTarget(GetComposedDoc(), false); 890 } 891 892 if (!ChromeOnlyAccessForEvents() && 893 !aVisitor.mRelatedTargetRetargetedInCurrentScope) { 894 // We don't support Shadow DOM in native anonymous content yet. 895 aVisitor.mRelatedTargetRetargetedInCurrentScope = true; 896 if (aVisitor.mEvent->mOriginalRelatedTarget) { 897 // https://dom.spec.whatwg.org/#concept-event-dispatch 898 // Step 3. 899 // "Let relatedTarget be the result of retargeting event's relatedTarget 900 // against target if event's relatedTarget is non-null, and null 901 // otherwise." 902 // 903 // This is a bit complicated because the event might be from native 904 // anonymous content, but we need to deal with non-native anonymous 905 // content there. 906 bool initialTarget = this == aVisitor.mEvent->mOriginalTarget; 907 nsINode* originalTargetAsNode = nullptr; 908 // Use of mOriginalTargetIsInAnon is an optimization here. 909 if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) { 910 originalTargetAsNode = FindChromeAccessOnlySubtreeOwnerForEvents( 911 aVisitor.mEvent->mOriginalTarget); 912 initialTarget = originalTargetAsNode == this; 913 } 914 if (initialTarget) { 915 nsINode* relatedTargetAsNode = 916 FindChromeAccessOnlySubtreeOwnerForEvents( 917 aVisitor.mEvent->mOriginalRelatedTarget); 918 if (!originalTargetAsNode) { 919 originalTargetAsNode = 920 nsINode::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget); 921 } 922 923 if (relatedTargetAsNode && originalTargetAsNode) { 924 nsINode* retargetedRelatedTarget = nsContentUtils::Retarget( 925 relatedTargetAsNode, originalTargetAsNode); 926 if (originalTargetAsNode == retargetedRelatedTarget && 927 retargetedRelatedTarget != relatedTargetAsNode) { 928 // Step 4. 929 // "If target is relatedTarget and target is not event's 930 // relatedTarget, then return true." 931 aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting(); 932 // Old code relies on mTarget to point to the first element which 933 // was not added to the event target chain because of mCanHandle 934 // being false, but in Shadow DOM case mTarget really should 935 // point to a node in Shadow DOM. 936 aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope; 937 return; 938 } 939 940 // Part of step 5. Retargeting target has happened already higher 941 // up in this method. 942 // "Append to an event path with event, target, targetOverride, 943 // relatedTarget, and false." 944 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget; 945 } 946 } else if (nsINode* relatedTargetAsNode = 947 FindChromeAccessOnlySubtreeOwnerForEvents( 948 aVisitor.mEvent->mOriginalRelatedTarget)) { 949 // Step 11.3. 950 // "Let relatedTarget be the result of retargeting event's 951 // relatedTarget against parent if event's relatedTarget is non-null, 952 // and null otherwise.". 953 nsINode* retargetedRelatedTarget = 954 nsContentUtils::Retarget(relatedTargetAsNode, this); 955 nsINode* targetInKnownToBeHandledScope = 956 FindChromeAccessOnlySubtreeOwnerForEvents( 957 aVisitor.mTargetInKnownToBeHandledScope); 958 // If aVisitor.mTargetInKnownToBeHandledScope wasn't nsINode, 959 // targetInKnownToBeHandledScope will be null. This may happen when 960 // dispatching event to Window object in a content page and 961 // propagating the event to a chrome Element. 962 if (targetInKnownToBeHandledScope && 963 IsShadowIncludingInclusiveDescendantOf( 964 targetInKnownToBeHandledScope->SubtreeRoot())) { 965 // Part of step 11.4. 966 // "If target's root is a shadow-including inclusive ancestor of 967 // parent, then" 968 // "...Append to an event path with event, parent, null, 969 // relatedTarget, " and slot-in-closed-tree." 970 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget; 971 } else if (this == retargetedRelatedTarget) { 972 // Step 11.5 973 // "Otherwise, if parent and relatedTarget are identical, then set 974 // parent to null." 975 aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting(); 976 // Old code relies on mTarget to point to the first element which 977 // was not added to the event target chain because of mCanHandle 978 // being false, but in Shadow DOM case mTarget really should 979 // point to a node in Shadow DOM. 980 aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope; 981 return; 982 } else if (targetInKnownToBeHandledScope) { 983 // Note, if targetInKnownToBeHandledScope is null, 984 // mTargetInKnownToBeHandledScope could be Window object in content 985 // page and we're in chrome document in the same process. 986 987 // Step 11.6 988 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget; 989 } 990 } 991 } 992 993 if (aVisitor.mEvent->mClass == eTouchEventClass) { 994 // Retarget touch objects. 995 MOZ_ASSERT(!aVisitor.mRetargetedTouchTargets.isSome()); 996 aVisitor.mRetargetedTouchTargets.emplace(); 997 WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent(); 998 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; 999 for (uint32_t i = 0; i < touches.Length(); ++i) { 1000 Touch* touch = touches[i]; 1001 EventTarget* originalTarget = touch->mOriginalTarget; 1002 EventTarget* touchTarget = originalTarget; 1003 nsCOMPtr<nsINode> targetAsNode = 1004 nsINode::FromEventTargetOrNull(originalTarget); 1005 if (targetAsNode) { 1006 EventTarget* retargeted = 1007 nsContentUtils::Retarget(targetAsNode, this); 1008 if (retargeted) { 1009 touchTarget = retargeted; 1010 } 1011 } 1012 aVisitor.mRetargetedTouchTargets->AppendElement(touchTarget); 1013 touch->mTarget = touchTarget; 1014 } 1015 MOZ_ASSERT(aVisitor.mRetargetedTouchTargets->Length() == 1016 touches.Length()); 1017 } 1018 } 1019 1020 if (slot) { 1021 // Inform that we're about to exit the current scope. 1022 aVisitor.mRelatedTargetRetargetedInCurrentScope = false; 1023 } 1024 } 1025 1026 Element* nsIContent::GetAutofocusDelegate(IsFocusableFlags aFlags) const { 1027 for (nsINode* node = GetFirstChild(); node; node = node->GetNextNode(this)) { 1028 auto* descendant = Element::FromNode(*node); 1029 if (!descendant || !descendant->GetBoolAttr(nsGkAtoms::autofocus)) { 1030 continue; 1031 } 1032 1033 nsIFrame* frame = descendant->GetPrimaryFrame(); 1034 if (frame && frame->IsFocusable(aFlags)) { 1035 return descendant; 1036 } 1037 } 1038 return nullptr; 1039 } 1040 1041 bool nsIContent::CanStartSelectionAsWebCompatHack() const { 1042 if (!StaticPrefs::dom_selection_mimic_chrome_tostring_enabled()) { 1043 return true; 1044 } 1045 1046 for (const nsIContent* content = this; content; 1047 content = content->GetFlattenedTreeParent()) { 1048 if (content->IsEditable()) { 1049 return true; 1050 } 1051 nsIFrame* frame = content->GetPrimaryFrame(); 1052 if (!frame) { 1053 return true; 1054 } 1055 if (!frame->IsSelectable()) { 1056 return false; 1057 } 1058 } 1059 1060 return true; 1061 } 1062 1063 Element* nsIContent::GetFocusDelegate(IsFocusableFlags aFlags) const { 1064 const nsIContent* whereToLook = this; 1065 if (ShadowRoot* root = GetShadowRoot()) { 1066 if (!root->DelegatesFocus()) { 1067 // 1. If focusTarget is a shadow host and its shadow root 's delegates 1068 // focus is false, then return null. 1069 return nullptr; 1070 } 1071 whereToLook = root; 1072 } 1073 1074 auto IsFocusable = [&](Element* aElement) -> Focusable { 1075 nsIFrame* frame = aElement->GetPrimaryFrame(); 1076 1077 if (!frame) { 1078 return {}; 1079 } 1080 1081 return frame->IsFocusable(aFlags); 1082 }; 1083 1084 Element* potentialFocus = nullptr; 1085 for (nsINode* node = whereToLook->GetFirstChild(); node; 1086 node = node->GetNextNode(whereToLook)) { 1087 auto* el = Element::FromNode(*node); 1088 if (!el) { 1089 continue; 1090 } 1091 1092 const bool autofocus = el->GetBoolAttr(nsGkAtoms::autofocus); 1093 1094 if (autofocus) { 1095 if (IsFocusable(el)) { 1096 // Found an autofocus candidate. 1097 return el; 1098 } 1099 } else if (!potentialFocus) { 1100 if (Focusable focusable = IsFocusable(el)) { 1101 if (IsHTMLElement(nsGkAtoms::dialog)) { 1102 if (focusable.mTabIndex >= 0) { 1103 // If focusTarget is a dialog element and descendant is sequentially 1104 // focusable, then set focusableArea to descendant. 1105 potentialFocus = el; 1106 } 1107 } else { 1108 // This element could be the one if we can't find an 1109 // autofocus candidate which has the precedence. 1110 potentialFocus = el; 1111 } 1112 } 1113 } 1114 1115 if (!autofocus && potentialFocus) { 1116 // Nothing else to do, we are not looking for more focusable elements 1117 // here. 1118 continue; 1119 } 1120 1121 if (auto* shadow = el->GetShadowRoot()) { 1122 if (shadow->DelegatesFocus()) { 1123 if (Element* delegatedFocus = shadow->GetFocusDelegate(aFlags)) { 1124 if (autofocus) { 1125 // This element has autofocus and we found an focus delegates 1126 // in its descendants, so use the focus delegates 1127 return delegatedFocus; 1128 } 1129 if (!potentialFocus) { 1130 potentialFocus = delegatedFocus; 1131 } 1132 } 1133 } 1134 } 1135 } 1136 1137 return potentialFocus; 1138 } 1139 1140 Focusable nsIContent::IsFocusableWithoutStyle(IsFocusableFlags) { 1141 // Default, not tabbable 1142 return {}; 1143 } 1144 1145 void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) { 1146 MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots()); 1147 ExtendedContentSlots()->mAssignedSlot = aSlot; 1148 } 1149 1150 #ifdef MOZ_DOM_LIST 1151 void nsIContent::Dump() { List(); } 1152 #endif 1153 1154 void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent, 1155 OOMReporter& aError) { 1156 if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) { 1157 aError.ReportOOM(); 1158 } 1159 } 1160 1161 void FragmentOrElement::SetTextContentInternal( 1162 const nsAString& aTextContent, nsIPrincipal* aSubjectPrincipal, 1163 ErrorResult& aError, MutationEffectOnScript aMutationEffectOnScript) { 1164 bool tryReuse = false; 1165 if (!aTextContent.IsEmpty()) { 1166 if (nsIContent* firstChild = GetFirstChild()) { 1167 tryReuse = firstChild->NodeType() == TEXT_NODE && 1168 !firstChild->GetNextSibling() && 1169 firstChild->OwnedOnlyByTheDOMAndFrameTrees() && 1170 #ifdef ACCESSIBILITY 1171 !GetAccService() && 1172 #endif 1173 !OwnerDoc()->MayHaveDOMMutationObservers() && 1174 !MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc(); 1175 } 1176 } 1177 1178 aError = nsContentUtils::SetNodeTextContent(this, aTextContent, tryReuse, 1179 aMutationEffectOnScript); 1180 } 1181 1182 void FragmentOrElement::DestroyContent() { 1183 // Drop any servo data. We do this before the RemovedFromDocument call below 1184 // so that it doesn't need to try to keep the style state sane when shuffling 1185 // around the flattened tree. 1186 // 1187 // TODO(emilio): I suspect this can be asserted against instead, with a bit of 1188 // effort to avoid calling Document::Destroy with a shell... 1189 if (IsElement()) { 1190 AsElement()->ClearServoData(); 1191 } 1192 1193 #ifdef DEBUG 1194 uint32_t oldChildCount = GetChildCount(); 1195 #endif 1196 1197 for (nsIContent* child = GetFirstChild(); child; 1198 child = child->GetNextSibling()) { 1199 child->DestroyContent(); 1200 MOZ_ASSERT(child->GetParent() == this, 1201 "Mutating the tree during XBL destructors is evil"); 1202 } 1203 1204 MOZ_ASSERT(oldChildCount == GetChildCount(), 1205 "Mutating the tree during XBL destructors is evil"); 1206 1207 if (ShadowRoot* shadowRoot = GetShadowRoot()) { 1208 shadowRoot->DestroyContent(); 1209 } 1210 } 1211 1212 void FragmentOrElement::SaveSubtreeState() { 1213 for (nsIContent* child = GetFirstChild(); child; 1214 child = child->GetNextSibling()) { 1215 child->SaveSubtreeState(); 1216 } 1217 1218 // FIXME(bug 1469277): Pretty sure this wants to dig into shadow trees as 1219 // well. 1220 } 1221 1222 //---------------------------------------------------------------------- 1223 1224 // nsISupports implementation 1225 1226 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500 1227 1228 class ContentUnbinder : public Runnable { 1229 public: 1230 ContentUnbinder() : Runnable("ContentUnbinder") { mLast = this; } 1231 1232 ~ContentUnbinder() { Run(); } 1233 1234 void UnbindSubtree(nsIContent* aNode) { 1235 if (!aNode->HasChildren()) { 1236 return; 1237 } 1238 if (aNode->NodeType() != nsINode::ELEMENT_NODE && 1239 aNode->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE) { 1240 return; 1241 } 1242 auto* container = static_cast<FragmentOrElement*>(aNode); 1243 // Invalidate cached array of child nodes 1244 container->InvalidateChildNodes(); 1245 BatchRemovalState state{}; 1246 while (nsCOMPtr<nsIContent> child = container->GetLastChild()) { 1247 // Hold a strong ref to the node when we remove it, because we may be 1248 // the last reference to it. We need to call DisconnectChild() 1249 // before calling UnbindFromTree, since this last can notify various 1250 // observers and they should really see consistent tree state. 1251 // If this code changes, change the corresponding code in 1252 // FragmentOrElement's and Document's unlink impls. 1253 container->DisconnectChild(child); 1254 UnbindSubtree(child); 1255 child->UnbindFromTree(/* aNewParent = */ nullptr, &state); 1256 state.mIsFirst = false; 1257 } 1258 } 1259 1260 NS_IMETHOD Run() override { 1261 nsAutoScriptBlocker scriptBlocker; 1262 uint32_t len = mSubtreeRoots.Length(); 1263 if (len) { 1264 for (uint32_t i = 0; i < len; ++i) { 1265 UnbindSubtree(mSubtreeRoots[i]); 1266 } 1267 mSubtreeRoots.Clear(); 1268 } 1269 nsCycleCollector_dispatchDeferredDeletion(); 1270 if (this == sContentUnbinder) { 1271 sContentUnbinder = nullptr; 1272 if (mNext) { 1273 RefPtr<ContentUnbinder> next; 1274 next.swap(mNext); 1275 sContentUnbinder = next; 1276 next->mLast = mLast; 1277 mLast = nullptr; 1278 NS_DispatchToCurrentThreadQueue(next.forget(), 1279 EventQueuePriority::Idle); 1280 } 1281 } 1282 return NS_OK; 1283 } 1284 1285 static void UnbindAll() { 1286 RefPtr<ContentUnbinder> ub = sContentUnbinder; 1287 sContentUnbinder = nullptr; 1288 while (ub) { 1289 ub->Run(); 1290 ub = ub->mNext; 1291 } 1292 } 1293 1294 static void Append(nsIContent* aSubtreeRoot) { 1295 if (!sContentUnbinder) { 1296 sContentUnbinder = new ContentUnbinder(); 1297 nsCOMPtr<nsIRunnable> e = sContentUnbinder; 1298 NS_DispatchToCurrentThreadQueue(e.forget(), EventQueuePriority::Idle); 1299 } 1300 1301 if (sContentUnbinder->mLast->mSubtreeRoots.Length() >= 1302 SUBTREE_UNBINDINGS_PER_RUNNABLE) { 1303 sContentUnbinder->mLast->mNext = new ContentUnbinder(); 1304 sContentUnbinder->mLast = sContentUnbinder->mLast->mNext; 1305 } 1306 sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot); 1307 } 1308 1309 private: 1310 AutoTArray<nsCOMPtr<nsIContent>, SUBTREE_UNBINDINGS_PER_RUNNABLE> 1311 mSubtreeRoots; 1312 RefPtr<ContentUnbinder> mNext; 1313 ContentUnbinder* mLast; 1314 static ContentUnbinder* sContentUnbinder; 1315 }; 1316 1317 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr; 1318 1319 void FragmentOrElement::ClearContentUnbinder() { ContentUnbinder::UnbindAll(); } 1320 1321 // Note, _INHERITED macro isn't used here since nsINode implementations are 1322 // rather special. 1323 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentOrElement) 1324 1325 // We purposefully don't UNLINK_BEGIN_INHERITED here. 1326 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement) 1327 nsIContent::Unlink(tmp); 1328 1329 // Unlink child content (and unbind our subtree). 1330 if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) { 1331 // Don't allow script to run while we're unbinding everything. 1332 nsAutoScriptBlocker scriptBlocker; 1333 BatchRemovalState state{}; 1334 while (nsCOMPtr<nsIContent> child = tmp->GetLastChild()) { 1335 // Hold a strong ref to the node when we remove it, because we may be 1336 // the last reference to it. 1337 // If this code changes, change the corresponding code in Document's 1338 // unlink impl and ContentUnbinder::UnbindSubtree. 1339 tmp->DisconnectChild(child); 1340 child->UnbindFromTree(/* aNewParent = */ nullptr, &state); 1341 state.mIsFirst = false; 1342 } 1343 } else if (!tmp->GetParent() && tmp->HasChildren()) { 1344 ContentUnbinder::Append(tmp); 1345 } /* else { 1346 The subtree root will end up to a ContentUnbinder, and that will 1347 unbind the child nodes. 1348 } */ 1349 1350 if (ShadowRoot* shadowRoot = tmp->GetShadowRoot()) { 1351 shadowRoot->Unbind(); 1352 tmp->ExtendedDOMSlots()->mShadowRoot = nullptr; 1353 } 1354 1355 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1356 1357 void FragmentOrElement::MarkNodeChildren(nsINode* aNode) { 1358 JSObject* o = GetJSObjectChild(aNode); 1359 if (o) { 1360 JS::ExposeObjectToActiveJS(o); 1361 } 1362 1363 EventListenerManager* elm = aNode->GetExistingListenerManager(); 1364 if (elm) { 1365 elm->MarkForCC(); 1366 } 1367 } 1368 1369 nsINode* FindOptimizableSubtreeRoot(nsINode* aNode) { 1370 nsINode* p; 1371 while ((p = aNode->GetParentNode())) { 1372 if (aNode->UnoptimizableCCNode()) { 1373 return nullptr; 1374 } 1375 aNode = p; 1376 } 1377 1378 if (aNode->UnoptimizableCCNode()) { 1379 return nullptr; 1380 } 1381 return aNode; 1382 } 1383 1384 StaticAutoPtr<nsTHashSet<nsINode*>> gCCBlackMarkedNodes; 1385 1386 static void ClearBlackMarkedNodes() { 1387 if (!gCCBlackMarkedNodes) { 1388 return; 1389 } 1390 for (nsINode* n : *gCCBlackMarkedNodes) { 1391 n->SetCCMarkedRoot(false); 1392 n->SetInCCBlackTree(false); 1393 } 1394 gCCBlackMarkedNodes = nullptr; 1395 } 1396 1397 // static 1398 void FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode) { 1399 if (!gCCBlackMarkedNodes) { 1400 return; 1401 } 1402 gCCBlackMarkedNodes->Remove(aNode); 1403 } 1404 1405 static bool IsCertainlyAliveNode(nsINode* aNode, Document* aDoc) { 1406 MOZ_ASSERT(aNode->GetComposedDoc() == aDoc); 1407 1408 // Marked to be in-CC-generation or if the document is an svg image that's 1409 // being kept alive by the image cache. (Note that an svg image's internal 1410 // SVG document will receive an OnPageHide() call when it gets purged from 1411 // the image cache; hence, we use IsVisible() as a hint that the document is 1412 // actively being kept alive by the cache.) 1413 return nsCCUncollectableMarker::InGeneration(aDoc->GetMarkedCCGeneration()) || 1414 (nsCCUncollectableMarker::sGeneration && aDoc->IsBeingUsedAsImage() && 1415 aDoc->IsVisible()); 1416 } 1417 1418 // static 1419 bool FragmentOrElement::CanSkipInCC(nsINode* aNode) { 1420 // Don't try to optimize anything during shutdown. 1421 if (nsCCUncollectableMarker::sGeneration == 0) { 1422 return false; 1423 } 1424 1425 Document* currentDoc = aNode->GetComposedDoc(); 1426 if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) { 1427 return !NeedsScriptTraverse(aNode); 1428 } 1429 1430 // Bail out early if aNode is somewhere in anonymous content, 1431 // or otherwise unusual. 1432 if (aNode->UnoptimizableCCNode()) { 1433 return false; 1434 } 1435 1436 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) 1437 : FindOptimizableSubtreeRoot(aNode); 1438 if (!root) { 1439 return false; 1440 } 1441 1442 // Subtree has been traversed already. 1443 if (root->CCMarkedRoot()) { 1444 return root->InCCBlackTree() && !NeedsScriptTraverse(aNode); 1445 } 1446 1447 if (!gCCBlackMarkedNodes) { 1448 gCCBlackMarkedNodes = new nsTHashSet<nsINode*>(1020); 1449 } 1450 1451 // nodesToUnpurple contains nodes which will be removed 1452 // from the purple buffer if the DOM tree is known-live. 1453 AutoTArray<nsIContent*, 1020> nodesToUnpurple; 1454 // grayNodes need script traverse, so they aren't removed from 1455 // the purple buffer, but are marked to be in known-live subtree so that 1456 // traverse is faster. 1457 AutoTArray<nsINode*, 1020> grayNodes; 1458 1459 bool foundLiveWrapper = root->HasKnownLiveWrapper(); 1460 if (root != currentDoc) { 1461 currentDoc = nullptr; 1462 if (NeedsScriptTraverse(root)) { 1463 grayNodes.AppendElement(root); 1464 } else if (static_cast<nsIContent*>(root)->IsPurple()) { 1465 nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root)); 1466 } 1467 } 1468 1469 // Traverse the subtree and check if we could know without CC 1470 // that it is known-live. 1471 // Note, this traverse is non-virtual and inline, so it should be a lot faster 1472 // than CC's generic traverse. 1473 for (nsIContent* node = root->GetFirstChild(); node; 1474 node = node->GetNextNode(root)) { 1475 foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper(); 1476 if (foundLiveWrapper && currentDoc) { 1477 // If we can mark the whole document known-live, no need to optimize 1478 // so much, since when the next purple node in the document will be 1479 // handled, it is fast to check that currentDoc is in CCGeneration. 1480 break; 1481 } 1482 if (NeedsScriptTraverse(node)) { 1483 // Gray nodes need real CC traverse. 1484 grayNodes.AppendElement(node); 1485 } else if (node->IsPurple()) { 1486 nodesToUnpurple.AppendElement(node); 1487 } 1488 } 1489 1490 root->SetCCMarkedRoot(true); 1491 root->SetInCCBlackTree(foundLiveWrapper); 1492 gCCBlackMarkedNodes->Insert(root); 1493 1494 if (!foundLiveWrapper) { 1495 return false; 1496 } 1497 1498 if (currentDoc) { 1499 // Special case documents. If we know the document is known-live, 1500 // we can mark the document to be in CCGeneration. 1501 currentDoc->MarkUncollectableForCCGeneration( 1502 nsCCUncollectableMarker::sGeneration); 1503 } else { 1504 for (uint32_t i = 0; i < grayNodes.Length(); ++i) { 1505 nsINode* node = grayNodes[i]; 1506 node->SetInCCBlackTree(true); 1507 gCCBlackMarkedNodes->Insert(node); 1508 } 1509 } 1510 1511 // Subtree is known-live, we can remove non-gray purple nodes from 1512 // purple buffer. 1513 for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) { 1514 nsIContent* purple = nodesToUnpurple[i]; 1515 // Can't remove currently handled purple node. 1516 if (purple != aNode) { 1517 purple->RemovePurple(); 1518 } 1519 } 1520 return !NeedsScriptTraverse(aNode); 1521 } 1522 1523 AutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr; 1524 AutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr; 1525 1526 void ClearCycleCollectorCleanupData() { 1527 if (gPurpleRoots) { 1528 uint32_t len = gPurpleRoots->Length(); 1529 for (uint32_t i = 0; i < len; ++i) { 1530 nsINode* n = gPurpleRoots->ElementAt(i); 1531 n->SetIsPurpleRoot(false); 1532 } 1533 delete gPurpleRoots; 1534 gPurpleRoots = nullptr; 1535 } 1536 if (gNodesToUnbind) { 1537 uint32_t len = gNodesToUnbind->Length(); 1538 for (uint32_t i = 0; i < len; ++i) { 1539 nsIContent* c = gNodesToUnbind->ElementAt(i); 1540 c->SetIsPurpleRoot(false); 1541 ContentUnbinder::Append(c); 1542 } 1543 delete gNodesToUnbind; 1544 gNodesToUnbind = nullptr; 1545 } 1546 } 1547 1548 static bool ShouldClearPurple(nsIContent* aContent) { 1549 MOZ_ASSERT(aContent); 1550 if (aContent->IsPurple()) { 1551 return true; 1552 } 1553 1554 JSObject* o = GetJSObjectChild(aContent); 1555 if (o && JS::ObjectIsMarkedGray(o)) { 1556 return true; 1557 } 1558 1559 if (aContent->HasListenerManager()) { 1560 return true; 1561 } 1562 1563 return aContent->HasProperties(); 1564 } 1565 1566 // If aNode is not optimizable, but is an element 1567 // with a frame in a document which has currently active presshell, 1568 // we can act as if it was optimizable. When the primary frame dies, aNode 1569 // will end up to the purple buffer because of the refcount change. 1570 bool NodeHasActiveFrame(Document* aCurrentDoc, nsINode* aNode) { 1571 return aCurrentDoc->GetPresShell() && aNode->IsElement() && 1572 aNode->AsElement()->GetPrimaryFrame(); 1573 } 1574 1575 // CanSkip checks if aNode is known-live, and if it is, returns true. If aNode 1576 // is in a known-live DOM tree, CanSkip may also remove other objects from 1577 // purple buffer and unmark event listeners and user data. If the root of the 1578 // DOM tree is a document, less optimizations are done since checking the 1579 // liveness of the current document is usually fast and we don't want slow down 1580 // such common cases. 1581 bool FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) { 1582 // Don't try to optimize anything during shutdown. 1583 if (nsCCUncollectableMarker::sGeneration == 0) { 1584 return false; 1585 } 1586 1587 bool unoptimizable = aNode->UnoptimizableCCNode(); 1588 Document* currentDoc = aNode->GetComposedDoc(); 1589 if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) && 1590 (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode))) { 1591 MarkNodeChildren(aNode); 1592 return true; 1593 } 1594 1595 if (unoptimizable) { 1596 return false; 1597 } 1598 1599 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) 1600 : FindOptimizableSubtreeRoot(aNode); 1601 if (!root) { 1602 return false; 1603 } 1604 1605 // Subtree has been traversed already, and aNode has 1606 // been handled in a way that doesn't require revisiting it. 1607 if (root->IsPurpleRoot()) { 1608 return false; 1609 } 1610 1611 // nodesToClear contains nodes which are either purple or 1612 // gray. 1613 AutoTArray<nsIContent*, 1020> nodesToClear; 1614 1615 bool foundLiveWrapper = root->HasKnownLiveWrapper(); 1616 bool domOnlyCycle = false; 1617 if (root != currentDoc) { 1618 currentDoc = nullptr; 1619 if (!foundLiveWrapper) { 1620 domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree(); 1621 } 1622 if (ShouldClearPurple(static_cast<nsIContent*>(root))) { 1623 nodesToClear.AppendElement(static_cast<nsIContent*>(root)); 1624 } 1625 } 1626 1627 // Traverse the subtree and check if we could know without CC 1628 // that it is known-live. 1629 // Note, this traverse is non-virtual and inline, so it should be a lot faster 1630 // than CC's generic traverse. 1631 for (nsIContent* node = root->GetFirstChild(); node; 1632 node = node->GetNextNode(root)) { 1633 foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper(); 1634 if (foundLiveWrapper) { 1635 domOnlyCycle = false; 1636 if (currentDoc) { 1637 // If we can mark the whole document live, no need to optimize 1638 // so much, since when the next purple node in the document will be 1639 // handled, it is fast to check that the currentDoc is in CCGeneration. 1640 break; 1641 } 1642 // No need to put stuff to the nodesToClear array, if we can clear it 1643 // already here. 1644 if (node->IsPurple() && (node != aNode || aRemovingAllowed)) { 1645 node->RemovePurple(); 1646 } 1647 MarkNodeChildren(node); 1648 } else { 1649 domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree(); 1650 if (ShouldClearPurple(node)) { 1651 // Collect interesting nodes which we can clear if we find that 1652 // they are kept alive in a known-live tree or are in a DOM-only cycle. 1653 nodesToClear.AppendElement(node); 1654 } 1655 } 1656 } 1657 1658 if (!currentDoc || !foundLiveWrapper) { 1659 root->SetIsPurpleRoot(true); 1660 if (domOnlyCycle) { 1661 if (!gNodesToUnbind) { 1662 gNodesToUnbind = new AutoTArray<nsIContent*, 1020>(); 1663 } 1664 gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root)); 1665 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) { 1666 nsIContent* n = nodesToClear[i]; 1667 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) { 1668 n->RemovePurple(); 1669 } 1670 } 1671 return true; 1672 } else { 1673 if (!gPurpleRoots) { 1674 gPurpleRoots = new AutoTArray<nsINode*, 1020>(); 1675 } 1676 gPurpleRoots->AppendElement(root); 1677 } 1678 } 1679 1680 if (!foundLiveWrapper) { 1681 return false; 1682 } 1683 1684 if (currentDoc) { 1685 // Special case documents. If we know the document is known-live, 1686 // we can mark the document to be in CCGeneration. 1687 currentDoc->MarkUncollectableForCCGeneration( 1688 nsCCUncollectableMarker::sGeneration); 1689 MarkNodeChildren(currentDoc); 1690 } 1691 1692 // Subtree is known-live, so we can remove purple nodes from 1693 // purple buffer and mark stuff that to be certainly alive. 1694 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) { 1695 nsIContent* n = nodesToClear[i]; 1696 MarkNodeChildren(n); 1697 // Can't remove currently handled purple node, 1698 // unless aRemovingAllowed is true. 1699 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) { 1700 n->RemovePurple(); 1701 } 1702 } 1703 return true; 1704 } 1705 1706 bool FragmentOrElement::CanSkipThis(nsINode* aNode) { 1707 if (nsCCUncollectableMarker::sGeneration == 0) { 1708 return false; 1709 } 1710 if (aNode->HasKnownLiveWrapper()) { 1711 return true; 1712 } 1713 Document* c = aNode->GetComposedDoc(); 1714 return ((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) && 1715 !NeedsScriptTraverse(aNode); 1716 } 1717 1718 void FragmentOrElement::InitCCCallbacks() { 1719 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData); 1720 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes); 1721 } 1722 1723 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement) 1724 return FragmentOrElement::CanSkip(tmp, aRemovingAllowed); 1725 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 1726 1727 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement) 1728 return FragmentOrElement::CanSkipInCC(tmp); 1729 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 1730 1731 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement) 1732 return FragmentOrElement::CanSkipThis(tmp); 1733 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 1734 1735 // We purposefully don't TRAVERSE_BEGIN_INHERITED here. All the bits 1736 // we should traverse should be added here or in nsINode::Traverse. 1737 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement) 1738 if (MOZ_UNLIKELY(cb.WantDebugInfo())) { 1739 char name[512]; 1740 uint32_t nsid = tmp->GetNameSpaceID(); 1741 nsAtomCString localName(tmp->NodeInfo()->NameAtom()); 1742 nsAutoCString uri; 1743 if (tmp->OwnerDoc()->GetDocumentURI()) { 1744 uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault(); 1745 } 1746 1747 nsAutoString id; 1748 nsAtom* idAtom = tmp->GetID(); 1749 if (idAtom) { 1750 id.AppendLiteral(" id='"); 1751 id.Append(nsDependentAtomString(idAtom)); 1752 id.Append('\''); 1753 } 1754 1755 nsAutoString classes; 1756 const nsAttrValue* classAttrValue = 1757 tmp->IsElement() ? tmp->AsElement()->GetClasses() : nullptr; 1758 if (classAttrValue) { 1759 classes.AppendLiteral(" class='"); 1760 nsAutoString classString; 1761 classAttrValue->ToString(classString); 1762 classString.ReplaceChar(char16_t('\n'), char16_t(' ')); 1763 classes.Append(classString); 1764 classes.Append('\''); 1765 } 1766 1767 nsAutoCString orphan; 1768 if (!tmp->IsInComposedDoc()) { 1769 orphan.AppendLiteral(" (orphan)"); 1770 } 1771 1772 const char* nsuri = nsNameSpaceManager::GetNameSpaceDisplayName(nsid); 1773 SprintfLiteral(name, "FragmentOrElement %s %s%s%s%s %s", nsuri, 1774 localName.get(), NS_ConvertUTF16toUTF8(id).get(), 1775 NS_ConvertUTF16toUTF8(classes).get(), orphan.get(), 1776 uri.get()); 1777 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); 1778 } else { 1779 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get()) 1780 } 1781 1782 if (!nsIContent::Traverse(tmp, cb)) { 1783 return NS_SUCCESS_INTERRUPTED_TRAVERSE; 1784 } 1785 if (tmp->IsElement()) { 1786 Element* element = tmp->AsElement(); 1787 // Traverse attribute names. 1788 uint32_t i; 1789 uint32_t attrs = element->GetAttrCount(); 1790 for (i = 0; i < attrs; i++) { 1791 const nsAttrName* name = element->GetUnsafeAttrNameAt(i); 1792 if (!name->IsAtom()) { 1793 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrs[i]->NodeInfo()"); 1794 cb.NoteNativeChild(name->NodeInfo(), 1795 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo)); 1796 } 1797 } 1798 } 1799 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1800 1801 NS_INTERFACE_MAP_BEGIN(FragmentOrElement) 1802 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement) 1803 NS_INTERFACE_MAP_END_INHERITING(nsIContent) 1804 1805 //---------------------------------------------------------------------- 1806 1807 const CharacterDataBuffer* FragmentOrElement::GetCharacterDataBuffer() const { 1808 return nullptr; 1809 } 1810 1811 uint32_t FragmentOrElement::TextLength() const { 1812 // We can remove this assertion if it turns out to be useful to be able 1813 // to depend on this returning 0 1814 MOZ_ASSERT_UNREACHABLE("called FragmentOrElement::TextLength"); 1815 1816 return 0; 1817 } 1818 1819 bool FragmentOrElement::TextIsOnlyWhitespace() { return false; } 1820 1821 bool FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const { return false; } 1822 1823 static inline bool IsVoidTag(const nsAtom* aTag) { 1824 static const nsAtom* voidElements[] = { 1825 nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont, 1826 nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col, 1827 nsGkAtoms::embed, nsGkAtoms::frame, nsGkAtoms::hr, 1828 nsGkAtoms::img, nsGkAtoms::input, nsGkAtoms::keygen, 1829 nsGkAtoms::link, nsGkAtoms::meta, nsGkAtoms::param, 1830 nsGkAtoms::source, nsGkAtoms::track, nsGkAtoms::wbr}; 1831 1832 static mozilla::BitBloomFilter<12, nsAtom> sFilter; 1833 static bool sInitialized = false; 1834 if (!sInitialized) { 1835 sInitialized = true; 1836 for (uint32_t i = 0; i < std::size(voidElements); ++i) { 1837 sFilter.add(voidElements[i]); 1838 } 1839 } 1840 1841 if (sFilter.mightContain(aTag)) { 1842 for (uint32_t i = 0; i < std::size(voidElements); ++i) { 1843 if (aTag == voidElements[i]) { 1844 return true; 1845 } 1846 } 1847 } 1848 return false; 1849 } 1850 1851 /* static */ 1852 bool FragmentOrElement::IsHTMLVoid(const nsAtom* aLocalName) { 1853 return aLocalName && IsVoidTag(aLocalName); 1854 } 1855 1856 void FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup) { 1857 aMarkup.Truncate(); 1858 1859 Document* doc = OwnerDoc(); 1860 if (IsInHTMLDocument()) { 1861 nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup, false, 1862 {}); 1863 return; 1864 } 1865 1866 nsAutoString contentType; 1867 doc->GetContentType(contentType); 1868 bool tryToCacheEncoder = !aIncludeSelf; 1869 1870 nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder(); 1871 if (!docEncoder) { 1872 docEncoder = do_createDocumentEncoder( 1873 PromiseFlatCString(NS_ConvertUTF16toUTF8(contentType)).get()); 1874 } 1875 if (!docEncoder) { 1876 // This could be some type for which we create a synthetic document. Try 1877 // again as XML 1878 contentType.AssignLiteral("application/xml"); 1879 docEncoder = do_createDocumentEncoder("application/xml"); 1880 // Don't try to cache the encoder since it would point to a different 1881 // contentType once it has been reinitialized. 1882 tryToCacheEncoder = false; 1883 } 1884 1885 NS_ENSURE_TRUE_VOID(docEncoder); 1886 1887 uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities | 1888 // Output DOM-standard newlines 1889 nsIDocumentEncoder::OutputLFLineBreak | 1890 // Don't do linebreaking that's not present in 1891 // the source 1892 nsIDocumentEncoder::OutputRaw | 1893 // Only check for mozdirty when necessary (bug 599983) 1894 nsIDocumentEncoder::OutputIgnoreMozDirty; 1895 1896 if (IsEditable()) { 1897 nsCOMPtr<Element> elem = do_QueryInterface(this); 1898 TextEditor* textEditor = elem ? elem->GetTextEditorInternal() : nullptr; 1899 if (textEditor && textEditor->OutputsMozDirty()) { 1900 flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty; 1901 } 1902 } 1903 1904 DebugOnly<nsresult> rv = docEncoder->Init(doc, contentType, flags); 1905 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1906 1907 if (aIncludeSelf) { 1908 docEncoder->SetNode(this); 1909 } else { 1910 docEncoder->SetContainerNode(this); 1911 } 1912 rv = docEncoder->EncodeToString(aMarkup); 1913 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1914 if (tryToCacheEncoder) { 1915 doc->SetCachedEncoder(docEncoder.forget()); 1916 } 1917 } 1918 1919 static bool ContainsMarkup(const nsAString& aStr) { 1920 // Note: we can't use FindCharInSet because null is one of the characters we 1921 // want to search for. 1922 const char16_t* start = aStr.BeginReading(); 1923 const char16_t* end = aStr.EndReading(); 1924 1925 #ifdef MOZ_MAY_HAVE_HTMLACCEL 1926 if (mozilla::htmlaccel::htmlaccelEnabled()) { 1927 // We need to check for the empty string in order to 1928 // dereference `start` for the '<' check. We might as well 1929 // check that we have a full SIMD stride. 1930 if (end - start >= 16) { 1931 // Optimize the case where the input starts with a tag. 1932 if (*start == u'<') { 1933 return true; 1934 } 1935 // Curiously, this doesn't look like much of an optimization on Zen 3, 1936 // but since it is an optimization on M3 Pro and Skylake, let's do this. 1937 return mozilla::htmlaccel::ContainsMarkup(start, end); 1938 } 1939 } 1940 #endif 1941 1942 while (start != end) { 1943 char16_t c = *start; 1944 if (c == char16_t('<') || c == char16_t('&') || c == char16_t('\r') || 1945 c == char16_t('\0')) { 1946 return true; 1947 } 1948 ++start; 1949 } 1950 1951 return false; 1952 } 1953 1954 void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, 1955 ErrorResult& aError) { 1956 // Keep "this" alive should be guaranteed by the caller, and also the content 1957 // of a template element (if this is one) should never been released by from 1958 // this during this call. Therefore, using raw pointer here is safe. 1959 FragmentOrElement* target = this; 1960 // Handle template case. 1961 if (target->IsTemplateElement()) { 1962 DocumentFragment* frag = 1963 static_cast<HTMLTemplateElement*>(target)->Content(); 1964 MOZ_ASSERT(frag); 1965 target = frag; 1966 } 1967 // Fast-path for strings with no markup. Limit this to short strings, to 1968 // avoid ContainsMarkup taking too long. The choice for 100 is based on 1969 // gut feeling. 1970 // 1971 // Don't do this for elements with a weird parser insertion mode, for 1972 // instance setting innerHTML = "" on a <html> element should add the 1973 // optional <head> and <body> elements. 1974 if (!target->HasWeirdParserInsertionMode() && aInnerHTML.Length() < 100 && 1975 !ContainsMarkup(aInnerHTML)) { 1976 aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false); 1977 return; 1978 } 1979 1980 const RefPtr<Document> doc = target->OwnerDoc(); 1981 1982 target->NotifyDevToolsOfRemovalsOfChildren(); 1983 1984 // Needed when innerHTML is used in combination with contenteditable 1985 mozAutoDocUpdate updateBatch(doc, true); 1986 1987 // Remove childnodes. 1988 nsAutoMutationBatch mb(target, true, false); 1989 target->RemoveAllChildren(true); 1990 mb.RemovalDone(); 1991 1992 nsAutoScriptLoaderDisabler sld(doc); 1993 1994 FragmentOrElement* parseContext = this; 1995 if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) { 1996 // Fix up the context to be the host of the ShadowRoot. See 1997 // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1. 1998 parseContext = shadowRoot->GetHost(); 1999 } 2000 2001 if (doc->IsHTMLDocument()) { 2002 doc->SuspendDOMNotifications(); 2003 nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom(); 2004 int32_t contextNameSpaceID = parseContext->GetNameSpaceID(); 2005 2006 aError = nsContentUtils::ParseFragmentHTML( 2007 aInnerHTML, target, contextLocalName, contextNameSpaceID, 2008 doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true); 2009 doc->ResumeDOMNotifications(); 2010 if (target->GetFirstChild()) { 2011 MutationObservers::NotifyContentAppended(target, target->GetFirstChild(), 2012 {}); 2013 } 2014 mb.NodesAdded(); 2015 } else { 2016 RefPtr<DocumentFragment> df = nsContentUtils::CreateContextualFragment( 2017 parseContext, aInnerHTML, true, aError); 2018 if (!aError.Failed()) { 2019 // Suppress assertion about node removal mutation events that can't have 2020 // listeners anyway, because no one has had the chance to register 2021 // mutation listeners on the fragment that comes from the parser. 2022 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker; 2023 2024 target->AppendChild(*df, aError); 2025 mb.NodesAdded(); 2026 } 2027 } 2028 } 2029 2030 void FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 2031 size_t* aNodeSize) const { 2032 nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize); 2033 2034 nsDOMSlots* slots = GetExistingDOMSlots(); 2035 if (slots) { 2036 *aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf); 2037 } 2038 }