content/base/src/nsNodeUtils.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/nsNodeUtils.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,661 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsNodeUtils.h"
    1.11 +#include "nsContentUtils.h"
    1.12 +#include "nsCxPusher.h"
    1.13 +#include "nsINode.h"
    1.14 +#include "nsIContent.h"
    1.15 +#include "mozilla/dom/Element.h"
    1.16 +#include "nsIMutationObserver.h"
    1.17 +#include "nsIDocument.h"
    1.18 +#include "nsIDOMUserDataHandler.h"
    1.19 +#include "mozilla/EventListenerManager.h"
    1.20 +#include "nsIXPConnect.h"
    1.21 +#include "pldhash.h"
    1.22 +#include "nsIDOMAttr.h"
    1.23 +#include "nsCOMArray.h"
    1.24 +#include "nsPIDOMWindow.h"
    1.25 +#include "nsDocument.h"
    1.26 +#ifdef MOZ_XUL
    1.27 +#include "nsXULElement.h"
    1.28 +#endif
    1.29 +#include "nsBindingManager.h"
    1.30 +#include "nsGenericHTMLElement.h"
    1.31 +#include "mozilla/dom/HTMLImageElement.h"
    1.32 +#include "mozilla/dom/HTMLMediaElement.h"
    1.33 +#include "nsWrapperCacheInlines.h"
    1.34 +#include "nsObjectLoadingContent.h"
    1.35 +#include "nsDOMMutationObserver.h"
    1.36 +#include "mozilla/dom/BindingUtils.h"
    1.37 +#include "mozilla/dom/HTMLTemplateElement.h"
    1.38 +#include "mozilla/dom/ShadowRoot.h"
    1.39 +
    1.40 +using namespace mozilla;
    1.41 +using namespace mozilla::dom;
    1.42 +using mozilla::AutoJSContext;
    1.43 +
    1.44 +// This macro expects the ownerDocument of content_ to be in scope as
    1.45 +// |nsIDocument* doc|
    1.46 +#define IMPL_MUTATION_NOTIFICATION(func_, content_, params_)      \
    1.47 +  PR_BEGIN_MACRO                                                  \
    1.48 +  bool needsEnterLeave = doc->MayHaveDOMMutationObservers();      \
    1.49 +  if (needsEnterLeave) {                                          \
    1.50 +    nsDOMMutationObserver::EnterMutationHandling();               \
    1.51 +  }                                                               \
    1.52 +  nsINode* node = content_;                                       \
    1.53 +  NS_ASSERTION(node->OwnerDoc() == doc, "Bogus document");        \
    1.54 +  if (doc) {                                                      \
    1.55 +    doc->BindingManager()->func_ params_;                         \
    1.56 +  }                                                               \
    1.57 +  do {                                                            \
    1.58 +    nsINode::nsSlots* slots = node->GetExistingSlots();           \
    1.59 +    if (slots && !slots->mMutationObservers.IsEmpty()) {          \
    1.60 +      /* No need to explicitly notify the first observer first    \
    1.61 +         since that'll happen anyway. */                          \
    1.62 +      NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(                         \
    1.63 +        slots->mMutationObservers, nsIMutationObserver,           \
    1.64 +        func_, params_);                                          \
    1.65 +    }                                                             \
    1.66 +    ShadowRoot* shadow = ShadowRoot::FromNode(node);              \
    1.67 +    if (shadow) {                                                 \
    1.68 +      node = shadow->GetPoolHost();                               \
    1.69 +    } else {                                                      \
    1.70 +      node = node->GetParentNode();                               \
    1.71 +    }                                                             \
    1.72 +  } while (node);                                                 \
    1.73 +  if (needsEnterLeave) {                                          \
    1.74 +    nsDOMMutationObserver::LeaveMutationHandling();               \
    1.75 +  }                                                               \
    1.76 +  PR_END_MACRO
    1.77 +
    1.78 +void
    1.79 +nsNodeUtils::CharacterDataWillChange(nsIContent* aContent,
    1.80 +                                     CharacterDataChangeInfo* aInfo)
    1.81 +{
    1.82 +  nsIDocument* doc = aContent->OwnerDoc();
    1.83 +  IMPL_MUTATION_NOTIFICATION(CharacterDataWillChange, aContent,
    1.84 +                             (doc, aContent, aInfo));
    1.85 +}
    1.86 +
    1.87 +void
    1.88 +nsNodeUtils::CharacterDataChanged(nsIContent* aContent,
    1.89 +                                  CharacterDataChangeInfo* aInfo)
    1.90 +{
    1.91 +  nsIDocument* doc = aContent->OwnerDoc();
    1.92 +  IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent,
    1.93 +                             (doc, aContent, aInfo));
    1.94 +}
    1.95 +
    1.96 +void
    1.97 +nsNodeUtils::AttributeWillChange(Element* aElement,
    1.98 +                                 int32_t aNameSpaceID,
    1.99 +                                 nsIAtom* aAttribute,
   1.100 +                                 int32_t aModType)
   1.101 +{
   1.102 +  nsIDocument* doc = aElement->OwnerDoc();
   1.103 +  IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement,
   1.104 +                             (doc, aElement, aNameSpaceID, aAttribute,
   1.105 +                              aModType));
   1.106 +}
   1.107 +
   1.108 +void
   1.109 +nsNodeUtils::AttributeChanged(Element* aElement,
   1.110 +                              int32_t aNameSpaceID,
   1.111 +                              nsIAtom* aAttribute,
   1.112 +                              int32_t aModType)
   1.113 +{
   1.114 +  nsIDocument* doc = aElement->OwnerDoc();
   1.115 +  IMPL_MUTATION_NOTIFICATION(AttributeChanged, aElement,
   1.116 +                             (doc, aElement, aNameSpaceID, aAttribute,
   1.117 +                              aModType));
   1.118 +}
   1.119 +
   1.120 +void
   1.121 +nsNodeUtils::AttributeSetToCurrentValue(Element* aElement,
   1.122 +                                        int32_t aNameSpaceID,
   1.123 +                                        nsIAtom* aAttribute)
   1.124 +{
   1.125 +  nsIDocument* doc = aElement->OwnerDoc();
   1.126 +  IMPL_MUTATION_NOTIFICATION(AttributeSetToCurrentValue, aElement,
   1.127 +                             (doc, aElement, aNameSpaceID, aAttribute));
   1.128 +}
   1.129 +
   1.130 +void
   1.131 +nsNodeUtils::ContentAppended(nsIContent* aContainer,
   1.132 +                             nsIContent* aFirstNewContent,
   1.133 +                             int32_t aNewIndexInContainer)
   1.134 +{
   1.135 +  nsIDocument* doc = aContainer->OwnerDoc();
   1.136 +
   1.137 +  IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer,
   1.138 +                             (doc, aContainer, aFirstNewContent,
   1.139 +                              aNewIndexInContainer));
   1.140 +}
   1.141 +
   1.142 +void
   1.143 +nsNodeUtils::ContentInserted(nsINode* aContainer,
   1.144 +                             nsIContent* aChild,
   1.145 +                             int32_t aIndexInContainer)
   1.146 +{
   1.147 +  NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) ||
   1.148 +                  aContainer->IsNodeOfType(nsINode::eDOCUMENT),
   1.149 +                  "container must be an nsIContent or an nsIDocument");
   1.150 +  nsIContent* container;
   1.151 +  nsIDocument* doc = aContainer->OwnerDoc();
   1.152 +  nsIDocument* document;
   1.153 +  if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
   1.154 +    container = static_cast<nsIContent*>(aContainer);
   1.155 +    document = doc;
   1.156 +  }
   1.157 +  else {
   1.158 +    container = nullptr;
   1.159 +    document = static_cast<nsIDocument*>(aContainer);
   1.160 +  }
   1.161 +
   1.162 +  IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer,
   1.163 +                             (document, container, aChild, aIndexInContainer));
   1.164 +}
   1.165 +
   1.166 +void
   1.167 +nsNodeUtils::ContentRemoved(nsINode* aContainer,
   1.168 +                            nsIContent* aChild,
   1.169 +                            int32_t aIndexInContainer,
   1.170 +                            nsIContent* aPreviousSibling)
   1.171 +{
   1.172 +  NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) ||
   1.173 +                  aContainer->IsNodeOfType(nsINode::eDOCUMENT),
   1.174 +                  "container must be an nsIContent or an nsIDocument");
   1.175 +  nsIContent* container;
   1.176 +  nsIDocument* doc = aContainer->OwnerDoc();
   1.177 +  nsIDocument* document;
   1.178 +  if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
   1.179 +    container = static_cast<nsIContent*>(aContainer);
   1.180 +    document = doc;
   1.181 +  }
   1.182 +  else {
   1.183 +    container = nullptr;
   1.184 +    document = static_cast<nsIDocument*>(aContainer);
   1.185 +  }
   1.186 +
   1.187 +  IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer,
   1.188 +                             (document, container, aChild, aIndexInContainer,
   1.189 +                              aPreviousSibling));
   1.190 +}
   1.191 +
   1.192 +void
   1.193 +nsNodeUtils::LastRelease(nsINode* aNode)
   1.194 +{
   1.195 +  nsINode::nsSlots* slots = aNode->GetExistingSlots();
   1.196 +  if (slots) {
   1.197 +    if (!slots->mMutationObservers.IsEmpty()) {
   1.198 +      NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers,
   1.199 +                                         nsIMutationObserver,
   1.200 +                                         NodeWillBeDestroyed, (aNode));
   1.201 +    }
   1.202 +
   1.203 +    delete slots;
   1.204 +    aNode->mSlots = nullptr;
   1.205 +  }
   1.206 +
   1.207 +  // Kill properties first since that may run external code, so we want to
   1.208 +  // be in as complete state as possible at that time.
   1.209 +  if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
   1.210 +    // Delete all properties before tearing down the document. Some of the
   1.211 +    // properties are bound to nsINode objects and the destructor functions of
   1.212 +    // the properties may want to use the owner document of the nsINode.
   1.213 +    static_cast<nsIDocument*>(aNode)->DeleteAllProperties();
   1.214 +  }
   1.215 +  else {
   1.216 +    if (aNode->HasProperties()) {
   1.217 +      // Strong reference to the document so that deleting properties can't
   1.218 +      // delete the document.
   1.219 +      nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
   1.220 +      document->DeleteAllPropertiesFor(aNode);
   1.221 +    }
   1.222 +
   1.223 +    // I wonder whether it's faster to do the HasFlag check first....
   1.224 +    if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
   1.225 +        aNode->HasFlag(ADDED_TO_FORM)) {
   1.226 +      // Tell the form (if any) this node is going away.  Don't
   1.227 +      // notify, since we're being destroyed in any case.
   1.228 +      static_cast<nsGenericHTMLFormElement*>(aNode)->ClearForm(true);
   1.229 +    }
   1.230 +
   1.231 +    if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::img) &&
   1.232 +        aNode->HasFlag(ADDED_TO_FORM)) {
   1.233 +      HTMLImageElement* imageElem = static_cast<HTMLImageElement*>(aNode);
   1.234 +      imageElem->ClearForm(true);
   1.235 +    }
   1.236 +  }
   1.237 +  aNode->UnsetFlags(NODE_HAS_PROPERTIES);
   1.238 +
   1.239 +  if (aNode->NodeType() != nsIDOMNode::DOCUMENT_NODE &&
   1.240 +      aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
   1.241 +#ifdef DEBUG
   1.242 +    if (nsContentUtils::IsInitialized()) {
   1.243 +      EventListenerManager* manager =
   1.244 +        nsContentUtils::GetExistingListenerManagerForNode(aNode);
   1.245 +      if (!manager) {
   1.246 +        NS_ERROR("Huh, our bit says we have a listener manager list, "
   1.247 +                 "but there's nothing in the hash!?!!");
   1.248 +      }
   1.249 +    }
   1.250 +#endif
   1.251 +
   1.252 +    nsContentUtils::RemoveListenerManager(aNode);
   1.253 +    aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER);
   1.254 +  }
   1.255 +
   1.256 +  if (aNode->IsElement()) {
   1.257 +    nsIDocument* ownerDoc = aNode->OwnerDoc();
   1.258 +    Element* elem = aNode->AsElement();
   1.259 +    ownerDoc->ClearBoxObjectFor(elem);
   1.260 +
   1.261 +    NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
   1.262 +                 !elem->GetXBLBinding(),
   1.263 +                 "Non-forced node has binding on destruction");
   1.264 +
   1.265 +    // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding
   1.266 +    // attached
   1.267 +    if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) &&
   1.268 +        ownerDoc->BindingManager()) {
   1.269 +      ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc);
   1.270 +    }
   1.271 +  }
   1.272 +
   1.273 +  aNode->ReleaseWrapper(aNode);
   1.274 +
   1.275 +  FragmentOrElement::RemoveBlackMarkedNode(aNode);
   1.276 +}
   1.277 +
   1.278 +struct MOZ_STACK_CLASS nsHandlerData
   1.279 +{
   1.280 +  uint16_t mOperation;
   1.281 +  nsCOMPtr<nsIDOMNode> mSource;
   1.282 +  nsCOMPtr<nsIDOMNode> mDest;
   1.283 +  nsCxPusher mPusher;
   1.284 +};
   1.285 +
   1.286 +static void
   1.287 +CallHandler(void *aObject, nsIAtom *aKey, void *aHandler, void *aData)
   1.288 +{
   1.289 +  nsHandlerData *handlerData = static_cast<nsHandlerData*>(aData);
   1.290 +  nsCOMPtr<nsIDOMUserDataHandler> handler =
   1.291 +    static_cast<nsIDOMUserDataHandler*>(aHandler);
   1.292 +  nsINode *node = static_cast<nsINode*>(aObject);
   1.293 +  nsCOMPtr<nsIVariant> data =
   1.294 +    static_cast<nsIVariant*>(node->GetProperty(DOM_USER_DATA, aKey));
   1.295 +  NS_ASSERTION(data, "Handler without data?");
   1.296 +
   1.297 +  if (!handlerData->mPusher.RePush(node)) {
   1.298 +    return;
   1.299 +  }
   1.300 +  nsAutoString key;
   1.301 +  aKey->ToString(key);
   1.302 +  handler->Handle(handlerData->mOperation, key, data, handlerData->mSource,
   1.303 +                  handlerData->mDest);
   1.304 +}
   1.305 +
   1.306 +/* static */
   1.307 +nsresult
   1.308 +nsNodeUtils::CallUserDataHandlers(nsCOMArray<nsINode> &aNodesWithProperties,
   1.309 +                                  nsIDocument *aOwnerDocument,
   1.310 +                                  uint16_t aOperation, bool aCloned)
   1.311 +{
   1.312 +  NS_PRECONDITION(!aCloned || (aNodesWithProperties.Count() % 2 == 0),
   1.313 +                  "Expected aNodesWithProperties to contain original and "
   1.314 +                  "cloned nodes.");
   1.315 +
   1.316 +  if (!nsContentUtils::IsSafeToRunScript()) {
   1.317 +    if (nsContentUtils::IsChromeDoc(aOwnerDocument)) {
   1.318 +      NS_WARNING("Fix the caller! Userdata callback disabled.");
   1.319 +    } else {
   1.320 +      NS_ERROR("This is unsafe! Fix the caller! Userdata callback disabled.");
   1.321 +    }
   1.322 +
   1.323 +    return NS_OK;
   1.324 +  }
   1.325 +
   1.326 +  nsPropertyTable *table = aOwnerDocument->PropertyTable(DOM_USER_DATA_HANDLER);
   1.327 +
   1.328 +  // Keep the document alive, just in case one of the handlers causes it to go
   1.329 +  // away.
   1.330 +  nsCOMPtr<nsIDocument> ownerDoc = aOwnerDocument;
   1.331 +
   1.332 +  nsHandlerData handlerData;
   1.333 +  handlerData.mOperation = aOperation;
   1.334 +
   1.335 +  uint32_t i, count = aNodesWithProperties.Count();
   1.336 +  for (i = 0; i < count; ++i) {
   1.337 +    nsINode *nodeWithProperties = aNodesWithProperties[i];
   1.338 +
   1.339 +    nsresult rv;
   1.340 +    handlerData.mSource = do_QueryInterface(nodeWithProperties, &rv);
   1.341 +    NS_ENSURE_SUCCESS(rv, rv);
   1.342 +
   1.343 +    if (aCloned) {
   1.344 +      handlerData.mDest = do_QueryInterface(aNodesWithProperties[++i], &rv);
   1.345 +      NS_ENSURE_SUCCESS(rv, rv);
   1.346 +    }
   1.347 +
   1.348 +    table->Enumerate(nodeWithProperties, CallHandler, &handlerData);
   1.349 +  }
   1.350 +
   1.351 +  return NS_OK;
   1.352 +}
   1.353 +
   1.354 +static void
   1.355 +NoteUserData(void *aObject, nsIAtom *aKey, void *aXPCOMChild, void *aData)
   1.356 +{
   1.357 +  nsCycleCollectionTraversalCallback* cb =
   1.358 +    static_cast<nsCycleCollectionTraversalCallback*>(aData);
   1.359 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "[user data (or handler)]");
   1.360 +  cb->NoteXPCOMChild(static_cast<nsISupports*>(aXPCOMChild));
   1.361 +}
   1.362 +
   1.363 +/* static */
   1.364 +void
   1.365 +nsNodeUtils::TraverseUserData(nsINode* aNode,
   1.366 +                              nsCycleCollectionTraversalCallback &aCb)
   1.367 +{
   1.368 +  nsIDocument* ownerDoc = aNode->OwnerDoc();
   1.369 +  ownerDoc->PropertyTable(DOM_USER_DATA)->Enumerate(aNode, NoteUserData, &aCb);
   1.370 +  ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->Enumerate(aNode, NoteUserData, &aCb);
   1.371 +}
   1.372 +
   1.373 +/* static */
   1.374 +nsresult
   1.375 +nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep,
   1.376 +                           bool aCallUserDataHandlers,
   1.377 +                           nsINode **aResult)
   1.378 +{
   1.379 +  *aResult = nullptr;
   1.380 +
   1.381 +  nsCOMPtr<nsINode> newNode;
   1.382 +  nsCOMArray<nsINode> nodesWithProperties;
   1.383 +  nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties,
   1.384 +                      getter_AddRefs(newNode));
   1.385 +  NS_ENSURE_SUCCESS(rv, rv);
   1.386 +
   1.387 +  if (aCallUserDataHandlers) {
   1.388 +    rv = CallUserDataHandlers(nodesWithProperties, aNode->OwnerDoc(),
   1.389 +                              nsIDOMUserDataHandler::NODE_CLONED, true);
   1.390 +    NS_ENSURE_SUCCESS(rv, rv);
   1.391 +  }
   1.392 +
   1.393 +  newNode.swap(*aResult);
   1.394 +
   1.395 +  return NS_OK;
   1.396 +}
   1.397 +
   1.398 +/* static */
   1.399 +nsresult
   1.400 +nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
   1.401 +                           nsNodeInfoManager *aNewNodeInfoManager,
   1.402 +                           JS::Handle<JSObject*> aReparentScope,
   1.403 +                           nsCOMArray<nsINode> &aNodesWithProperties,
   1.404 +                           nsINode *aParent, nsINode **aResult)
   1.405 +{
   1.406 +  NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aReparentScope,
   1.407 +                  "If cloning or not getting a new nodeinfo we shouldn't "
   1.408 +                  "rewrap");
   1.409 +  NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
   1.410 +                  "Can't insert document or attribute nodes into a parent");
   1.411 +
   1.412 +  *aResult = nullptr;
   1.413 +
   1.414 +  // First deal with aNode and walk its attributes (and their children). Then,
   1.415 +  // if aDeep is true, deal with aNode's children (and recurse into their
   1.416 +  // attributes and children).
   1.417 +
   1.418 +  AutoJSContext cx;
   1.419 +  nsresult rv;
   1.420 +
   1.421 +  nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
   1.422 +
   1.423 +  // aNode.
   1.424 +  nsINodeInfo *nodeInfo = aNode->mNodeInfo;
   1.425 +  nsCOMPtr<nsINodeInfo> newNodeInfo;
   1.426 +  if (nodeInfoManager) {
   1.427 +
   1.428 +    // Don't allow importing/adopting nodes from non-privileged "scriptable"
   1.429 +    // documents to "non-scriptable" documents.
   1.430 +    nsIDocument* newDoc = nodeInfoManager->GetDocument();
   1.431 +    NS_ENSURE_STATE(newDoc);
   1.432 +    bool hasHadScriptHandlingObject = false;
   1.433 +    if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
   1.434 +        !hasHadScriptHandlingObject) {
   1.435 +      nsIDocument* currentDoc = aNode->OwnerDoc();
   1.436 +      NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) ||
   1.437 +                       (!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
   1.438 +                        !hasHadScriptHandlingObject)));
   1.439 +    }
   1.440 +
   1.441 +    newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(),
   1.442 +                                               nodeInfo->GetPrefixAtom(),
   1.443 +                                               nodeInfo->NamespaceID(),
   1.444 +                                               nodeInfo->NodeType(),
   1.445 +                                               nodeInfo->GetExtraName());
   1.446 +
   1.447 +    nodeInfo = newNodeInfo;
   1.448 +  }
   1.449 +
   1.450 +  Element *elem = aNode->IsElement() ? aNode->AsElement() : nullptr;
   1.451 +
   1.452 +  nsCOMPtr<nsINode> clone;
   1.453 +  if (aClone) {
   1.454 +    rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
   1.455 +    NS_ENSURE_SUCCESS(rv, rv);
   1.456 +
   1.457 +    if (aParent) {
   1.458 +      // If we're cloning we need to insert the cloned children into the cloned
   1.459 +      // parent.
   1.460 +      rv = aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
   1.461 +                                  false);
   1.462 +      NS_ENSURE_SUCCESS(rv, rv);
   1.463 +    }
   1.464 +    else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
   1.465 +      // After cloning the document itself, we want to clone the children into
   1.466 +      // the cloned document (somewhat like cloning and importing them into the
   1.467 +      // cloned document).
   1.468 +      nodeInfoManager = clone->mNodeInfo->NodeInfoManager();
   1.469 +    }
   1.470 +  }
   1.471 +  else if (nodeInfoManager) {
   1.472 +    nsIDocument* oldDoc = aNode->OwnerDoc();
   1.473 +    bool wasRegistered = false;
   1.474 +    if (aNode->IsElement()) {
   1.475 +      Element* element = aNode->AsElement();
   1.476 +      oldDoc->ClearBoxObjectFor(element);
   1.477 +      wasRegistered = oldDoc->UnregisterFreezableElement(element);
   1.478 +    }
   1.479 +
   1.480 +    aNode->mNodeInfo.swap(newNodeInfo);
   1.481 +    if (elem) {
   1.482 +      elem->NodeInfoChanged(newNodeInfo);
   1.483 +    }
   1.484 +
   1.485 +    nsIDocument* newDoc = aNode->OwnerDoc();
   1.486 +    if (newDoc) {
   1.487 +      // XXX what if oldDoc is null, we don't know if this should be
   1.488 +      // registered or not! Can that really happen?
   1.489 +      if (wasRegistered) {
   1.490 +        newDoc->RegisterFreezableElement(aNode->AsElement());
   1.491 +      }
   1.492 +
   1.493 +      nsPIDOMWindow* window = newDoc->GetInnerWindow();
   1.494 +      if (window) {
   1.495 +        EventListenerManager* elm = aNode->GetExistingListenerManager();
   1.496 +        if (elm) {
   1.497 +          window->SetMutationListeners(elm->MutationListenerBits());
   1.498 +          if (elm->MayHavePaintEventListener()) {
   1.499 +            window->SetHasPaintEventListeners();
   1.500 +          }
   1.501 +          if (elm->MayHaveTouchEventListener()) {
   1.502 +            window->SetHasTouchEventListeners();
   1.503 +          }
   1.504 +          if (elm->MayHaveMouseEnterLeaveEventListener()) {
   1.505 +            window->SetHasMouseEnterLeaveEventListeners();
   1.506 +          }
   1.507 +          if (elm->MayHavePointerEnterLeaveEventListener()) {
   1.508 +            window->SetHasPointerEnterLeaveEventListeners();
   1.509 +          }
   1.510 +        }
   1.511 +      }
   1.512 +    }
   1.513 +
   1.514 +    if (wasRegistered && oldDoc != newDoc) {
   1.515 +      nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aNode));
   1.516 +      if (domMediaElem) {
   1.517 +        HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(aNode);
   1.518 +        mediaElem->NotifyOwnerDocumentActivityChanged();
   1.519 +      }
   1.520 +      nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aNode));
   1.521 +      if (objectLoadingContent) {
   1.522 +        nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
   1.523 +        olc->NotifyOwnerDocumentActivityChanged();
   1.524 +      }
   1.525 +    }
   1.526 +
   1.527 +    if (oldDoc != newDoc && oldDoc->MayHaveDOMMutationObservers()) {
   1.528 +      newDoc->SetMayHaveDOMMutationObservers();
   1.529 +    }
   1.530 +
   1.531 +    if (elem) {
   1.532 +      elem->RecompileScriptEventListeners();
   1.533 +    }
   1.534 +
   1.535 +    if (aReparentScope) {
   1.536 +      JS::Rooted<JSObject*> wrapper(cx);
   1.537 +      if ((wrapper = aNode->GetWrapper())) {
   1.538 +        if (IsDOMObject(wrapper)) {
   1.539 +          JSAutoCompartment ac(cx, wrapper);
   1.540 +          rv = ReparentWrapper(cx, wrapper);
   1.541 +        } else {
   1.542 +          nsIXPConnect *xpc = nsContentUtils::XPConnect();
   1.543 +          if (xpc) {
   1.544 +            rv = xpc->ReparentWrappedNativeIfFound(cx, wrapper, aReparentScope, aNode);
   1.545 +          } else {
   1.546 +            rv = NS_ERROR_FAILURE;
   1.547 +          }
   1.548 +        }
   1.549 +        if (NS_FAILED(rv)) {
   1.550 +          aNode->mNodeInfo.swap(nodeInfo);
   1.551 +
   1.552 +          return rv;
   1.553 +        }
   1.554 +      }
   1.555 +    }
   1.556 +  }
   1.557 +
   1.558 +  // XXX If there are any attribute nodes on this element with UserDataHandlers
   1.559 +  // we should technically adopt/clone/import such attribute nodes and notify
   1.560 +  // those handlers. However we currently don't have code to do so without
   1.561 +  // also notifying when it's not safe so we're not doing that at this time.
   1.562 +
   1.563 +  if (aDeep && (!aClone || !aNode->IsNodeOfType(nsINode::eATTRIBUTE))) {
   1.564 +    // aNode's children.
   1.565 +    for (nsIContent* cloneChild = aNode->GetFirstChild();
   1.566 +         cloneChild;
   1.567 +         cloneChild = cloneChild->GetNextSibling()) {
   1.568 +      nsCOMPtr<nsINode> child;
   1.569 +      rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
   1.570 +                         aReparentScope, aNodesWithProperties, clone,
   1.571 +                         getter_AddRefs(child));
   1.572 +      NS_ENSURE_SUCCESS(rv, rv);
   1.573 +    }
   1.574 +  }
   1.575 +
   1.576 +  // Cloning template element.
   1.577 +  if (aDeep && aClone && IsTemplateElement(aNode)) {
   1.578 +    DocumentFragment* origContent =
   1.579 +      static_cast<HTMLTemplateElement*>(aNode)->Content();
   1.580 +    DocumentFragment* cloneContent =
   1.581 +      static_cast<HTMLTemplateElement*>(clone.get())->Content();
   1.582 +
   1.583 +    // Clone the children into the clone's template content owner
   1.584 +    // document's nodeinfo manager.
   1.585 +    nsNodeInfoManager* ownerNodeInfoManager =
   1.586 +      cloneContent->mNodeInfo->NodeInfoManager();
   1.587 +
   1.588 +    for (nsIContent* cloneChild = origContent->GetFirstChild();
   1.589 +         cloneChild;
   1.590 +         cloneChild = cloneChild->GetNextSibling()) {
   1.591 +      nsCOMPtr<nsINode> child;
   1.592 +      rv = CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
   1.593 +                         aReparentScope, aNodesWithProperties, cloneContent,
   1.594 +                         getter_AddRefs(child));
   1.595 +      NS_ENSURE_SUCCESS(rv, rv);
   1.596 +    }
   1.597 +  }
   1.598 +
   1.599 +  // XXX setting document on some nodes not in a document so XBL will bind
   1.600 +  // and chrome won't break. Make XBL bind to document-less nodes!
   1.601 +  // XXXbz Once this is fixed, fix up the asserts in all implementations of
   1.602 +  // BindToTree to assert what they would like to assert, and fix the
   1.603 +  // ChangeDocumentFor() call in nsXULElement::BindToTree as well.  Also,
   1.604 +  // remove the UnbindFromTree call in ~nsXULElement, and add back in the
   1.605 +  // precondition in nsXULElement::UnbindFromTree and remove the line in
   1.606 +  // nsXULElement.h that makes nsNodeUtils a friend of nsXULElement.
   1.607 +  // Note: Make sure to do this witchery _after_ we've done any deep
   1.608 +  // cloning, so kids of the new node aren't confused about whether they're
   1.609 +  // in a document.
   1.610 +#ifdef MOZ_XUL
   1.611 +  if (aClone && !aParent && aNode->IsElement() &&
   1.612 +      aNode->AsElement()->IsXUL()) {
   1.613 +    if (!aNode->OwnerDoc()->IsLoadedAsInteractiveData()) {
   1.614 +      clone->SetFlags(NODE_FORCE_XBL_BINDINGS);
   1.615 +    }
   1.616 +  }
   1.617 +#endif
   1.618 +
   1.619 +  if (aNode->HasProperties()) {
   1.620 +    bool ok = aNodesWithProperties.AppendObject(aNode);
   1.621 +    if (aClone) {
   1.622 +      ok = ok && aNodesWithProperties.AppendObject(clone);
   1.623 +    }
   1.624 +
   1.625 +    NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   1.626 +  }
   1.627 +
   1.628 +  clone.forget(aResult);
   1.629 +
   1.630 +  return NS_OK;
   1.631 +}
   1.632 +
   1.633 +
   1.634 +/* static */
   1.635 +void
   1.636 +nsNodeUtils::UnlinkUserData(nsINode *aNode)
   1.637 +{
   1.638 +  NS_ASSERTION(aNode->HasProperties(), "Call to UnlinkUserData not needed.");
   1.639 +
   1.640 +  // Strong reference to the document so that deleting properties can't
   1.641 +  // delete the document.
   1.642 +  nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
   1.643 +  document->PropertyTable(DOM_USER_DATA)->DeleteAllPropertiesFor(aNode);
   1.644 +  document->PropertyTable(DOM_USER_DATA_HANDLER)->DeleteAllPropertiesFor(aNode);
   1.645 +}
   1.646 +
   1.647 +bool
   1.648 +nsNodeUtils::IsTemplateElement(const nsINode *aNode)
   1.649 +{
   1.650 +  return aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::_template);
   1.651 +}
   1.652 +
   1.653 +nsIContent*
   1.654 +nsNodeUtils::GetFirstChildOfTemplateOrNode(nsINode* aNode)
   1.655 +{
   1.656 +  if (nsNodeUtils::IsTemplateElement(aNode)) {
   1.657 +    DocumentFragment* frag =
   1.658 +      static_cast<HTMLTemplateElement*>(aNode)->Content();
   1.659 +    return frag->GetFirstChild();
   1.660 +  }
   1.661 +
   1.662 +  return aNode->GetFirstChild();
   1.663 +}
   1.664 +

mercurial