Bumping manifests a=b2g-bump
[gecko.git] / dom / base / FragmentOrElement.cpp
blob890ba6a5494d19844b3b6d90ec1bedff3fa9efb9
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=79: */
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/. */
7 /*
8 * Base class for all element classes; this provides an implementation
9 * of DOM Core's nsIDOMElement, implements nsIContent, provides
10 * utility methods for subclasses, and so forth.
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/Likely.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/StaticPtr.h"
18 #include "mozilla/dom/FragmentOrElement.h"
20 #include "mozilla/AsyncEventDispatcher.h"
21 #include "mozilla/EventDispatcher.h"
22 #include "mozilla/EventListenerManager.h"
23 #include "mozilla/EventStates.h"
24 #include "mozilla/dom/Attr.h"
25 #include "nsDOMAttributeMap.h"
26 #include "nsIAtom.h"
27 #include "mozilla/dom/NodeInfo.h"
28 #include "mozilla/dom/Event.h"
29 #include "nsIDocumentInlines.h"
30 #include "nsIDocumentEncoder.h"
31 #include "nsIDOMNodeList.h"
32 #include "nsIContentIterator.h"
33 #include "nsFocusManager.h"
34 #include "nsILinkHandler.h"
35 #include "nsIScriptGlobalObject.h"
36 #include "nsIURL.h"
37 #include "nsNetUtil.h"
38 #include "nsIFrame.h"
39 #include "nsIAnonymousContentCreator.h"
40 #include "nsIPresShell.h"
41 #include "nsPresContext.h"
42 #include "nsStyleConsts.h"
43 #include "nsString.h"
44 #include "nsUnicharUtils.h"
45 #include "nsIDOMEvent.h"
46 #include "nsDOMCID.h"
47 #include "nsIServiceManager.h"
48 #include "nsIDOMCSSStyleDeclaration.h"
49 #include "nsDOMCSSAttrDeclaration.h"
50 #include "nsNameSpaceManager.h"
51 #include "nsContentList.h"
52 #include "nsDOMTokenList.h"
53 #include "nsXBLPrototypeBinding.h"
54 #include "nsError.h"
55 #include "nsDOMString.h"
56 #include "nsIScriptSecurityManager.h"
57 #include "nsIDOMMutationEvent.h"
58 #include "mozilla/InternalMutationEvent.h"
59 #include "mozilla/MouseEvents.h"
60 #include "nsNodeUtils.h"
61 #include "nsDocument.h"
62 #include "nsAttrValueOrString.h"
63 #ifdef MOZ_XUL
64 #include "nsXULElement.h"
65 #endif /* MOZ_XUL */
66 #include "nsFrameSelection.h"
67 #ifdef DEBUG
68 #include "nsRange.h"
69 #endif
71 #include "nsBindingManager.h"
72 #include "nsXBLBinding.h"
73 #include "nsPIDOMWindow.h"
74 #include "nsPIBoxObject.h"
75 #include "nsSVGUtils.h"
76 #include "nsLayoutUtils.h"
77 #include "nsGkAtoms.h"
78 #include "nsContentUtils.h"
79 #include "nsTextFragment.h"
80 #include "nsContentCID.h"
82 #include "nsIDOMEventListener.h"
83 #include "nsIWebNavigation.h"
84 #include "nsIBaseWindow.h"
85 #include "nsIWidget.h"
87 #include "js/GCAPI.h"
89 #include "nsNodeInfoManager.h"
90 #include "nsICategoryManager.h"
91 #include "nsGenericHTMLElement.h"
92 #include "nsIEditor.h"
93 #include "nsIEditorIMESupport.h"
94 #include "nsContentCreatorFunctions.h"
95 #include "nsIControllers.h"
96 #include "nsView.h"
97 #include "nsViewManager.h"
98 #include "nsIScrollableFrame.h"
99 #include "ChildIterator.h"
100 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
101 #include "nsRuleProcessorData.h"
102 #include "nsTextNode.h"
103 #include "mozilla/dom/NodeListBinding.h"
104 #include "mozilla/dom/UndoManager.h"
106 #ifdef MOZ_XUL
107 #include "nsIXULDocument.h"
108 #endif /* MOZ_XUL */
110 #include "nsCCUncollectableMarker.h"
112 #include "mozAutoDocUpdate.h"
114 #include "prprf.h"
115 #include "nsDOMMutationObserver.h"
116 #include "nsWrapperCacheInlines.h"
117 #include "nsCycleCollector.h"
118 #include "xpcpublic.h"
119 #include "nsIScriptError.h"
120 #include "mozilla/Telemetry.h"
122 #include "mozilla/CORSMode.h"
124 #include "mozilla/dom/ShadowRoot.h"
125 #include "mozilla/dom/HTMLTemplateElement.h"
127 #include "nsStyledElement.h"
128 #include "nsIContentInlines.h"
130 using namespace mozilla;
131 using namespace mozilla::dom;
133 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
134 bool nsIContent::sTabFocusModelAppliesToXUL = false;
135 uint64_t nsMutationGuard::sGeneration = 0;
137 nsIContent*
138 nsIContent::FindFirstNonChromeOnlyAccessContent() const
140 // This handles also nested native anonymous content.
141 for (const nsIContent *content = this; content;
142 content = content->GetBindingParent()) {
143 if (!content->ChromeOnlyAccess()) {
144 // Oops, this function signature allows casting const to
145 // non-const. (Then again, so does GetChildAt(0)->GetParent().)
146 return const_cast<nsIContent*>(content);
149 return nullptr;
152 nsIContent*
153 nsIContent::GetFlattenedTreeParent() const
155 nsIContent* parent = GetParent();
157 if (parent && nsContentUtils::HasDistributedChildren(parent) &&
158 nsContentUtils::IsInSameAnonymousTree(parent, this)) {
159 // This node is distributed to insertion points, thus we
160 // need to consult the destination insertion points list to
161 // figure out where this node was inserted in the flattened tree.
162 // It may be the case that |parent| distributes its children
163 // but the child does not match any insertion points, thus
164 // the flattened tree parent is nullptr.
165 nsTArray<nsIContent*>* destInsertionPoints = GetExistingDestInsertionPoints();
166 parent = destInsertionPoints && !destInsertionPoints->IsEmpty() ?
167 destInsertionPoints->LastElement()->GetParent() : nullptr;
168 } else if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
169 nsIContent* insertionParent = GetXBLInsertionParent();
170 if (insertionParent) {
171 parent = insertionParent;
175 // Shadow roots never shows up in the flattened tree. Return the host
176 // instead.
177 if (parent && parent->IsInShadowTree()) {
178 ShadowRoot* parentShadowRoot = ShadowRoot::FromNode(parent);
179 if (parentShadowRoot) {
180 return parentShadowRoot->GetHost();
184 return parent;
187 nsIContent::IMEState
188 nsIContent::GetDesiredIMEState()
190 if (!IsEditableInternal()) {
191 // Check for the special case where we're dealing with elements which don't
192 // have the editable flag set, but are readwrite (such as text controls).
193 if (!IsElement() ||
194 !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
195 return IMEState(IMEState::DISABLED);
198 // NOTE: The content for independent editors (e.g., input[type=text],
199 // textarea) must override this method, so, we don't need to worry about
200 // that here.
201 nsIContent *editableAncestor = GetEditingHost();
203 // This is in another editable content, use the result of it.
204 if (editableAncestor && editableAncestor != this) {
205 return editableAncestor->GetDesiredIMEState();
207 nsIDocument* doc = GetComposedDoc();
208 if (!doc) {
209 return IMEState(IMEState::DISABLED);
211 nsIPresShell* ps = doc->GetShell();
212 if (!ps) {
213 return IMEState(IMEState::DISABLED);
215 nsPresContext* pc = ps->GetPresContext();
216 if (!pc) {
217 return IMEState(IMEState::DISABLED);
219 nsIEditor* editor = nsContentUtils::GetHTMLEditor(pc);
220 nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(editor);
221 if (!imeEditor) {
222 return IMEState(IMEState::DISABLED);
224 IMEState state;
225 imeEditor->GetPreferredIMEState(&state);
226 return state;
229 bool
230 nsIContent::HasIndependentSelection()
232 nsIFrame* frame = GetPrimaryFrame();
233 return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
236 dom::Element*
237 nsIContent::GetEditingHost()
239 // If this isn't editable, return nullptr.
240 NS_ENSURE_TRUE(IsEditableInternal(), nullptr);
242 nsIDocument* doc = GetComposedDoc();
243 NS_ENSURE_TRUE(doc, nullptr);
244 // If this is in designMode, we should return <body>
245 if (doc->HasFlag(NODE_IS_EDITABLE) && !IsInShadowTree()) {
246 return doc->GetBodyElement();
249 nsIContent* content = this;
250 for (dom::Element* parent = GetParentElement();
251 parent && parent->HasFlag(NODE_IS_EDITABLE);
252 parent = content->GetParentElement()) {
253 content = parent;
255 return content->AsElement();
258 nsresult
259 nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
260 nsAString& aNamespaceURI) const
262 if (aNamespacePrefix.EqualsLiteral("xml")) {
263 // Special-case for xml prefix
264 aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
265 return NS_OK;
268 if (aNamespacePrefix.EqualsLiteral("xmlns")) {
269 // Special-case for xmlns prefix
270 aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
271 return NS_OK;
274 nsCOMPtr<nsIAtom> name;
275 if (!aNamespacePrefix.IsEmpty()) {
276 name = do_GetAtom(aNamespacePrefix);
277 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
279 else {
280 name = nsGkAtoms::xmlns;
282 // Trace up the content parent chain looking for the namespace
283 // declaration that declares aNamespacePrefix.
284 const nsIContent* content = this;
285 do {
286 if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
287 return NS_OK;
288 } while ((content = content->GetParent()));
289 return NS_ERROR_FAILURE;
292 already_AddRefed<nsIURI>
293 nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
295 nsIDocument* doc = OwnerDoc();
296 // Start with document base
297 nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
299 // Collect array of xml:base attribute values up the parent chain. This
300 // is slightly slower for the case when there are xml:base attributes, but
301 // faster for the far more common case of there not being any such
302 // attributes.
303 // Also check for SVG elements which require special handling
304 nsAutoTArray<nsString, 5> baseAttrs;
305 nsString attr;
306 const nsIContent *elem = this;
307 do {
308 // First check for SVG specialness (why is this SVG specific?)
309 if (elem->IsSVG()) {
310 nsIContent* bindingParent = elem->GetBindingParent();
311 if (bindingParent) {
312 nsXBLBinding* binding = bindingParent->GetXBLBinding();
313 if (binding) {
314 // XXX sXBL/XBL2 issue
315 // If this is an anonymous XBL element use the binding
316 // document for the base URI.
317 // XXX Will fail with xml:base
318 base = binding->PrototypeBinding()->DocURI();
319 break;
324 nsIURI* explicitBaseURI = elem->GetExplicitBaseURI();
325 if (explicitBaseURI) {
326 base = explicitBaseURI;
327 break;
330 // Otherwise check for xml:base attribute
331 elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
332 if (!attr.IsEmpty()) {
333 baseAttrs.AppendElement(attr);
335 elem = elem->GetParent();
336 } while(elem);
338 // Now resolve against all xml:base attrs
339 for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) {
340 nsCOMPtr<nsIURI> newBase;
341 nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i],
342 doc->GetDocumentCharacterSet().get(), base);
343 // Do a security check, almost the same as nsDocument::SetBaseURL()
344 // Only need to do this on the final uri
345 if (NS_SUCCEEDED(rv) && i == 0) {
346 rv = nsContentUtils::GetSecurityManager()->
347 CheckLoadURIWithPrincipal(NodePrincipal(), newBase,
348 nsIScriptSecurityManager::STANDARD);
350 if (NS_SUCCEEDED(rv)) {
351 base.swap(newBase);
355 return base.forget();
358 //----------------------------------------------------------------------
360 static inline JSObject*
361 GetJSObjectChild(nsWrapperCache* aCache)
363 return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() : nullptr;
366 static bool
367 NeedsScriptTraverse(nsINode* aNode)
369 return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
370 !aNode->IsBlackAndDoesNotNeedTracing(aNode);
373 //----------------------------------------------------------------------
375 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
376 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList)
378 // If nsChildContentList is changed so that any additional fields are
379 // traversed by the cycle collector, then CAN_SKIP must be updated to
380 // check that the additional fields are null.
381 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsChildContentList)
383 // nsChildContentList only ever has a single child, its wrapper, so if
384 // the wrapper is black, the list can't be part of a garbage cycle.
385 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList)
386 return tmp->IsBlack();
387 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
389 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList)
390 return tmp->IsBlackAndDoesNotNeedTracing(tmp);
391 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
393 // CanSkipThis returns false to avoid problems with incomplete unlinking.
394 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList)
395 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
397 NS_INTERFACE_TABLE_HEAD(nsChildContentList)
398 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
399 NS_INTERFACE_TABLE(nsChildContentList, nsINodeList, nsIDOMNodeList)
400 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsChildContentList)
401 NS_INTERFACE_MAP_END
403 JSObject*
404 nsChildContentList::WrapObject(JSContext *cx)
406 return NodeListBinding::Wrap(cx, this);
409 NS_IMETHODIMP
410 nsChildContentList::GetLength(uint32_t* aLength)
412 *aLength = mNode ? mNode->GetChildCount() : 0;
414 return NS_OK;
417 NS_IMETHODIMP
418 nsChildContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
420 nsINode* node = Item(aIndex);
421 if (!node) {
422 *aReturn = nullptr;
424 return NS_OK;
427 return CallQueryInterface(node, aReturn);
430 nsIContent*
431 nsChildContentList::Item(uint32_t aIndex)
433 if (mNode) {
434 return mNode->GetChildAt(aIndex);
437 return nullptr;
440 int32_t
441 nsChildContentList::IndexOf(nsIContent* aContent)
443 if (mNode) {
444 return mNode->IndexOf(aContent);
447 return -1;
450 //----------------------------------------------------------------------
452 nsIHTMLCollection*
453 FragmentOrElement::Children()
455 FragmentOrElement::nsDOMSlots *slots = DOMSlots();
457 if (!slots->mChildrenList) {
458 slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard,
459 nsGkAtoms::_asterix, nsGkAtoms::_asterix,
460 false);
463 return slots->mChildrenList;
467 //----------------------------------------------------------------------
470 NS_IMPL_ISUPPORTS(nsNodeWeakReference,
471 nsIWeakReference)
473 nsNodeWeakReference::~nsNodeWeakReference()
475 if (mNode) {
476 NS_ASSERTION(mNode->Slots()->mWeakReference == this,
477 "Weak reference has wrong value");
478 mNode->Slots()->mWeakReference = nullptr;
482 NS_IMETHODIMP
483 nsNodeWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr)
485 return mNode ? mNode->QueryInterface(aIID, aInstancePtr) :
486 NS_ERROR_NULL_POINTER;
489 size_t
490 nsNodeWeakReference::SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const
492 return aMallocSizeOf(this);
496 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
498 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
499 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
500 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
502 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
503 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
505 NS_IMETHODIMP
506 nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr)
508 nsINode::nsSlots* slots = mNode->Slots();
509 if (!slots->mWeakReference) {
510 slots->mWeakReference = new nsNodeWeakReference(mNode);
511 NS_ENSURE_TRUE(slots->mWeakReference, NS_ERROR_OUT_OF_MEMORY);
514 NS_ADDREF(*aInstancePtr = slots->mWeakReference);
516 return NS_OK;
519 //----------------------------------------------------------------------
520 FragmentOrElement::nsDOMSlots::nsDOMSlots()
521 : nsINode::nsSlots(),
522 mDataset(nullptr),
523 mUndoManager(nullptr),
524 mBindingParent(nullptr)
528 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
530 if (mAttributeMap) {
531 mAttributeMap->DropReference();
535 void
536 FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL)
538 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
539 cb.NoteXPCOMChild(mStyle.get());
541 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle");
542 cb.NoteXPCOMChild(mSMILOverrideStyle.get());
544 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
545 cb.NoteXPCOMChild(mAttributeMap.get());
547 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mUndoManager");
548 cb.NoteXPCOMChild(mUndoManager.get());
550 if (aIsXUL) {
551 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers");
552 cb.NoteXPCOMChild(mControllers);
555 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
556 cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
558 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
559 cb.NoteXPCOMChild(mXBLInsertionParent.get());
561 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
562 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
564 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
565 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
567 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
568 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
570 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
571 cb.NoteXPCOMChild(mClassList.get());
573 if (mCustomElementData) {
574 for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
575 mCustomElementData->mCallbackQueue[i]->Traverse(cb);
580 void
581 FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
583 mStyle = nullptr;
584 mSMILOverrideStyle = nullptr;
585 if (mAttributeMap) {
586 mAttributeMap->DropReference();
587 mAttributeMap = nullptr;
589 if (aIsXUL)
590 NS_IF_RELEASE(mControllers);
591 mXBLBinding = nullptr;
592 mXBLInsertionParent = nullptr;
593 mShadowRoot = nullptr;
594 mContainingShadow = nullptr;
595 mChildrenList = nullptr;
596 mUndoManager = nullptr;
597 mCustomElementData = nullptr;
598 mClassList = nullptr;
601 size_t
602 FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
604 size_t n = aMallocSizeOf(this);
606 if (mAttributeMap) {
607 n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
610 // Measurement of the following members may be added later if DMD finds it is
611 // worthwhile:
612 // - Superclass members (nsINode::nsSlots)
613 // - mStyle
614 // - mDataSet
615 // - mSMILOverrideStyle
616 // - mSMILOverrideStyleRule
617 // - mChildrenList
618 // - mClassList
620 // The following members are not measured:
621 // - mBindingParent / mControllers: because they're non-owning
622 return n;
625 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
626 : nsIContent(aNodeInfo)
630 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
631 : nsIContent(aNodeInfo)
635 FragmentOrElement::~FragmentOrElement()
637 NS_PRECONDITION(!IsInDoc(),
638 "Please remove this from the document properly");
639 if (GetParent()) {
640 NS_RELEASE(mParent);
644 already_AddRefed<nsINodeList>
645 FragmentOrElement::GetChildren(uint32_t aFilter)
647 nsRefPtr<nsSimpleContentList> list = new nsSimpleContentList(this);
648 AllChildrenIterator iter(this, aFilter);
649 while (nsIContent* kid = iter.GetNextChild()) {
650 list->AppendElement(kid);
653 return list.forget();
656 static nsIContent*
657 FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
659 if (aContent->ChromeOnlyAccess()) {
660 bool chromeAccessOnly = false;
661 while (aContent && !chromeAccessOnly) {
662 chromeAccessOnly = aContent->IsRootOfChromeAccessOnlySubtree();
663 aContent = aContent->GetParent();
666 return aContent;
669 nsresult
670 nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor)
672 //FIXME! Document how this event retargeting works, Bug 329124.
673 aVisitor.mCanHandle = true;
674 aVisitor.mMayHaveListenerManager = HasListenerManager();
676 // Don't propagate mouseover and mouseout events when mouse is moving
677 // inside chrome access only content.
678 bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
679 if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
680 aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH ||
681 aVisitor.mEvent->message == NS_POINTER_OVER ||
682 aVisitor.mEvent->message == NS_POINTER_OUT) &&
683 // Check if we should stop event propagation when event has just been
684 // dispatched or when we're about to propagate from
685 // chrome access only subtree or if we are about to propagate out of
686 // a shadow root to a shadow root host.
687 ((this == aVisitor.mEvent->originalTarget &&
688 !ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
689 nsCOMPtr<nsIContent> relatedTarget =
690 do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget);
691 if (relatedTarget &&
692 relatedTarget->OwnerDoc() == OwnerDoc()) {
694 // In the web components case, we may need to stop propagation of events
695 // at shadow root host.
696 if (GetShadowRoot()) {
697 nsIContent* adjustedTarget =
698 Event::GetShadowRelatedTarget(this, relatedTarget);
699 if (this == adjustedTarget) {
700 aVisitor.mParentTarget = nullptr;
701 aVisitor.mCanHandle = false;
702 return NS_OK;
706 // If current target is anonymous for events or we know that related
707 // target is descendant of an element which is anonymous for events,
708 // we may want to stop event propagation.
709 // If this is the original target, aVisitor.mRelatedTargetIsInAnon
710 // must be updated.
711 if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
712 (aVisitor.mEvent->originalTarget == this &&
713 (aVisitor.mRelatedTargetIsInAnon =
714 relatedTarget->ChromeOnlyAccess()))) {
715 nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
716 if (anonOwner) {
717 nsIContent* anonOwnerRelated =
718 FindChromeAccessOnlySubtreeOwner(relatedTarget);
719 if (anonOwnerRelated) {
720 // Note, anonOwnerRelated may still be inside some other
721 // native anonymous subtree. The case where anonOwner is still
722 // inside native anonymous subtree will be handled when event
723 // propagates up in the DOM tree.
724 while (anonOwner != anonOwnerRelated &&
725 anonOwnerRelated->ChromeOnlyAccess()) {
726 anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
728 if (anonOwner == anonOwnerRelated) {
729 #ifdef DEBUG_smaug
730 nsCOMPtr<nsIContent> originalTarget =
731 do_QueryInterface(aVisitor.mEvent->originalTarget);
732 nsAutoString ot, ct, rt;
733 if (originalTarget) {
734 originalTarget->Tag()->ToString(ot);
736 Tag()->ToString(ct);
737 relatedTarget->Tag()->ToString(rt);
738 printf("Stopping %s propagation:"
739 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
740 "\n\trelatedTarget=%s %s \n%s",
741 (aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH)
742 ? "mouseover" : "mouseout",
743 NS_ConvertUTF16toUTF8(ot).get(),
744 NS_ConvertUTF16toUTF8(ct).get(),
745 isAnonForEvents
746 ? "(is native anonymous)"
747 : (ChromeOnlyAccess()
748 ? "(is in native anonymous subtree)" : ""),
749 NS_ConvertUTF16toUTF8(rt).get(),
750 relatedTarget->ChromeOnlyAccess()
751 ? "(is in native anonymous subtree)" : "",
752 (originalTarget &&
753 relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
754 originalTarget->FindFirstNonChromeOnlyAccessContent())
755 ? "" : "Wrong event propagation!?!\n");
756 #endif
757 aVisitor.mParentTarget = nullptr;
758 // Event should not propagate to non-anon content.
759 aVisitor.mCanHandle = isAnonForEvents;
760 return NS_OK;
768 nsIContent* parent = GetParent();
770 // Web components have a special event chain that need to account
771 // for destination insertion points where nodes have been distributed.
772 nsTArray<nsIContent*>* destPoints = GetExistingDestInsertionPoints();
773 if (destPoints && !destPoints->IsEmpty()) {
774 // Push destination insertion points to aVisitor.mDestInsertionPoints
775 // excluding shadow insertion points.
776 bool didPushNonShadowInsertionPoint = false;
777 for (uint32_t i = 0; i < destPoints->Length(); i++) {
778 nsIContent* point = destPoints->ElementAt(i);
779 if (!ShadowRoot::IsShadowInsertionPoint(point)) {
780 aVisitor.mDestInsertionPoints.AppendElement(point);
781 didPushNonShadowInsertionPoint = true;
785 // Next node in the event path is the final destination
786 // (non-shadow) insertion point that was pushed.
787 if (didPushNonShadowInsertionPoint) {
788 parent = aVisitor.mDestInsertionPoints.LastElement();
789 aVisitor.mDestInsertionPoints.SetLength(
790 aVisitor.mDestInsertionPoints.Length() - 1);
794 ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this);
795 if (thisShadowRoot) {
796 // The following events must always be stopped at the root node of the node tree:
797 // abort
798 // error
799 // select
800 // change
801 // load
802 // reset
803 // resize
804 // scroll
805 // selectstart
806 bool stopEvent = false;
807 switch (aVisitor.mEvent->message) {
808 case NS_IMAGE_ABORT:
809 case NS_LOAD_ERROR:
810 case NS_FORM_SELECTED:
811 case NS_FORM_CHANGE:
812 case NS_LOAD:
813 case NS_FORM_RESET:
814 case NS_RESIZE_EVENT:
815 case NS_SCROLL_EVENT:
816 stopEvent = true;
817 break;
818 case NS_USER_DEFINED_EVENT:
819 if (aVisitor.mDOMEvent) {
820 nsAutoString eventType;
821 aVisitor.mDOMEvent->GetType(eventType);
822 if (eventType.EqualsLiteral("abort") ||
823 eventType.EqualsLiteral("error") ||
824 eventType.EqualsLiteral("select") ||
825 eventType.EqualsLiteral("change") ||
826 eventType.EqualsLiteral("load") ||
827 eventType.EqualsLiteral("reset") ||
828 eventType.EqualsLiteral("resize") ||
829 eventType.EqualsLiteral("scroll") ||
830 eventType.EqualsLiteral("selectstart")) {
831 stopEvent = true;
834 break;
837 if (stopEvent) {
838 // If we do stop propagation, we still want to propagate
839 // the event to chrome (nsPIDOMWindow::GetParentTarget()).
840 // The load event is special in that we don't ever propagate it
841 // to chrome.
842 nsCOMPtr<nsPIDOMWindow> win = OwnerDoc()->GetWindow();
843 EventTarget* parentTarget = win && aVisitor.mEvent->message != NS_LOAD
844 ? win->GetParentTarget() : nullptr;
846 aVisitor.mParentTarget = parentTarget;
847 return NS_OK;
850 if (!aVisitor.mDestInsertionPoints.IsEmpty()) {
851 parent = aVisitor.mDestInsertionPoints.LastElement();
852 aVisitor.mDestInsertionPoints.SetLength(
853 aVisitor.mDestInsertionPoints.Length() - 1);
854 } else {
855 // The pool host for the youngest shadow root is shadow DOM host,
856 // for older shadow roots, it is the shadow insertion point
857 // where the shadow root is projected, nullptr if none exists.
858 parent = thisShadowRoot->GetPoolHost();
862 // Event may need to be retargeted if this is the root of a native
863 // anonymous content subtree or event is dispatched somewhere inside XBL.
864 if (isAnonForEvents) {
865 #ifdef DEBUG
866 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
867 // all the events are allowed even in the native anonymous content..
868 nsCOMPtr<nsIContent> t = do_QueryInterface(aVisitor.mEvent->originalTarget);
869 NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
870 aVisitor.mEvent->mClass != eMutationEventClass ||
871 aVisitor.mDOMEvent,
872 "Mutation event dispatched in native anonymous content!?!");
873 #endif
874 aVisitor.mEventTargetAtParent = parent;
875 } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
876 nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->target));
877 if (content && content->GetBindingParent() == parent) {
878 aVisitor.mEventTargetAtParent = parent;
882 // check for an anonymous parent
883 // XXX XBL2/sXBL issue
884 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
885 nsIContent* insertionParent = GetXBLInsertionParent();
886 NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
887 aVisitor.mEventTargetAtParent != insertionParent),
888 "Retargeting and having insertion parent!");
889 if (insertionParent) {
890 parent = insertionParent;
894 if (parent) {
895 aVisitor.mParentTarget = parent;
896 } else {
897 aVisitor.mParentTarget = GetComposedDoc();
899 return NS_OK;
902 bool
903 nsIContent::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
904 nsAString& aResult) const
906 if (IsElement()) {
907 return AsElement()->GetAttr(aNameSpaceID, aName, aResult);
909 aResult.Truncate();
910 return false;
913 bool
914 nsIContent::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const
916 return IsElement() && AsElement()->HasAttr(aNameSpaceID, aName);
919 bool
920 nsIContent::AttrValueIs(int32_t aNameSpaceID,
921 nsIAtom* aName,
922 const nsAString& aValue,
923 nsCaseTreatment aCaseSensitive) const
925 return IsElement() &&
926 AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
929 bool
930 nsIContent::AttrValueIs(int32_t aNameSpaceID,
931 nsIAtom* aName,
932 nsIAtom* aValue,
933 nsCaseTreatment aCaseSensitive) const
935 return IsElement() &&
936 AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
939 bool
940 nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse)
942 bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
943 // Ensure that the return value and aTabIndex are consistent in the case
944 // we're in userfocusignored context.
945 if (focusable || (aTabIndex && *aTabIndex != -1)) {
946 if (nsContentUtils::IsUserFocusIgnored(this)) {
947 if (aTabIndex) {
948 *aTabIndex = -1;
950 return false;
952 return focusable;
954 return false;
957 bool
958 nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
960 if (aTabIndex) {
961 *aTabIndex = -1; // Default, not tabbable
963 return false;
966 NS_IMETHODIMP
967 FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
969 return NS_OK;
972 bool
973 FragmentOrElement::IsLink(nsIURI** aURI) const
975 *aURI = nullptr;
976 return false;
979 nsIContent*
980 FragmentOrElement::GetBindingParent() const
982 nsDOMSlots *slots = GetExistingDOMSlots();
984 if (slots) {
985 return slots->mBindingParent;
987 return nullptr;
990 nsXBLBinding*
991 FragmentOrElement::GetXBLBinding() const
993 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
994 nsDOMSlots *slots = GetExistingDOMSlots();
995 if (slots) {
996 return slots->mXBLBinding;
1000 return nullptr;
1003 void
1004 FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
1005 nsBindingManager* aOldBindingManager)
1007 nsBindingManager* bindingManager;
1008 if (aOldBindingManager) {
1009 MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
1010 "when removing a binding.");
1011 bindingManager = aOldBindingManager;
1012 } else {
1013 bindingManager = OwnerDoc()->BindingManager();
1016 // After this point, aBinding will be the most-derived binding for aContent.
1017 // If we already have a binding for aContent, make sure to
1018 // remove it from the attached stack. Otherwise we might end up firing its
1019 // constructor twice (if aBinding inherits from it) or firing its constructor
1020 // after aContent has been deleted (if aBinding is null and the content node
1021 // dies before we process mAttachedStack).
1022 nsRefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
1023 if (oldBinding) {
1024 bindingManager->RemoveFromAttachedQueue(oldBinding);
1027 if (aBinding) {
1028 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
1029 nsDOMSlots *slots = DOMSlots();
1030 slots->mXBLBinding = aBinding;
1031 bindingManager->AddBoundContent(this);
1032 } else {
1033 nsDOMSlots *slots = GetExistingDOMSlots();
1034 if (slots) {
1035 slots->mXBLBinding = nullptr;
1037 bindingManager->RemoveBoundContent(this);
1038 if (oldBinding) {
1039 oldBinding->SetBoundElement(nullptr);
1044 nsIContent*
1045 FragmentOrElement::GetXBLInsertionParent() const
1047 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1048 nsDOMSlots *slots = GetExistingDOMSlots();
1049 if (slots) {
1050 return slots->mXBLInsertionParent;
1054 return nullptr;
1057 ShadowRoot*
1058 FragmentOrElement::GetShadowRoot() const
1060 nsDOMSlots *slots = GetExistingDOMSlots();
1061 if (slots) {
1062 return slots->mShadowRoot;
1064 return nullptr;
1067 ShadowRoot*
1068 FragmentOrElement::GetContainingShadow() const
1070 nsDOMSlots *slots = GetExistingDOMSlots();
1071 if (slots) {
1072 return slots->mContainingShadow;
1074 return nullptr;
1077 void
1078 FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
1080 nsDOMSlots *slots = DOMSlots();
1081 slots->mShadowRoot = aShadowRoot;
1084 nsTArray<nsIContent*>&
1085 FragmentOrElement::DestInsertionPoints()
1087 nsDOMSlots *slots = DOMSlots();
1088 return slots->mDestInsertionPoints;
1091 nsTArray<nsIContent*>*
1092 FragmentOrElement::GetExistingDestInsertionPoints() const
1094 nsDOMSlots *slots = GetExistingDOMSlots();
1095 if (slots) {
1096 return &slots->mDestInsertionPoints;
1098 return nullptr;
1101 void
1102 FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
1104 if (aContent) {
1105 nsDOMSlots *slots = DOMSlots();
1106 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
1107 slots->mXBLInsertionParent = aContent;
1108 } else {
1109 nsDOMSlots *slots = GetExistingDOMSlots();
1110 if (slots) {
1111 slots->mXBLInsertionParent = nullptr;
1116 CustomElementData*
1117 FragmentOrElement::GetCustomElementData() const
1119 nsDOMSlots *slots = GetExistingDOMSlots();
1120 if (slots) {
1121 return slots->mCustomElementData;
1123 return nullptr;
1126 void
1127 FragmentOrElement::SetCustomElementData(CustomElementData* aData)
1129 nsDOMSlots *slots = DOMSlots();
1130 MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
1131 slots->mCustomElementData = aData;
1134 nsresult
1135 FragmentOrElement::InsertChildAt(nsIContent* aKid,
1136 uint32_t aIndex,
1137 bool aNotify)
1139 NS_PRECONDITION(aKid, "null ptr");
1141 return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
1144 void
1145 FragmentOrElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
1147 nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex);
1148 NS_ASSERTION(oldKid == GetChildAt(aIndex), "Unexpected child in RemoveChildAt");
1150 if (oldKid) {
1151 doRemoveChildAt(aIndex, aNotify, oldKid, mAttrsAndChildren);
1155 void
1156 FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
1157 ErrorResult& aError)
1159 if(!nsContentUtils::GetNodeTextContent(this, true, aTextContent)) {
1160 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
1164 void
1165 FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
1166 ErrorResult& aError)
1168 aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
1171 void
1172 FragmentOrElement::DestroyContent()
1174 nsIDocument *document = OwnerDoc();
1175 document->BindingManager()->RemovedFromDocument(this, document);
1176 document->ClearBoxObjectFor(this);
1178 // XXX We really should let cycle collection do this, but that currently still
1179 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
1180 ReleaseWrapper(this);
1182 uint32_t i, count = mAttrsAndChildren.ChildCount();
1183 for (i = 0; i < count; ++i) {
1184 // The child can remove itself from the parent in BindToTree.
1185 mAttrsAndChildren.ChildAt(i)->DestroyContent();
1189 void
1190 FragmentOrElement::SaveSubtreeState()
1192 uint32_t i, count = mAttrsAndChildren.ChildCount();
1193 for (i = 0; i < count; ++i) {
1194 mAttrsAndChildren.ChildAt(i)->SaveSubtreeState();
1198 //----------------------------------------------------------------------
1200 // Generic DOMNode implementations
1202 void
1203 FragmentOrElement::FireNodeInserted(nsIDocument* aDoc,
1204 nsINode* aParent,
1205 nsTArray<nsCOMPtr<nsIContent> >& aNodes)
1207 uint32_t count = aNodes.Length();
1208 for (uint32_t i = 0; i < count; ++i) {
1209 nsIContent* childContent = aNodes[i];
1211 if (nsContentUtils::HasMutationListeners(childContent,
1212 NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
1213 InternalMutationEvent mutation(true, NS_MUTATION_NODEINSERTED);
1214 mutation.mRelatedNode = do_QueryInterface(aParent);
1216 mozAutoSubtreeModified subtree(aDoc, aParent);
1217 (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
1222 //----------------------------------------------------------------------
1224 // nsISupports implementation
1226 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1228 class ContentUnbinder : public nsRunnable
1230 public:
1231 ContentUnbinder()
1233 mLast = this;
1236 ~ContentUnbinder()
1238 Run();
1241 void UnbindSubtree(nsIContent* aNode)
1243 if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
1244 aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
1245 return;
1247 FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
1248 uint32_t childCount = container->mAttrsAndChildren.ChildCount();
1249 if (childCount) {
1250 while (childCount-- > 0) {
1251 // Hold a strong ref to the node when we remove it, because we may be
1252 // the last reference to it. We need to call TakeChildAt() and
1253 // update mFirstChild before calling UnbindFromTree, since this last
1254 // can notify various observers and they should really see consistent
1255 // tree state.
1256 nsCOMPtr<nsIContent> child =
1257 container->mAttrsAndChildren.TakeChildAt(childCount);
1258 if (childCount == 0) {
1259 container->mFirstChild = nullptr;
1261 UnbindSubtree(child);
1262 child->UnbindFromTree();
1267 NS_IMETHOD Run()
1269 nsAutoScriptBlocker scriptBlocker;
1270 uint32_t len = mSubtreeRoots.Length();
1271 if (len) {
1272 PRTime start = PR_Now();
1273 for (uint32_t i = 0; i < len; ++i) {
1274 UnbindSubtree(mSubtreeRoots[i]);
1276 mSubtreeRoots.Clear();
1277 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
1278 uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC);
1280 nsCycleCollector_dispatchDeferredDeletion();
1281 if (this == sContentUnbinder) {
1282 sContentUnbinder = nullptr;
1283 if (mNext) {
1284 nsRefPtr<ContentUnbinder> next;
1285 next.swap(mNext);
1286 sContentUnbinder = next;
1287 next->mLast = mLast;
1288 mLast = nullptr;
1289 NS_DispatchToMainThread(next);
1292 return NS_OK;
1295 static void UnbindAll()
1297 nsRefPtr<ContentUnbinder> ub = sContentUnbinder;
1298 sContentUnbinder = nullptr;
1299 while (ub) {
1300 ub->Run();
1301 ub = ub->mNext;
1305 static void Append(nsIContent* aSubtreeRoot)
1307 if (!sContentUnbinder) {
1308 sContentUnbinder = new ContentUnbinder();
1309 nsCOMPtr<nsIRunnable> e = sContentUnbinder;
1310 NS_DispatchToMainThread(e);
1313 if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
1314 SUBTREE_UNBINDINGS_PER_RUNNABLE) {
1315 sContentUnbinder->mLast->mNext = new ContentUnbinder();
1316 sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
1318 sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
1321 private:
1322 nsAutoTArray<nsCOMPtr<nsIContent>,
1323 SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
1324 nsRefPtr<ContentUnbinder> mNext;
1325 ContentUnbinder* mLast;
1326 static ContentUnbinder* sContentUnbinder;
1329 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
1331 void
1332 FragmentOrElement::ClearContentUnbinder()
1334 ContentUnbinder::UnbindAll();
1337 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
1339 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
1340 nsINode::Unlink(tmp);
1342 // The XBL binding is removed by RemoveFromBindingManagerRunnable
1343 // which is dispatched in UnbindFromTree.
1345 if (tmp->HasProperties()) {
1346 if (tmp->IsHTML() || tmp->IsSVG()) {
1347 nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
1348 for (uint32_t i = 0; props[i]; ++i) {
1349 tmp->DeleteProperty(*props[i]);
1354 // Unlink child content (and unbind our subtree).
1355 if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
1356 uint32_t childCount = tmp->mAttrsAndChildren.ChildCount();
1357 if (childCount) {
1358 // Don't allow script to run while we're unbinding everything.
1359 nsAutoScriptBlocker scriptBlocker;
1360 while (childCount-- > 0) {
1361 // Hold a strong ref to the node when we remove it, because we may be
1362 // the last reference to it. We need to call TakeChildAt() and
1363 // update mFirstChild before calling UnbindFromTree, since this last
1364 // can notify various observers and they should really see consistent
1365 // tree state.
1366 nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
1367 if (childCount == 0) {
1368 tmp->mFirstChild = nullptr;
1370 child->UnbindFromTree();
1373 } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
1374 ContentUnbinder::Append(tmp);
1375 } /* else {
1376 The subtree root will end up to a ContentUnbinder, and that will
1377 unbind the child nodes.
1378 } */
1380 // Clear flag here because unlinking slots will clear the
1381 // containing shadow root pointer.
1382 tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
1384 // Unlink any DOM slots of interest.
1386 nsDOMSlots *slots = tmp->GetExistingDOMSlots();
1387 if (slots) {
1388 slots->Unlink(tmp->IsXUL());
1393 nsIDocument *doc;
1394 if (!tmp->GetParentNode() && (doc = tmp->OwnerDoc())) {
1395 doc->BindingManager()->RemovedFromDocument(tmp, doc);
1398 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1400 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
1402 void
1403 FragmentOrElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
1404 void* aData)
1406 uint32_t* gen = static_cast<uint32_t*>(aData);
1407 xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
1410 void
1411 FragmentOrElement::MarkNodeChildren(nsINode* aNode)
1413 JSObject* o = GetJSObjectChild(aNode);
1414 if (o) {
1415 JS::ExposeObjectToActiveJS(o);
1418 EventListenerManager* elm = aNode->GetExistingListenerManager();
1419 if (elm) {
1420 elm->MarkForCC();
1423 if (aNode->HasProperties()) {
1424 nsIDocument* ownerDoc = aNode->OwnerDoc();
1425 ownerDoc->PropertyTable(DOM_USER_DATA)->
1426 Enumerate(aNode, FragmentOrElement::MarkUserData,
1427 &nsCCUncollectableMarker::sGeneration);
1431 nsINode*
1432 FindOptimizableSubtreeRoot(nsINode* aNode)
1434 nsINode* p;
1435 while ((p = aNode->GetParentNode())) {
1436 if (aNode->UnoptimizableCCNode()) {
1437 return nullptr;
1439 aNode = p;
1442 if (aNode->UnoptimizableCCNode()) {
1443 return nullptr;
1445 return aNode;
1448 StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
1450 static PLDHashOperator
1451 VisitBlackMarkedNode(nsPtrHashKey<nsINode>* aEntry, void*)
1453 nsINode* n = aEntry->GetKey();
1454 n->SetCCMarkedRoot(false);
1455 n->SetInCCBlackTree(false);
1456 return PL_DHASH_NEXT;
1459 static void
1460 ClearBlackMarkedNodes()
1462 if (!gCCBlackMarkedNodes) {
1463 return;
1465 gCCBlackMarkedNodes->EnumerateEntries(VisitBlackMarkedNode, nullptr);
1466 gCCBlackMarkedNodes = nullptr;
1469 // static
1470 void
1471 FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
1473 if (!gCCBlackMarkedNodes) {
1474 return;
1476 gCCBlackMarkedNodes->RemoveEntry(aNode);
1479 // static
1480 bool
1481 FragmentOrElement::CanSkipInCC(nsINode* aNode)
1483 // Don't try to optimize anything during shutdown.
1484 if (nsCCUncollectableMarker::sGeneration == 0) {
1485 return false;
1488 //XXXsmaug Need to figure out in which cases Shadow DOM can be optimized out
1489 // from the CC graph.
1490 nsIDocument* currentDoc = aNode->GetUncomposedDoc();
1491 if (currentDoc &&
1492 nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
1493 return !NeedsScriptTraverse(aNode);
1496 // Bail out early if aNode is somewhere in anonymous content,
1497 // or otherwise unusual.
1498 if (aNode->UnoptimizableCCNode()) {
1499 return false;
1502 nsINode* root =
1503 currentDoc ? static_cast<nsINode*>(currentDoc) :
1504 FindOptimizableSubtreeRoot(aNode);
1505 if (!root) {
1506 return false;
1509 // Subtree has been traversed already.
1510 if (root->CCMarkedRoot()) {
1511 return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
1514 if (!gCCBlackMarkedNodes) {
1515 gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
1518 // nodesToUnpurple contains nodes which will be removed
1519 // from the purple buffer if the DOM tree is black.
1520 nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
1521 // grayNodes need script traverse, so they aren't removed from
1522 // the purple buffer, but are marked to be in black subtree so that
1523 // traverse is faster.
1524 nsAutoTArray<nsINode*, 1020> grayNodes;
1526 bool foundBlack = root->IsBlack();
1527 if (root != currentDoc) {
1528 currentDoc = nullptr;
1529 if (NeedsScriptTraverse(root)) {
1530 grayNodes.AppendElement(root);
1531 } else if (static_cast<nsIContent*>(root)->IsPurple()) {
1532 nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
1536 // Traverse the subtree and check if we could know without CC
1537 // that it is black.
1538 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1539 // than CC's generic traverse.
1540 for (nsIContent* node = root->GetFirstChild(); node;
1541 node = node->GetNextNode(root)) {
1542 foundBlack = foundBlack || node->IsBlack();
1543 if (foundBlack && currentDoc) {
1544 // If we can mark the whole document black, no need to optimize
1545 // so much, since when the next purple node in the document will be
1546 // handled, it is fast to check that currentDoc is in CCGeneration.
1547 break;
1549 if (NeedsScriptTraverse(node)) {
1550 // Gray nodes need real CC traverse.
1551 grayNodes.AppendElement(node);
1552 } else if (node->IsPurple()) {
1553 nodesToUnpurple.AppendElement(node);
1557 root->SetCCMarkedRoot(true);
1558 root->SetInCCBlackTree(foundBlack);
1559 gCCBlackMarkedNodes->PutEntry(root);
1561 if (!foundBlack) {
1562 return false;
1565 if (currentDoc) {
1566 // Special case documents. If we know the document is black,
1567 // we can mark the document to be in CCGeneration.
1568 currentDoc->
1569 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1570 } else {
1571 for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
1572 nsINode* node = grayNodes[i];
1573 node->SetInCCBlackTree(true);
1574 gCCBlackMarkedNodes->PutEntry(node);
1578 // Subtree is black, we can remove non-gray purple nodes from
1579 // purple buffer.
1580 for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
1581 nsIContent* purple = nodesToUnpurple[i];
1582 // Can't remove currently handled purple node.
1583 if (purple != aNode) {
1584 purple->RemovePurple();
1587 return !NeedsScriptTraverse(aNode);
1590 nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
1591 nsAutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
1593 void ClearCycleCollectorCleanupData()
1595 if (gPurpleRoots) {
1596 uint32_t len = gPurpleRoots->Length();
1597 for (uint32_t i = 0; i < len; ++i) {
1598 nsINode* n = gPurpleRoots->ElementAt(i);
1599 n->SetIsPurpleRoot(false);
1601 delete gPurpleRoots;
1602 gPurpleRoots = nullptr;
1604 if (gNodesToUnbind) {
1605 uint32_t len = gNodesToUnbind->Length();
1606 for (uint32_t i = 0; i < len; ++i) {
1607 nsIContent* c = gNodesToUnbind->ElementAt(i);
1608 c->SetIsPurpleRoot(false);
1609 ContentUnbinder::Append(c);
1611 delete gNodesToUnbind;
1612 gNodesToUnbind = nullptr;
1616 static bool
1617 ShouldClearPurple(nsIContent* aContent)
1619 if (aContent && aContent->IsPurple()) {
1620 return true;
1623 JSObject* o = GetJSObjectChild(aContent);
1624 if (o && JS::ObjectIsMarkedGray(o)) {
1625 return true;
1628 if (aContent->HasListenerManager()) {
1629 return true;
1632 return aContent->HasProperties();
1635 // If aNode is not optimizable, but is an element
1636 // with a frame in a document which has currently active presshell,
1637 // we can act as if it was optimizable. When the primary frame dies, aNode
1638 // will end up to the purple buffer because of the refcount change.
1639 bool
1640 NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
1642 return aCurrentDoc->GetShell() && aNode->IsElement() &&
1643 aNode->AsElement()->GetPrimaryFrame();
1646 bool
1647 OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
1649 return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
1652 // CanSkip checks if aNode is black, and if it is, returns
1653 // true. If aNode is in a black DOM tree, CanSkip may also remove other objects
1654 // from purple buffer and unmark event listeners and user data.
1655 // If the root of the DOM tree is a document, less optimizations are done
1656 // since checking the blackness of the current document is usually fast and we
1657 // don't want slow down such common cases.
1658 bool
1659 FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
1661 // Don't try to optimize anything during shutdown.
1662 if (nsCCUncollectableMarker::sGeneration == 0) {
1663 return false;
1666 bool unoptimizable = aNode->UnoptimizableCCNode();
1667 nsIDocument* currentDoc = aNode->GetUncomposedDoc();
1668 if (currentDoc &&
1669 nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration()) &&
1670 (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) ||
1671 OwnedByBindingManager(currentDoc, aNode))) {
1672 MarkNodeChildren(aNode);
1673 return true;
1676 if (unoptimizable) {
1677 return false;
1680 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
1681 FindOptimizableSubtreeRoot(aNode);
1682 if (!root) {
1683 return false;
1686 // Subtree has been traversed already, and aNode has
1687 // been handled in a way that doesn't require revisiting it.
1688 if (root->IsPurpleRoot()) {
1689 return false;
1692 // nodesToClear contains nodes which are either purple or
1693 // gray.
1694 nsAutoTArray<nsIContent*, 1020> nodesToClear;
1696 bool foundBlack = root->IsBlack();
1697 bool domOnlyCycle = false;
1698 if (root != currentDoc) {
1699 currentDoc = nullptr;
1700 if (!foundBlack) {
1701 domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
1703 if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
1704 nodesToClear.AppendElement(static_cast<nsIContent*>(root));
1708 // Traverse the subtree and check if we could know without CC
1709 // that it is black.
1710 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1711 // than CC's generic traverse.
1712 for (nsIContent* node = root->GetFirstChild(); node;
1713 node = node->GetNextNode(root)) {
1714 foundBlack = foundBlack || node->IsBlack();
1715 if (foundBlack) {
1716 domOnlyCycle = false;
1717 if (currentDoc) {
1718 // If we can mark the whole document black, no need to optimize
1719 // so much, since when the next purple node in the document will be
1720 // handled, it is fast to check that the currentDoc is in CCGeneration.
1721 break;
1723 // No need to put stuff to the nodesToClear array, if we can clear it
1724 // already here.
1725 if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
1726 node->RemovePurple();
1728 MarkNodeChildren(node);
1729 } else {
1730 domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
1731 if (ShouldClearPurple(node)) {
1732 // Collect interesting nodes which we can clear if we find that
1733 // they are kept alive in a black tree or are in a DOM-only cycle.
1734 nodesToClear.AppendElement(node);
1739 if (!currentDoc || !foundBlack) {
1740 root->SetIsPurpleRoot(true);
1741 if (domOnlyCycle) {
1742 if (!gNodesToUnbind) {
1743 gNodesToUnbind = new nsAutoTArray<nsIContent*, 1020>();
1745 gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
1746 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1747 nsIContent* n = nodesToClear[i];
1748 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1749 n->RemovePurple();
1752 return true;
1753 } else {
1754 if (!gPurpleRoots) {
1755 gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
1757 gPurpleRoots->AppendElement(root);
1761 if (!foundBlack) {
1762 return false;
1765 if (currentDoc) {
1766 // Special case documents. If we know the document is black,
1767 // we can mark the document to be in CCGeneration.
1768 currentDoc->
1769 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1770 MarkNodeChildren(currentDoc);
1773 // Subtree is black, so we can remove purple nodes from
1774 // purple buffer and mark stuff that to be certainly alive.
1775 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1776 nsIContent* n = nodesToClear[i];
1777 MarkNodeChildren(n);
1778 // Can't remove currently handled purple node,
1779 // unless aRemovingAllowed is true.
1780 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1781 n->RemovePurple();
1784 return true;
1787 bool
1788 FragmentOrElement::CanSkipThis(nsINode* aNode)
1790 if (nsCCUncollectableMarker::sGeneration == 0) {
1791 return false;
1793 if (aNode->IsBlack()) {
1794 return true;
1796 nsIDocument* c = aNode->GetUncomposedDoc();
1797 return
1798 ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) ||
1799 aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode);
1802 void
1803 FragmentOrElement::InitCCCallbacks()
1805 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
1806 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
1809 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
1810 return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
1811 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1813 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
1814 return FragmentOrElement::CanSkipInCC(tmp);
1815 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1817 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
1818 return FragmentOrElement::CanSkipThis(tmp);
1819 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1821 static const char* kNSURIs[] = {
1822 " ([none])",
1823 " (xmlns)",
1824 " (xml)",
1825 " (xhtml)",
1826 " (XLink)",
1827 " (XSLT)",
1828 " (XBL)",
1829 " (MathML)",
1830 " (RDF)",
1831 " (XUL)",
1832 " (SVG)",
1833 " (XML Events)"
1836 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
1837 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1838 char name[512];
1839 uint32_t nsid = tmp->GetNameSpaceID();
1840 nsAtomCString localName(tmp->NodeInfo()->NameAtom());
1841 nsAutoCString uri;
1842 if (tmp->OwnerDoc()->GetDocumentURI()) {
1843 tmp->OwnerDoc()->GetDocumentURI()->GetSpec(uri);
1846 nsAutoString id;
1847 nsIAtom* idAtom = tmp->GetID();
1848 if (idAtom) {
1849 id.AppendLiteral(" id='");
1850 id.Append(nsDependentAtomString(idAtom));
1851 id.Append('\'');
1854 nsAutoString classes;
1855 const nsAttrValue* classAttrValue = tmp->GetClasses();
1856 if (classAttrValue) {
1857 classes.AppendLiteral(" class='");
1858 nsAutoString classString;
1859 classAttrValue->ToString(classString);
1860 classString.ReplaceChar(char16_t('\n'), char16_t(' '));
1861 classes.Append(classString);
1862 classes.Append('\'');
1865 nsAutoCString orphan;
1866 if (!tmp->IsInDoc() &&
1867 // Ignore xbl:content, which is never in the document and hence always
1868 // appears to be orphaned.
1869 !tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) {
1870 orphan.AppendLiteral(" (orphan)");
1873 const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
1874 PR_snprintf(name, sizeof(name), "FragmentOrElement%s %s%s%s%s %s",
1875 nsuri,
1876 localName.get(),
1877 NS_ConvertUTF16toUTF8(id).get(),
1878 NS_ConvertUTF16toUTF8(classes).get(),
1879 orphan.get(),
1880 uri.get());
1881 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1883 else {
1884 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
1887 // Always need to traverse script objects, so do that before we check
1888 // if we're uncollectable.
1889 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1891 if (!nsINode::Traverse(tmp, cb)) {
1892 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1895 tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
1897 if (tmp->HasProperties()) {
1898 if (tmp->IsHTML() || tmp->IsSVG()) {
1899 nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
1900 for (uint32_t i = 0; props[i]; ++i) {
1901 nsISupports* property =
1902 static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
1903 cb.NoteXPCOMChild(property);
1908 // Traverse attribute names and child content.
1910 uint32_t i;
1911 uint32_t attrs = tmp->mAttrsAndChildren.AttrCount();
1912 for (i = 0; i < attrs; i++) {
1913 const nsAttrName* name = tmp->mAttrsAndChildren.AttrNameAt(i);
1914 if (!name->IsAtom()) {
1915 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1916 "mAttrsAndChildren[i]->NodeInfo()");
1917 cb.NoteNativeChild(name->NodeInfo(),
1918 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1922 uint32_t kids = tmp->mAttrsAndChildren.ChildCount();
1923 for (i = 0; i < kids; i++) {
1924 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
1925 cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
1929 // Traverse any DOM slots of interest.
1931 nsDOMSlots *slots = tmp->GetExistingDOMSlots();
1932 if (slots) {
1933 slots->Traverse(cb, tmp->IsXUL());
1936 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1939 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
1940 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1941 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
1942 NS_INTERFACE_MAP_ENTRY(Element)
1943 NS_INTERFACE_MAP_ENTRY(nsIContent)
1944 NS_INTERFACE_MAP_ENTRY(nsINode)
1945 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
1946 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1947 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
1948 new nsNodeSupportsWeakRefTearoff(this))
1949 // DOM bindings depend on the identity pointer being the
1950 // same as nsINode (which nsIContent inherits).
1951 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
1952 NS_INTERFACE_MAP_END
1954 NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
1955 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
1956 nsNodeUtils::LastRelease(this))
1958 //----------------------------------------------------------------------
1960 nsresult
1961 FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst)
1963 uint32_t i, count = mAttrsAndChildren.AttrCount();
1964 for (i = 0; i < count; ++i) {
1965 const nsAttrName* name = mAttrsAndChildren.AttrNameAt(i);
1966 const nsAttrValue* value = mAttrsAndChildren.AttrAt(i);
1967 nsAutoString valStr;
1968 value->ToString(valStr);
1969 nsresult rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
1970 name->GetPrefix(), valStr, false);
1971 NS_ENSURE_SUCCESS(rv, rv);
1974 return NS_OK;
1977 const nsTextFragment*
1978 FragmentOrElement::GetText()
1980 return nullptr;
1983 uint32_t
1984 FragmentOrElement::TextLength() const
1986 // We can remove this assertion if it turns out to be useful to be able
1987 // to depend on this returning 0
1988 NS_NOTREACHED("called FragmentOrElement::TextLength");
1990 return 0;
1993 nsresult
1994 FragmentOrElement::SetText(const char16_t* aBuffer, uint32_t aLength,
1995 bool aNotify)
1997 NS_ERROR("called FragmentOrElement::SetText");
1999 return NS_ERROR_FAILURE;
2002 nsresult
2003 FragmentOrElement::AppendText(const char16_t* aBuffer, uint32_t aLength,
2004 bool aNotify)
2006 NS_ERROR("called FragmentOrElement::AppendText");
2008 return NS_ERROR_FAILURE;
2011 bool
2012 FragmentOrElement::TextIsOnlyWhitespace()
2014 return false;
2017 bool
2018 FragmentOrElement::HasTextForTranslation()
2020 return false;
2023 void
2024 FragmentOrElement::AppendTextTo(nsAString& aResult)
2026 // We can remove this assertion if it turns out to be useful to be able
2027 // to depend on this appending nothing.
2028 NS_NOTREACHED("called FragmentOrElement::TextLength");
2031 bool
2032 FragmentOrElement::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&)
2034 // We can remove this assertion if it turns out to be useful to be able
2035 // to depend on this appending nothing.
2036 NS_NOTREACHED("called FragmentOrElement::TextLength");
2038 return false;
2041 uint32_t
2042 FragmentOrElement::GetChildCount() const
2044 return mAttrsAndChildren.ChildCount();
2047 nsIContent *
2048 FragmentOrElement::GetChildAt(uint32_t aIndex) const
2050 return mAttrsAndChildren.GetSafeChildAt(aIndex);
2053 nsIContent * const *
2054 FragmentOrElement::GetChildArray(uint32_t* aChildCount) const
2056 return mAttrsAndChildren.GetChildArray(aChildCount);
2059 int32_t
2060 FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
2062 return mAttrsAndChildren.IndexOfChild(aPossibleChild);
2065 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
2066 #define STRING_BUFFER_UNITS 1020
2068 namespace {
2070 // We put StringBuilder in the anonymous namespace to prevent anything outside
2071 // this file from accidentally being linked against it.
2073 class StringBuilder
2075 private:
2076 class Unit
2078 public:
2079 Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
2081 MOZ_COUNT_CTOR(StringBuilder::Unit);
2083 ~Unit()
2085 if (mType == eString || mType == eStringWithEncode) {
2086 delete mString;
2088 MOZ_COUNT_DTOR(StringBuilder::Unit);
2091 enum Type
2093 eUnknown,
2094 eAtom,
2095 eString,
2096 eStringWithEncode,
2097 eLiteral,
2098 eTextFragment,
2099 eTextFragmentWithEncode,
2102 union
2104 nsIAtom* mAtom;
2105 const char* mLiteral;
2106 nsAutoString* mString;
2107 const nsTextFragment* mTextFragment;
2109 Type mType;
2110 uint32_t mLength;
2112 public:
2113 StringBuilder() : mLast(this), mLength(0)
2115 MOZ_COUNT_CTOR(StringBuilder);
2118 ~StringBuilder()
2120 MOZ_COUNT_DTOR(StringBuilder);
2123 void Append(nsIAtom* aAtom)
2125 Unit* u = AddUnit();
2126 u->mAtom = aAtom;
2127 u->mType = Unit::eAtom;
2128 uint32_t len = aAtom->GetLength();
2129 u->mLength = len;
2130 mLength += len;
2133 template<int N>
2134 void Append(const char (&aLiteral)[N])
2136 Unit* u = AddUnit();
2137 u->mLiteral = aLiteral;
2138 u->mType = Unit::eLiteral;
2139 uint32_t len = N - 1;
2140 u->mLength = len;
2141 mLength += len;
2144 template<int N>
2145 void Append(char (&aLiteral)[N])
2147 Unit* u = AddUnit();
2148 u->mLiteral = aLiteral;
2149 u->mType = Unit::eLiteral;
2150 uint32_t len = N - 1;
2151 u->mLength = len;
2152 mLength += len;
2155 void Append(const nsAString& aString)
2157 Unit* u = AddUnit();
2158 u->mString = new nsAutoString(aString);
2159 u->mType = Unit::eString;
2160 uint32_t len = aString.Length();
2161 u->mLength = len;
2162 mLength += len;
2165 void Append(nsAutoString* aString)
2167 Unit* u = AddUnit();
2168 u->mString = aString;
2169 u->mType = Unit::eString;
2170 uint32_t len = aString->Length();
2171 u->mLength = len;
2172 mLength += len;
2175 void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
2177 Unit* u = AddUnit();
2178 u->mString = aString;
2179 u->mType = Unit::eStringWithEncode;
2180 u->mLength = aLen;
2181 mLength += aLen;
2184 void Append(const nsTextFragment* aTextFragment)
2186 Unit* u = AddUnit();
2187 u->mTextFragment = aTextFragment;
2188 u->mType = Unit::eTextFragment;
2189 uint32_t len = aTextFragment->GetLength();
2190 u->mLength = len;
2191 mLength += len;
2194 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
2196 Unit* u = AddUnit();
2197 u->mTextFragment = aTextFragment;
2198 u->mType = Unit::eTextFragmentWithEncode;
2199 u->mLength = aLen;
2200 mLength += aLen;
2203 bool ToString(nsAString& aOut)
2205 if (!aOut.SetCapacity(mLength, fallible_t())) {
2206 return false;
2209 for (StringBuilder* current = this; current; current = current->mNext) {
2210 uint32_t len = current->mUnits.Length();
2211 for (uint32_t i = 0; i < len; ++i) {
2212 Unit& u = current->mUnits[i];
2213 switch (u.mType) {
2214 case Unit::eAtom:
2215 aOut.Append(nsDependentAtomString(u.mAtom));
2216 break;
2217 case Unit::eString:
2218 aOut.Append(*(u.mString));
2219 break;
2220 case Unit::eStringWithEncode:
2221 EncodeAttrString(*(u.mString), aOut);
2222 break;
2223 case Unit::eLiteral:
2224 aOut.AppendASCII(u.mLiteral, u.mLength);
2225 break;
2226 case Unit::eTextFragment:
2227 u.mTextFragment->AppendTo(aOut);
2228 break;
2229 case Unit::eTextFragmentWithEncode:
2230 EncodeTextFragment(u.mTextFragment, aOut);
2231 break;
2232 default:
2233 MOZ_CRASH("Unknown unit type?");
2237 return true;
2239 private:
2240 Unit* AddUnit()
2242 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
2243 new StringBuilder(this);
2245 return mLast->mUnits.AppendElement();
2248 explicit StringBuilder(StringBuilder* aFirst)
2249 : mLast(nullptr), mLength(0)
2251 MOZ_COUNT_CTOR(StringBuilder);
2252 aFirst->mLast->mNext = this;
2253 aFirst->mLast = this;
2256 void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
2258 const char16_t* c = aValue.BeginReading();
2259 const char16_t* end = aValue.EndReading();
2260 while (c < end) {
2261 switch (*c) {
2262 case '"':
2263 aOut.AppendLiteral("&quot;");
2264 break;
2265 case '&':
2266 aOut.AppendLiteral("&amp;");
2267 break;
2268 case 0x00A0:
2269 aOut.AppendLiteral("&nbsp;");
2270 break;
2271 default:
2272 aOut.Append(*c);
2273 break;
2275 ++c;
2279 void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
2281 uint32_t len = aValue->GetLength();
2282 if (aValue->Is2b()) {
2283 const char16_t* data = aValue->Get2b();
2284 for (uint32_t i = 0; i < len; ++i) {
2285 const char16_t c = data[i];
2286 switch (c) {
2287 case '<':
2288 aOut.AppendLiteral("&lt;");
2289 break;
2290 case '>':
2291 aOut.AppendLiteral("&gt;");
2292 break;
2293 case '&':
2294 aOut.AppendLiteral("&amp;");
2295 break;
2296 case 0x00A0:
2297 aOut.AppendLiteral("&nbsp;");
2298 break;
2299 default:
2300 aOut.Append(c);
2301 break;
2304 } else {
2305 const char* data = aValue->Get1b();
2306 for (uint32_t i = 0; i < len; ++i) {
2307 const unsigned char c = data[i];
2308 switch (c) {
2309 case '<':
2310 aOut.AppendLiteral("&lt;");
2311 break;
2312 case '>':
2313 aOut.AppendLiteral("&gt;");
2314 break;
2315 case '&':
2316 aOut.AppendLiteral("&amp;");
2317 break;
2318 case 0x00A0:
2319 aOut.AppendLiteral("&nbsp;");
2320 break;
2321 default:
2322 aOut.Append(c);
2323 break;
2329 nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
2330 nsAutoPtr<StringBuilder> mNext;
2331 StringBuilder* mLast;
2332 // mLength is used only in the first StringBuilder object in the linked list.
2333 uint32_t mLength;
2336 } // anonymous namespace
2338 static void
2339 AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
2341 uint32_t extraSpaceNeeded = 0;
2342 uint32_t len = aText->GetLength();
2343 if (aText->Is2b()) {
2344 const char16_t* data = aText->Get2b();
2345 for (uint32_t i = 0; i < len; ++i) {
2346 const char16_t c = data[i];
2347 switch (c) {
2348 case '<':
2349 extraSpaceNeeded += ArrayLength("&lt;") - 2;
2350 break;
2351 case '>':
2352 extraSpaceNeeded += ArrayLength("&gt;") - 2;
2353 break;
2354 case '&':
2355 extraSpaceNeeded += ArrayLength("&amp;") - 2;
2356 break;
2357 case 0x00A0:
2358 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
2359 break;
2360 default:
2361 break;
2364 } else {
2365 const char* data = aText->Get1b();
2366 for (uint32_t i = 0; i < len; ++i) {
2367 const unsigned char c = data[i];
2368 switch (c) {
2369 case '<':
2370 extraSpaceNeeded += ArrayLength("&lt;") - 2;
2371 break;
2372 case '>':
2373 extraSpaceNeeded += ArrayLength("&gt;") - 2;
2374 break;
2375 case '&':
2376 extraSpaceNeeded += ArrayLength("&amp;") - 2;
2377 break;
2378 case 0x00A0:
2379 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
2380 break;
2381 default:
2382 break;
2387 if (extraSpaceNeeded) {
2388 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
2389 } else {
2390 aBuilder.Append(aText);
2394 static void
2395 AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
2397 const char16_t* c = aValue->BeginReading();
2398 const char16_t* end = aValue->EndReading();
2400 uint32_t extraSpaceNeeded = 0;
2401 while (c < end) {
2402 switch (*c) {
2403 case '"':
2404 extraSpaceNeeded += ArrayLength("&quot;") - 2;
2405 break;
2406 case '&':
2407 extraSpaceNeeded += ArrayLength("&amp;") - 2;
2408 break;
2409 case 0x00A0:
2410 extraSpaceNeeded += ArrayLength("&nbsp;") - 2;
2411 break;
2412 default:
2413 break;
2415 ++c;
2418 if (extraSpaceNeeded) {
2419 aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
2420 } else {
2421 aBuilder.Append(aValue);
2425 static void
2426 StartElement(Element* aContent, StringBuilder& aBuilder)
2428 nsIAtom* localName = aContent->Tag();
2429 int32_t tagNS = aContent->GetNameSpaceID();
2431 aBuilder.Append("<");
2432 if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
2433 aBuilder.Append(localName);
2434 } else {
2435 aBuilder.Append(aContent->NodeName());
2438 int32_t count = aContent->GetAttrCount();
2439 for (int32_t i = count; i > 0;) {
2440 --i;
2441 const nsAttrName* name = aContent->GetAttrNameAt(i);
2442 int32_t attNs = name->NamespaceID();
2443 nsIAtom* attName = name->LocalName();
2445 // Filter out any attribute starting with [-|_]moz
2446 nsDependentAtomString attrNameStr(attName);
2447 if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
2448 StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
2449 continue;
2452 nsAutoString* attValue = new nsAutoString();
2453 aContent->GetAttr(attNs, attName, *attValue);
2455 // Filter out special case of <br type="_moz*"> used by the editor.
2456 // Bug 16988. Yuck.
2457 if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
2458 attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
2459 StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
2460 delete attValue;
2461 continue;
2464 aBuilder.Append(" ");
2466 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
2467 (attNs == kNameSpaceID_XMLNS &&
2468 attName == nsGkAtoms::xmlns)) {
2469 // Nothing else required
2470 } else if (attNs == kNameSpaceID_XML) {
2471 aBuilder.Append("xml:");
2472 } else if (attNs == kNameSpaceID_XMLNS) {
2473 aBuilder.Append("xmlns:");
2474 } else if (attNs == kNameSpaceID_XLink) {
2475 aBuilder.Append("xlink:");
2476 } else {
2477 nsIAtom* prefix = name->GetPrefix();
2478 if (prefix) {
2479 aBuilder.Append(prefix);
2480 aBuilder.Append(":");
2484 aBuilder.Append(attName);
2485 aBuilder.Append("=\"");
2486 AppendEncodedAttributeValue(attValue, aBuilder);
2487 aBuilder.Append("\"");
2490 aBuilder.Append(">");
2493 // Per HTML spec we should append one \n if the first child of
2494 // pre/textarea/listing is a textnode and starts with a \n.
2495 // But because browsers haven't traditionally had that behavior,
2496 // we're not changing our behavior either - yet.
2497 if (aContent->IsHTML()) {
2498 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
2499 localName == nsGkAtoms::listing) {
2500 nsIContent* fc = aContent->GetFirstChild();
2501 if (fc &&
2502 (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
2503 fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
2504 const nsTextFragment* text = fc->GetText();
2505 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
2506 aBuilder.Append("\n");
2513 static inline bool
2514 ShouldEscape(nsIContent* aParent)
2516 if (!aParent || !aParent->IsHTML()) {
2517 return true;
2520 static const nsIAtom* nonEscapingElements[] = {
2521 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
2522 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
2523 nsGkAtoms::plaintext,
2524 // Per the current spec noscript should be escaped in case
2525 // scripts are disabled or if document doesn't have
2526 // browsing context. However the latter seems to be a spec bug
2527 // and Gecko hasn't traditionally done the former.
2528 nsGkAtoms::noscript
2530 static mozilla::BloomFilter<12, nsIAtom> sFilter;
2531 static bool sInitialized = false;
2532 if (!sInitialized) {
2533 sInitialized = true;
2534 for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
2535 sFilter.add(nonEscapingElements[i]);
2539 nsIAtom* tag = aParent->Tag();
2540 if (sFilter.mightContain(tag)) {
2541 for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
2542 if (tag == nonEscapingElements[i]) {
2543 return false;
2547 return true;
2550 static inline bool
2551 IsVoidTag(Element* aElement)
2553 if (!aElement->IsHTML()) {
2554 return false;
2557 static const nsIAtom* voidElements[] = {
2558 nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
2559 nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
2560 nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
2561 nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
2562 nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
2563 nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
2564 nsGkAtoms::wbr
2567 static mozilla::BloomFilter<12, nsIAtom> sFilter;
2568 static bool sInitialized = false;
2569 if (!sInitialized) {
2570 sInitialized = true;
2571 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2572 sFilter.add(voidElements[i]);
2576 nsIAtom* tag = aElement->Tag();
2577 if (sFilter.mightContain(tag)) {
2578 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2579 if (tag == voidElements[i]) {
2580 return true;
2584 return false;
2587 static bool
2588 Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
2590 nsINode* current = aDescendentsOnly ?
2591 nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
2593 if (!current) {
2594 return true;
2597 StringBuilder builder;
2598 nsIContent* next;
2599 while (true) {
2600 bool isVoid = false;
2601 switch (current->NodeType()) {
2602 case nsIDOMNode::ELEMENT_NODE: {
2603 Element* elem = current->AsElement();
2604 StartElement(elem, builder);
2605 isVoid = IsVoidTag(elem);
2606 if (!isVoid &&
2607 (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
2608 current = next;
2609 continue;
2611 break;
2614 case nsIDOMNode::TEXT_NODE:
2615 case nsIDOMNode::CDATA_SECTION_NODE: {
2616 const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
2617 nsIContent* parent = current->GetParent();
2618 if (ShouldEscape(parent)) {
2619 AppendEncodedCharacters(text, builder);
2620 } else {
2621 builder.Append(text);
2623 break;
2626 case nsIDOMNode::COMMENT_NODE: {
2627 builder.Append("<!--");
2628 builder.Append(static_cast<nsIContent*>(current)->GetText());
2629 builder.Append("-->");
2630 break;
2633 case nsIDOMNode::DOCUMENT_TYPE_NODE: {
2634 builder.Append("<!DOCTYPE ");
2635 builder.Append(current->NodeName());
2636 builder.Append(">");
2637 break;
2640 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
2641 builder.Append("<?");
2642 builder.Append(current->NodeName());
2643 builder.Append(" ");
2644 builder.Append(static_cast<nsIContent*>(current)->GetText());
2645 builder.Append(">");
2646 break;
2650 while (true) {
2651 if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
2652 builder.Append("</");
2653 nsIContent* elem = static_cast<nsIContent*>(current);
2654 if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
2655 builder.Append(elem->Tag());
2656 } else {
2657 builder.Append(current->NodeName());
2659 builder.Append(">");
2661 isVoid = false;
2663 if (current == aRoot) {
2664 return builder.ToString(aOut);
2667 if ((next = current->GetNextSibling())) {
2668 current = next;
2669 break;
2672 current = current->GetParentNode();
2674 // Handle template element. If the parent is a template's content,
2675 // then adjust the parent to be the template element.
2676 if (current != aRoot &&
2677 current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
2678 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
2679 nsIContent* fragHost = frag->GetHost();
2680 if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
2681 current = fragHost;
2685 if (aDescendentsOnly && current == aRoot) {
2686 return builder.ToString(aOut);
2692 void
2693 FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
2695 aMarkup.Truncate();
2697 nsIDocument* doc = OwnerDoc();
2698 if (IsInHTMLDocument()) {
2699 Serialize(this, !aIncludeSelf, aMarkup);
2700 return;
2703 nsAutoString contentType;
2704 doc->GetContentType(contentType);
2705 bool tryToCacheEncoder = !aIncludeSelf;
2707 nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
2708 if (!docEncoder) {
2709 docEncoder =
2710 do_CreateInstance(PromiseFlatCString(
2711 nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
2712 NS_ConvertUTF16toUTF8(contentType)
2713 ).get());
2715 if (!docEncoder) {
2716 // This could be some type for which we create a synthetic document. Try
2717 // again as XML
2718 contentType.AssignLiteral("application/xml");
2719 docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
2720 // Don't try to cache the encoder since it would point to a different
2721 // contentType once it has been reinitialized.
2722 tryToCacheEncoder = false;
2725 NS_ENSURE_TRUE_VOID(docEncoder);
2727 uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
2728 // Output DOM-standard newlines
2729 nsIDocumentEncoder::OutputLFLineBreak |
2730 // Don't do linebreaking that's not present in
2731 // the source
2732 nsIDocumentEncoder::OutputRaw |
2733 // Only check for mozdirty when necessary (bug 599983)
2734 nsIDocumentEncoder::OutputIgnoreMozDirty;
2736 if (IsEditable()) {
2737 nsCOMPtr<Element> elem = do_QueryInterface(this);
2738 nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
2739 if (editor && editor->OutputsMozDirty()) {
2740 flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
2744 DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
2745 MOZ_ASSERT(NS_SUCCEEDED(rv));
2747 if (aIncludeSelf) {
2748 docEncoder->SetNativeNode(this);
2749 } else {
2750 docEncoder->SetNativeContainerNode(this);
2752 rv = docEncoder->EncodeToString(aMarkup);
2753 MOZ_ASSERT(NS_SUCCEEDED(rv));
2754 if (tryToCacheEncoder) {
2755 doc->SetCachedEncoder(docEncoder.forget());
2759 static bool
2760 ContainsMarkup(const nsAString& aStr)
2762 // Note: we can't use FindCharInSet because null is one of the characters we
2763 // want to search for.
2764 const char16_t* start = aStr.BeginReading();
2765 const char16_t* end = aStr.EndReading();
2767 while (start != end) {
2768 char16_t c = *start;
2769 if (c == char16_t('<') ||
2770 c == char16_t('&') ||
2771 c == char16_t('\r') ||
2772 c == char16_t('\0')) {
2773 return true;
2775 ++start;
2778 return false;
2781 void
2782 FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
2784 FragmentOrElement* target = this;
2785 // Handle template case.
2786 if (nsNodeUtils::IsTemplateElement(target)) {
2787 DocumentFragment* frag =
2788 static_cast<HTMLTemplateElement*>(target)->Content();
2789 MOZ_ASSERT(frag);
2790 target = frag;
2793 // Fast-path for strings with no markup. Limit this to short strings, to
2794 // avoid ContainsMarkup taking too long. The choice for 100 is based on
2795 // gut feeling.
2797 // Don't do this for elements with a weird parser insertion mode, for
2798 // instance setting innerHTML = "" on a <html> element should add the
2799 // optional <head> and <body> elements.
2800 if (!target->HasWeirdParserInsertionMode() &&
2801 aInnerHTML.Length() < 100 && !ContainsMarkup(aInnerHTML)) {
2802 aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
2803 return;
2806 nsIDocument* doc = target->OwnerDoc();
2808 // Batch possible DOMSubtreeModified events.
2809 mozAutoSubtreeModified subtree(doc, nullptr);
2811 target->FireNodeRemovedForChildren();
2813 // Needed when innerHTML is used in combination with contenteditable
2814 mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
2816 // Remove childnodes.
2817 uint32_t childCount = target->GetChildCount();
2818 nsAutoMutationBatch mb(target, true, false);
2819 for (uint32_t i = 0; i < childCount; ++i) {
2820 target->RemoveChildAt(0, true);
2822 mb.RemovalDone();
2824 nsAutoScriptLoaderDisabler sld(doc);
2826 nsIAtom* contextLocalName = Tag();
2827 int32_t contextNameSpaceID = GetNameSpaceID();
2829 ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
2830 if (shadowRoot) {
2831 // Fix up the context to be the host of the ShadowRoot.
2832 contextLocalName = shadowRoot->GetHost()->Tag();
2833 contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
2836 if (doc->IsHTML()) {
2837 int32_t oldChildCount = target->GetChildCount();
2838 aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
2839 target,
2840 contextLocalName,
2841 contextNameSpaceID,
2842 doc->GetCompatibilityMode() ==
2843 eCompatibility_NavQuirks,
2844 true);
2845 mb.NodesAdded();
2846 // HTML5 parser has notified, but not fired mutation events.
2847 nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
2848 oldChildCount);
2849 } else {
2850 nsRefPtr<DocumentFragment> df =
2851 nsContentUtils::CreateContextualFragment(target, aInnerHTML, true, aError);
2852 if (!aError.Failed()) {
2853 // Suppress assertion about node removal mutation events that can't have
2854 // listeners anyway, because no one has had the chance to register mutation
2855 // listeners on the fragment that comes from the parser.
2856 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
2858 static_cast<nsINode*>(target)->AppendChild(*df, aError);
2859 mb.NodesAdded();
2864 nsINode::nsSlots*
2865 FragmentOrElement::CreateSlots()
2867 return new nsDOMSlots();
2870 void
2871 FragmentOrElement::FireNodeRemovedForChildren()
2873 nsIDocument* doc = OwnerDoc();
2874 // Optimize the common case
2875 if (!nsContentUtils::
2876 HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
2877 return;
2880 nsCOMPtr<nsIDocument> owningDoc = doc;
2882 nsCOMPtr<nsINode> child;
2883 for (child = GetFirstChild();
2884 child && child->GetParentNode() == this;
2885 child = child->GetNextSibling()) {
2886 nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
2890 size_t
2891 FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
2893 size_t n = 0;
2894 n += nsIContent::SizeOfExcludingThis(aMallocSizeOf);
2895 n += mAttrsAndChildren.SizeOfExcludingThis(aMallocSizeOf);
2897 nsDOMSlots* slots = GetExistingDOMSlots();
2898 if (slots) {
2899 n += slots->SizeOfIncludingThis(aMallocSizeOf);
2902 return n;
2905 void
2906 FragmentOrElement::SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope)
2908 if (aInStyleScope && IsElementInStyleScope()) {
2909 return;
2912 if (IsElement()) {
2913 SetIsElementInStyleScope(aInStyleScope);
2914 SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope);
2917 nsIContent* n = GetNextNode(this);
2918 while (n) {
2919 if (n->IsElementInStyleScope()) {
2920 n = n->GetNextNonChildNode(this);
2921 } else {
2922 if (n->IsElement()) {
2923 n->SetIsElementInStyleScope(aInStyleScope);
2924 n->AsElement()->SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope);
2926 n = n->GetNextNode(this);
2931 void
2932 FragmentOrElement::SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope)
2934 NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree "
2935 "on a non-Element is useless");
2936 ShadowRoot* shadowRoot = GetShadowRoot();
2937 while (shadowRoot) {
2938 shadowRoot->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope);
2939 shadowRoot = shadowRoot->GetOlderShadowRoot();