/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=79: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Base class for all element classes; this provides an implementation * of DOM Core's nsIDOMElement, implements nsIContent, provides * utility methods for subclasses, and so forth. */ #include "mozilla/MemoryReporting.h" #include "mozilla/Util.h" #include "mozilla/Likely.h" #include "mozilla/dom/FragmentOrElement.h" #include "mozilla/dom/Attr.h" #include "nsDOMAttributeMap.h" #include "nsIAtom.h" #include "nsINodeInfo.h" #include "nsIDocumentInlines.h" #include "nsIDOMNodeList.h" #include "nsIDOMDocument.h" #include "nsIContentIterator.h" #include "nsEventListenerManager.h" #include "nsFocusManager.h" #include "nsILinkHandler.h" #include "nsIScriptGlobalObject.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsIFrame.h" #include "nsIAnonymousContentCreator.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsStyleConsts.h" #include "nsString.h" #include "nsUnicharUtils.h" #include "nsEventStateManager.h" #include "nsIDOMEvent.h" #include "nsDOMCID.h" #include "nsIServiceManager.h" #include "nsIDOMCSSStyleDeclaration.h" #include "nsDOMCSSAttrDeclaration.h" #include "nsINameSpaceManager.h" #include "nsContentList.h" #include "nsDOMTokenList.h" #include "nsXBLPrototypeBinding.h" #include "nsError.h" #include "nsDOMString.h" #include "nsIScriptSecurityManager.h" #include "nsIDOMMutationEvent.h" #include "nsMutationEvent.h" #include "nsNodeUtils.h" #include "nsDocument.h" #include "nsAttrValueOrString.h" #ifdef MOZ_XUL #include "nsXULElement.h" #endif /* MOZ_XUL */ #include "nsFrameManager.h" #include "nsFrameSelection.h" #ifdef DEBUG #include "nsRange.h" #endif #include "nsBindingManager.h" #include "nsXBLBinding.h" #include "nsPIDOMWindow.h" #include "nsPIBoxObject.h" #include "nsClientRect.h" #include "nsSVGUtils.h" #include "nsLayoutUtils.h" #include "nsGkAtoms.h" #include "nsContentUtils.h" #include "nsIDOMEventListener.h" #include "nsIWebNavigation.h" #include "nsIBaseWindow.h" #include "nsIWidget.h" #include "jsapi.h" #include "nsNodeInfoManager.h" #include "nsICategoryManager.h" #include "nsIDOMDocumentType.h" #include "nsIDOMUserDataHandler.h" #include "nsGenericHTMLElement.h" #include "nsIEditor.h" #include "nsIEditorIMESupport.h" #include "nsEventDispatcher.h" #include "nsContentCreatorFunctions.h" #include "nsIControllers.h" #include "nsView.h" #include "nsViewManager.h" #include "nsIScrollableFrame.h" #include "ChildIterator.h" #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */ #include "nsRuleProcessorData.h" #include "nsAsyncDOMEvent.h" #include "nsTextNode.h" #include "mozilla/dom/NodeListBinding.h" #include "mozilla/dom/UndoManager.h" #ifdef MOZ_XUL #include "nsIXULDocument.h" #endif /* MOZ_XUL */ #include "nsCycleCollectionParticipant.h" #include "nsCCUncollectableMarker.h" #include "mozAutoDocUpdate.h" #include "prprf.h" #include "nsDOMMutationObserver.h" #include "nsWrapperCacheInlines.h" #include "nsCycleCollector.h" #include "xpcpublic.h" #include "nsIScriptError.h" #include "nsLayoutStatics.h" #include "mozilla/Telemetry.h" #include "mozilla/CORSMode.h" #include "nsStyledElement.h" using namespace mozilla; using namespace mozilla::dom; int32_t nsIContent::sTabFocusModel = eTabFocus_any; bool nsIContent::sTabFocusModelAppliesToXUL = false; uint32_t nsMutationGuard::sMutationCount = 0; nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const { // This handles also nested native anonymous content. for (const nsIContent *content = this; content; content = content->GetBindingParent()) { if (!content->ChromeOnlyAccess()) { // Oops, this function signature allows casting const to // non-const. (Then again, so does GetChildAt(0)->GetParent().) return const_cast(content); } } return nullptr; } nsIContent* nsIContent::GetFlattenedTreeParent() const { if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { nsIContent* parent = OwnerDoc()->BindingManager()-> GetInsertionParent(const_cast(this)); if (parent) { return parent; } } return GetParent(); } nsIContent::IMEState nsIContent::GetDesiredIMEState() { if (!IsEditableInternal()) { // Check for the special case where we're dealing with elements which don't // have the editable flag set, but are readwrite (such as text controls). if (!IsElement() || !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) { return IMEState(IMEState::DISABLED); } } // NOTE: The content for independent editors (e.g., input[type=text], // textarea) must override this method, so, we don't need to worry about // that here. nsIContent *editableAncestor = GetEditingHost(); // This is in another editable content, use the result of it. if (editableAncestor && editableAncestor != this) { return editableAncestor->GetDesiredIMEState(); } nsIDocument* doc = GetCurrentDoc(); if (!doc) { return IMEState(IMEState::DISABLED); } nsIPresShell* ps = doc->GetShell(); if (!ps) { return IMEState(IMEState::DISABLED); } nsPresContext* pc = ps->GetPresContext(); if (!pc) { return IMEState(IMEState::DISABLED); } nsIEditor* editor = nsContentUtils::GetHTMLEditor(pc); nsCOMPtr imeEditor = do_QueryInterface(editor); if (!imeEditor) { return IMEState(IMEState::DISABLED); } IMEState state; imeEditor->GetPreferredIMEState(&state); return state; } bool nsIContent::HasIndependentSelection() { nsIFrame* frame = GetPrimaryFrame(); return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION); } dom::Element* nsIContent::GetEditingHost() { // If this isn't editable, return nullptr. NS_ENSURE_TRUE(IsEditableInternal(), nullptr); nsIDocument* doc = GetCurrentDoc(); NS_ENSURE_TRUE(doc, nullptr); // If this is in designMode, we should return if (doc->HasFlag(NODE_IS_EDITABLE)) { return doc->GetBodyElement(); } nsIContent* content = this; for (dom::Element* parent = GetParentElement(); parent && parent->HasFlag(NODE_IS_EDITABLE); parent = content->GetParentElement()) { content = parent; } return content->AsElement(); } nsresult nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) const { if (aNamespacePrefix.EqualsLiteral("xml")) { // Special-case for xml prefix aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace"); return NS_OK; } if (aNamespacePrefix.EqualsLiteral("xmlns")) { // Special-case for xmlns prefix aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/"); return NS_OK; } nsCOMPtr name; if (!aNamespacePrefix.IsEmpty()) { name = do_GetAtom(aNamespacePrefix); NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY); } else { name = nsGkAtoms::xmlns; } // Trace up the content parent chain looking for the namespace // declaration that declares aNamespacePrefix. const nsIContent* content = this; do { if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) return NS_OK; } while ((content = content->GetParent())); return NS_ERROR_FAILURE; } already_AddRefed nsIContent::GetBaseURI() const { nsIDocument* doc = OwnerDoc(); // Start with document base nsCOMPtr base = doc->GetDocBaseURI(); // Collect array of xml:base attribute values up the parent chain. This // is slightly slower for the case when there are xml:base attributes, but // faster for the far more common case of there not being any such // attributes. // Also check for SVG elements which require special handling nsAutoTArray baseAttrs; nsString attr; const nsIContent *elem = this; do { // First check for SVG specialness (why is this SVG specific?) if (elem->IsSVG()) { nsIContent* bindingParent = elem->GetBindingParent(); if (bindingParent) { nsXBLBinding* binding = bindingParent->GetXBLBinding(); if (binding) { // XXX sXBL/XBL2 issue // If this is an anonymous XBL element use the binding // document for the base URI. // XXX Will fail with xml:base base = binding->PrototypeBinding()->DocURI(); break; } } } nsIURI* explicitBaseURI = elem->GetExplicitBaseURI(); if (explicitBaseURI) { base = explicitBaseURI; break; } // Otherwise check for xml:base attribute elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr); if (!attr.IsEmpty()) { baseAttrs.AppendElement(attr); } elem = elem->GetParent(); } while(elem); // Now resolve against all xml:base attrs for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) { nsCOMPtr newBase; nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i], doc->GetDocumentCharacterSet().get(), base); // Do a security check, almost the same as nsDocument::SetBaseURL() // Only need to do this on the final uri if (NS_SUCCEEDED(rv) && i == 0) { rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(NodePrincipal(), newBase, nsIScriptSecurityManager::STANDARD); } if (NS_SUCCEEDED(rv)) { base.swap(newBase); } } return base.forget(); } //---------------------------------------------------------------------- static inline JSObject* GetJSObjectChild(nsWrapperCache* aCache) { return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() : nullptr; } static bool NeedsScriptTraverse(nsWrapperCache* aCache) { JSObject* o = GetJSObjectChild(aCache); return o && xpc_IsGrayGCThing(o); } //---------------------------------------------------------------------- NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList) // If nsChildContentList is changed so that any additional fields are // traversed by the cycle collector, then CAN_SKIP must be updated to // check that the additional fields are null. NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsChildContentList) // nsChildContentList only ever has a single child, its wrapper, so if // the wrapper is black, the list can't be part of a garbage cycle. NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList) return !NeedsScriptTraverse(tmp); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList) return !NeedsScriptTraverse(tmp); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END // CanSkipThis returns false to avoid problems with incomplete unlinking. NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList) NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END NS_INTERFACE_TABLE_HEAD(nsChildContentList) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_TABLE2(nsChildContentList, nsINodeList, nsIDOMNodeList) NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsChildContentList) NS_INTERFACE_MAP_END JSObject* nsChildContentList::WrapObject(JSContext *cx, JS::Handle scope) { return NodeListBinding::Wrap(cx, scope, this); } NS_IMETHODIMP nsChildContentList::GetLength(uint32_t* aLength) { *aLength = mNode ? mNode->GetChildCount() : 0; return NS_OK; } NS_IMETHODIMP nsChildContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn) { nsINode* node = Item(aIndex); if (!node) { *aReturn = nullptr; return NS_OK; } return CallQueryInterface(node, aReturn); } nsIContent* nsChildContentList::Item(uint32_t aIndex) { if (mNode) { return mNode->GetChildAt(aIndex); } return nullptr; } int32_t nsChildContentList::IndexOf(nsIContent* aContent) { if (mNode) { return mNode->IndexOf(aContent); } return -1; } //---------------------------------------------------------------------- NS_IMPL_CYCLE_COLLECTION_1(nsNode3Tearoff, mNode) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNode3Tearoff) NS_INTERFACE_MAP_ENTRY(nsIDOMXPathNSResolver) NS_INTERFACE_MAP_END_AGGREGATED(mNode) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNode3Tearoff) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNode3Tearoff) NS_IMETHODIMP nsNode3Tearoff::LookupNamespaceURI(const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) { mNode->LookupNamespaceURI(aNamespacePrefix, aNamespaceURI); return NS_OK; } nsIHTMLCollection* FragmentOrElement::Children() { FragmentOrElement::nsDOMSlots *slots = DOMSlots(); if (!slots->mChildrenList) { slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterix, nsGkAtoms::_asterix, false); } return slots->mChildrenList; } //---------------------------------------------------------------------- NS_IMPL_ISUPPORTS1(nsNodeWeakReference, nsIWeakReference) nsNodeWeakReference::~nsNodeWeakReference() { if (mNode) { NS_ASSERTION(mNode->Slots()->mWeakReference == this, "Weak reference has wrong value"); mNode->Slots()->mWeakReference = nullptr; } } NS_IMETHODIMP nsNodeWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr) { return mNode ? mNode->QueryInterface(aIID, aInstancePtr) : NS_ERROR_NULL_POINTER; } NS_IMPL_CYCLE_COLLECTION_1(nsNodeSupportsWeakRefTearoff, mNode) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_END_AGGREGATED(mNode) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff) NS_IMETHODIMP nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr) { nsINode::nsSlots* slots = mNode->Slots(); if (!slots->mWeakReference) { slots->mWeakReference = new nsNodeWeakReference(mNode); NS_ENSURE_TRUE(slots->mWeakReference, NS_ERROR_OUT_OF_MEMORY); } NS_ADDREF(*aInstancePtr = slots->mWeakReference); return NS_OK; } //---------------------------------------------------------------------- NS_IMPL_CYCLE_COLLECTION_1(nsTouchEventReceiverTearoff, mElement) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTouchEventReceiverTearoff) NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver) NS_INTERFACE_MAP_END_AGGREGATED(mElement) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTouchEventReceiverTearoff) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTouchEventReceiverTearoff) //---------------------------------------------------------------------- NS_IMPL_CYCLE_COLLECTION_1(nsInlineEventHandlersTearoff, mElement) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsInlineEventHandlersTearoff) NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers) NS_INTERFACE_MAP_END_AGGREGATED(mElement) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsInlineEventHandlersTearoff) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsInlineEventHandlersTearoff) //---------------------------------------------------------------------- FragmentOrElement::nsDOMSlots::nsDOMSlots() : nsINode::nsSlots(), mDataset(nullptr), mUndoManager(nullptr), mBindingParent(nullptr) { } FragmentOrElement::nsDOMSlots::~nsDOMSlots() { if (mAttributeMap) { mAttributeMap->DropReference(); } if (mClassList) { mClassList->DropReference(); } } void FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle"); cb.NoteXPCOMChild(mStyle.get()); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle"); cb.NoteXPCOMChild(mSMILOverrideStyle.get()); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap"); cb.NoteXPCOMChild(mAttributeMap.get()); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mUndoManager"); cb.NoteXPCOMChild(mUndoManager.get()); if (aIsXUL) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers"); cb.NoteXPCOMChild(mControllers); } NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding"); cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding)); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList"); cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList)); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList"); cb.NoteXPCOMChild(mClassList.get()); } void FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL) { mStyle = nullptr; mSMILOverrideStyle = nullptr; if (mAttributeMap) { mAttributeMap->DropReference(); mAttributeMap = nullptr; } if (aIsXUL) NS_IF_RELEASE(mControllers); mXBLBinding = nullptr; mChildrenList = nullptr; mUndoManager = nullptr; if (mClassList) { mClassList->DropReference(); mClassList = nullptr; } } size_t FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); if (mAttributeMap) { n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf); } // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - Superclass members (nsINode::nsSlots) // - mStyle // - mDataSet // - mSMILOverrideStyle // - mSMILOverrideStyleRule // - mChildrenList // - mClassList // The following members are not measured: // - mBindingParent / mControllers: because they're non-owning return n; } FragmentOrElement::FragmentOrElement(already_AddRefed aNodeInfo) : nsIContent(aNodeInfo) { } FragmentOrElement::~FragmentOrElement() { NS_PRECONDITION(!IsInDoc(), "Please remove this from the document properly"); if (GetParent()) { NS_RELEASE(mParent); } } already_AddRefed FragmentOrElement::GetChildren(uint32_t aFilter) { nsRefPtr list = new nsSimpleContentList(this); if (!list) { return nullptr; } nsIFrame *frame = GetPrimaryFrame(); // Append :before generated content. if (frame) { nsIFrame *beforeFrame = nsLayoutUtils::GetBeforeFrame(frame); if (beforeFrame) { list->AppendElement(beforeFrame->GetContent()); } } // If XBL is bound to this node then append XBL anonymous content including // explict content altered by insertion point if we were requested for XBL // anonymous content, otherwise append explicit content with respect to // insertion point if any. if (!(aFilter & eAllButXBL)) { FlattenedChildIterator iter(this); for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { list->AppendElement(child); } } else { ExplicitChildIterator iter(this); for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { list->AppendElement(child); } } if (frame) { // Append native anonymous content to the end. nsIAnonymousContentCreator* creator = do_QueryFrame(frame); if (creator) { creator->AppendAnonymousContentTo(*list, aFilter); } // Append :after generated content. nsIFrame *afterFrame = nsLayoutUtils::GetAfterFrame(frame); if (afterFrame) { list->AppendElement(afterFrame->GetContent()); } } return list.forget(); } static nsIContent* FindChromeAccessOnlySubtreeOwner(nsIContent* aContent) { if (aContent->ChromeOnlyAccess()) { bool chromeAccessOnly = false; while (aContent && !chromeAccessOnly) { chromeAccessOnly = aContent->IsRootOfChromeAccessOnlySubtree(); aContent = aContent->GetParent(); } } return aContent; } nsresult nsIContent::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { //FIXME! Document how this event retargeting works, Bug 329124. aVisitor.mCanHandle = true; aVisitor.mMayHaveListenerManager = HasListenerManager(); // Don't propagate mouseover and mouseout events when mouse is moving // inside chrome access only content. bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree(); if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH || aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH) && // Check if we should stop event propagation when event has just been // dispatched or when we're about to propagate from // chrome access only subtree. ((this == aVisitor.mEvent->originalTarget && !ChromeOnlyAccess()) || isAnonForEvents)) { nsCOMPtr relatedTarget = do_QueryInterface(static_cast (aVisitor.mEvent)->relatedTarget); if (relatedTarget && relatedTarget->OwnerDoc() == OwnerDoc()) { // If current target is anonymous for events or we know that related // target is descendant of an element which is anonymous for events, // we may want to stop event propagation. // If this is the original target, aVisitor.mRelatedTargetIsInAnon // must be updated. if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon || (aVisitor.mEvent->originalTarget == this && (aVisitor.mRelatedTargetIsInAnon = relatedTarget->ChromeOnlyAccess()))) { nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this); if (anonOwner) { nsIContent* anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(relatedTarget); if (anonOwnerRelated) { // Note, anonOwnerRelated may still be inside some other // native anonymous subtree. The case where anonOwner is still // inside native anonymous subtree will be handled when event // propagates up in the DOM tree. while (anonOwner != anonOwnerRelated && anonOwnerRelated->ChromeOnlyAccess()) { anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(anonOwnerRelated); } if (anonOwner == anonOwnerRelated) { #ifdef DEBUG_smaug nsCOMPtr originalTarget = do_QueryInterface(aVisitor.mEvent->originalTarget); nsAutoString ot, ct, rt; if (originalTarget) { originalTarget->Tag()->ToString(ot); } Tag()->ToString(ct); relatedTarget->Tag()->ToString(rt); printf("Stopping %s propagation:" "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s" "\n\trelatedTarget=%s %s \n%s", (aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH) ? "mouseover" : "mouseout", NS_ConvertUTF16toUTF8(ot).get(), NS_ConvertUTF16toUTF8(ct).get(), isAnonForEvents ? "(is native anonymous)" : (ChromeOnlyAccess() ? "(is in native anonymous subtree)" : ""), NS_ConvertUTF16toUTF8(rt).get(), relatedTarget->ChromeOnlyAccess() ? "(is in native anonymous subtree)" : "", (originalTarget && relatedTarget->FindFirstNonChromeOnlyAccessContent() == originalTarget->FindFirstNonChromeOnlyAccessContent()) ? "" : "Wrong event propagation!?!\n"); #endif aVisitor.mParentTarget = nullptr; // Event should not propagate to non-anon content. aVisitor.mCanHandle = isAnonForEvents; return NS_OK; } } } } } } nsIContent* parent = GetParent(); // Event may need to be retargeted if this is the root of a native // anonymous content subtree or event is dispatched somewhere inside XBL. if (isAnonForEvents) { #ifdef DEBUG // If a DOM event is explicitly dispatched using node.dispatchEvent(), then // all the events are allowed even in the native anonymous content.. nsCOMPtr t = do_QueryInterface(aVisitor.mEvent->originalTarget); NS_ASSERTION(!t || !t->ChromeOnlyAccess() || aVisitor.mEvent->eventStructType != NS_MUTATION_EVENT || aVisitor.mDOMEvent, "Mutation event dispatched in native anonymous content!?!"); #endif aVisitor.mEventTargetAtParent = parent; } else if (parent && aVisitor.mOriginalTargetIsInAnon) { nsCOMPtr content(do_QueryInterface(aVisitor.mEvent->target)); if (content && content->GetBindingParent() == parent) { aVisitor.mEventTargetAtParent = parent; } } // check for an anonymous parent // XXX XBL2/sXBL issue if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { nsIContent* insertionParent = OwnerDoc()->BindingManager()-> GetInsertionParent(this); NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent && aVisitor.mEventTargetAtParent != insertionParent), "Retargeting and having insertion parent!"); if (insertionParent) { parent = insertionParent; } } if (parent) { aVisitor.mParentTarget = parent; } else { aVisitor.mParentTarget = GetCurrentDoc(); } return NS_OK; } bool nsIContent::GetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsAString& aResult) const { if (IsElement()) { return AsElement()->GetAttr(aNameSpaceID, aName, aResult); } aResult.Truncate(); return false; } bool nsIContent::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const { return IsElement() && AsElement()->HasAttr(aNameSpaceID, aName); } bool nsIContent::AttrValueIs(int32_t aNameSpaceID, nsIAtom* aName, const nsAString& aValue, nsCaseTreatment aCaseSensitive) const { return IsElement() && AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive); } bool nsIContent::AttrValueIs(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const { return IsElement() && AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive); } const nsAttrValue* FragmentOrElement::DoGetClasses() const { NS_NOTREACHED("Shouldn't ever be called"); return nullptr; } NS_IMETHODIMP FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) { return NS_OK; } bool FragmentOrElement::IsLink(nsIURI** aURI) const { *aURI = nullptr; return false; } nsIContent* FragmentOrElement::GetBindingParent() const { nsDOMSlots *slots = GetExistingDOMSlots(); if (slots) { return slots->mBindingParent; } return nullptr; } nsXBLBinding* FragmentOrElement::GetXBLBinding() const { if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { nsDOMSlots *slots = GetExistingDOMSlots(); if (slots) { return slots->mXBLBinding; } } return nullptr; } void FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding, nsBindingManager* aOldBindingManager) { nsBindingManager* bindingManager; if (aOldBindingManager) { MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided " "when removing a binding."); bindingManager = aOldBindingManager; } else { bindingManager = OwnerDoc()->BindingManager(); } // After this point, aBinding will be the most-derived binding for aContent. // If we already have a binding for aContent, make sure to // remove it from the attached stack. Otherwise we might end up firing its // constructor twice (if aBinding inherits from it) or firing its constructor // after aContent has been deleted (if aBinding is null and the content node // dies before we process mAttachedStack). nsRefPtr oldBinding = GetXBLBinding(); if (oldBinding) { bindingManager->RemoveFromAttachedQueue(oldBinding); } nsDOMSlots *slots = DOMSlots(); if (aBinding) { SetFlags(NODE_MAY_BE_IN_BINDING_MNGR); slots->mXBLBinding = aBinding; bindingManager->AddBoundContent(this); } else { slots->mXBLBinding = nullptr; bindingManager->RemoveBoundContent(this); if (oldBinding) { oldBinding->SetBoundElement(nullptr); } } } nsresult FragmentOrElement::InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) { NS_PRECONDITION(aKid, "null ptr"); return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren); } void FragmentOrElement::RemoveChildAt(uint32_t aIndex, bool aNotify) { nsCOMPtr oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex); NS_ASSERTION(oldKid == GetChildAt(aIndex), "Unexpected child in RemoveChildAt"); if (oldKid) { doRemoveChildAt(aIndex, aNotify, oldKid, mAttrsAndChildren); } } void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent) { nsContentUtils::GetNodeTextContent(this, true, aTextContent); } void FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent, ErrorResult& aError) { aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false); } void FragmentOrElement::DestroyContent() { nsIDocument *document = OwnerDoc(); document->BindingManager()->RemovedFromDocument(this, document); document->ClearBoxObjectFor(this); // XXX We really should let cycle collection do this, but that currently still // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). nsContentUtils::ReleaseWrapper(this, this); uint32_t i, count = mAttrsAndChildren.ChildCount(); for (i = 0; i < count; ++i) { // The child can remove itself from the parent in BindToTree. mAttrsAndChildren.ChildAt(i)->DestroyContent(); } } void FragmentOrElement::SaveSubtreeState() { uint32_t i, count = mAttrsAndChildren.ChildCount(); for (i = 0; i < count; ++i) { mAttrsAndChildren.ChildAt(i)->SaveSubtreeState(); } } //---------------------------------------------------------------------- // Generic DOMNode implementations void FragmentOrElement::FireNodeInserted(nsIDocument* aDoc, nsINode* aParent, nsTArray >& aNodes) { uint32_t count = aNodes.Length(); for (uint32_t i = 0; i < count; ++i) { nsIContent* childContent = aNodes[i]; if (nsContentUtils::HasMutationListeners(childContent, NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) { nsMutationEvent mutation(true, NS_MUTATION_NODEINSERTED); mutation.mRelatedNode = do_QueryInterface(aParent); mozAutoSubtreeModified subtree(aDoc, aParent); (new nsAsyncDOMEvent(childContent, mutation))->RunDOMEventWhenSafe(); } } } //---------------------------------------------------------------------- // nsISupports implementation #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500 class ContentUnbinder : public nsRunnable { public: ContentUnbinder() { nsLayoutStatics::AddRef(); mLast = this; } ~ContentUnbinder() { Run(); nsLayoutStatics::Release(); } void UnbindSubtree(nsIContent* aNode) { if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE && aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { return; } FragmentOrElement* container = static_cast(aNode); uint32_t childCount = container->mAttrsAndChildren.ChildCount(); if (childCount) { while (childCount-- > 0) { // Hold a strong ref to the node when we remove it, because we may be // the last reference to it. We need to call TakeChildAt() and // update mFirstChild before calling UnbindFromTree, since this last // can notify various observers and they should really see consistent // tree state. nsCOMPtr child = container->mAttrsAndChildren.TakeChildAt(childCount); if (childCount == 0) { container->mFirstChild = nullptr; } UnbindSubtree(child); child->UnbindFromTree(); } } } NS_IMETHOD Run() { nsAutoScriptBlocker scriptBlocker; uint32_t len = mSubtreeRoots.Length(); if (len) { PRTime start = PR_Now(); for (uint32_t i = 0; i < len; ++i) { UnbindSubtree(mSubtreeRoots[i]); } mSubtreeRoots.Clear(); Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND, uint32_t(PR_Now() - start) / PR_USEC_PER_MSEC); } nsCycleCollector_dispatchDeferredDeletion(); if (this == sContentUnbinder) { sContentUnbinder = nullptr; if (mNext) { nsRefPtr next; next.swap(mNext); sContentUnbinder = next; next->mLast = mLast; mLast = nullptr; NS_DispatchToMainThread(next); } } return NS_OK; } static void UnbindAll() { nsRefPtr ub = sContentUnbinder; sContentUnbinder = nullptr; while (ub) { ub->Run(); ub = ub->mNext; } } static void Append(nsIContent* aSubtreeRoot) { if (!sContentUnbinder) { sContentUnbinder = new ContentUnbinder(); nsCOMPtr e = sContentUnbinder; NS_DispatchToMainThread(e); } if (sContentUnbinder->mLast->mSubtreeRoots.Length() >= SUBTREE_UNBINDINGS_PER_RUNNABLE) { sContentUnbinder->mLast->mNext = new ContentUnbinder(); sContentUnbinder->mLast = sContentUnbinder->mLast->mNext; } sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot); } private: nsAutoTArray, SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots; nsRefPtr mNext; ContentUnbinder* mLast; static ContentUnbinder* sContentUnbinder; }; ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr; void FragmentOrElement::ClearContentUnbinder() { ContentUnbinder::UnbindAll(); } NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement) nsINode::Unlink(tmp); if (tmp->HasProperties()) { if (tmp->IsHTML()) { tmp->DeleteProperty(nsGkAtoms::microdataProperties); tmp->DeleteProperty(nsGkAtoms::itemtype); tmp->DeleteProperty(nsGkAtoms::itemref); tmp->DeleteProperty(nsGkAtoms::itemprop); } } // Unlink child content (and unbind our subtree). if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) { uint32_t childCount = tmp->mAttrsAndChildren.ChildCount(); if (childCount) { // Don't allow script to run while we're unbinding everything. nsAutoScriptBlocker scriptBlocker; while (childCount-- > 0) { // Hold a strong ref to the node when we remove it, because we may be // the last reference to it. We need to call TakeChildAt() and // update mFirstChild before calling UnbindFromTree, since this last // can notify various observers and they should really see consistent // tree state. nsCOMPtr child = tmp->mAttrsAndChildren.TakeChildAt(childCount); if (childCount == 0) { tmp->mFirstChild = nullptr; } child->UnbindFromTree(); } } } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) { ContentUnbinder::Append(tmp); } /* else { The subtree root will end up to a ContentUnbinder, and that will unbind the child nodes. } */ // Unlink any DOM slots of interest. { nsDOMSlots *slots = tmp->GetExistingDOMSlots(); if (slots) { slots->Unlink(tmp->IsXUL()); } } { nsIDocument *doc; if (!tmp->GetParentNode() && (doc = tmp->OwnerDoc())) { doc->BindingManager()->RemovedFromDocument(tmp, doc); } } NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement) void FragmentOrElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild, void* aData) { uint32_t* gen = static_cast(aData); xpc_MarkInCCGeneration(static_cast(aChild), *gen); } void FragmentOrElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild, void* aData) { xpc_TryUnmarkWrappedGrayObject(static_cast(aChild)); } void FragmentOrElement::MarkNodeChildren(nsINode* aNode) { JSObject* o = GetJSObjectChild(aNode); xpc_UnmarkGrayObject(o); nsEventListenerManager* elm = aNode->GetListenerManager(false); if (elm) { elm->MarkForCC(); } if (aNode->HasProperties()) { nsIDocument* ownerDoc = aNode->OwnerDoc(); ownerDoc->PropertyTable(DOM_USER_DATA)-> Enumerate(aNode, FragmentOrElement::MarkUserData, &nsCCUncollectableMarker::sGeneration); ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)-> Enumerate(aNode, FragmentOrElement::MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration); } } nsINode* FindOptimizableSubtreeRoot(nsINode* aNode) { nsINode* p; while ((p = aNode->GetParentNode())) { if (aNode->UnoptimizableCCNode()) { return nullptr; } aNode = p; } if (aNode->UnoptimizableCCNode()) { return nullptr; } return aNode; } nsAutoTArray* gCCBlackMarkedNodes = nullptr; void ClearBlackMarkedNodes() { if (!gCCBlackMarkedNodes) { return; } uint32_t len = gCCBlackMarkedNodes->Length(); for (uint32_t i = 0; i < len; ++i) { nsINode* n = gCCBlackMarkedNodes->ElementAt(i); n->SetCCMarkedRoot(false); n->SetInCCBlackTree(false); } delete gCCBlackMarkedNodes; gCCBlackMarkedNodes = nullptr; } // static bool FragmentOrElement::CanSkipInCC(nsINode* aNode) { // Don't try to optimize anything during shutdown. if (nsCCUncollectableMarker::sGeneration == 0) { return false; } nsIDocument* currentDoc = aNode->GetCurrentDoc(); if (currentDoc && nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) { return !NeedsScriptTraverse(aNode); } // Bail out early if aNode is somewhere in anonymous content, // or otherwise unusual. if (aNode->UnoptimizableCCNode()) { return false; } nsINode* root = currentDoc ? static_cast(currentDoc) : FindOptimizableSubtreeRoot(aNode); if (!root) { return false; } // Subtree has been traversed already. if (root->CCMarkedRoot()) { return root->InCCBlackTree() && !NeedsScriptTraverse(aNode); } if (!gCCBlackMarkedNodes) { gCCBlackMarkedNodes = new nsAutoTArray; } // nodesToUnpurple contains nodes which will be removed // from the purple buffer if the DOM tree is black. nsAutoTArray nodesToUnpurple; // grayNodes need script traverse, so they aren't removed from // the purple buffer, but are marked to be in black subtree so that // traverse is faster. nsAutoTArray grayNodes; bool foundBlack = root->IsBlack(); if (root != currentDoc) { currentDoc = nullptr; if (NeedsScriptTraverse(root)) { grayNodes.AppendElement(root); } else if (static_cast(root)->IsPurple()) { nodesToUnpurple.AppendElement(static_cast(root)); } } // Traverse the subtree and check if we could know without CC // that it is black. // Note, this traverse is non-virtual and inline, so it should be a lot faster // than CC's generic traverse. for (nsIContent* node = root->GetFirstChild(); node; node = node->GetNextNode(root)) { foundBlack = foundBlack || node->IsBlack(); if (foundBlack && currentDoc) { // If we can mark the whole document black, no need to optimize // so much, since when the next purple node in the document will be // handled, it is fast to check that currentDoc is in CCGeneration. break; } if (NeedsScriptTraverse(node)) { // Gray nodes need real CC traverse. grayNodes.AppendElement(node); } else if (node->IsPurple()) { nodesToUnpurple.AppendElement(node); } } root->SetCCMarkedRoot(true); root->SetInCCBlackTree(foundBlack); gCCBlackMarkedNodes->AppendElement(root); if (!foundBlack) { return false; } if (currentDoc) { // Special case documents. If we know the document is black, // we can mark the document to be in CCGeneration. currentDoc-> MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); } else { for (uint32_t i = 0; i < grayNodes.Length(); ++i) { nsINode* node = grayNodes[i]; node->SetInCCBlackTree(true); } gCCBlackMarkedNodes->AppendElements(grayNodes); } // Subtree is black, we can remove non-gray purple nodes from // purple buffer. for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) { nsIContent* purple = nodesToUnpurple[i]; // Can't remove currently handled purple node. if (purple != aNode) { purple->RemovePurple(); } } return !NeedsScriptTraverse(aNode); } nsAutoTArray* gPurpleRoots = nullptr; nsAutoTArray* gNodesToUnbind = nullptr; void ClearCycleCollectorCleanupData() { if (gPurpleRoots) { uint32_t len = gPurpleRoots->Length(); for (uint32_t i = 0; i < len; ++i) { nsINode* n = gPurpleRoots->ElementAt(i); n->SetIsPurpleRoot(false); } delete gPurpleRoots; gPurpleRoots = nullptr; } if (gNodesToUnbind) { uint32_t len = gNodesToUnbind->Length(); for (uint32_t i = 0; i < len; ++i) { nsIContent* c = gNodesToUnbind->ElementAt(i); c->SetIsPurpleRoot(false); ContentUnbinder::Append(c); } delete gNodesToUnbind; gNodesToUnbind = nullptr; } } static bool ShouldClearPurple(nsIContent* aContent) { if (aContent && aContent->IsPurple()) { return true; } JSObject* o = GetJSObjectChild(aContent); if (o && xpc_IsGrayGCThing(o)) { return true; } if (aContent->HasListenerManager()) { return true; } return aContent->HasProperties(); } // If aNode is not optimizable, but is an element // with a frame in a document which has currently active presshell, // we can act as if it was optimizable. When the primary frame dies, aNode // will end up to the purple buffer because of the refcount change. bool NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode) { return aCurrentDoc->GetShell() && aNode->IsElement() && aNode->AsElement()->GetPrimaryFrame(); } bool OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode) { return aNode->IsElement() && aNode->AsElement()->GetXBLBinding(); } // CanSkip checks if aNode is black, and if it is, returns // true. If aNode is in a black DOM tree, CanSkip may also remove other objects // from purple buffer and unmark event listeners and user data. // If the root of the DOM tree is a document, less optimizations are done // since checking the blackness of the current document is usually fast and we // don't want slow down such common cases. bool FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) { // Don't try to optimize anything during shutdown. if (nsCCUncollectableMarker::sGeneration == 0) { return false; } bool unoptimizable = aNode->UnoptimizableCCNode(); nsIDocument* currentDoc = aNode->GetCurrentDoc(); if (currentDoc && nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration()) && (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) || OwnedByBindingManager(currentDoc, aNode))) { MarkNodeChildren(aNode); return true; } if (unoptimizable) { return false; } nsINode* root = currentDoc ? static_cast(currentDoc) : FindOptimizableSubtreeRoot(aNode); if (!root) { return false; } // Subtree has been traversed already, and aNode has // been handled in a way that doesn't require revisiting it. if (root->IsPurpleRoot()) { return false; } // nodesToClear contains nodes which are either purple or // gray. nsAutoTArray nodesToClear; bool foundBlack = root->IsBlack(); bool domOnlyCycle = false; if (root != currentDoc) { currentDoc = nullptr; if (!foundBlack) { domOnlyCycle = static_cast(root)->OwnedOnlyByTheDOMTree(); } if (ShouldClearPurple(static_cast(root))) { nodesToClear.AppendElement(static_cast(root)); } } // Traverse the subtree and check if we could know without CC // that it is black. // Note, this traverse is non-virtual and inline, so it should be a lot faster // than CC's generic traverse. for (nsIContent* node = root->GetFirstChild(); node; node = node->GetNextNode(root)) { foundBlack = foundBlack || node->IsBlack(); if (foundBlack) { domOnlyCycle = false; if (currentDoc) { // If we can mark the whole document black, no need to optimize // so much, since when the next purple node in the document will be // handled, it is fast to check that the currentDoc is in CCGeneration. break; } // No need to put stuff to the nodesToClear array, if we can clear it // already here. if (node->IsPurple() && (node != aNode || aRemovingAllowed)) { node->RemovePurple(); } MarkNodeChildren(node); } else { domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree(); if (ShouldClearPurple(node)) { // Collect interesting nodes which we can clear if we find that // they are kept alive in a black tree or are in a DOM-only cycle. nodesToClear.AppendElement(node); } } } if (!currentDoc || !foundBlack) { root->SetIsPurpleRoot(true); if (domOnlyCycle) { if (!gNodesToUnbind) { gNodesToUnbind = new nsAutoTArray(); } gNodesToUnbind->AppendElement(static_cast(root)); for (uint32_t i = 0; i < nodesToClear.Length(); ++i) { nsIContent* n = nodesToClear[i]; if ((n != aNode || aRemovingAllowed) && n->IsPurple()) { n->RemovePurple(); } } return true; } else { if (!gPurpleRoots) { gPurpleRoots = new nsAutoTArray(); } gPurpleRoots->AppendElement(root); } } if (!foundBlack) { return false; } if (currentDoc) { // Special case documents. If we know the document is black, // we can mark the document to be in CCGeneration. currentDoc-> MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration); MarkNodeChildren(currentDoc); } // Subtree is black, so we can remove purple nodes from // purple buffer and mark stuff that to be certainly alive. for (uint32_t i = 0; i < nodesToClear.Length(); ++i) { nsIContent* n = nodesToClear[i]; MarkNodeChildren(n); // Can't remove currently handled purple node, // unless aRemovingAllowed is true. if ((n != aNode || aRemovingAllowed) && n->IsPurple()) { n->RemovePurple(); } } return true; } bool FragmentOrElement::CanSkipThis(nsINode* aNode) { if (nsCCUncollectableMarker::sGeneration == 0) { return false; } if (aNode->IsBlack()) { return true; } nsIDocument* c = aNode->GetCurrentDoc(); return ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) || aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode); } void FragmentOrElement::InitCCCallbacks() { nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData); nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes); } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement) return FragmentOrElement::CanSkip(tmp, aRemovingAllowed); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement) return FragmentOrElement::CanSkipInCC(tmp); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement) return FragmentOrElement::CanSkipThis(tmp); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END static const char* kNSURIs[] = { " ([none])", " (xmlns)", " (xml)", " (xhtml)", " (XLink)", " (XSLT)", " (XBL)", " (MathML)", " (RDF)", " (XUL)", " (SVG)", " (XML Events)" }; NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement) if (MOZ_UNLIKELY(cb.WantDebugInfo())) { char name[512]; uint32_t nsid = tmp->GetNameSpaceID(); nsAtomCString localName(tmp->NodeInfo()->NameAtom()); nsAutoCString uri; if (tmp->OwnerDoc()->GetDocumentURI()) { tmp->OwnerDoc()->GetDocumentURI()->GetSpec(uri); } nsAutoString id; nsIAtom* idAtom = tmp->GetID(); if (idAtom) { id.AppendLiteral(" id='"); id.Append(nsDependentAtomString(idAtom)); id.AppendLiteral("'"); } nsAutoString classes; const nsAttrValue* classAttrValue = tmp->GetClasses(); if (classAttrValue) { classes.AppendLiteral(" class='"); nsAutoString classString; classAttrValue->ToString(classString); classString.ReplaceChar(PRUnichar('\n'), PRUnichar(' ')); classes.Append(classString); classes.AppendLiteral("'"); } const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : ""; PR_snprintf(name, sizeof(name), "FragmentOrElement%s %s%s%s %s", nsuri, localName.get(), NS_ConvertUTF16toUTF8(id).get(), NS_ConvertUTF16toUTF8(classes).get(), uri.get()); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); } else { NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get()) } // Always need to traverse script objects, so do that before we check // if we're uncollectable. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS if (!nsINode::Traverse(tmp, cb)) { return NS_SUCCESS_INTERRUPTED_TRAVERSE; } tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb); if (tmp->HasProperties()) { if (tmp->IsHTML()) { nsISupports* property = static_cast (tmp->GetProperty(nsGkAtoms::microdataProperties)); cb.NoteXPCOMChild(property); property = static_cast(tmp->GetProperty(nsGkAtoms::itemref)); cb.NoteXPCOMChild(property); property = static_cast(tmp->GetProperty(nsGkAtoms::itemprop)); cb.NoteXPCOMChild(property); property = static_cast(tmp->GetProperty(nsGkAtoms::itemtype)); cb.NoteXPCOMChild(property); } } // Traverse attribute names and child content. { uint32_t i; uint32_t attrs = tmp->mAttrsAndChildren.AttrCount(); for (i = 0; i < attrs; i++) { const nsAttrName* name = tmp->mAttrsAndChildren.AttrNameAt(i); if (!name->IsAtom()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]->NodeInfo()"); cb.NoteXPCOMChild(name->NodeInfo()); } } uint32_t kids = tmp->mAttrsAndChildren.ChildCount(); for (i = 0; i < kids; i++) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]"); cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i)); } } // Traverse any DOM slots of interest. { nsDOMSlots *slots = tmp->GetExistingDOMSlots(); if (slots) { slots->Traverse(cb, tmp->IsXUL()); } } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN(FragmentOrElement) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement) NS_INTERFACE_MAP_ENTRY(Element) NS_INTERFACE_MAP_ENTRY(nsIContent) NS_INTERFACE_MAP_ENTRY(nsINode) NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference, new nsNodeSupportsWeakRefTearoff(this)) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver, new nsNode3Tearoff(this)) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsITouchEventReceiver, new nsTouchEventReceiverTearoff(this)) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIInlineEventHandlers, new nsInlineEventHandlersTearoff(this)) // DOM bindings depend on the identity pointer being the // same as nsINode (which nsIContent inherits). NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement) NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement, nsNodeUtils::LastRelease(this)) nsresult FragmentOrElement::PostQueryInterface(REFNSIID aIID, void** aInstancePtr) { return OwnerDoc()->BindingManager()->GetBindingImplementation(this, aIID, aInstancePtr); } //---------------------------------------------------------------------- nsresult FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst) { uint32_t i, count = mAttrsAndChildren.AttrCount(); for (i = 0; i < count; ++i) { const nsAttrName* name = mAttrsAndChildren.AttrNameAt(i); const nsAttrValue* value = mAttrsAndChildren.AttrAt(i); nsAutoString valStr; value->ToString(valStr); nsresult rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(), valStr, false); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } const nsTextFragment* FragmentOrElement::GetText() { return nullptr; } uint32_t FragmentOrElement::TextLength() const { // We can remove this assertion if it turns out to be useful to be able // to depend on this returning 0 NS_NOTREACHED("called FragmentOrElement::TextLength"); return 0; } nsresult FragmentOrElement::SetText(const PRUnichar* aBuffer, uint32_t aLength, bool aNotify) { NS_ERROR("called FragmentOrElement::SetText"); return NS_ERROR_FAILURE; } nsresult FragmentOrElement::AppendText(const PRUnichar* aBuffer, uint32_t aLength, bool aNotify) { NS_ERROR("called FragmentOrElement::AppendText"); return NS_ERROR_FAILURE; } bool FragmentOrElement::TextIsOnlyWhitespace() { return false; } void FragmentOrElement::AppendTextTo(nsAString& aResult) { // We can remove this assertion if it turns out to be useful to be able // to depend on this appending nothing. NS_NOTREACHED("called FragmentOrElement::TextLength"); } uint32_t FragmentOrElement::GetChildCount() const { return mAttrsAndChildren.ChildCount(); } nsIContent * FragmentOrElement::GetChildAt(uint32_t aIndex) const { return mAttrsAndChildren.GetSafeChildAt(aIndex); } nsIContent * const * FragmentOrElement::GetChildArray(uint32_t* aChildCount) const { return mAttrsAndChildren.GetChildArray(aChildCount); } int32_t FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const { return mAttrsAndChildren.IndexOfChild(aPossibleChild); } nsINode::nsSlots* FragmentOrElement::CreateSlots() { return new nsDOMSlots(); } void FragmentOrElement::FireNodeRemovedForChildren() { nsIDocument* doc = OwnerDoc(); // Optimize the common case if (!nsContentUtils:: HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) { return; } nsCOMPtr owningDoc = doc; nsCOMPtr child; for (child = GetFirstChild(); child && child->GetParentNode() == this; child = child->GetNextSibling()) { nsContentUtils::MaybeFireNodeRemoved(child, this, doc); } } size_t FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = 0; n += nsIContent::SizeOfExcludingThis(aMallocSizeOf); n += mAttrsAndChildren.SizeOfExcludingThis(aMallocSizeOf); nsDOMSlots* slots = GetExistingDOMSlots(); if (slots) { n += slots->SizeOfIncludingThis(aMallocSizeOf); } return n; }