Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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.
11 */
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 "nsINodeInfo.h"
28 #include "nsIDocumentInlines.h"
29 #include "nsIDocumentEncoder.h"
30 #include "nsIDOMNodeList.h"
31 #include "nsIContentIterator.h"
32 #include "nsFocusManager.h"
33 #include "nsILinkHandler.h"
34 #include "nsIScriptGlobalObject.h"
35 #include "nsIURL.h"
36 #include "nsNetUtil.h"
37 #include "nsIFrame.h"
38 #include "nsIAnonymousContentCreator.h"
39 #include "nsIPresShell.h"
40 #include "nsPresContext.h"
41 #include "nsStyleConsts.h"
42 #include "nsString.h"
43 #include "nsUnicharUtils.h"
44 #include "nsIDOMEvent.h"
45 #include "nsDOMCID.h"
46 #include "nsIServiceManager.h"
47 #include "nsIDOMCSSStyleDeclaration.h"
48 #include "nsDOMCSSAttrDeclaration.h"
49 #include "nsNameSpaceManager.h"
50 #include "nsContentList.h"
51 #include "nsDOMTokenList.h"
52 #include "nsXBLPrototypeBinding.h"
53 #include "nsError.h"
54 #include "nsDOMString.h"
55 #include "nsIScriptSecurityManager.h"
56 #include "nsIDOMMutationEvent.h"
57 #include "mozilla/InternalMutationEvent.h"
58 #include "mozilla/MouseEvents.h"
59 #include "nsNodeUtils.h"
60 #include "nsDocument.h"
61 #include "nsAttrValueOrString.h"
62 #ifdef MOZ_XUL
63 #include "nsXULElement.h"
64 #endif /* MOZ_XUL */
65 #include "nsFrameManager.h"
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 "nsIDOMUserDataHandler.h"
92 #include "nsGenericHTMLElement.h"
93 #include "nsIEditor.h"
94 #include "nsIEditorIMESupport.h"
95 #include "nsContentCreatorFunctions.h"
96 #include "nsIControllers.h"
97 #include "nsView.h"
98 #include "nsViewManager.h"
99 #include "nsIScrollableFrame.h"
100 #include "ChildIterator.h"
101 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
102 #include "nsRuleProcessorData.h"
103 #include "nsTextNode.h"
104 #include "mozilla/dom/NodeListBinding.h"
105 #include "mozilla/dom/UndoManager.h"
107 #ifdef MOZ_XUL
108 #include "nsIXULDocument.h"
109 #endif /* MOZ_XUL */
111 #include "nsCCUncollectableMarker.h"
113 #include "mozAutoDocUpdate.h"
115 #include "prprf.h"
116 #include "nsDOMMutationObserver.h"
117 #include "nsWrapperCacheInlines.h"
118 #include "nsCycleCollector.h"
119 #include "xpcpublic.h"
120 #include "nsIScriptError.h"
121 #include "mozilla/Telemetry.h"
123 #include "mozilla/CORSMode.h"
125 #include "mozilla/dom/ShadowRoot.h"
126 #include "mozilla/dom/HTMLTemplateElement.h"
128 #include "nsStyledElement.h"
129 #include "nsIContentInlines.h"
131 using namespace mozilla;
132 using namespace mozilla::dom;
134 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
135 bool nsIContent::sTabFocusModelAppliesToXUL = false;
136 uint32_t nsMutationGuard::sMutationCount = 0;
138 nsIContent*
139 nsIContent::FindFirstNonChromeOnlyAccessContent() const
140 {
141 // This handles also nested native anonymous content.
142 for (const nsIContent *content = this; content;
143 content = content->GetBindingParent()) {
144 if (!content->ChromeOnlyAccess()) {
145 // Oops, this function signature allows casting const to
146 // non-const. (Then again, so does GetChildAt(0)->GetParent().)
147 return const_cast<nsIContent*>(content);
148 }
149 }
150 return nullptr;
151 }
153 nsIContent*
154 nsIContent::GetFlattenedTreeParent() const
155 {
156 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
157 nsIContent* parent = GetXBLInsertionParent();
158 if (parent) {
159 return parent;
160 }
161 }
163 return GetParent();
164 }
166 nsIContent::IMEState
167 nsIContent::GetDesiredIMEState()
168 {
169 if (!IsEditableInternal()) {
170 // Check for the special case where we're dealing with elements which don't
171 // have the editable flag set, but are readwrite (such as text controls).
172 if (!IsElement() ||
173 !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
174 return IMEState(IMEState::DISABLED);
175 }
176 }
177 // NOTE: The content for independent editors (e.g., input[type=text],
178 // textarea) must override this method, so, we don't need to worry about
179 // that here.
180 nsIContent *editableAncestor = GetEditingHost();
182 // This is in another editable content, use the result of it.
183 if (editableAncestor && editableAncestor != this) {
184 return editableAncestor->GetDesiredIMEState();
185 }
186 nsIDocument* doc = GetCurrentDoc();
187 if (!doc) {
188 return IMEState(IMEState::DISABLED);
189 }
190 nsIPresShell* ps = doc->GetShell();
191 if (!ps) {
192 return IMEState(IMEState::DISABLED);
193 }
194 nsPresContext* pc = ps->GetPresContext();
195 if (!pc) {
196 return IMEState(IMEState::DISABLED);
197 }
198 nsIEditor* editor = nsContentUtils::GetHTMLEditor(pc);
199 nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(editor);
200 if (!imeEditor) {
201 return IMEState(IMEState::DISABLED);
202 }
203 IMEState state;
204 imeEditor->GetPreferredIMEState(&state);
205 return state;
206 }
208 bool
209 nsIContent::HasIndependentSelection()
210 {
211 nsIFrame* frame = GetPrimaryFrame();
212 return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
213 }
215 dom::Element*
216 nsIContent::GetEditingHost()
217 {
218 // If this isn't editable, return nullptr.
219 NS_ENSURE_TRUE(IsEditableInternal(), nullptr);
221 nsIDocument* doc = GetCurrentDoc();
222 NS_ENSURE_TRUE(doc, nullptr);
223 // If this is in designMode, we should return <body>
224 if (doc->HasFlag(NODE_IS_EDITABLE)) {
225 return doc->GetBodyElement();
226 }
228 nsIContent* content = this;
229 for (dom::Element* parent = GetParentElement();
230 parent && parent->HasFlag(NODE_IS_EDITABLE);
231 parent = content->GetParentElement()) {
232 content = parent;
233 }
234 return content->AsElement();
235 }
237 nsresult
238 nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
239 nsAString& aNamespaceURI) const
240 {
241 if (aNamespacePrefix.EqualsLiteral("xml")) {
242 // Special-case for xml prefix
243 aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
244 return NS_OK;
245 }
247 if (aNamespacePrefix.EqualsLiteral("xmlns")) {
248 // Special-case for xmlns prefix
249 aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
250 return NS_OK;
251 }
253 nsCOMPtr<nsIAtom> name;
254 if (!aNamespacePrefix.IsEmpty()) {
255 name = do_GetAtom(aNamespacePrefix);
256 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
257 }
258 else {
259 name = nsGkAtoms::xmlns;
260 }
261 // Trace up the content parent chain looking for the namespace
262 // declaration that declares aNamespacePrefix.
263 const nsIContent* content = this;
264 do {
265 if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
266 return NS_OK;
267 } while ((content = content->GetParent()));
268 return NS_ERROR_FAILURE;
269 }
271 already_AddRefed<nsIURI>
272 nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
273 {
274 nsIDocument* doc = OwnerDoc();
275 // Start with document base
276 nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
278 // Collect array of xml:base attribute values up the parent chain. This
279 // is slightly slower for the case when there are xml:base attributes, but
280 // faster for the far more common case of there not being any such
281 // attributes.
282 // Also check for SVG elements which require special handling
283 nsAutoTArray<nsString, 5> baseAttrs;
284 nsString attr;
285 const nsIContent *elem = this;
286 do {
287 // First check for SVG specialness (why is this SVG specific?)
288 if (elem->IsSVG()) {
289 nsIContent* bindingParent = elem->GetBindingParent();
290 if (bindingParent) {
291 nsXBLBinding* binding = bindingParent->GetXBLBinding();
292 if (binding) {
293 // XXX sXBL/XBL2 issue
294 // If this is an anonymous XBL element use the binding
295 // document for the base URI.
296 // XXX Will fail with xml:base
297 base = binding->PrototypeBinding()->DocURI();
298 break;
299 }
300 }
301 }
303 nsIURI* explicitBaseURI = elem->GetExplicitBaseURI();
304 if (explicitBaseURI) {
305 base = explicitBaseURI;
306 break;
307 }
309 // Otherwise check for xml:base attribute
310 elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
311 if (!attr.IsEmpty()) {
312 baseAttrs.AppendElement(attr);
313 }
314 elem = elem->GetParent();
315 } while(elem);
317 // Now resolve against all xml:base attrs
318 for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) {
319 nsCOMPtr<nsIURI> newBase;
320 nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i],
321 doc->GetDocumentCharacterSet().get(), base);
322 // Do a security check, almost the same as nsDocument::SetBaseURL()
323 // Only need to do this on the final uri
324 if (NS_SUCCEEDED(rv) && i == 0) {
325 rv = nsContentUtils::GetSecurityManager()->
326 CheckLoadURIWithPrincipal(NodePrincipal(), newBase,
327 nsIScriptSecurityManager::STANDARD);
328 }
329 if (NS_SUCCEEDED(rv)) {
330 base.swap(newBase);
331 }
332 }
334 return base.forget();
335 }
337 //----------------------------------------------------------------------
339 static inline JSObject*
340 GetJSObjectChild(nsWrapperCache* aCache)
341 {
342 return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() : nullptr;
343 }
345 static bool
346 NeedsScriptTraverse(nsINode* aNode)
347 {
348 return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
349 !aNode->IsBlackAndDoesNotNeedTracing(aNode);
350 }
352 //----------------------------------------------------------------------
354 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
355 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList)
357 // If nsChildContentList is changed so that any additional fields are
358 // traversed by the cycle collector, then CAN_SKIP must be updated to
359 // check that the additional fields are null.
360 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsChildContentList)
362 // nsChildContentList only ever has a single child, its wrapper, so if
363 // the wrapper is black, the list can't be part of a garbage cycle.
364 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList)
365 return tmp->IsBlack();
366 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
368 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList)
369 return tmp->IsBlackAndDoesNotNeedTracing(tmp);
370 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
372 // CanSkipThis returns false to avoid problems with incomplete unlinking.
373 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList)
374 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
376 NS_INTERFACE_TABLE_HEAD(nsChildContentList)
377 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
378 NS_INTERFACE_TABLE(nsChildContentList, nsINodeList, nsIDOMNodeList)
379 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsChildContentList)
380 NS_INTERFACE_MAP_END
382 JSObject*
383 nsChildContentList::WrapObject(JSContext *cx)
384 {
385 return NodeListBinding::Wrap(cx, this);
386 }
388 NS_IMETHODIMP
389 nsChildContentList::GetLength(uint32_t* aLength)
390 {
391 *aLength = mNode ? mNode->GetChildCount() : 0;
393 return NS_OK;
394 }
396 NS_IMETHODIMP
397 nsChildContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
398 {
399 nsINode* node = Item(aIndex);
400 if (!node) {
401 *aReturn = nullptr;
403 return NS_OK;
404 }
406 return CallQueryInterface(node, aReturn);
407 }
409 nsIContent*
410 nsChildContentList::Item(uint32_t aIndex)
411 {
412 if (mNode) {
413 return mNode->GetChildAt(aIndex);
414 }
416 return nullptr;
417 }
419 int32_t
420 nsChildContentList::IndexOf(nsIContent* aContent)
421 {
422 if (mNode) {
423 return mNode->IndexOf(aContent);
424 }
426 return -1;
427 }
429 //----------------------------------------------------------------------
431 NS_IMPL_CYCLE_COLLECTION(nsNode3Tearoff, mNode)
433 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNode3Tearoff)
434 NS_INTERFACE_MAP_ENTRY(nsIDOMXPathNSResolver)
435 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
437 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNode3Tearoff)
438 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNode3Tearoff)
440 NS_IMETHODIMP
441 nsNode3Tearoff::LookupNamespaceURI(const nsAString& aNamespacePrefix,
442 nsAString& aNamespaceURI)
443 {
444 mNode->LookupNamespaceURI(aNamespacePrefix, aNamespaceURI);
445 return NS_OK;
446 }
448 nsIHTMLCollection*
449 FragmentOrElement::Children()
450 {
451 FragmentOrElement::nsDOMSlots *slots = DOMSlots();
453 if (!slots->mChildrenList) {
454 slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard,
455 nsGkAtoms::_asterix, nsGkAtoms::_asterix,
456 false);
457 }
459 return slots->mChildrenList;
460 }
463 //----------------------------------------------------------------------
466 NS_IMPL_ISUPPORTS(nsNodeWeakReference,
467 nsIWeakReference)
469 nsNodeWeakReference::~nsNodeWeakReference()
470 {
471 if (mNode) {
472 NS_ASSERTION(mNode->Slots()->mWeakReference == this,
473 "Weak reference has wrong value");
474 mNode->Slots()->mWeakReference = nullptr;
475 }
476 }
478 NS_IMETHODIMP
479 nsNodeWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr)
480 {
481 return mNode ? mNode->QueryInterface(aIID, aInstancePtr) :
482 NS_ERROR_NULL_POINTER;
483 }
486 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
488 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
489 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
490 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
492 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
493 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
495 NS_IMETHODIMP
496 nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr)
497 {
498 nsINode::nsSlots* slots = mNode->Slots();
499 if (!slots->mWeakReference) {
500 slots->mWeakReference = new nsNodeWeakReference(mNode);
501 NS_ENSURE_TRUE(slots->mWeakReference, NS_ERROR_OUT_OF_MEMORY);
502 }
504 NS_ADDREF(*aInstancePtr = slots->mWeakReference);
506 return NS_OK;
507 }
509 //----------------------------------------------------------------------
510 FragmentOrElement::nsDOMSlots::nsDOMSlots()
511 : nsINode::nsSlots(),
512 mDataset(nullptr),
513 mUndoManager(nullptr),
514 mBindingParent(nullptr)
515 {
516 }
518 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
519 {
520 if (mAttributeMap) {
521 mAttributeMap->DropReference();
522 }
523 }
525 void
526 FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL)
527 {
528 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
529 cb.NoteXPCOMChild(mStyle.get());
531 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle");
532 cb.NoteXPCOMChild(mSMILOverrideStyle.get());
534 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
535 cb.NoteXPCOMChild(mAttributeMap.get());
537 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mUndoManager");
538 cb.NoteXPCOMChild(mUndoManager.get());
540 if (aIsXUL) {
541 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers");
542 cb.NoteXPCOMChild(mControllers);
543 }
545 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
546 cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
548 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
549 cb.NoteXPCOMChild(mXBLInsertionParent.get());
551 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
552 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
554 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
555 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
557 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
558 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
560 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
561 cb.NoteXPCOMChild(mClassList.get());
563 if (mCustomElementData) {
564 for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
565 mCustomElementData->mCallbackQueue[i]->Traverse(cb);
566 }
567 }
568 }
570 void
571 FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
572 {
573 mStyle = nullptr;
574 mSMILOverrideStyle = nullptr;
575 if (mAttributeMap) {
576 mAttributeMap->DropReference();
577 mAttributeMap = nullptr;
578 }
579 if (aIsXUL)
580 NS_IF_RELEASE(mControllers);
581 mXBLBinding = nullptr;
582 mXBLInsertionParent = nullptr;
583 mShadowRoot = nullptr;
584 mContainingShadow = nullptr;
585 mChildrenList = nullptr;
586 mUndoManager = nullptr;
587 mCustomElementData = nullptr;
588 mClassList = nullptr;
589 }
591 size_t
592 FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
593 {
594 size_t n = aMallocSizeOf(this);
596 if (mAttributeMap) {
597 n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
598 }
600 // Measurement of the following members may be added later if DMD finds it is
601 // worthwhile:
602 // - Superclass members (nsINode::nsSlots)
603 // - mStyle
604 // - mDataSet
605 // - mSMILOverrideStyle
606 // - mSMILOverrideStyleRule
607 // - mChildrenList
608 // - mClassList
610 // The following members are not measured:
611 // - mBindingParent / mControllers: because they're non-owning
612 return n;
613 }
615 FragmentOrElement::FragmentOrElement(already_AddRefed<nsINodeInfo>& aNodeInfo)
616 : nsIContent(aNodeInfo)
617 {
618 }
620 FragmentOrElement::FragmentOrElement(already_AddRefed<nsINodeInfo>&& aNodeInfo)
621 : nsIContent(aNodeInfo)
622 {
623 }
625 FragmentOrElement::~FragmentOrElement()
626 {
627 NS_PRECONDITION(!IsInDoc(),
628 "Please remove this from the document properly");
629 if (GetParent()) {
630 NS_RELEASE(mParent);
631 }
632 }
634 already_AddRefed<nsINodeList>
635 FragmentOrElement::GetChildren(uint32_t aFilter)
636 {
637 nsRefPtr<nsSimpleContentList> list = new nsSimpleContentList(this);
638 if (!list) {
639 return nullptr;
640 }
642 nsIFrame *frame = GetPrimaryFrame();
644 // Append :before generated content.
645 if (frame) {
646 nsIFrame *beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
647 if (beforeFrame) {
648 list->AppendElement(beforeFrame->GetContent());
649 }
650 }
652 // If XBL is bound to this node then append XBL anonymous content including
653 // explict content altered by insertion point if we were requested for XBL
654 // anonymous content, otherwise append explicit content with respect to
655 // insertion point if any.
656 if (!(aFilter & eAllButXBL)) {
657 FlattenedChildIterator iter(this);
658 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
659 list->AppendElement(child);
660 }
661 } else {
662 ExplicitChildIterator iter(this);
663 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
664 list->AppendElement(child);
665 }
666 }
668 if (frame) {
669 // Append native anonymous content to the end.
670 nsIAnonymousContentCreator* creator = do_QueryFrame(frame);
671 if (creator) {
672 creator->AppendAnonymousContentTo(*list, aFilter);
673 }
675 // Append :after generated content.
676 nsIFrame *afterFrame = nsLayoutUtils::GetAfterFrame(frame);
677 if (afterFrame) {
678 list->AppendElement(afterFrame->GetContent());
679 }
680 }
682 return list.forget();
683 }
685 static nsIContent*
686 FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
687 {
688 if (aContent->ChromeOnlyAccess()) {
689 bool chromeAccessOnly = false;
690 while (aContent && !chromeAccessOnly) {
691 chromeAccessOnly = aContent->IsRootOfChromeAccessOnlySubtree();
692 aContent = aContent->GetParent();
693 }
694 }
695 return aContent;
696 }
698 nsresult
699 nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor)
700 {
701 //FIXME! Document how this event retargeting works, Bug 329124.
702 aVisitor.mCanHandle = true;
703 aVisitor.mMayHaveListenerManager = HasListenerManager();
705 // Don't propagate mouseover and mouseout events when mouse is moving
706 // inside chrome access only content.
707 bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
708 if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
709 aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH ||
710 aVisitor.mEvent->message == NS_POINTER_OVER ||
711 aVisitor.mEvent->message == NS_POINTER_OUT) &&
712 // Check if we should stop event propagation when event has just been
713 // dispatched or when we're about to propagate from
714 // chrome access only subtree.
715 ((this == aVisitor.mEvent->originalTarget &&
716 !ChromeOnlyAccess()) || isAnonForEvents)) {
717 nsCOMPtr<nsIContent> relatedTarget =
718 do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget);
719 if (relatedTarget &&
720 relatedTarget->OwnerDoc() == OwnerDoc()) {
722 // If current target is anonymous for events or we know that related
723 // target is descendant of an element which is anonymous for events,
724 // we may want to stop event propagation.
725 // If this is the original target, aVisitor.mRelatedTargetIsInAnon
726 // must be updated.
727 if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
728 (aVisitor.mEvent->originalTarget == this &&
729 (aVisitor.mRelatedTargetIsInAnon =
730 relatedTarget->ChromeOnlyAccess()))) {
731 nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
732 if (anonOwner) {
733 nsIContent* anonOwnerRelated =
734 FindChromeAccessOnlySubtreeOwner(relatedTarget);
735 if (anonOwnerRelated) {
736 // Note, anonOwnerRelated may still be inside some other
737 // native anonymous subtree. The case where anonOwner is still
738 // inside native anonymous subtree will be handled when event
739 // propagates up in the DOM tree.
740 while (anonOwner != anonOwnerRelated &&
741 anonOwnerRelated->ChromeOnlyAccess()) {
742 anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
743 }
744 if (anonOwner == anonOwnerRelated) {
745 #ifdef DEBUG_smaug
746 nsCOMPtr<nsIContent> originalTarget =
747 do_QueryInterface(aVisitor.mEvent->originalTarget);
748 nsAutoString ot, ct, rt;
749 if (originalTarget) {
750 originalTarget->Tag()->ToString(ot);
751 }
752 Tag()->ToString(ct);
753 relatedTarget->Tag()->ToString(rt);
754 printf("Stopping %s propagation:"
755 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
756 "\n\trelatedTarget=%s %s \n%s",
757 (aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH)
758 ? "mouseover" : "mouseout",
759 NS_ConvertUTF16toUTF8(ot).get(),
760 NS_ConvertUTF16toUTF8(ct).get(),
761 isAnonForEvents
762 ? "(is native anonymous)"
763 : (ChromeOnlyAccess()
764 ? "(is in native anonymous subtree)" : ""),
765 NS_ConvertUTF16toUTF8(rt).get(),
766 relatedTarget->ChromeOnlyAccess()
767 ? "(is in native anonymous subtree)" : "",
768 (originalTarget &&
769 relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
770 originalTarget->FindFirstNonChromeOnlyAccessContent())
771 ? "" : "Wrong event propagation!?!\n");
772 #endif
773 aVisitor.mParentTarget = nullptr;
774 // Event should not propagate to non-anon content.
775 aVisitor.mCanHandle = isAnonForEvents;
776 return NS_OK;
777 }
778 }
779 }
780 }
781 }
782 }
784 nsIContent* parent = GetParent();
785 // Event may need to be retargeted if this is the root of a native
786 // anonymous content subtree or event is dispatched somewhere inside XBL.
787 if (isAnonForEvents) {
788 #ifdef DEBUG
789 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
790 // all the events are allowed even in the native anonymous content..
791 nsCOMPtr<nsIContent> t = do_QueryInterface(aVisitor.mEvent->originalTarget);
792 NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
793 aVisitor.mEvent->eventStructType != NS_MUTATION_EVENT ||
794 aVisitor.mDOMEvent,
795 "Mutation event dispatched in native anonymous content!?!");
796 #endif
797 aVisitor.mEventTargetAtParent = parent;
798 } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
799 nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->target));
800 if (content && content->GetBindingParent() == parent) {
801 aVisitor.mEventTargetAtParent = parent;
802 }
803 }
805 // check for an anonymous parent
806 // XXX XBL2/sXBL issue
807 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
808 nsIContent* insertionParent = GetXBLInsertionParent();
809 NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
810 aVisitor.mEventTargetAtParent != insertionParent),
811 "Retargeting and having insertion parent!");
812 if (insertionParent) {
813 parent = insertionParent;
814 }
815 }
817 if (parent) {
818 aVisitor.mParentTarget = parent;
819 } else {
820 aVisitor.mParentTarget = GetCurrentDoc();
821 }
822 return NS_OK;
823 }
825 bool
826 nsIContent::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
827 nsAString& aResult) const
828 {
829 if (IsElement()) {
830 return AsElement()->GetAttr(aNameSpaceID, aName, aResult);
831 }
832 aResult.Truncate();
833 return false;
834 }
836 bool
837 nsIContent::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const
838 {
839 return IsElement() && AsElement()->HasAttr(aNameSpaceID, aName);
840 }
842 bool
843 nsIContent::AttrValueIs(int32_t aNameSpaceID,
844 nsIAtom* aName,
845 const nsAString& aValue,
846 nsCaseTreatment aCaseSensitive) const
847 {
848 return IsElement() &&
849 AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
850 }
852 bool
853 nsIContent::AttrValueIs(int32_t aNameSpaceID,
854 nsIAtom* aName,
855 nsIAtom* aValue,
856 nsCaseTreatment aCaseSensitive) const
857 {
858 return IsElement() &&
859 AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
860 }
862 bool
863 nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse)
864 {
865 bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
866 // Ensure that the return value and aTabIndex are consistent in the case
867 // we're in userfocusignored context.
868 if (focusable || (aTabIndex && *aTabIndex != -1)) {
869 if (nsContentUtils::IsUserFocusIgnored(this)) {
870 if (aTabIndex) {
871 *aTabIndex = -1;
872 }
873 return false;
874 }
875 return focusable;
876 }
877 return false;
878 }
880 bool
881 nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
882 {
883 if (aTabIndex) {
884 *aTabIndex = -1; // Default, not tabbable
885 }
886 return false;
887 }
889 const nsAttrValue*
890 FragmentOrElement::DoGetClasses() const
891 {
892 NS_NOTREACHED("Shouldn't ever be called");
893 return nullptr;
894 }
896 NS_IMETHODIMP
897 FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
898 {
899 return NS_OK;
900 }
902 bool
903 FragmentOrElement::IsLink(nsIURI** aURI) const
904 {
905 *aURI = nullptr;
906 return false;
907 }
909 nsIContent*
910 FragmentOrElement::GetBindingParent() const
911 {
912 nsDOMSlots *slots = GetExistingDOMSlots();
914 if (slots) {
915 return slots->mBindingParent;
916 }
917 return nullptr;
918 }
920 nsXBLBinding*
921 FragmentOrElement::GetXBLBinding() const
922 {
923 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
924 nsDOMSlots *slots = GetExistingDOMSlots();
925 if (slots) {
926 return slots->mXBLBinding;
927 }
928 }
930 return nullptr;
931 }
933 void
934 FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
935 nsBindingManager* aOldBindingManager)
936 {
937 nsBindingManager* bindingManager;
938 if (aOldBindingManager) {
939 MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
940 "when removing a binding.");
941 bindingManager = aOldBindingManager;
942 } else {
943 bindingManager = OwnerDoc()->BindingManager();
944 }
946 // After this point, aBinding will be the most-derived binding for aContent.
947 // If we already have a binding for aContent, make sure to
948 // remove it from the attached stack. Otherwise we might end up firing its
949 // constructor twice (if aBinding inherits from it) or firing its constructor
950 // after aContent has been deleted (if aBinding is null and the content node
951 // dies before we process mAttachedStack).
952 nsRefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
953 if (oldBinding) {
954 bindingManager->RemoveFromAttachedQueue(oldBinding);
955 }
957 nsDOMSlots *slots = DOMSlots();
958 if (aBinding) {
959 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
960 slots->mXBLBinding = aBinding;
961 bindingManager->AddBoundContent(this);
962 } else {
963 slots->mXBLBinding = nullptr;
964 bindingManager->RemoveBoundContent(this);
965 if (oldBinding) {
966 oldBinding->SetBoundElement(nullptr);
967 }
968 }
969 }
971 nsIContent*
972 FragmentOrElement::GetXBLInsertionParent() const
973 {
974 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
975 nsDOMSlots *slots = GetExistingDOMSlots();
976 if (slots) {
977 return slots->mXBLInsertionParent;
978 }
979 }
981 return nullptr;
982 }
984 ShadowRoot*
985 FragmentOrElement::GetShadowRoot() const
986 {
987 nsDOMSlots *slots = GetExistingDOMSlots();
988 if (slots) {
989 return slots->mShadowRoot;
990 }
991 return nullptr;
992 }
994 ShadowRoot*
995 FragmentOrElement::GetContainingShadow() const
996 {
997 nsDOMSlots *slots = GetExistingDOMSlots();
998 if (slots) {
999 return slots->mContainingShadow;
1000 }
1001 return nullptr;
1002 }
1004 void
1005 FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
1006 {
1007 nsDOMSlots *slots = DOMSlots();
1008 slots->mShadowRoot = aShadowRoot;
1009 }
1011 void
1012 FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
1013 {
1014 nsDOMSlots *slots = DOMSlots();
1015 if (aContent) {
1016 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
1017 }
1018 slots->mXBLInsertionParent = aContent;
1019 }
1021 CustomElementData*
1022 FragmentOrElement::GetCustomElementData() const
1023 {
1024 nsDOMSlots *slots = GetExistingDOMSlots();
1025 if (slots) {
1026 return slots->mCustomElementData;
1027 }
1028 return nullptr;
1029 }
1031 void
1032 FragmentOrElement::SetCustomElementData(CustomElementData* aData)
1033 {
1034 nsDOMSlots *slots = DOMSlots();
1035 MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
1036 slots->mCustomElementData = aData;
1037 }
1039 nsresult
1040 FragmentOrElement::InsertChildAt(nsIContent* aKid,
1041 uint32_t aIndex,
1042 bool aNotify)
1043 {
1044 NS_PRECONDITION(aKid, "null ptr");
1046 return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
1047 }
1049 void
1050 FragmentOrElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
1051 {
1052 nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex);
1053 NS_ASSERTION(oldKid == GetChildAt(aIndex), "Unexpected child in RemoveChildAt");
1055 if (oldKid) {
1056 doRemoveChildAt(aIndex, aNotify, oldKid, mAttrsAndChildren);
1057 }
1058 }
1060 void
1061 FragmentOrElement::GetTextContentInternal(nsAString& aTextContent)
1062 {
1063 if(!nsContentUtils::GetNodeTextContent(this, true, aTextContent))
1064 NS_RUNTIMEABORT("OOM");
1065 }
1067 void
1068 FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
1069 ErrorResult& aError)
1070 {
1071 aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
1072 }
1074 void
1075 FragmentOrElement::DestroyContent()
1076 {
1077 nsIDocument *document = OwnerDoc();
1078 document->BindingManager()->RemovedFromDocument(this, document);
1079 document->ClearBoxObjectFor(this);
1081 // XXX We really should let cycle collection do this, but that currently still
1082 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
1083 ReleaseWrapper(this);
1085 uint32_t i, count = mAttrsAndChildren.ChildCount();
1086 for (i = 0; i < count; ++i) {
1087 // The child can remove itself from the parent in BindToTree.
1088 mAttrsAndChildren.ChildAt(i)->DestroyContent();
1089 }
1090 }
1092 void
1093 FragmentOrElement::SaveSubtreeState()
1094 {
1095 uint32_t i, count = mAttrsAndChildren.ChildCount();
1096 for (i = 0; i < count; ++i) {
1097 mAttrsAndChildren.ChildAt(i)->SaveSubtreeState();
1098 }
1099 }
1101 //----------------------------------------------------------------------
1103 // Generic DOMNode implementations
1105 void
1106 FragmentOrElement::FireNodeInserted(nsIDocument* aDoc,
1107 nsINode* aParent,
1108 nsTArray<nsCOMPtr<nsIContent> >& aNodes)
1109 {
1110 uint32_t count = aNodes.Length();
1111 for (uint32_t i = 0; i < count; ++i) {
1112 nsIContent* childContent = aNodes[i];
1114 if (nsContentUtils::HasMutationListeners(childContent,
1115 NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
1116 InternalMutationEvent mutation(true, NS_MUTATION_NODEINSERTED);
1117 mutation.mRelatedNode = do_QueryInterface(aParent);
1119 mozAutoSubtreeModified subtree(aDoc, aParent);
1120 (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
1121 }
1122 }
1123 }
1125 //----------------------------------------------------------------------
1127 // nsISupports implementation
1129 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1131 class ContentUnbinder : public nsRunnable
1132 {
1133 public:
1134 ContentUnbinder()
1135 {
1136 mLast = this;
1137 }
1139 ~ContentUnbinder()
1140 {
1141 Run();
1142 }
1144 void UnbindSubtree(nsIContent* aNode)
1145 {
1146 if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
1147 aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
1148 return;
1149 }
1150 FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
1151 uint32_t childCount = container->mAttrsAndChildren.ChildCount();
1152 if (childCount) {
1153 while (childCount-- > 0) {
1154 // Hold a strong ref to the node when we remove it, because we may be
1155 // the last reference to it. We need to call TakeChildAt() and
1156 // update mFirstChild before calling UnbindFromTree, since this last
1157 // can notify various observers and they should really see consistent
1158 // tree state.
1159 nsCOMPtr<nsIContent> child =
1160 container->mAttrsAndChildren.TakeChildAt(childCount);
1161 if (childCount == 0) {
1162 container->mFirstChild = nullptr;
1163 }
1164 UnbindSubtree(child);
1165 child->UnbindFromTree();
1166 }
1167 }
1168 }
1170 NS_IMETHOD Run()
1171 {
1172 nsAutoScriptBlocker scriptBlocker;
1173 uint32_t len = mSubtreeRoots.Length();
1174 if (len) {
1175 PRTime start = PR_Now();
1176 for (uint32_t i = 0; i < len; ++i) {
1177 UnbindSubtree(mSubtreeRoots[i]);
1178 }
1179 mSubtreeRoots.Clear();
1180 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
1181 uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC);
1182 }
1183 nsCycleCollector_dispatchDeferredDeletion();
1184 if (this == sContentUnbinder) {
1185 sContentUnbinder = nullptr;
1186 if (mNext) {
1187 nsRefPtr<ContentUnbinder> next;
1188 next.swap(mNext);
1189 sContentUnbinder = next;
1190 next->mLast = mLast;
1191 mLast = nullptr;
1192 NS_DispatchToMainThread(next);
1193 }
1194 }
1195 return NS_OK;
1196 }
1198 static void UnbindAll()
1199 {
1200 nsRefPtr<ContentUnbinder> ub = sContentUnbinder;
1201 sContentUnbinder = nullptr;
1202 while (ub) {
1203 ub->Run();
1204 ub = ub->mNext;
1205 }
1206 }
1208 static void Append(nsIContent* aSubtreeRoot)
1209 {
1210 if (!sContentUnbinder) {
1211 sContentUnbinder = new ContentUnbinder();
1212 nsCOMPtr<nsIRunnable> e = sContentUnbinder;
1213 NS_DispatchToMainThread(e);
1214 }
1216 if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
1217 SUBTREE_UNBINDINGS_PER_RUNNABLE) {
1218 sContentUnbinder->mLast->mNext = new ContentUnbinder();
1219 sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
1220 }
1221 sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
1222 }
1224 private:
1225 nsAutoTArray<nsCOMPtr<nsIContent>,
1226 SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
1227 nsRefPtr<ContentUnbinder> mNext;
1228 ContentUnbinder* mLast;
1229 static ContentUnbinder* sContentUnbinder;
1230 };
1232 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
1234 void
1235 FragmentOrElement::ClearContentUnbinder()
1236 {
1237 ContentUnbinder::UnbindAll();
1238 }
1240 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
1242 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
1243 nsINode::Unlink(tmp);
1245 if (tmp->HasProperties()) {
1246 if (tmp->IsHTML()) {
1247 nsIAtom*** props = nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
1248 for (uint32_t i = 0; props[i]; ++i) {
1249 tmp->DeleteProperty(*props[i]);
1250 }
1251 }
1252 }
1254 // Unlink child content (and unbind our subtree).
1255 if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
1256 uint32_t childCount = tmp->mAttrsAndChildren.ChildCount();
1257 if (childCount) {
1258 // Don't allow script to run while we're unbinding everything.
1259 nsAutoScriptBlocker scriptBlocker;
1260 while (childCount-- > 0) {
1261 // Hold a strong ref to the node when we remove it, because we may be
1262 // the last reference to it. We need to call TakeChildAt() and
1263 // update mFirstChild before calling UnbindFromTree, since this last
1264 // can notify various observers and they should really see consistent
1265 // tree state.
1266 nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
1267 if (childCount == 0) {
1268 tmp->mFirstChild = nullptr;
1269 }
1270 child->UnbindFromTree();
1271 }
1272 }
1273 } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
1274 ContentUnbinder::Append(tmp);
1275 } /* else {
1276 The subtree root will end up to a ContentUnbinder, and that will
1277 unbind the child nodes.
1278 } */
1280 // Unlink any DOM slots of interest.
1281 {
1282 nsDOMSlots *slots = tmp->GetExistingDOMSlots();
1283 if (slots) {
1284 slots->Unlink(tmp->IsXUL());
1285 }
1286 }
1288 {
1289 nsIDocument *doc;
1290 if (!tmp->GetParentNode() && (doc = tmp->OwnerDoc())) {
1291 doc->BindingManager()->RemovedFromDocument(tmp, doc);
1292 }
1293 }
1294 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1296 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
1298 void
1299 FragmentOrElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
1300 void* aData)
1301 {
1302 uint32_t* gen = static_cast<uint32_t*>(aData);
1303 xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
1304 }
1306 void
1307 FragmentOrElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey,
1308 void* aChild, void* aData)
1309 {
1310 xpc_TryUnmarkWrappedGrayObject(static_cast<nsISupports*>(aChild));
1311 }
1313 void
1314 FragmentOrElement::MarkNodeChildren(nsINode* aNode)
1315 {
1316 JSObject* o = GetJSObjectChild(aNode);
1317 if (o) {
1318 JS::ExposeObjectToActiveJS(o);
1319 }
1321 EventListenerManager* elm = aNode->GetExistingListenerManager();
1322 if (elm) {
1323 elm->MarkForCC();
1324 }
1326 if (aNode->HasProperties()) {
1327 nsIDocument* ownerDoc = aNode->OwnerDoc();
1328 ownerDoc->PropertyTable(DOM_USER_DATA)->
1329 Enumerate(aNode, FragmentOrElement::MarkUserData,
1330 &nsCCUncollectableMarker::sGeneration);
1331 ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->
1332 Enumerate(aNode, FragmentOrElement::MarkUserDataHandler,
1333 &nsCCUncollectableMarker::sGeneration);
1334 }
1335 }
1337 nsINode*
1338 FindOptimizableSubtreeRoot(nsINode* aNode)
1339 {
1340 nsINode* p;
1341 while ((p = aNode->GetParentNode())) {
1342 if (aNode->UnoptimizableCCNode()) {
1343 return nullptr;
1344 }
1345 aNode = p;
1346 }
1348 if (aNode->UnoptimizableCCNode()) {
1349 return nullptr;
1350 }
1351 return aNode;
1352 }
1354 StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
1356 static PLDHashOperator
1357 VisitBlackMarkedNode(nsPtrHashKey<nsINode>* aEntry, void*)
1358 {
1359 nsINode* n = aEntry->GetKey();
1360 n->SetCCMarkedRoot(false);
1361 n->SetInCCBlackTree(false);
1362 return PL_DHASH_NEXT;
1363 }
1365 static void
1366 ClearBlackMarkedNodes()
1367 {
1368 if (!gCCBlackMarkedNodes) {
1369 return;
1370 }
1371 gCCBlackMarkedNodes->EnumerateEntries(VisitBlackMarkedNode, nullptr);
1372 gCCBlackMarkedNodes = nullptr;
1373 }
1375 // static
1376 void
1377 FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
1378 {
1379 if (!gCCBlackMarkedNodes) {
1380 return;
1381 }
1382 gCCBlackMarkedNodes->RemoveEntry(aNode);
1383 }
1385 // static
1386 bool
1387 FragmentOrElement::CanSkipInCC(nsINode* aNode)
1388 {
1389 // Don't try to optimize anything during shutdown.
1390 if (nsCCUncollectableMarker::sGeneration == 0) {
1391 return false;
1392 }
1394 nsIDocument* currentDoc = aNode->GetCurrentDoc();
1395 if (currentDoc &&
1396 nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
1397 return !NeedsScriptTraverse(aNode);
1398 }
1400 // Bail out early if aNode is somewhere in anonymous content,
1401 // or otherwise unusual.
1402 if (aNode->UnoptimizableCCNode()) {
1403 return false;
1404 }
1406 nsINode* root =
1407 currentDoc ? static_cast<nsINode*>(currentDoc) :
1408 FindOptimizableSubtreeRoot(aNode);
1409 if (!root) {
1410 return false;
1411 }
1413 // Subtree has been traversed already.
1414 if (root->CCMarkedRoot()) {
1415 return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
1416 }
1418 if (!gCCBlackMarkedNodes) {
1419 gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
1420 }
1422 // nodesToUnpurple contains nodes which will be removed
1423 // from the purple buffer if the DOM tree is black.
1424 nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
1425 // grayNodes need script traverse, so they aren't removed from
1426 // the purple buffer, but are marked to be in black subtree so that
1427 // traverse is faster.
1428 nsAutoTArray<nsINode*, 1020> grayNodes;
1430 bool foundBlack = root->IsBlack();
1431 if (root != currentDoc) {
1432 currentDoc = nullptr;
1433 if (NeedsScriptTraverse(root)) {
1434 grayNodes.AppendElement(root);
1435 } else if (static_cast<nsIContent*>(root)->IsPurple()) {
1436 nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
1437 }
1438 }
1440 // Traverse the subtree and check if we could know without CC
1441 // that it is black.
1442 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1443 // than CC's generic traverse.
1444 for (nsIContent* node = root->GetFirstChild(); node;
1445 node = node->GetNextNode(root)) {
1446 foundBlack = foundBlack || node->IsBlack();
1447 if (foundBlack && currentDoc) {
1448 // If we can mark the whole document black, no need to optimize
1449 // so much, since when the next purple node in the document will be
1450 // handled, it is fast to check that currentDoc is in CCGeneration.
1451 break;
1452 }
1453 if (NeedsScriptTraverse(node)) {
1454 // Gray nodes need real CC traverse.
1455 grayNodes.AppendElement(node);
1456 } else if (node->IsPurple()) {
1457 nodesToUnpurple.AppendElement(node);
1458 }
1459 }
1461 root->SetCCMarkedRoot(true);
1462 root->SetInCCBlackTree(foundBlack);
1463 gCCBlackMarkedNodes->PutEntry(root);
1465 if (!foundBlack) {
1466 return false;
1467 }
1469 if (currentDoc) {
1470 // Special case documents. If we know the document is black,
1471 // we can mark the document to be in CCGeneration.
1472 currentDoc->
1473 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1474 } else {
1475 for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
1476 nsINode* node = grayNodes[i];
1477 node->SetInCCBlackTree(true);
1478 gCCBlackMarkedNodes->PutEntry(node);
1479 }
1480 }
1482 // Subtree is black, we can remove non-gray purple nodes from
1483 // purple buffer.
1484 for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
1485 nsIContent* purple = nodesToUnpurple[i];
1486 // Can't remove currently handled purple node.
1487 if (purple != aNode) {
1488 purple->RemovePurple();
1489 }
1490 }
1491 return !NeedsScriptTraverse(aNode);
1492 }
1494 nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
1495 nsAutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
1497 void ClearCycleCollectorCleanupData()
1498 {
1499 if (gPurpleRoots) {
1500 uint32_t len = gPurpleRoots->Length();
1501 for (uint32_t i = 0; i < len; ++i) {
1502 nsINode* n = gPurpleRoots->ElementAt(i);
1503 n->SetIsPurpleRoot(false);
1504 }
1505 delete gPurpleRoots;
1506 gPurpleRoots = nullptr;
1507 }
1508 if (gNodesToUnbind) {
1509 uint32_t len = gNodesToUnbind->Length();
1510 for (uint32_t i = 0; i < len; ++i) {
1511 nsIContent* c = gNodesToUnbind->ElementAt(i);
1512 c->SetIsPurpleRoot(false);
1513 ContentUnbinder::Append(c);
1514 }
1515 delete gNodesToUnbind;
1516 gNodesToUnbind = nullptr;
1517 }
1518 }
1520 static bool
1521 ShouldClearPurple(nsIContent* aContent)
1522 {
1523 if (aContent && aContent->IsPurple()) {
1524 return true;
1525 }
1527 JSObject* o = GetJSObjectChild(aContent);
1528 if (o && xpc_IsGrayGCThing(o)) {
1529 return true;
1530 }
1532 if (aContent->HasListenerManager()) {
1533 return true;
1534 }
1536 return aContent->HasProperties();
1537 }
1539 // If aNode is not optimizable, but is an element
1540 // with a frame in a document which has currently active presshell,
1541 // we can act as if it was optimizable. When the primary frame dies, aNode
1542 // will end up to the purple buffer because of the refcount change.
1543 bool
1544 NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
1545 {
1546 return aCurrentDoc->GetShell() && aNode->IsElement() &&
1547 aNode->AsElement()->GetPrimaryFrame();
1548 }
1550 bool
1551 OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
1552 {
1553 return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
1554 }
1556 // CanSkip checks if aNode is black, and if it is, returns
1557 // true. If aNode is in a black DOM tree, CanSkip may also remove other objects
1558 // from purple buffer and unmark event listeners and user data.
1559 // If the root of the DOM tree is a document, less optimizations are done
1560 // since checking the blackness of the current document is usually fast and we
1561 // don't want slow down such common cases.
1562 bool
1563 FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
1564 {
1565 // Don't try to optimize anything during shutdown.
1566 if (nsCCUncollectableMarker::sGeneration == 0) {
1567 return false;
1568 }
1570 bool unoptimizable = aNode->UnoptimizableCCNode();
1571 nsIDocument* currentDoc = aNode->GetCurrentDoc();
1572 if (currentDoc &&
1573 nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration()) &&
1574 (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) ||
1575 OwnedByBindingManager(currentDoc, aNode))) {
1576 MarkNodeChildren(aNode);
1577 return true;
1578 }
1580 if (unoptimizable) {
1581 return false;
1582 }
1584 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
1585 FindOptimizableSubtreeRoot(aNode);
1586 if (!root) {
1587 return false;
1588 }
1590 // Subtree has been traversed already, and aNode has
1591 // been handled in a way that doesn't require revisiting it.
1592 if (root->IsPurpleRoot()) {
1593 return false;
1594 }
1596 // nodesToClear contains nodes which are either purple or
1597 // gray.
1598 nsAutoTArray<nsIContent*, 1020> nodesToClear;
1600 bool foundBlack = root->IsBlack();
1601 bool domOnlyCycle = false;
1602 if (root != currentDoc) {
1603 currentDoc = nullptr;
1604 if (!foundBlack) {
1605 domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
1606 }
1607 if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
1608 nodesToClear.AppendElement(static_cast<nsIContent*>(root));
1609 }
1610 }
1612 // Traverse the subtree and check if we could know without CC
1613 // that it is black.
1614 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1615 // than CC's generic traverse.
1616 for (nsIContent* node = root->GetFirstChild(); node;
1617 node = node->GetNextNode(root)) {
1618 foundBlack = foundBlack || node->IsBlack();
1619 if (foundBlack) {
1620 domOnlyCycle = false;
1621 if (currentDoc) {
1622 // If we can mark the whole document black, no need to optimize
1623 // so much, since when the next purple node in the document will be
1624 // handled, it is fast to check that the currentDoc is in CCGeneration.
1625 break;
1626 }
1627 // No need to put stuff to the nodesToClear array, if we can clear it
1628 // already here.
1629 if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
1630 node->RemovePurple();
1631 }
1632 MarkNodeChildren(node);
1633 } else {
1634 domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
1635 if (ShouldClearPurple(node)) {
1636 // Collect interesting nodes which we can clear if we find that
1637 // they are kept alive in a black tree or are in a DOM-only cycle.
1638 nodesToClear.AppendElement(node);
1639 }
1640 }
1641 }
1643 if (!currentDoc || !foundBlack) {
1644 root->SetIsPurpleRoot(true);
1645 if (domOnlyCycle) {
1646 if (!gNodesToUnbind) {
1647 gNodesToUnbind = new nsAutoTArray<nsIContent*, 1020>();
1648 }
1649 gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
1650 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1651 nsIContent* n = nodesToClear[i];
1652 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1653 n->RemovePurple();
1654 }
1655 }
1656 return true;
1657 } else {
1658 if (!gPurpleRoots) {
1659 gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
1660 }
1661 gPurpleRoots->AppendElement(root);
1662 }
1663 }
1665 if (!foundBlack) {
1666 return false;
1667 }
1669 if (currentDoc) {
1670 // Special case documents. If we know the document is black,
1671 // we can mark the document to be in CCGeneration.
1672 currentDoc->
1673 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1674 MarkNodeChildren(currentDoc);
1675 }
1677 // Subtree is black, so we can remove purple nodes from
1678 // purple buffer and mark stuff that to be certainly alive.
1679 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1680 nsIContent* n = nodesToClear[i];
1681 MarkNodeChildren(n);
1682 // Can't remove currently handled purple node,
1683 // unless aRemovingAllowed is true.
1684 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1685 n->RemovePurple();
1686 }
1687 }
1688 return true;
1689 }
1691 bool
1692 FragmentOrElement::CanSkipThis(nsINode* aNode)
1693 {
1694 if (nsCCUncollectableMarker::sGeneration == 0) {
1695 return false;
1696 }
1697 if (aNode->IsBlack()) {
1698 return true;
1699 }
1700 nsIDocument* c = aNode->GetCurrentDoc();
1701 return
1702 ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) ||
1703 aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode);
1704 }
1706 void
1707 FragmentOrElement::InitCCCallbacks()
1708 {
1709 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
1710 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
1711 }
1713 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
1714 return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
1715 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1717 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
1718 return FragmentOrElement::CanSkipInCC(tmp);
1719 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1721 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
1722 return FragmentOrElement::CanSkipThis(tmp);
1723 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1725 static const char* kNSURIs[] = {
1726 " ([none])",
1727 " (xmlns)",
1728 " (xml)",
1729 " (xhtml)",
1730 " (XLink)",
1731 " (XSLT)",
1732 " (XBL)",
1733 " (MathML)",
1734 " (RDF)",
1735 " (XUL)",
1736 " (SVG)",
1737 " (XML Events)"
1738 };
1740 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
1741 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1742 char name[512];
1743 uint32_t nsid = tmp->GetNameSpaceID();
1744 nsAtomCString localName(tmp->NodeInfo()->NameAtom());
1745 nsAutoCString uri;
1746 if (tmp->OwnerDoc()->GetDocumentURI()) {
1747 tmp->OwnerDoc()->GetDocumentURI()->GetSpec(uri);
1748 }
1750 nsAutoString id;
1751 nsIAtom* idAtom = tmp->GetID();
1752 if (idAtom) {
1753 id.AppendLiteral(" id='");
1754 id.Append(nsDependentAtomString(idAtom));
1755 id.AppendLiteral("'");
1756 }
1758 nsAutoString classes;
1759 const nsAttrValue* classAttrValue = tmp->GetClasses();
1760 if (classAttrValue) {
1761 classes.AppendLiteral(" class='");
1762 nsAutoString classString;
1763 classAttrValue->ToString(classString);
1764 classString.ReplaceChar(char16_t('\n'), char16_t(' '));
1765 classes.Append(classString);
1766 classes.AppendLiteral("'");
1767 }
1769 nsAutoCString orphan;
1770 if (!tmp->IsInDoc() &&
1771 // Ignore xbl:content, which is never in the document and hence always
1772 // appears to be orphaned.
1773 !tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) {
1774 orphan.AppendLiteral(" (orphan)");
1775 }
1777 const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
1778 PR_snprintf(name, sizeof(name), "FragmentOrElement%s %s%s%s%s %s",
1779 nsuri,
1780 localName.get(),
1781 NS_ConvertUTF16toUTF8(id).get(),
1782 NS_ConvertUTF16toUTF8(classes).get(),
1783 orphan.get(),
1784 uri.get());
1785 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1786 }
1787 else {
1788 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
1789 }
1791 // Always need to traverse script objects, so do that before we check
1792 // if we're uncollectable.
1793 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1795 if (!nsINode::Traverse(tmp, cb)) {
1796 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1797 }
1799 tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
1801 if (tmp->HasProperties()) {
1802 if (tmp->IsHTML()) {
1803 nsIAtom*** props = nsGenericHTMLElement::PropertiesToTraverseAndUnlink();
1804 for (uint32_t i = 0; props[i]; ++i) {
1805 nsISupports* property =
1806 static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
1807 cb.NoteXPCOMChild(property);
1808 }
1809 }
1810 }
1812 // Traverse attribute names and child content.
1813 {
1814 uint32_t i;
1815 uint32_t attrs = tmp->mAttrsAndChildren.AttrCount();
1816 for (i = 0; i < attrs; i++) {
1817 const nsAttrName* name = tmp->mAttrsAndChildren.AttrNameAt(i);
1818 if (!name->IsAtom()) {
1819 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1820 "mAttrsAndChildren[i]->NodeInfo()");
1821 cb.NoteXPCOMChild(name->NodeInfo());
1822 }
1823 }
1825 uint32_t kids = tmp->mAttrsAndChildren.ChildCount();
1826 for (i = 0; i < kids; i++) {
1827 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
1828 cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
1829 }
1830 }
1832 // Traverse any DOM slots of interest.
1833 {
1834 nsDOMSlots *slots = tmp->GetExistingDOMSlots();
1835 if (slots) {
1836 slots->Traverse(cb, tmp->IsXUL());
1837 }
1838 }
1839 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1842 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
1843 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1844 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
1845 NS_INTERFACE_MAP_ENTRY(Element)
1846 NS_INTERFACE_MAP_ENTRY(nsIContent)
1847 NS_INTERFACE_MAP_ENTRY(nsINode)
1848 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
1849 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1850 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
1851 new nsNodeSupportsWeakRefTearoff(this))
1852 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
1853 new nsNode3Tearoff(this))
1854 // DOM bindings depend on the identity pointer being the
1855 // same as nsINode (which nsIContent inherits).
1856 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
1857 NS_INTERFACE_MAP_END
1859 NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
1860 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
1861 nsNodeUtils::LastRelease(this))
1863 //----------------------------------------------------------------------
1865 nsresult
1866 FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst)
1867 {
1868 uint32_t i, count = mAttrsAndChildren.AttrCount();
1869 for (i = 0; i < count; ++i) {
1870 const nsAttrName* name = mAttrsAndChildren.AttrNameAt(i);
1871 const nsAttrValue* value = mAttrsAndChildren.AttrAt(i);
1872 nsAutoString valStr;
1873 value->ToString(valStr);
1874 nsresult rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
1875 name->GetPrefix(), valStr, false);
1876 NS_ENSURE_SUCCESS(rv, rv);
1877 }
1879 return NS_OK;
1880 }
1882 const nsTextFragment*
1883 FragmentOrElement::GetText()
1884 {
1885 return nullptr;
1886 }
1888 uint32_t
1889 FragmentOrElement::TextLength() const
1890 {
1891 // We can remove this assertion if it turns out to be useful to be able
1892 // to depend on this returning 0
1893 NS_NOTREACHED("called FragmentOrElement::TextLength");
1895 return 0;
1896 }
1898 nsresult
1899 FragmentOrElement::SetText(const char16_t* aBuffer, uint32_t aLength,
1900 bool aNotify)
1901 {
1902 NS_ERROR("called FragmentOrElement::SetText");
1904 return NS_ERROR_FAILURE;
1905 }
1907 nsresult
1908 FragmentOrElement::AppendText(const char16_t* aBuffer, uint32_t aLength,
1909 bool aNotify)
1910 {
1911 NS_ERROR("called FragmentOrElement::AppendText");
1913 return NS_ERROR_FAILURE;
1914 }
1916 bool
1917 FragmentOrElement::TextIsOnlyWhitespace()
1918 {
1919 return false;
1920 }
1922 bool
1923 FragmentOrElement::HasTextForTranslation()
1924 {
1925 return false;
1926 }
1928 void
1929 FragmentOrElement::AppendTextTo(nsAString& aResult)
1930 {
1931 // We can remove this assertion if it turns out to be useful to be able
1932 // to depend on this appending nothing.
1933 NS_NOTREACHED("called FragmentOrElement::TextLength");
1934 }
1936 bool
1937 FragmentOrElement::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&)
1938 {
1939 // We can remove this assertion if it turns out to be useful to be able
1940 // to depend on this appending nothing.
1941 NS_NOTREACHED("called FragmentOrElement::TextLength");
1943 return false;
1944 }
1946 uint32_t
1947 FragmentOrElement::GetChildCount() const
1948 {
1949 return mAttrsAndChildren.ChildCount();
1950 }
1952 nsIContent *
1953 FragmentOrElement::GetChildAt(uint32_t aIndex) const
1954 {
1955 return mAttrsAndChildren.GetSafeChildAt(aIndex);
1956 }
1958 nsIContent * const *
1959 FragmentOrElement::GetChildArray(uint32_t* aChildCount) const
1960 {
1961 return mAttrsAndChildren.GetChildArray(aChildCount);
1962 }
1964 int32_t
1965 FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
1966 {
1967 return mAttrsAndChildren.IndexOfChild(aPossibleChild);
1968 }
1970 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
1971 #define STRING_BUFFER_UNITS 1020
1973 namespace {
1975 // We put StringBuilder in the anonymous namespace to prevent anything outside
1976 // this file from accidentally being linked against it.
1978 class StringBuilder
1979 {
1980 private:
1981 class Unit
1982 {
1983 public:
1984 Unit() : mAtom(nullptr), mType(eUnknown), mLength(0)
1985 {
1986 MOZ_COUNT_CTOR(StringBuilder::Unit);
1987 }
1988 ~Unit()
1989 {
1990 if (mType == eString || mType == eStringWithEncode) {
1991 delete mString;
1992 }
1993 MOZ_COUNT_DTOR(StringBuilder::Unit);
1994 }
1996 enum Type
1997 {
1998 eUnknown,
1999 eAtom,
2000 eString,
2001 eStringWithEncode,
2002 eLiteral,
2003 eTextFragment,
2004 eTextFragmentWithEncode,
2005 };
2007 union
2008 {
2009 nsIAtom* mAtom;
2010 const char* mLiteral;
2011 nsAutoString* mString;
2012 const nsTextFragment* mTextFragment;
2013 };
2014 Type mType;
2015 uint32_t mLength;
2016 };
2017 public:
2018 StringBuilder() : mLast(MOZ_THIS_IN_INITIALIZER_LIST()), mLength(0)
2019 {
2020 MOZ_COUNT_CTOR(StringBuilder);
2021 }
2023 ~StringBuilder()
2024 {
2025 MOZ_COUNT_DTOR(StringBuilder);
2026 }
2028 void Append(nsIAtom* aAtom)
2029 {
2030 Unit* u = AddUnit();
2031 u->mAtom = aAtom;
2032 u->mType = Unit::eAtom;
2033 uint32_t len = aAtom->GetLength();
2034 u->mLength = len;
2035 mLength += len;
2036 }
2038 template<int N>
2039 void Append(const char (&aLiteral)[N])
2040 {
2041 Unit* u = AddUnit();
2042 u->mLiteral = aLiteral;
2043 u->mType = Unit::eLiteral;
2044 uint32_t len = N - 1;
2045 u->mLength = len;
2046 mLength += len;
2047 }
2049 template<int N>
2050 void Append(char (&aLiteral)[N])
2051 {
2052 Unit* u = AddUnit();
2053 u->mLiteral = aLiteral;
2054 u->mType = Unit::eLiteral;
2055 uint32_t len = N - 1;
2056 u->mLength = len;
2057 mLength += len;
2058 }
2060 void Append(const nsAString& aString)
2061 {
2062 Unit* u = AddUnit();
2063 u->mString = new nsAutoString(aString);
2064 u->mType = Unit::eString;
2065 uint32_t len = aString.Length();
2066 u->mLength = len;
2067 mLength += len;
2068 }
2070 void Append(nsAutoString* aString)
2071 {
2072 Unit* u = AddUnit();
2073 u->mString = aString;
2074 u->mType = Unit::eString;
2075 uint32_t len = aString->Length();
2076 u->mLength = len;
2077 mLength += len;
2078 }
2080 void AppendWithAttrEncode(nsAutoString* aString, uint32_t aLen)
2081 {
2082 Unit* u = AddUnit();
2083 u->mString = aString;
2084 u->mType = Unit::eStringWithEncode;
2085 u->mLength = aLen;
2086 mLength += aLen;
2087 }
2089 void Append(const nsTextFragment* aTextFragment)
2090 {
2091 Unit* u = AddUnit();
2092 u->mTextFragment = aTextFragment;
2093 u->mType = Unit::eTextFragment;
2094 uint32_t len = aTextFragment->GetLength();
2095 u->mLength = len;
2096 mLength += len;
2097 }
2099 void AppendWithEncode(const nsTextFragment* aTextFragment, uint32_t aLen)
2100 {
2101 Unit* u = AddUnit();
2102 u->mTextFragment = aTextFragment;
2103 u->mType = Unit::eTextFragmentWithEncode;
2104 u->mLength = aLen;
2105 mLength += aLen;
2106 }
2108 bool ToString(nsAString& aOut)
2109 {
2110 if (!aOut.SetCapacity(mLength, fallible_t())) {
2111 return false;
2112 }
2114 for (StringBuilder* current = this; current; current = current->mNext) {
2115 uint32_t len = current->mUnits.Length();
2116 for (uint32_t i = 0; i < len; ++i) {
2117 Unit& u = current->mUnits[i];
2118 switch (u.mType) {
2119 case Unit::eAtom:
2120 aOut.Append(nsDependentAtomString(u.mAtom));
2121 break;
2122 case Unit::eString:
2123 aOut.Append(*(u.mString));
2124 break;
2125 case Unit::eStringWithEncode:
2126 EncodeAttrString(*(u.mString), aOut);
2127 break;
2128 case Unit::eLiteral:
2129 aOut.AppendASCII(u.mLiteral, u.mLength);
2130 break;
2131 case Unit::eTextFragment:
2132 u.mTextFragment->AppendTo(aOut);
2133 break;
2134 case Unit::eTextFragmentWithEncode:
2135 EncodeTextFragment(u.mTextFragment, aOut);
2136 break;
2137 default:
2138 MOZ_CRASH("Unknown unit type?");
2139 }
2140 }
2141 }
2142 return true;
2143 }
2144 private:
2145 Unit* AddUnit()
2146 {
2147 if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
2148 new StringBuilder(this);
2149 }
2150 return mLast->mUnits.AppendElement();
2151 }
2153 StringBuilder(StringBuilder* aFirst)
2154 : mLast(nullptr), mLength(0)
2155 {
2156 MOZ_COUNT_CTOR(StringBuilder);
2157 aFirst->mLast->mNext = this;
2158 aFirst->mLast = this;
2159 }
2161 void EncodeAttrString(const nsAutoString& aValue, nsAString& aOut)
2162 {
2163 const char16_t* c = aValue.BeginReading();
2164 const char16_t* end = aValue.EndReading();
2165 while (c < end) {
2166 switch (*c) {
2167 case '"':
2168 aOut.AppendLiteral(""");
2169 break;
2170 case '&':
2171 aOut.AppendLiteral("&");
2172 break;
2173 case 0x00A0:
2174 aOut.AppendLiteral(" ");
2175 break;
2176 default:
2177 aOut.Append(*c);
2178 break;
2179 }
2180 ++c;
2181 }
2182 }
2184 void EncodeTextFragment(const nsTextFragment* aValue, nsAString& aOut)
2185 {
2186 uint32_t len = aValue->GetLength();
2187 if (aValue->Is2b()) {
2188 const char16_t* data = aValue->Get2b();
2189 for (uint32_t i = 0; i < len; ++i) {
2190 const char16_t c = data[i];
2191 switch (c) {
2192 case '<':
2193 aOut.AppendLiteral("<");
2194 break;
2195 case '>':
2196 aOut.AppendLiteral(">");
2197 break;
2198 case '&':
2199 aOut.AppendLiteral("&");
2200 break;
2201 case 0x00A0:
2202 aOut.AppendLiteral(" ");
2203 break;
2204 default:
2205 aOut.Append(c);
2206 break;
2207 }
2208 }
2209 } else {
2210 const char* data = aValue->Get1b();
2211 for (uint32_t i = 0; i < len; ++i) {
2212 const unsigned char c = data[i];
2213 switch (c) {
2214 case '<':
2215 aOut.AppendLiteral("<");
2216 break;
2217 case '>':
2218 aOut.AppendLiteral(">");
2219 break;
2220 case '&':
2221 aOut.AppendLiteral("&");
2222 break;
2223 case 0x00A0:
2224 aOut.AppendLiteral(" ");
2225 break;
2226 default:
2227 aOut.Append(c);
2228 break;
2229 }
2230 }
2231 }
2232 }
2234 nsAutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
2235 nsAutoPtr<StringBuilder> mNext;
2236 StringBuilder* mLast;
2237 // mLength is used only in the first StringBuilder object in the linked list.
2238 uint32_t mLength;
2239 };
2241 } // anonymous namespace
2243 static void
2244 AppendEncodedCharacters(const nsTextFragment* aText, StringBuilder& aBuilder)
2245 {
2246 uint32_t extraSpaceNeeded = 0;
2247 uint32_t len = aText->GetLength();
2248 if (aText->Is2b()) {
2249 const char16_t* data = aText->Get2b();
2250 for (uint32_t i = 0; i < len; ++i) {
2251 const char16_t c = data[i];
2252 switch (c) {
2253 case '<':
2254 extraSpaceNeeded += ArrayLength("<") - 2;
2255 break;
2256 case '>':
2257 extraSpaceNeeded += ArrayLength(">") - 2;
2258 break;
2259 case '&':
2260 extraSpaceNeeded += ArrayLength("&") - 2;
2261 break;
2262 case 0x00A0:
2263 extraSpaceNeeded += ArrayLength(" ") - 2;
2264 break;
2265 default:
2266 break;
2267 }
2268 }
2269 } else {
2270 const char* data = aText->Get1b();
2271 for (uint32_t i = 0; i < len; ++i) {
2272 const unsigned char c = data[i];
2273 switch (c) {
2274 case '<':
2275 extraSpaceNeeded += ArrayLength("<") - 2;
2276 break;
2277 case '>':
2278 extraSpaceNeeded += ArrayLength(">") - 2;
2279 break;
2280 case '&':
2281 extraSpaceNeeded += ArrayLength("&") - 2;
2282 break;
2283 case 0x00A0:
2284 extraSpaceNeeded += ArrayLength(" ") - 2;
2285 break;
2286 default:
2287 break;
2288 }
2289 }
2290 }
2292 if (extraSpaceNeeded) {
2293 aBuilder.AppendWithEncode(aText, len + extraSpaceNeeded);
2294 } else {
2295 aBuilder.Append(aText);
2296 }
2297 }
2299 static void
2300 AppendEncodedAttributeValue(nsAutoString* aValue, StringBuilder& aBuilder)
2301 {
2302 const char16_t* c = aValue->BeginReading();
2303 const char16_t* end = aValue->EndReading();
2305 uint32_t extraSpaceNeeded = 0;
2306 while (c < end) {
2307 switch (*c) {
2308 case '"':
2309 extraSpaceNeeded += ArrayLength(""") - 2;
2310 break;
2311 case '&':
2312 extraSpaceNeeded += ArrayLength("&") - 2;
2313 break;
2314 case 0x00A0:
2315 extraSpaceNeeded += ArrayLength(" ") - 2;
2316 break;
2317 default:
2318 break;
2319 }
2320 ++c;
2321 }
2323 if (extraSpaceNeeded) {
2324 aBuilder.AppendWithAttrEncode(aValue, aValue->Length() + extraSpaceNeeded);
2325 } else {
2326 aBuilder.Append(aValue);
2327 }
2328 }
2330 static void
2331 StartElement(Element* aContent, StringBuilder& aBuilder)
2332 {
2333 nsIAtom* localName = aContent->Tag();
2334 int32_t tagNS = aContent->GetNameSpaceID();
2336 aBuilder.Append("<");
2337 if (aContent->IsHTML() || aContent->IsSVG() || aContent->IsMathML()) {
2338 aBuilder.Append(localName);
2339 } else {
2340 aBuilder.Append(aContent->NodeName());
2341 }
2343 int32_t count = aContent->GetAttrCount();
2344 for (int32_t i = count; i > 0;) {
2345 --i;
2346 const nsAttrName* name = aContent->GetAttrNameAt(i);
2347 int32_t attNs = name->NamespaceID();
2348 nsIAtom* attName = name->LocalName();
2350 // Filter out any attribute starting with [-|_]moz
2351 nsDependentAtomString attrNameStr(attName);
2352 if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
2353 StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
2354 continue;
2355 }
2357 nsAutoString* attValue = new nsAutoString();
2358 aContent->GetAttr(attNs, attName, *attValue);
2360 // Filter out special case of <br type="_moz*"> used by the editor.
2361 // Bug 16988. Yuck.
2362 if (localName == nsGkAtoms::br && tagNS == kNameSpaceID_XHTML &&
2363 attName == nsGkAtoms::type && attNs == kNameSpaceID_None &&
2364 StringBeginsWith(*attValue, NS_LITERAL_STRING("_moz"))) {
2365 delete attValue;
2366 continue;
2367 }
2369 aBuilder.Append(" ");
2371 if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
2372 (attNs == kNameSpaceID_XMLNS &&
2373 attName == nsGkAtoms::xmlns)) {
2374 // Nothing else required
2375 } else if (attNs == kNameSpaceID_XML) {
2376 aBuilder.Append("xml:");
2377 } else if (attNs == kNameSpaceID_XMLNS) {
2378 aBuilder.Append("xmlns:");
2379 } else if (attNs == kNameSpaceID_XLink) {
2380 aBuilder.Append("xlink:");
2381 } else {
2382 nsIAtom* prefix = name->GetPrefix();
2383 if (prefix) {
2384 aBuilder.Append(prefix);
2385 aBuilder.Append(":");
2386 }
2387 }
2389 aBuilder.Append(attName);
2390 aBuilder.Append("=\"");
2391 AppendEncodedAttributeValue(attValue, aBuilder);
2392 aBuilder.Append("\"");
2393 }
2395 aBuilder.Append(">");
2397 /*
2398 // Per HTML spec we should append one \n if the first child of
2399 // pre/textarea/listing is a textnode and starts with a \n.
2400 // But because browsers haven't traditionally had that behavior,
2401 // we're not changing our behavior either - yet.
2402 if (aContent->IsHTML()) {
2403 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
2404 localName == nsGkAtoms::listing) {
2405 nsIContent* fc = aContent->GetFirstChild();
2406 if (fc &&
2407 (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
2408 fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
2409 const nsTextFragment* text = fc->GetText();
2410 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
2411 aBuilder.Append("\n");
2412 }
2413 }
2414 }
2415 }*/
2416 }
2418 static inline bool
2419 ShouldEscape(nsIContent* aParent)
2420 {
2421 if (!aParent || !aParent->IsHTML()) {
2422 return true;
2423 }
2425 static const nsIAtom* nonEscapingElements[] = {
2426 nsGkAtoms::style, nsGkAtoms::script, nsGkAtoms::xmp,
2427 nsGkAtoms::iframe, nsGkAtoms::noembed, nsGkAtoms::noframes,
2428 nsGkAtoms::plaintext,
2429 // Per the current spec noscript should be escaped in case
2430 // scripts are disabled or if document doesn't have
2431 // browsing context. However the latter seems to be a spec bug
2432 // and Gecko hasn't traditionally done the former.
2433 nsGkAtoms::noscript
2434 };
2435 static mozilla::BloomFilter<12, nsIAtom> sFilter;
2436 static bool sInitialized = false;
2437 if (!sInitialized) {
2438 sInitialized = true;
2439 for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
2440 sFilter.add(nonEscapingElements[i]);
2441 }
2442 }
2444 nsIAtom* tag = aParent->Tag();
2445 if (sFilter.mightContain(tag)) {
2446 for (uint32_t i = 0; i < ArrayLength(nonEscapingElements); ++i) {
2447 if (tag == nonEscapingElements[i]) {
2448 return false;
2449 }
2450 }
2451 }
2452 return true;
2453 }
2455 static inline bool
2456 IsVoidTag(Element* aElement)
2457 {
2458 if (!aElement->IsHTML()) {
2459 return false;
2460 }
2462 static const nsIAtom* voidElements[] = {
2463 nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
2464 nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
2465 nsGkAtoms::command, nsGkAtoms::embed, nsGkAtoms::frame,
2466 nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
2467 nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
2468 nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
2469 nsGkAtoms::wbr
2470 };
2472 static mozilla::BloomFilter<12, nsIAtom> sFilter;
2473 static bool sInitialized = false;
2474 if (!sInitialized) {
2475 sInitialized = true;
2476 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2477 sFilter.add(voidElements[i]);
2478 }
2479 }
2481 nsIAtom* tag = aElement->Tag();
2482 if (sFilter.mightContain(tag)) {
2483 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2484 if (tag == voidElements[i]) {
2485 return true;
2486 }
2487 }
2488 }
2489 return false;
2490 }
2492 static bool
2493 Serialize(FragmentOrElement* aRoot, bool aDescendentsOnly, nsAString& aOut)
2494 {
2495 nsINode* current = aDescendentsOnly ?
2496 nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot) : aRoot;
2498 if (!current) {
2499 return true;
2500 }
2502 StringBuilder builder;
2503 nsIContent* next;
2504 while (true) {
2505 bool isVoid = false;
2506 switch (current->NodeType()) {
2507 case nsIDOMNode::ELEMENT_NODE: {
2508 Element* elem = current->AsElement();
2509 StartElement(elem, builder);
2510 isVoid = IsVoidTag(elem);
2511 if (!isVoid &&
2512 (next = nsNodeUtils::GetFirstChildOfTemplateOrNode(current))) {
2513 current = next;
2514 continue;
2515 }
2516 break;
2517 }
2519 case nsIDOMNode::TEXT_NODE:
2520 case nsIDOMNode::CDATA_SECTION_NODE: {
2521 const nsTextFragment* text = static_cast<nsIContent*>(current)->GetText();
2522 nsIContent* parent = current->GetParent();
2523 if (ShouldEscape(parent)) {
2524 AppendEncodedCharacters(text, builder);
2525 } else {
2526 builder.Append(text);
2527 }
2528 break;
2529 }
2531 case nsIDOMNode::COMMENT_NODE: {
2532 builder.Append("<!--");
2533 builder.Append(static_cast<nsIContent*>(current)->GetText());
2534 builder.Append("-->");
2535 break;
2536 }
2538 case nsIDOMNode::DOCUMENT_TYPE_NODE: {
2539 builder.Append("<!DOCTYPE ");
2540 builder.Append(current->NodeName());
2541 builder.Append(">");
2542 break;
2543 }
2545 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: {
2546 builder.Append("<?");
2547 builder.Append(current->NodeName());
2548 builder.Append(" ");
2549 builder.Append(static_cast<nsIContent*>(current)->GetText());
2550 builder.Append(">");
2551 break;
2552 }
2553 }
2555 while (true) {
2556 if (!isVoid && current->NodeType() == nsIDOMNode::ELEMENT_NODE) {
2557 builder.Append("</");
2558 nsIContent* elem = static_cast<nsIContent*>(current);
2559 if (elem->IsHTML() || elem->IsSVG() || elem->IsMathML()) {
2560 builder.Append(elem->Tag());
2561 } else {
2562 builder.Append(current->NodeName());
2563 }
2564 builder.Append(">");
2565 }
2566 isVoid = false;
2568 if (current == aRoot) {
2569 return builder.ToString(aOut);
2570 }
2572 if ((next = current->GetNextSibling())) {
2573 current = next;
2574 break;
2575 }
2577 current = current->GetParentNode();
2579 // Template case, if we are in a template's content, then the parent
2580 // should be the host template element.
2581 if (current->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
2582 DocumentFragment* frag = static_cast<DocumentFragment*>(current);
2583 nsIContent* fragHost = frag->GetHost();
2584 if (fragHost && nsNodeUtils::IsTemplateElement(fragHost)) {
2585 current = fragHost;
2586 }
2587 }
2589 if (aDescendentsOnly && current == aRoot) {
2590 return builder.ToString(aOut);
2591 }
2592 }
2593 }
2594 }
2596 void
2597 FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
2598 {
2599 aMarkup.Truncate();
2601 nsIDocument* doc = OwnerDoc();
2602 if (IsInHTMLDocument()) {
2603 Serialize(this, !aIncludeSelf, aMarkup);
2604 return;
2605 }
2607 nsAutoString contentType;
2608 doc->GetContentType(contentType);
2609 bool tryToCacheEncoder = !aIncludeSelf;
2611 nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
2612 if (!docEncoder) {
2613 docEncoder =
2614 do_CreateInstance(PromiseFlatCString(
2615 nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
2616 NS_ConvertUTF16toUTF8(contentType)
2617 ).get());
2618 }
2619 if (!docEncoder) {
2620 // This could be some type for which we create a synthetic document. Try
2621 // again as XML
2622 contentType.AssignLiteral("application/xml");
2623 docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
2624 // Don't try to cache the encoder since it would point to a different
2625 // contentType once it has been reinitialized.
2626 tryToCacheEncoder = false;
2627 }
2629 NS_ENSURE_TRUE_VOID(docEncoder);
2631 uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
2632 // Output DOM-standard newlines
2633 nsIDocumentEncoder::OutputLFLineBreak |
2634 // Don't do linebreaking that's not present in
2635 // the source
2636 nsIDocumentEncoder::OutputRaw |
2637 // Only check for mozdirty when necessary (bug 599983)
2638 nsIDocumentEncoder::OutputIgnoreMozDirty;
2640 if (IsEditable()) {
2641 nsCOMPtr<Element> elem = do_QueryInterface(this);
2642 nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
2643 if (editor && editor->OutputsMozDirty()) {
2644 flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
2645 }
2646 }
2648 DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
2649 MOZ_ASSERT(NS_SUCCEEDED(rv));
2651 if (aIncludeSelf) {
2652 docEncoder->SetNativeNode(this);
2653 } else {
2654 docEncoder->SetNativeContainerNode(this);
2655 }
2656 rv = docEncoder->EncodeToString(aMarkup);
2657 MOZ_ASSERT(NS_SUCCEEDED(rv));
2658 if (tryToCacheEncoder) {
2659 doc->SetCachedEncoder(docEncoder.forget());
2660 }
2661 }
2663 static bool
2664 ContainsMarkup(const nsAString& aStr)
2665 {
2666 // Note: we can't use FindCharInSet because null is one of the characters we
2667 // want to search for.
2668 const char16_t* start = aStr.BeginReading();
2669 const char16_t* end = aStr.EndReading();
2671 while (start != end) {
2672 char16_t c = *start;
2673 if (c == char16_t('<') ||
2674 c == char16_t('&') ||
2675 c == char16_t('\r') ||
2676 c == char16_t('\0')) {
2677 return true;
2678 }
2679 ++start;
2680 }
2682 return false;
2683 }
2685 void
2686 FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
2687 {
2688 FragmentOrElement* target = this;
2689 // Handle template case.
2690 if (nsNodeUtils::IsTemplateElement(target)) {
2691 DocumentFragment* frag =
2692 static_cast<HTMLTemplateElement*>(target)->Content();
2693 MOZ_ASSERT(frag);
2694 target = frag;
2695 }
2697 // Fast-path for strings with no markup. Limit this to short strings, to
2698 // avoid ContainsMarkup taking too long. The choice for 100 is based on
2699 // gut feeling.
2700 //
2701 // Don't do this for elements with a weird parser insertion mode, for
2702 // instance setting innerHTML = "" on a <html> element should add the
2703 // optional <head> and <body> elements.
2704 if (!target->HasWeirdParserInsertionMode() &&
2705 aInnerHTML.Length() < 100 && !ContainsMarkup(aInnerHTML)) {
2706 aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
2707 return;
2708 }
2710 nsIDocument* doc = target->OwnerDoc();
2712 // Batch possible DOMSubtreeModified events.
2713 mozAutoSubtreeModified subtree(doc, nullptr);
2715 target->FireNodeRemovedForChildren();
2717 // Needed when innerHTML is used in combination with contenteditable
2718 mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
2720 // Remove childnodes.
2721 uint32_t childCount = target->GetChildCount();
2722 nsAutoMutationBatch mb(target, true, false);
2723 for (uint32_t i = 0; i < childCount; ++i) {
2724 target->RemoveChildAt(0, true);
2725 }
2726 mb.RemovalDone();
2728 nsAutoScriptLoaderDisabler sld(doc);
2730 nsIAtom* contextLocalName = Tag();
2731 int32_t contextNameSpaceID = GetNameSpaceID();
2733 ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
2734 if (shadowRoot) {
2735 // Fix up the context to be the host of the ShadowRoot.
2736 contextLocalName = shadowRoot->GetHost()->Tag();
2737 contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
2738 }
2740 if (doc->IsHTML()) {
2741 int32_t oldChildCount = target->GetChildCount();
2742 aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
2743 target,
2744 contextLocalName,
2745 contextNameSpaceID,
2746 doc->GetCompatibilityMode() ==
2747 eCompatibility_NavQuirks,
2748 true);
2749 mb.NodesAdded();
2750 // HTML5 parser has notified, but not fired mutation events.
2751 nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
2752 oldChildCount);
2753 } else {
2754 nsRefPtr<DocumentFragment> df =
2755 nsContentUtils::CreateContextualFragment(target, aInnerHTML, true, aError);
2756 if (!aError.Failed()) {
2757 // Suppress assertion about node removal mutation events that can't have
2758 // listeners anyway, because no one has had the chance to register mutation
2759 // listeners on the fragment that comes from the parser.
2760 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
2762 static_cast<nsINode*>(target)->AppendChild(*df, aError);
2763 mb.NodesAdded();
2764 }
2765 }
2766 }
2768 nsINode::nsSlots*
2769 FragmentOrElement::CreateSlots()
2770 {
2771 return new nsDOMSlots();
2772 }
2774 void
2775 FragmentOrElement::FireNodeRemovedForChildren()
2776 {
2777 nsIDocument* doc = OwnerDoc();
2778 // Optimize the common case
2779 if (!nsContentUtils::
2780 HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
2781 return;
2782 }
2784 nsCOMPtr<nsIDocument> owningDoc = doc;
2786 nsCOMPtr<nsINode> child;
2787 for (child = GetFirstChild();
2788 child && child->GetParentNode() == this;
2789 child = child->GetNextSibling()) {
2790 nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
2791 }
2792 }
2794 size_t
2795 FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
2796 {
2797 size_t n = 0;
2798 n += nsIContent::SizeOfExcludingThis(aMallocSizeOf);
2799 n += mAttrsAndChildren.SizeOfExcludingThis(aMallocSizeOf);
2801 nsDOMSlots* slots = GetExistingDOMSlots();
2802 if (slots) {
2803 n += slots->SizeOfIncludingThis(aMallocSizeOf);
2804 }
2806 return n;
2807 }