From 3d8ce1a11a7347cc94a937719c4bc8df46fb8d14 Mon Sep 17 00:00:00 2001 From: Pale Moon Date: Thu, 1 Sep 2016 13:39:08 +0200 Subject: Base import of Tycho code (warning: huge commit) --- dom/base/nsNodeUtils.cpp | 582 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 582 insertions(+) create mode 100644 dom/base/nsNodeUtils.cpp (limited to 'dom/base/nsNodeUtils.cpp') diff --git a/dom/base/nsNodeUtils.cpp b/dom/base/nsNodeUtils.cpp new file mode 100644 index 000000000..5da2f0e19 --- /dev/null +++ b/dom/base/nsNodeUtils.cpp @@ -0,0 +1,582 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=99: */ +/* 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/. */ + +#include "nsNodeUtils.h" +#include "nsContentUtils.h" +#include "nsINode.h" +#include "nsIContent.h" +#include "mozilla/dom/Element.h" +#include "nsIMutationObserver.h" +#include "nsIDocument.h" +#include "mozilla/EventListenerManager.h" +#include "nsIXPConnect.h" +#include "pldhash.h" +#include "nsIDOMAttr.h" +#include "nsCOMArray.h" +#include "nsPIDOMWindow.h" +#include "nsDocument.h" +#ifdef MOZ_XUL +#include "nsXULElement.h" +#endif +#include "nsBindingManager.h" +#include "nsGenericHTMLElement.h" +#include "mozilla/Assertions.h" +#include "mozilla/dom/HTMLImageElement.h" +#include "mozilla/dom/HTMLMediaElement.h" +#include "nsWrapperCacheInlines.h" +#include "nsObjectLoadingContent.h" +#include "nsDOMMutationObserver.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/HTMLTemplateElement.h" +#include "mozilla/dom/ShadowRoot.h" + +using namespace mozilla; +using namespace mozilla::dom; +using mozilla::AutoJSContext; + +// This macro expects the ownerDocument of content_ to be in scope as +// |nsIDocument* doc| +#define IMPL_MUTATION_NOTIFICATION(func_, content_, params_) \ + PR_BEGIN_MACRO \ + bool needsEnterLeave = doc->MayHaveDOMMutationObservers(); \ + if (needsEnterLeave) { \ + nsDOMMutationObserver::EnterMutationHandling(); \ + } \ + nsINode* node = content_; \ + NS_ASSERTION(node->OwnerDoc() == doc, "Bogus document"); \ + if (doc) { \ + doc->BindingManager()->func_ params_; \ + } \ + do { \ + nsINode::nsSlots* slots = node->GetExistingSlots(); \ + if (slots && !slots->mMutationObservers.IsEmpty()) { \ + /* No need to explicitly notify the first observer first \ + since that'll happen anyway. */ \ + NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS( \ + slots->mMutationObservers, nsIMutationObserver, \ + func_, params_); \ + } \ + ShadowRoot* shadow = ShadowRoot::FromNode(node); \ + if (shadow) { \ + node = shadow->GetPoolHost(); \ + } else { \ + node = node->GetParentNode(); \ + } \ + } while (node); \ + if (needsEnterLeave) { \ + nsDOMMutationObserver::LeaveMutationHandling(); \ + } \ + PR_END_MACRO + +void +nsNodeUtils::CharacterDataWillChange(nsIContent* aContent, + CharacterDataChangeInfo* aInfo) +{ + nsIDocument* doc = aContent->OwnerDoc(); + IMPL_MUTATION_NOTIFICATION(CharacterDataWillChange, aContent, + (doc, aContent, aInfo)); +} + +void +nsNodeUtils::CharacterDataChanged(nsIContent* aContent, + CharacterDataChangeInfo* aInfo) +{ + nsIDocument* doc = aContent->OwnerDoc(); + IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent, + (doc, aContent, aInfo)); +} + +void +nsNodeUtils::AttributeWillChange(Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) +{ + nsIDocument* doc = aElement->OwnerDoc(); + IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement, + (doc, aElement, aNameSpaceID, aAttribute, + aModType)); +} + +void +nsNodeUtils::AttributeChanged(Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) +{ + nsIDocument* doc = aElement->OwnerDoc(); + IMPL_MUTATION_NOTIFICATION(AttributeChanged, aElement, + (doc, aElement, aNameSpaceID, aAttribute, + aModType)); +} + +void +nsNodeUtils::AttributeSetToCurrentValue(Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute) +{ + nsIDocument* doc = aElement->OwnerDoc(); + IMPL_MUTATION_NOTIFICATION(AttributeSetToCurrentValue, aElement, + (doc, aElement, aNameSpaceID, aAttribute)); +} + +void +nsNodeUtils::ContentAppended(nsIContent* aContainer, + nsIContent* aFirstNewContent, + int32_t aNewIndexInContainer) +{ + nsIDocument* doc = aContainer->OwnerDoc(); + + IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer, + (doc, aContainer, aFirstNewContent, + aNewIndexInContainer)); +} + +void +nsNodeUtils::ContentInserted(nsINode* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer) +{ + NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) || + aContainer->IsNodeOfType(nsINode::eDOCUMENT), + "container must be an nsIContent or an nsIDocument"); + nsIContent* container; + nsIDocument* doc = aContainer->OwnerDoc(); + nsIDocument* document; + if (aContainer->IsNodeOfType(nsINode::eCONTENT)) { + container = static_cast(aContainer); + document = doc; + } + else { + container = nullptr; + document = static_cast(aContainer); + } + + IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer, + (document, container, aChild, aIndexInContainer)); +} + +void +nsNodeUtils::ContentRemoved(nsINode* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer, + nsIContent* aPreviousSibling) +{ + NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) || + aContainer->IsNodeOfType(nsINode::eDOCUMENT), + "container must be an nsIContent or an nsIDocument"); + nsIContent* container; + nsIDocument* doc = aContainer->OwnerDoc(); + nsIDocument* document; + if (aContainer->IsNodeOfType(nsINode::eCONTENT)) { + container = static_cast(aContainer); + document = doc; + } + else { + container = nullptr; + document = static_cast(aContainer); + } + + IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer, + (document, container, aChild, aIndexInContainer, + aPreviousSibling)); +} + +void +nsNodeUtils::LastRelease(nsINode* aNode) +{ + nsINode::nsSlots* slots = aNode->GetExistingSlots(); + if (slots) { + if (!slots->mMutationObservers.IsEmpty()) { + NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers, + nsIMutationObserver, + NodeWillBeDestroyed, (aNode)); + } + + delete slots; + aNode->mSlots = nullptr; + } + + // Kill properties first since that may run external code, so we want to + // be in as complete state as possible at that time. + if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) { + // Delete all properties before tearing down the document. Some of the + // properties are bound to nsINode objects and the destructor functions of + // the properties may want to use the owner document of the nsINode. + static_cast(aNode)->DeleteAllProperties(); + } + else { + if (aNode->HasProperties()) { + // Strong reference to the document so that deleting properties can't + // delete the document. + nsCOMPtr document = aNode->OwnerDoc(); + document->DeleteAllPropertiesFor(aNode); + } + + // I wonder whether it's faster to do the HasFlag check first.... + if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) && + aNode->HasFlag(ADDED_TO_FORM)) { + // Tell the form (if any) this node is going away. Don't + // notify, since we're being destroyed in any case. + static_cast(aNode)->ClearForm(true); + } + + if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::img) && + aNode->HasFlag(ADDED_TO_FORM)) { + HTMLImageElement* imageElem = static_cast(aNode); + imageElem->ClearForm(true); + } + } + aNode->UnsetFlags(NODE_HAS_PROPERTIES); + + if (aNode->NodeType() != nsIDOMNode::DOCUMENT_NODE && + aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) { +#ifdef DEBUG + if (nsContentUtils::IsInitialized()) { + EventListenerManager* manager = + nsContentUtils::GetExistingListenerManagerForNode(aNode); + if (!manager) { + NS_ERROR("Huh, our bit says we have a listener manager list, " + "but there's nothing in the hash!?!!"); + } + } +#endif + + nsContentUtils::RemoveListenerManager(aNode); + aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER); + } + + if (aNode->IsElement()) { + nsIDocument* ownerDoc = aNode->OwnerDoc(); + Element* elem = aNode->AsElement(); + ownerDoc->ClearBoxObjectFor(elem); + + NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) || + !elem->GetXBLBinding(), + "Non-forced node has binding on destruction"); + + // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding + // attached + if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) && + ownerDoc->BindingManager()) { + ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc); + } + } + + aNode->ReleaseWrapper(aNode); + + FragmentOrElement::RemoveBlackMarkedNode(aNode); +} + +static void +NoteUserData(void *aObject, nsIAtom *aKey, void *aXPCOMChild, void *aData) +{ + nsCycleCollectionTraversalCallback* cb = + static_cast(aData); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "[user data]"); + cb->NoteXPCOMChild(static_cast(aXPCOMChild)); +} + +/* static */ +void +nsNodeUtils::TraverseUserData(nsINode* aNode, + nsCycleCollectionTraversalCallback &aCb) +{ + nsIDocument* ownerDoc = aNode->OwnerDoc(); + ownerDoc->PropertyTable(DOM_USER_DATA)->Enumerate(aNode, NoteUserData, &aCb); +} + +/* static */ +nsresult +nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, nsINode **aResult) +{ + *aResult = nullptr; + + nsCOMPtr newNode; + nsCOMArray nodesWithProperties; + nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties, + getter_AddRefs(newNode)); + NS_ENSURE_SUCCESS(rv, rv); + + newNode.swap(*aResult); + return NS_OK; +} + +/* static */ +nsresult +nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, + nsNodeInfoManager *aNewNodeInfoManager, + JS::Handle aReparentScope, + nsCOMArray &aNodesWithProperties, + nsINode *aParent, nsINode **aResult) +{ + NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aReparentScope, + "If cloning or not getting a new nodeinfo we shouldn't " + "rewrap"); + NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT), + "Can't insert document or attribute nodes into a parent"); + + *aResult = nullptr; + + // First deal with aNode and walk its attributes (and their children). Then, + // if aDeep is true, deal with aNode's children (and recurse into their + // attributes and children). + + nsAutoScriptBlocker scriptBlocker; + AutoJSContext cx; + nsresult rv; + + nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager; + + // aNode. + NodeInfo *nodeInfo = aNode->mNodeInfo; + nsRefPtr newNodeInfo; + if (nodeInfoManager) { + + // Don't allow importing/adopting nodes from non-privileged "scriptable" + // documents to "non-scriptable" documents. + nsIDocument* newDoc = nodeInfoManager->GetDocument(); + NS_ENSURE_STATE(newDoc); + bool hasHadScriptHandlingObject = false; + if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) && + !hasHadScriptHandlingObject) { + nsIDocument* currentDoc = aNode->OwnerDoc(); + NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) || + (!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) && + !hasHadScriptHandlingObject))); + } + + newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(), + nodeInfo->GetPrefixAtom(), + nodeInfo->NamespaceID(), + nodeInfo->NodeType(), + nodeInfo->GetExtraName()); + + nodeInfo = newNodeInfo; + } + + Element *elem = aNode->IsElement() ? aNode->AsElement() : nullptr; + + nsCOMPtr clone; + if (aClone) { + rv = aNode->Clone(nodeInfo, getter_AddRefs(clone)); + NS_ENSURE_SUCCESS(rv, rv); + + if (clone->IsElement()) { + // The cloned node may be a custom element that may require + // enqueing created callback and prototype swizzling. + Element* elem = clone->AsElement(); + if (nsContentUtils::IsCustomElementName(nodeInfo->NameAtom())) { + elem->OwnerDoc()->SetupCustomElement(elem, nodeInfo->NamespaceID()); + } else { + // Check if node may be custom element by type extension. + // ex.