michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsNodeUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsINode.h" michael@0: #include "nsIContent.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsIMutationObserver.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMUserDataHandler.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "pldhash.h" michael@0: #include "nsIDOMAttr.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsDocument.h" michael@0: #ifdef MOZ_XUL michael@0: #include "nsXULElement.h" michael@0: #endif michael@0: #include "nsBindingManager.h" michael@0: #include "nsGenericHTMLElement.h" michael@0: #include "mozilla/dom/HTMLImageElement.h" michael@0: #include "mozilla/dom/HTMLMediaElement.h" michael@0: #include "nsWrapperCacheInlines.h" michael@0: #include "nsObjectLoadingContent.h" michael@0: #include "nsDOMMutationObserver.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/HTMLTemplateElement.h" michael@0: #include "mozilla/dom/ShadowRoot.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using mozilla::AutoJSContext; michael@0: michael@0: // This macro expects the ownerDocument of content_ to be in scope as michael@0: // |nsIDocument* doc| michael@0: #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_) \ michael@0: PR_BEGIN_MACRO \ michael@0: bool needsEnterLeave = doc->MayHaveDOMMutationObservers(); \ michael@0: if (needsEnterLeave) { \ michael@0: nsDOMMutationObserver::EnterMutationHandling(); \ michael@0: } \ michael@0: nsINode* node = content_; \ michael@0: NS_ASSERTION(node->OwnerDoc() == doc, "Bogus document"); \ michael@0: if (doc) { \ michael@0: doc->BindingManager()->func_ params_; \ michael@0: } \ michael@0: do { \ michael@0: nsINode::nsSlots* slots = node->GetExistingSlots(); \ michael@0: if (slots && !slots->mMutationObservers.IsEmpty()) { \ michael@0: /* No need to explicitly notify the first observer first \ michael@0: since that'll happen anyway. */ \ michael@0: NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS( \ michael@0: slots->mMutationObservers, nsIMutationObserver, \ michael@0: func_, params_); \ michael@0: } \ michael@0: ShadowRoot* shadow = ShadowRoot::FromNode(node); \ michael@0: if (shadow) { \ michael@0: node = shadow->GetPoolHost(); \ michael@0: } else { \ michael@0: node = node->GetParentNode(); \ michael@0: } \ michael@0: } while (node); \ michael@0: if (needsEnterLeave) { \ michael@0: nsDOMMutationObserver::LeaveMutationHandling(); \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: void michael@0: nsNodeUtils::CharacterDataWillChange(nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo) michael@0: { michael@0: nsIDocument* doc = aContent->OwnerDoc(); michael@0: IMPL_MUTATION_NOTIFICATION(CharacterDataWillChange, aContent, michael@0: (doc, aContent, aInfo)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::CharacterDataChanged(nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo) michael@0: { michael@0: nsIDocument* doc = aContent->OwnerDoc(); michael@0: IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent, michael@0: (doc, aContent, aInfo)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::AttributeWillChange(Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: nsIDocument* doc = aElement->OwnerDoc(); michael@0: IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement, michael@0: (doc, aElement, aNameSpaceID, aAttribute, michael@0: aModType)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::AttributeChanged(Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: nsIDocument* doc = aElement->OwnerDoc(); michael@0: IMPL_MUTATION_NOTIFICATION(AttributeChanged, aElement, michael@0: (doc, aElement, aNameSpaceID, aAttribute, michael@0: aModType)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::AttributeSetToCurrentValue(Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute) michael@0: { michael@0: nsIDocument* doc = aElement->OwnerDoc(); michael@0: IMPL_MUTATION_NOTIFICATION(AttributeSetToCurrentValue, aElement, michael@0: (doc, aElement, aNameSpaceID, aAttribute)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::ContentAppended(nsIContent* aContainer, michael@0: nsIContent* aFirstNewContent, michael@0: int32_t aNewIndexInContainer) michael@0: { michael@0: nsIDocument* doc = aContainer->OwnerDoc(); michael@0: michael@0: IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer, michael@0: (doc, aContainer, aFirstNewContent, michael@0: aNewIndexInContainer)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::ContentInserted(nsINode* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer) michael@0: { michael@0: NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) || michael@0: aContainer->IsNodeOfType(nsINode::eDOCUMENT), michael@0: "container must be an nsIContent or an nsIDocument"); michael@0: nsIContent* container; michael@0: nsIDocument* doc = aContainer->OwnerDoc(); michael@0: nsIDocument* document; michael@0: if (aContainer->IsNodeOfType(nsINode::eCONTENT)) { michael@0: container = static_cast(aContainer); michael@0: document = doc; michael@0: } michael@0: else { michael@0: container = nullptr; michael@0: document = static_cast(aContainer); michael@0: } michael@0: michael@0: IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer, michael@0: (document, container, aChild, aIndexInContainer)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::ContentRemoved(nsINode* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer, michael@0: nsIContent* aPreviousSibling) michael@0: { michael@0: NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) || michael@0: aContainer->IsNodeOfType(nsINode::eDOCUMENT), michael@0: "container must be an nsIContent or an nsIDocument"); michael@0: nsIContent* container; michael@0: nsIDocument* doc = aContainer->OwnerDoc(); michael@0: nsIDocument* document; michael@0: if (aContainer->IsNodeOfType(nsINode::eCONTENT)) { michael@0: container = static_cast(aContainer); michael@0: document = doc; michael@0: } michael@0: else { michael@0: container = nullptr; michael@0: document = static_cast(aContainer); michael@0: } michael@0: michael@0: IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer, michael@0: (document, container, aChild, aIndexInContainer, michael@0: aPreviousSibling)); michael@0: } michael@0: michael@0: void michael@0: nsNodeUtils::LastRelease(nsINode* aNode) michael@0: { michael@0: nsINode::nsSlots* slots = aNode->GetExistingSlots(); michael@0: if (slots) { michael@0: if (!slots->mMutationObservers.IsEmpty()) { michael@0: NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers, michael@0: nsIMutationObserver, michael@0: NodeWillBeDestroyed, (aNode)); michael@0: } michael@0: michael@0: delete slots; michael@0: aNode->mSlots = nullptr; michael@0: } michael@0: michael@0: // Kill properties first since that may run external code, so we want to michael@0: // be in as complete state as possible at that time. michael@0: if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) { michael@0: // Delete all properties before tearing down the document. Some of the michael@0: // properties are bound to nsINode objects and the destructor functions of michael@0: // the properties may want to use the owner document of the nsINode. michael@0: static_cast(aNode)->DeleteAllProperties(); michael@0: } michael@0: else { michael@0: if (aNode->HasProperties()) { michael@0: // Strong reference to the document so that deleting properties can't michael@0: // delete the document. michael@0: nsCOMPtr document = aNode->OwnerDoc(); michael@0: document->DeleteAllPropertiesFor(aNode); michael@0: } michael@0: michael@0: // I wonder whether it's faster to do the HasFlag check first.... michael@0: if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) && michael@0: aNode->HasFlag(ADDED_TO_FORM)) { michael@0: // Tell the form (if any) this node is going away. Don't michael@0: // notify, since we're being destroyed in any case. michael@0: static_cast(aNode)->ClearForm(true); michael@0: } michael@0: michael@0: if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::img) && michael@0: aNode->HasFlag(ADDED_TO_FORM)) { michael@0: HTMLImageElement* imageElem = static_cast(aNode); michael@0: imageElem->ClearForm(true); michael@0: } michael@0: } michael@0: aNode->UnsetFlags(NODE_HAS_PROPERTIES); michael@0: michael@0: if (aNode->NodeType() != nsIDOMNode::DOCUMENT_NODE && michael@0: aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) { michael@0: #ifdef DEBUG michael@0: if (nsContentUtils::IsInitialized()) { michael@0: EventListenerManager* manager = michael@0: nsContentUtils::GetExistingListenerManagerForNode(aNode); michael@0: if (!manager) { michael@0: NS_ERROR("Huh, our bit says we have a listener manager list, " michael@0: "but there's nothing in the hash!?!!"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: nsContentUtils::RemoveListenerManager(aNode); michael@0: aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER); michael@0: } michael@0: michael@0: if (aNode->IsElement()) { michael@0: nsIDocument* ownerDoc = aNode->OwnerDoc(); michael@0: Element* elem = aNode->AsElement(); michael@0: ownerDoc->ClearBoxObjectFor(elem); michael@0: michael@0: NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) || michael@0: !elem->GetXBLBinding(), michael@0: "Non-forced node has binding on destruction"); michael@0: michael@0: // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding michael@0: // attached michael@0: if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) && michael@0: ownerDoc->BindingManager()) { michael@0: ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc); michael@0: } michael@0: } michael@0: michael@0: aNode->ReleaseWrapper(aNode); michael@0: michael@0: FragmentOrElement::RemoveBlackMarkedNode(aNode); michael@0: } michael@0: michael@0: struct MOZ_STACK_CLASS nsHandlerData michael@0: { michael@0: uint16_t mOperation; michael@0: nsCOMPtr mSource; michael@0: nsCOMPtr mDest; michael@0: nsCxPusher mPusher; michael@0: }; michael@0: michael@0: static void michael@0: CallHandler(void *aObject, nsIAtom *aKey, void *aHandler, void *aData) michael@0: { michael@0: nsHandlerData *handlerData = static_cast(aData); michael@0: nsCOMPtr handler = michael@0: static_cast(aHandler); michael@0: nsINode *node = static_cast(aObject); michael@0: nsCOMPtr data = michael@0: static_cast(node->GetProperty(DOM_USER_DATA, aKey)); michael@0: NS_ASSERTION(data, "Handler without data?"); michael@0: michael@0: if (!handlerData->mPusher.RePush(node)) { michael@0: return; michael@0: } michael@0: nsAutoString key; michael@0: aKey->ToString(key); michael@0: handler->Handle(handlerData->mOperation, key, data, handlerData->mSource, michael@0: handlerData->mDest); michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: nsNodeUtils::CallUserDataHandlers(nsCOMArray &aNodesWithProperties, michael@0: nsIDocument *aOwnerDocument, michael@0: uint16_t aOperation, bool aCloned) michael@0: { michael@0: NS_PRECONDITION(!aCloned || (aNodesWithProperties.Count() % 2 == 0), michael@0: "Expected aNodesWithProperties to contain original and " michael@0: "cloned nodes."); michael@0: michael@0: if (!nsContentUtils::IsSafeToRunScript()) { michael@0: if (nsContentUtils::IsChromeDoc(aOwnerDocument)) { michael@0: NS_WARNING("Fix the caller! Userdata callback disabled."); michael@0: } else { michael@0: NS_ERROR("This is unsafe! Fix the caller! Userdata callback disabled."); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsPropertyTable *table = aOwnerDocument->PropertyTable(DOM_USER_DATA_HANDLER); michael@0: michael@0: // Keep the document alive, just in case one of the handlers causes it to go michael@0: // away. michael@0: nsCOMPtr ownerDoc = aOwnerDocument; michael@0: michael@0: nsHandlerData handlerData; michael@0: handlerData.mOperation = aOperation; michael@0: michael@0: uint32_t i, count = aNodesWithProperties.Count(); michael@0: for (i = 0; i < count; ++i) { michael@0: nsINode *nodeWithProperties = aNodesWithProperties[i]; michael@0: michael@0: nsresult rv; michael@0: handlerData.mSource = do_QueryInterface(nodeWithProperties, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aCloned) { michael@0: handlerData.mDest = do_QueryInterface(aNodesWithProperties[++i], &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: table->Enumerate(nodeWithProperties, CallHandler, &handlerData); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: NoteUserData(void *aObject, nsIAtom *aKey, void *aXPCOMChild, void *aData) michael@0: { michael@0: nsCycleCollectionTraversalCallback* cb = michael@0: static_cast(aData); michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "[user data (or handler)]"); michael@0: cb->NoteXPCOMChild(static_cast(aXPCOMChild)); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: nsNodeUtils::TraverseUserData(nsINode* aNode, michael@0: nsCycleCollectionTraversalCallback &aCb) michael@0: { michael@0: nsIDocument* ownerDoc = aNode->OwnerDoc(); michael@0: ownerDoc->PropertyTable(DOM_USER_DATA)->Enumerate(aNode, NoteUserData, &aCb); michael@0: ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->Enumerate(aNode, NoteUserData, &aCb); michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, michael@0: bool aCallUserDataHandlers, michael@0: nsINode **aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: michael@0: nsCOMPtr newNode; michael@0: nsCOMArray nodesWithProperties; michael@0: nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties, michael@0: getter_AddRefs(newNode)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aCallUserDataHandlers) { michael@0: rv = CallUserDataHandlers(nodesWithProperties, aNode->OwnerDoc(), michael@0: nsIDOMUserDataHandler::NODE_CLONED, true); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: newNode.swap(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, michael@0: nsNodeInfoManager *aNewNodeInfoManager, michael@0: JS::Handle aReparentScope, michael@0: nsCOMArray &aNodesWithProperties, michael@0: nsINode *aParent, nsINode **aResult) michael@0: { michael@0: NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aReparentScope, michael@0: "If cloning or not getting a new nodeinfo we shouldn't " michael@0: "rewrap"); michael@0: NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT), michael@0: "Can't insert document or attribute nodes into a parent"); michael@0: michael@0: *aResult = nullptr; michael@0: michael@0: // First deal with aNode and walk its attributes (and their children). Then, michael@0: // if aDeep is true, deal with aNode's children (and recurse into their michael@0: // attributes and children). michael@0: michael@0: AutoJSContext cx; michael@0: nsresult rv; michael@0: michael@0: nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager; michael@0: michael@0: // aNode. michael@0: nsINodeInfo *nodeInfo = aNode->mNodeInfo; michael@0: nsCOMPtr newNodeInfo; michael@0: if (nodeInfoManager) { michael@0: michael@0: // Don't allow importing/adopting nodes from non-privileged "scriptable" michael@0: // documents to "non-scriptable" documents. michael@0: nsIDocument* newDoc = nodeInfoManager->GetDocument(); michael@0: NS_ENSURE_STATE(newDoc); michael@0: bool hasHadScriptHandlingObject = false; michael@0: if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) && michael@0: !hasHadScriptHandlingObject) { michael@0: nsIDocument* currentDoc = aNode->OwnerDoc(); michael@0: NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) || michael@0: (!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) && michael@0: !hasHadScriptHandlingObject))); michael@0: } michael@0: michael@0: newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(), michael@0: nodeInfo->GetPrefixAtom(), michael@0: nodeInfo->NamespaceID(), michael@0: nodeInfo->NodeType(), michael@0: nodeInfo->GetExtraName()); michael@0: michael@0: nodeInfo = newNodeInfo; michael@0: } michael@0: michael@0: Element *elem = aNode->IsElement() ? aNode->AsElement() : nullptr; michael@0: michael@0: nsCOMPtr clone; michael@0: if (aClone) { michael@0: rv = aNode->Clone(nodeInfo, getter_AddRefs(clone)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aParent) { michael@0: // If we're cloning we need to insert the cloned children into the cloned michael@0: // parent. michael@0: rv = aParent->AppendChildTo(static_cast(clone.get()), michael@0: false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) { michael@0: // After cloning the document itself, we want to clone the children into michael@0: // the cloned document (somewhat like cloning and importing them into the michael@0: // cloned document). michael@0: nodeInfoManager = clone->mNodeInfo->NodeInfoManager(); michael@0: } michael@0: } michael@0: else if (nodeInfoManager) { michael@0: nsIDocument* oldDoc = aNode->OwnerDoc(); michael@0: bool wasRegistered = false; michael@0: if (aNode->IsElement()) { michael@0: Element* element = aNode->AsElement(); michael@0: oldDoc->ClearBoxObjectFor(element); michael@0: wasRegistered = oldDoc->UnregisterFreezableElement(element); michael@0: } michael@0: michael@0: aNode->mNodeInfo.swap(newNodeInfo); michael@0: if (elem) { michael@0: elem->NodeInfoChanged(newNodeInfo); michael@0: } michael@0: michael@0: nsIDocument* newDoc = aNode->OwnerDoc(); michael@0: if (newDoc) { michael@0: // XXX what if oldDoc is null, we don't know if this should be michael@0: // registered or not! Can that really happen? michael@0: if (wasRegistered) { michael@0: newDoc->RegisterFreezableElement(aNode->AsElement()); michael@0: } michael@0: michael@0: nsPIDOMWindow* window = newDoc->GetInnerWindow(); michael@0: if (window) { michael@0: EventListenerManager* elm = aNode->GetExistingListenerManager(); michael@0: if (elm) { michael@0: window->SetMutationListeners(elm->MutationListenerBits()); michael@0: if (elm->MayHavePaintEventListener()) { michael@0: window->SetHasPaintEventListeners(); michael@0: } michael@0: if (elm->MayHaveTouchEventListener()) { michael@0: window->SetHasTouchEventListeners(); michael@0: } michael@0: if (elm->MayHaveMouseEnterLeaveEventListener()) { michael@0: window->SetHasMouseEnterLeaveEventListeners(); michael@0: } michael@0: if (elm->MayHavePointerEnterLeaveEventListener()) { michael@0: window->SetHasPointerEnterLeaveEventListeners(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (wasRegistered && oldDoc != newDoc) { michael@0: nsCOMPtr domMediaElem(do_QueryInterface(aNode)); michael@0: if (domMediaElem) { michael@0: HTMLMediaElement* mediaElem = static_cast(aNode); michael@0: mediaElem->NotifyOwnerDocumentActivityChanged(); michael@0: } michael@0: nsCOMPtr objectLoadingContent(do_QueryInterface(aNode)); michael@0: if (objectLoadingContent) { michael@0: nsObjectLoadingContent* olc = static_cast(objectLoadingContent.get()); michael@0: olc->NotifyOwnerDocumentActivityChanged(); michael@0: } michael@0: } michael@0: michael@0: if (oldDoc != newDoc && oldDoc->MayHaveDOMMutationObservers()) { michael@0: newDoc->SetMayHaveDOMMutationObservers(); michael@0: } michael@0: michael@0: if (elem) { michael@0: elem->RecompileScriptEventListeners(); michael@0: } michael@0: michael@0: if (aReparentScope) { michael@0: JS::Rooted wrapper(cx); michael@0: if ((wrapper = aNode->GetWrapper())) { michael@0: if (IsDOMObject(wrapper)) { michael@0: JSAutoCompartment ac(cx, wrapper); michael@0: rv = ReparentWrapper(cx, wrapper); michael@0: } else { michael@0: nsIXPConnect *xpc = nsContentUtils::XPConnect(); michael@0: if (xpc) { michael@0: rv = xpc->ReparentWrappedNativeIfFound(cx, wrapper, aReparentScope, aNode); michael@0: } else { michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: aNode->mNodeInfo.swap(nodeInfo); michael@0: michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // XXX If there are any attribute nodes on this element with UserDataHandlers michael@0: // we should technically adopt/clone/import such attribute nodes and notify michael@0: // those handlers. However we currently don't have code to do so without michael@0: // also notifying when it's not safe so we're not doing that at this time. michael@0: michael@0: if (aDeep && (!aClone || !aNode->IsNodeOfType(nsINode::eATTRIBUTE))) { michael@0: // aNode's children. michael@0: for (nsIContent* cloneChild = aNode->GetFirstChild(); michael@0: cloneChild; michael@0: cloneChild = cloneChild->GetNextSibling()) { michael@0: nsCOMPtr child; michael@0: rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager, michael@0: aReparentScope, aNodesWithProperties, clone, michael@0: getter_AddRefs(child)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: // Cloning template element. michael@0: if (aDeep && aClone && IsTemplateElement(aNode)) { michael@0: DocumentFragment* origContent = michael@0: static_cast(aNode)->Content(); michael@0: DocumentFragment* cloneContent = michael@0: static_cast(clone.get())->Content(); michael@0: michael@0: // Clone the children into the clone's template content owner michael@0: // document's nodeinfo manager. michael@0: nsNodeInfoManager* ownerNodeInfoManager = michael@0: cloneContent->mNodeInfo->NodeInfoManager(); michael@0: michael@0: for (nsIContent* cloneChild = origContent->GetFirstChild(); michael@0: cloneChild; michael@0: cloneChild = cloneChild->GetNextSibling()) { michael@0: nsCOMPtr child; michael@0: rv = CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager, michael@0: aReparentScope, aNodesWithProperties, cloneContent, michael@0: getter_AddRefs(child)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: // XXX setting document on some nodes not in a document so XBL will bind michael@0: // and chrome won't break. Make XBL bind to document-less nodes! michael@0: // XXXbz Once this is fixed, fix up the asserts in all implementations of michael@0: // BindToTree to assert what they would like to assert, and fix the michael@0: // ChangeDocumentFor() call in nsXULElement::BindToTree as well. Also, michael@0: // remove the UnbindFromTree call in ~nsXULElement, and add back in the michael@0: // precondition in nsXULElement::UnbindFromTree and remove the line in michael@0: // nsXULElement.h that makes nsNodeUtils a friend of nsXULElement. michael@0: // Note: Make sure to do this witchery _after_ we've done any deep michael@0: // cloning, so kids of the new node aren't confused about whether they're michael@0: // in a document. michael@0: #ifdef MOZ_XUL michael@0: if (aClone && !aParent && aNode->IsElement() && michael@0: aNode->AsElement()->IsXUL()) { michael@0: if (!aNode->OwnerDoc()->IsLoadedAsInteractiveData()) { michael@0: clone->SetFlags(NODE_FORCE_XBL_BINDINGS); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (aNode->HasProperties()) { michael@0: bool ok = aNodesWithProperties.AppendObject(aNode); michael@0: if (aClone) { michael@0: ok = ok && aNodesWithProperties.AppendObject(clone); michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: clone.forget(aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /* static */ michael@0: void michael@0: nsNodeUtils::UnlinkUserData(nsINode *aNode) michael@0: { michael@0: NS_ASSERTION(aNode->HasProperties(), "Call to UnlinkUserData not needed."); michael@0: michael@0: // Strong reference to the document so that deleting properties can't michael@0: // delete the document. michael@0: nsCOMPtr document = aNode->OwnerDoc(); michael@0: document->PropertyTable(DOM_USER_DATA)->DeleteAllPropertiesFor(aNode); michael@0: document->PropertyTable(DOM_USER_DATA_HANDLER)->DeleteAllPropertiesFor(aNode); michael@0: } michael@0: michael@0: bool michael@0: nsNodeUtils::IsTemplateElement(const nsINode *aNode) michael@0: { michael@0: return aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::_template); michael@0: } michael@0: michael@0: nsIContent* michael@0: nsNodeUtils::GetFirstChildOfTemplateOrNode(nsINode* aNode) michael@0: { michael@0: if (nsNodeUtils::IsTemplateElement(aNode)) { michael@0: DocumentFragment* frag = michael@0: static_cast(aNode)->Content(); michael@0: return frag->GetFirstChild(); michael@0: } michael@0: michael@0: return aNode->GetFirstChild(); michael@0: } michael@0: