content/base/src/nsNodeUtils.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=99: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsNodeUtils.h"
michael@0 8 #include "nsContentUtils.h"
michael@0 9 #include "nsCxPusher.h"
michael@0 10 #include "nsINode.h"
michael@0 11 #include "nsIContent.h"
michael@0 12 #include "mozilla/dom/Element.h"
michael@0 13 #include "nsIMutationObserver.h"
michael@0 14 #include "nsIDocument.h"
michael@0 15 #include "nsIDOMUserDataHandler.h"
michael@0 16 #include "mozilla/EventListenerManager.h"
michael@0 17 #include "nsIXPConnect.h"
michael@0 18 #include "pldhash.h"
michael@0 19 #include "nsIDOMAttr.h"
michael@0 20 #include "nsCOMArray.h"
michael@0 21 #include "nsPIDOMWindow.h"
michael@0 22 #include "nsDocument.h"
michael@0 23 #ifdef MOZ_XUL
michael@0 24 #include "nsXULElement.h"
michael@0 25 #endif
michael@0 26 #include "nsBindingManager.h"
michael@0 27 #include "nsGenericHTMLElement.h"
michael@0 28 #include "mozilla/dom/HTMLImageElement.h"
michael@0 29 #include "mozilla/dom/HTMLMediaElement.h"
michael@0 30 #include "nsWrapperCacheInlines.h"
michael@0 31 #include "nsObjectLoadingContent.h"
michael@0 32 #include "nsDOMMutationObserver.h"
michael@0 33 #include "mozilla/dom/BindingUtils.h"
michael@0 34 #include "mozilla/dom/HTMLTemplateElement.h"
michael@0 35 #include "mozilla/dom/ShadowRoot.h"
michael@0 36
michael@0 37 using namespace mozilla;
michael@0 38 using namespace mozilla::dom;
michael@0 39 using mozilla::AutoJSContext;
michael@0 40
michael@0 41 // This macro expects the ownerDocument of content_ to be in scope as
michael@0 42 // |nsIDocument* doc|
michael@0 43 #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_) \
michael@0 44 PR_BEGIN_MACRO \
michael@0 45 bool needsEnterLeave = doc->MayHaveDOMMutationObservers(); \
michael@0 46 if (needsEnterLeave) { \
michael@0 47 nsDOMMutationObserver::EnterMutationHandling(); \
michael@0 48 } \
michael@0 49 nsINode* node = content_; \
michael@0 50 NS_ASSERTION(node->OwnerDoc() == doc, "Bogus document"); \
michael@0 51 if (doc) { \
michael@0 52 doc->BindingManager()->func_ params_; \
michael@0 53 } \
michael@0 54 do { \
michael@0 55 nsINode::nsSlots* slots = node->GetExistingSlots(); \
michael@0 56 if (slots && !slots->mMutationObservers.IsEmpty()) { \
michael@0 57 /* No need to explicitly notify the first observer first \
michael@0 58 since that'll happen anyway. */ \
michael@0 59 NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS( \
michael@0 60 slots->mMutationObservers, nsIMutationObserver, \
michael@0 61 func_, params_); \
michael@0 62 } \
michael@0 63 ShadowRoot* shadow = ShadowRoot::FromNode(node); \
michael@0 64 if (shadow) { \
michael@0 65 node = shadow->GetPoolHost(); \
michael@0 66 } else { \
michael@0 67 node = node->GetParentNode(); \
michael@0 68 } \
michael@0 69 } while (node); \
michael@0 70 if (needsEnterLeave) { \
michael@0 71 nsDOMMutationObserver::LeaveMutationHandling(); \
michael@0 72 } \
michael@0 73 PR_END_MACRO
michael@0 74
michael@0 75 void
michael@0 76 nsNodeUtils::CharacterDataWillChange(nsIContent* aContent,
michael@0 77 CharacterDataChangeInfo* aInfo)
michael@0 78 {
michael@0 79 nsIDocument* doc = aContent->OwnerDoc();
michael@0 80 IMPL_MUTATION_NOTIFICATION(CharacterDataWillChange, aContent,
michael@0 81 (doc, aContent, aInfo));
michael@0 82 }
michael@0 83
michael@0 84 void
michael@0 85 nsNodeUtils::CharacterDataChanged(nsIContent* aContent,
michael@0 86 CharacterDataChangeInfo* aInfo)
michael@0 87 {
michael@0 88 nsIDocument* doc = aContent->OwnerDoc();
michael@0 89 IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent,
michael@0 90 (doc, aContent, aInfo));
michael@0 91 }
michael@0 92
michael@0 93 void
michael@0 94 nsNodeUtils::AttributeWillChange(Element* aElement,
michael@0 95 int32_t aNameSpaceID,
michael@0 96 nsIAtom* aAttribute,
michael@0 97 int32_t aModType)
michael@0 98 {
michael@0 99 nsIDocument* doc = aElement->OwnerDoc();
michael@0 100 IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement,
michael@0 101 (doc, aElement, aNameSpaceID, aAttribute,
michael@0 102 aModType));
michael@0 103 }
michael@0 104
michael@0 105 void
michael@0 106 nsNodeUtils::AttributeChanged(Element* aElement,
michael@0 107 int32_t aNameSpaceID,
michael@0 108 nsIAtom* aAttribute,
michael@0 109 int32_t aModType)
michael@0 110 {
michael@0 111 nsIDocument* doc = aElement->OwnerDoc();
michael@0 112 IMPL_MUTATION_NOTIFICATION(AttributeChanged, aElement,
michael@0 113 (doc, aElement, aNameSpaceID, aAttribute,
michael@0 114 aModType));
michael@0 115 }
michael@0 116
michael@0 117 void
michael@0 118 nsNodeUtils::AttributeSetToCurrentValue(Element* aElement,
michael@0 119 int32_t aNameSpaceID,
michael@0 120 nsIAtom* aAttribute)
michael@0 121 {
michael@0 122 nsIDocument* doc = aElement->OwnerDoc();
michael@0 123 IMPL_MUTATION_NOTIFICATION(AttributeSetToCurrentValue, aElement,
michael@0 124 (doc, aElement, aNameSpaceID, aAttribute));
michael@0 125 }
michael@0 126
michael@0 127 void
michael@0 128 nsNodeUtils::ContentAppended(nsIContent* aContainer,
michael@0 129 nsIContent* aFirstNewContent,
michael@0 130 int32_t aNewIndexInContainer)
michael@0 131 {
michael@0 132 nsIDocument* doc = aContainer->OwnerDoc();
michael@0 133
michael@0 134 IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer,
michael@0 135 (doc, aContainer, aFirstNewContent,
michael@0 136 aNewIndexInContainer));
michael@0 137 }
michael@0 138
michael@0 139 void
michael@0 140 nsNodeUtils::ContentInserted(nsINode* aContainer,
michael@0 141 nsIContent* aChild,
michael@0 142 int32_t aIndexInContainer)
michael@0 143 {
michael@0 144 NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) ||
michael@0 145 aContainer->IsNodeOfType(nsINode::eDOCUMENT),
michael@0 146 "container must be an nsIContent or an nsIDocument");
michael@0 147 nsIContent* container;
michael@0 148 nsIDocument* doc = aContainer->OwnerDoc();
michael@0 149 nsIDocument* document;
michael@0 150 if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
michael@0 151 container = static_cast<nsIContent*>(aContainer);
michael@0 152 document = doc;
michael@0 153 }
michael@0 154 else {
michael@0 155 container = nullptr;
michael@0 156 document = static_cast<nsIDocument*>(aContainer);
michael@0 157 }
michael@0 158
michael@0 159 IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer,
michael@0 160 (document, container, aChild, aIndexInContainer));
michael@0 161 }
michael@0 162
michael@0 163 void
michael@0 164 nsNodeUtils::ContentRemoved(nsINode* aContainer,
michael@0 165 nsIContent* aChild,
michael@0 166 int32_t aIndexInContainer,
michael@0 167 nsIContent* aPreviousSibling)
michael@0 168 {
michael@0 169 NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) ||
michael@0 170 aContainer->IsNodeOfType(nsINode::eDOCUMENT),
michael@0 171 "container must be an nsIContent or an nsIDocument");
michael@0 172 nsIContent* container;
michael@0 173 nsIDocument* doc = aContainer->OwnerDoc();
michael@0 174 nsIDocument* document;
michael@0 175 if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
michael@0 176 container = static_cast<nsIContent*>(aContainer);
michael@0 177 document = doc;
michael@0 178 }
michael@0 179 else {
michael@0 180 container = nullptr;
michael@0 181 document = static_cast<nsIDocument*>(aContainer);
michael@0 182 }
michael@0 183
michael@0 184 IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer,
michael@0 185 (document, container, aChild, aIndexInContainer,
michael@0 186 aPreviousSibling));
michael@0 187 }
michael@0 188
michael@0 189 void
michael@0 190 nsNodeUtils::LastRelease(nsINode* aNode)
michael@0 191 {
michael@0 192 nsINode::nsSlots* slots = aNode->GetExistingSlots();
michael@0 193 if (slots) {
michael@0 194 if (!slots->mMutationObservers.IsEmpty()) {
michael@0 195 NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers,
michael@0 196 nsIMutationObserver,
michael@0 197 NodeWillBeDestroyed, (aNode));
michael@0 198 }
michael@0 199
michael@0 200 delete slots;
michael@0 201 aNode->mSlots = nullptr;
michael@0 202 }
michael@0 203
michael@0 204 // Kill properties first since that may run external code, so we want to
michael@0 205 // be in as complete state as possible at that time.
michael@0 206 if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
michael@0 207 // Delete all properties before tearing down the document. Some of the
michael@0 208 // properties are bound to nsINode objects and the destructor functions of
michael@0 209 // the properties may want to use the owner document of the nsINode.
michael@0 210 static_cast<nsIDocument*>(aNode)->DeleteAllProperties();
michael@0 211 }
michael@0 212 else {
michael@0 213 if (aNode->HasProperties()) {
michael@0 214 // Strong reference to the document so that deleting properties can't
michael@0 215 // delete the document.
michael@0 216 nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
michael@0 217 document->DeleteAllPropertiesFor(aNode);
michael@0 218 }
michael@0 219
michael@0 220 // I wonder whether it's faster to do the HasFlag check first....
michael@0 221 if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
michael@0 222 aNode->HasFlag(ADDED_TO_FORM)) {
michael@0 223 // Tell the form (if any) this node is going away. Don't
michael@0 224 // notify, since we're being destroyed in any case.
michael@0 225 static_cast<nsGenericHTMLFormElement*>(aNode)->ClearForm(true);
michael@0 226 }
michael@0 227
michael@0 228 if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::img) &&
michael@0 229 aNode->HasFlag(ADDED_TO_FORM)) {
michael@0 230 HTMLImageElement* imageElem = static_cast<HTMLImageElement*>(aNode);
michael@0 231 imageElem->ClearForm(true);
michael@0 232 }
michael@0 233 }
michael@0 234 aNode->UnsetFlags(NODE_HAS_PROPERTIES);
michael@0 235
michael@0 236 if (aNode->NodeType() != nsIDOMNode::DOCUMENT_NODE &&
michael@0 237 aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
michael@0 238 #ifdef DEBUG
michael@0 239 if (nsContentUtils::IsInitialized()) {
michael@0 240 EventListenerManager* manager =
michael@0 241 nsContentUtils::GetExistingListenerManagerForNode(aNode);
michael@0 242 if (!manager) {
michael@0 243 NS_ERROR("Huh, our bit says we have a listener manager list, "
michael@0 244 "but there's nothing in the hash!?!!");
michael@0 245 }
michael@0 246 }
michael@0 247 #endif
michael@0 248
michael@0 249 nsContentUtils::RemoveListenerManager(aNode);
michael@0 250 aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER);
michael@0 251 }
michael@0 252
michael@0 253 if (aNode->IsElement()) {
michael@0 254 nsIDocument* ownerDoc = aNode->OwnerDoc();
michael@0 255 Element* elem = aNode->AsElement();
michael@0 256 ownerDoc->ClearBoxObjectFor(elem);
michael@0 257
michael@0 258 NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
michael@0 259 !elem->GetXBLBinding(),
michael@0 260 "Non-forced node has binding on destruction");
michael@0 261
michael@0 262 // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding
michael@0 263 // attached
michael@0 264 if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) &&
michael@0 265 ownerDoc->BindingManager()) {
michael@0 266 ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc);
michael@0 267 }
michael@0 268 }
michael@0 269
michael@0 270 aNode->ReleaseWrapper(aNode);
michael@0 271
michael@0 272 FragmentOrElement::RemoveBlackMarkedNode(aNode);
michael@0 273 }
michael@0 274
michael@0 275 struct MOZ_STACK_CLASS nsHandlerData
michael@0 276 {
michael@0 277 uint16_t mOperation;
michael@0 278 nsCOMPtr<nsIDOMNode> mSource;
michael@0 279 nsCOMPtr<nsIDOMNode> mDest;
michael@0 280 nsCxPusher mPusher;
michael@0 281 };
michael@0 282
michael@0 283 static void
michael@0 284 CallHandler(void *aObject, nsIAtom *aKey, void *aHandler, void *aData)
michael@0 285 {
michael@0 286 nsHandlerData *handlerData = static_cast<nsHandlerData*>(aData);
michael@0 287 nsCOMPtr<nsIDOMUserDataHandler> handler =
michael@0 288 static_cast<nsIDOMUserDataHandler*>(aHandler);
michael@0 289 nsINode *node = static_cast<nsINode*>(aObject);
michael@0 290 nsCOMPtr<nsIVariant> data =
michael@0 291 static_cast<nsIVariant*>(node->GetProperty(DOM_USER_DATA, aKey));
michael@0 292 NS_ASSERTION(data, "Handler without data?");
michael@0 293
michael@0 294 if (!handlerData->mPusher.RePush(node)) {
michael@0 295 return;
michael@0 296 }
michael@0 297 nsAutoString key;
michael@0 298 aKey->ToString(key);
michael@0 299 handler->Handle(handlerData->mOperation, key, data, handlerData->mSource,
michael@0 300 handlerData->mDest);
michael@0 301 }
michael@0 302
michael@0 303 /* static */
michael@0 304 nsresult
michael@0 305 nsNodeUtils::CallUserDataHandlers(nsCOMArray<nsINode> &aNodesWithProperties,
michael@0 306 nsIDocument *aOwnerDocument,
michael@0 307 uint16_t aOperation, bool aCloned)
michael@0 308 {
michael@0 309 NS_PRECONDITION(!aCloned || (aNodesWithProperties.Count() % 2 == 0),
michael@0 310 "Expected aNodesWithProperties to contain original and "
michael@0 311 "cloned nodes.");
michael@0 312
michael@0 313 if (!nsContentUtils::IsSafeToRunScript()) {
michael@0 314 if (nsContentUtils::IsChromeDoc(aOwnerDocument)) {
michael@0 315 NS_WARNING("Fix the caller! Userdata callback disabled.");
michael@0 316 } else {
michael@0 317 NS_ERROR("This is unsafe! Fix the caller! Userdata callback disabled.");
michael@0 318 }
michael@0 319
michael@0 320 return NS_OK;
michael@0 321 }
michael@0 322
michael@0 323 nsPropertyTable *table = aOwnerDocument->PropertyTable(DOM_USER_DATA_HANDLER);
michael@0 324
michael@0 325 // Keep the document alive, just in case one of the handlers causes it to go
michael@0 326 // away.
michael@0 327 nsCOMPtr<nsIDocument> ownerDoc = aOwnerDocument;
michael@0 328
michael@0 329 nsHandlerData handlerData;
michael@0 330 handlerData.mOperation = aOperation;
michael@0 331
michael@0 332 uint32_t i, count = aNodesWithProperties.Count();
michael@0 333 for (i = 0; i < count; ++i) {
michael@0 334 nsINode *nodeWithProperties = aNodesWithProperties[i];
michael@0 335
michael@0 336 nsresult rv;
michael@0 337 handlerData.mSource = do_QueryInterface(nodeWithProperties, &rv);
michael@0 338 NS_ENSURE_SUCCESS(rv, rv);
michael@0 339
michael@0 340 if (aCloned) {
michael@0 341 handlerData.mDest = do_QueryInterface(aNodesWithProperties[++i], &rv);
michael@0 342 NS_ENSURE_SUCCESS(rv, rv);
michael@0 343 }
michael@0 344
michael@0 345 table->Enumerate(nodeWithProperties, CallHandler, &handlerData);
michael@0 346 }
michael@0 347
michael@0 348 return NS_OK;
michael@0 349 }
michael@0 350
michael@0 351 static void
michael@0 352 NoteUserData(void *aObject, nsIAtom *aKey, void *aXPCOMChild, void *aData)
michael@0 353 {
michael@0 354 nsCycleCollectionTraversalCallback* cb =
michael@0 355 static_cast<nsCycleCollectionTraversalCallback*>(aData);
michael@0 356 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "[user data (or handler)]");
michael@0 357 cb->NoteXPCOMChild(static_cast<nsISupports*>(aXPCOMChild));
michael@0 358 }
michael@0 359
michael@0 360 /* static */
michael@0 361 void
michael@0 362 nsNodeUtils::TraverseUserData(nsINode* aNode,
michael@0 363 nsCycleCollectionTraversalCallback &aCb)
michael@0 364 {
michael@0 365 nsIDocument* ownerDoc = aNode->OwnerDoc();
michael@0 366 ownerDoc->PropertyTable(DOM_USER_DATA)->Enumerate(aNode, NoteUserData, &aCb);
michael@0 367 ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->Enumerate(aNode, NoteUserData, &aCb);
michael@0 368 }
michael@0 369
michael@0 370 /* static */
michael@0 371 nsresult
michael@0 372 nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep,
michael@0 373 bool aCallUserDataHandlers,
michael@0 374 nsINode **aResult)
michael@0 375 {
michael@0 376 *aResult = nullptr;
michael@0 377
michael@0 378 nsCOMPtr<nsINode> newNode;
michael@0 379 nsCOMArray<nsINode> nodesWithProperties;
michael@0 380 nsresult rv = Clone(aNode, aDeep, nullptr, nodesWithProperties,
michael@0 381 getter_AddRefs(newNode));
michael@0 382 NS_ENSURE_SUCCESS(rv, rv);
michael@0 383
michael@0 384 if (aCallUserDataHandlers) {
michael@0 385 rv = CallUserDataHandlers(nodesWithProperties, aNode->OwnerDoc(),
michael@0 386 nsIDOMUserDataHandler::NODE_CLONED, true);
michael@0 387 NS_ENSURE_SUCCESS(rv, rv);
michael@0 388 }
michael@0 389
michael@0 390 newNode.swap(*aResult);
michael@0 391
michael@0 392 return NS_OK;
michael@0 393 }
michael@0 394
michael@0 395 /* static */
michael@0 396 nsresult
michael@0 397 nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
michael@0 398 nsNodeInfoManager *aNewNodeInfoManager,
michael@0 399 JS::Handle<JSObject*> aReparentScope,
michael@0 400 nsCOMArray<nsINode> &aNodesWithProperties,
michael@0 401 nsINode *aParent, nsINode **aResult)
michael@0 402 {
michael@0 403 NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aReparentScope,
michael@0 404 "If cloning or not getting a new nodeinfo we shouldn't "
michael@0 405 "rewrap");
michael@0 406 NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
michael@0 407 "Can't insert document or attribute nodes into a parent");
michael@0 408
michael@0 409 *aResult = nullptr;
michael@0 410
michael@0 411 // First deal with aNode and walk its attributes (and their children). Then,
michael@0 412 // if aDeep is true, deal with aNode's children (and recurse into their
michael@0 413 // attributes and children).
michael@0 414
michael@0 415 AutoJSContext cx;
michael@0 416 nsresult rv;
michael@0 417
michael@0 418 nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
michael@0 419
michael@0 420 // aNode.
michael@0 421 nsINodeInfo *nodeInfo = aNode->mNodeInfo;
michael@0 422 nsCOMPtr<nsINodeInfo> newNodeInfo;
michael@0 423 if (nodeInfoManager) {
michael@0 424
michael@0 425 // Don't allow importing/adopting nodes from non-privileged "scriptable"
michael@0 426 // documents to "non-scriptable" documents.
michael@0 427 nsIDocument* newDoc = nodeInfoManager->GetDocument();
michael@0 428 NS_ENSURE_STATE(newDoc);
michael@0 429 bool hasHadScriptHandlingObject = false;
michael@0 430 if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
michael@0 431 !hasHadScriptHandlingObject) {
michael@0 432 nsIDocument* currentDoc = aNode->OwnerDoc();
michael@0 433 NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) ||
michael@0 434 (!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
michael@0 435 !hasHadScriptHandlingObject)));
michael@0 436 }
michael@0 437
michael@0 438 newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(),
michael@0 439 nodeInfo->GetPrefixAtom(),
michael@0 440 nodeInfo->NamespaceID(),
michael@0 441 nodeInfo->NodeType(),
michael@0 442 nodeInfo->GetExtraName());
michael@0 443
michael@0 444 nodeInfo = newNodeInfo;
michael@0 445 }
michael@0 446
michael@0 447 Element *elem = aNode->IsElement() ? aNode->AsElement() : nullptr;
michael@0 448
michael@0 449 nsCOMPtr<nsINode> clone;
michael@0 450 if (aClone) {
michael@0 451 rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
michael@0 452 NS_ENSURE_SUCCESS(rv, rv);
michael@0 453
michael@0 454 if (aParent) {
michael@0 455 // If we're cloning we need to insert the cloned children into the cloned
michael@0 456 // parent.
michael@0 457 rv = aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
michael@0 458 false);
michael@0 459 NS_ENSURE_SUCCESS(rv, rv);
michael@0 460 }
michael@0 461 else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
michael@0 462 // After cloning the document itself, we want to clone the children into
michael@0 463 // the cloned document (somewhat like cloning and importing them into the
michael@0 464 // cloned document).
michael@0 465 nodeInfoManager = clone->mNodeInfo->NodeInfoManager();
michael@0 466 }
michael@0 467 }
michael@0 468 else if (nodeInfoManager) {
michael@0 469 nsIDocument* oldDoc = aNode->OwnerDoc();
michael@0 470 bool wasRegistered = false;
michael@0 471 if (aNode->IsElement()) {
michael@0 472 Element* element = aNode->AsElement();
michael@0 473 oldDoc->ClearBoxObjectFor(element);
michael@0 474 wasRegistered = oldDoc->UnregisterFreezableElement(element);
michael@0 475 }
michael@0 476
michael@0 477 aNode->mNodeInfo.swap(newNodeInfo);
michael@0 478 if (elem) {
michael@0 479 elem->NodeInfoChanged(newNodeInfo);
michael@0 480 }
michael@0 481
michael@0 482 nsIDocument* newDoc = aNode->OwnerDoc();
michael@0 483 if (newDoc) {
michael@0 484 // XXX what if oldDoc is null, we don't know if this should be
michael@0 485 // registered or not! Can that really happen?
michael@0 486 if (wasRegistered) {
michael@0 487 newDoc->RegisterFreezableElement(aNode->AsElement());
michael@0 488 }
michael@0 489
michael@0 490 nsPIDOMWindow* window = newDoc->GetInnerWindow();
michael@0 491 if (window) {
michael@0 492 EventListenerManager* elm = aNode->GetExistingListenerManager();
michael@0 493 if (elm) {
michael@0 494 window->SetMutationListeners(elm->MutationListenerBits());
michael@0 495 if (elm->MayHavePaintEventListener()) {
michael@0 496 window->SetHasPaintEventListeners();
michael@0 497 }
michael@0 498 if (elm->MayHaveTouchEventListener()) {
michael@0 499 window->SetHasTouchEventListeners();
michael@0 500 }
michael@0 501 if (elm->MayHaveMouseEnterLeaveEventListener()) {
michael@0 502 window->SetHasMouseEnterLeaveEventListeners();
michael@0 503 }
michael@0 504 if (elm->MayHavePointerEnterLeaveEventListener()) {
michael@0 505 window->SetHasPointerEnterLeaveEventListeners();
michael@0 506 }
michael@0 507 }
michael@0 508 }
michael@0 509 }
michael@0 510
michael@0 511 if (wasRegistered && oldDoc != newDoc) {
michael@0 512 nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aNode));
michael@0 513 if (domMediaElem) {
michael@0 514 HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(aNode);
michael@0 515 mediaElem->NotifyOwnerDocumentActivityChanged();
michael@0 516 }
michael@0 517 nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aNode));
michael@0 518 if (objectLoadingContent) {
michael@0 519 nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
michael@0 520 olc->NotifyOwnerDocumentActivityChanged();
michael@0 521 }
michael@0 522 }
michael@0 523
michael@0 524 if (oldDoc != newDoc && oldDoc->MayHaveDOMMutationObservers()) {
michael@0 525 newDoc->SetMayHaveDOMMutationObservers();
michael@0 526 }
michael@0 527
michael@0 528 if (elem) {
michael@0 529 elem->RecompileScriptEventListeners();
michael@0 530 }
michael@0 531
michael@0 532 if (aReparentScope) {
michael@0 533 JS::Rooted<JSObject*> wrapper(cx);
michael@0 534 if ((wrapper = aNode->GetWrapper())) {
michael@0 535 if (IsDOMObject(wrapper)) {
michael@0 536 JSAutoCompartment ac(cx, wrapper);
michael@0 537 rv = ReparentWrapper(cx, wrapper);
michael@0 538 } else {
michael@0 539 nsIXPConnect *xpc = nsContentUtils::XPConnect();
michael@0 540 if (xpc) {
michael@0 541 rv = xpc->ReparentWrappedNativeIfFound(cx, wrapper, aReparentScope, aNode);
michael@0 542 } else {
michael@0 543 rv = NS_ERROR_FAILURE;
michael@0 544 }
michael@0 545 }
michael@0 546 if (NS_FAILED(rv)) {
michael@0 547 aNode->mNodeInfo.swap(nodeInfo);
michael@0 548
michael@0 549 return rv;
michael@0 550 }
michael@0 551 }
michael@0 552 }
michael@0 553 }
michael@0 554
michael@0 555 // XXX If there are any attribute nodes on this element with UserDataHandlers
michael@0 556 // we should technically adopt/clone/import such attribute nodes and notify
michael@0 557 // those handlers. However we currently don't have code to do so without
michael@0 558 // also notifying when it's not safe so we're not doing that at this time.
michael@0 559
michael@0 560 if (aDeep && (!aClone || !aNode->IsNodeOfType(nsINode::eATTRIBUTE))) {
michael@0 561 // aNode's children.
michael@0 562 for (nsIContent* cloneChild = aNode->GetFirstChild();
michael@0 563 cloneChild;
michael@0 564 cloneChild = cloneChild->GetNextSibling()) {
michael@0 565 nsCOMPtr<nsINode> child;
michael@0 566 rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
michael@0 567 aReparentScope, aNodesWithProperties, clone,
michael@0 568 getter_AddRefs(child));
michael@0 569 NS_ENSURE_SUCCESS(rv, rv);
michael@0 570 }
michael@0 571 }
michael@0 572
michael@0 573 // Cloning template element.
michael@0 574 if (aDeep && aClone && IsTemplateElement(aNode)) {
michael@0 575 DocumentFragment* origContent =
michael@0 576 static_cast<HTMLTemplateElement*>(aNode)->Content();
michael@0 577 DocumentFragment* cloneContent =
michael@0 578 static_cast<HTMLTemplateElement*>(clone.get())->Content();
michael@0 579
michael@0 580 // Clone the children into the clone's template content owner
michael@0 581 // document's nodeinfo manager.
michael@0 582 nsNodeInfoManager* ownerNodeInfoManager =
michael@0 583 cloneContent->mNodeInfo->NodeInfoManager();
michael@0 584
michael@0 585 for (nsIContent* cloneChild = origContent->GetFirstChild();
michael@0 586 cloneChild;
michael@0 587 cloneChild = cloneChild->GetNextSibling()) {
michael@0 588 nsCOMPtr<nsINode> child;
michael@0 589 rv = CloneAndAdopt(cloneChild, aClone, aDeep, ownerNodeInfoManager,
michael@0 590 aReparentScope, aNodesWithProperties, cloneContent,
michael@0 591 getter_AddRefs(child));
michael@0 592 NS_ENSURE_SUCCESS(rv, rv);
michael@0 593 }
michael@0 594 }
michael@0 595
michael@0 596 // XXX setting document on some nodes not in a document so XBL will bind
michael@0 597 // and chrome won't break. Make XBL bind to document-less nodes!
michael@0 598 // XXXbz Once this is fixed, fix up the asserts in all implementations of
michael@0 599 // BindToTree to assert what they would like to assert, and fix the
michael@0 600 // ChangeDocumentFor() call in nsXULElement::BindToTree as well. Also,
michael@0 601 // remove the UnbindFromTree call in ~nsXULElement, and add back in the
michael@0 602 // precondition in nsXULElement::UnbindFromTree and remove the line in
michael@0 603 // nsXULElement.h that makes nsNodeUtils a friend of nsXULElement.
michael@0 604 // Note: Make sure to do this witchery _after_ we've done any deep
michael@0 605 // cloning, so kids of the new node aren't confused about whether they're
michael@0 606 // in a document.
michael@0 607 #ifdef MOZ_XUL
michael@0 608 if (aClone && !aParent && aNode->IsElement() &&
michael@0 609 aNode->AsElement()->IsXUL()) {
michael@0 610 if (!aNode->OwnerDoc()->IsLoadedAsInteractiveData()) {
michael@0 611 clone->SetFlags(NODE_FORCE_XBL_BINDINGS);
michael@0 612 }
michael@0 613 }
michael@0 614 #endif
michael@0 615
michael@0 616 if (aNode->HasProperties()) {
michael@0 617 bool ok = aNodesWithProperties.AppendObject(aNode);
michael@0 618 if (aClone) {
michael@0 619 ok = ok && aNodesWithProperties.AppendObject(clone);
michael@0 620 }
michael@0 621
michael@0 622 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
michael@0 623 }
michael@0 624
michael@0 625 clone.forget(aResult);
michael@0 626
michael@0 627 return NS_OK;
michael@0 628 }
michael@0 629
michael@0 630
michael@0 631 /* static */
michael@0 632 void
michael@0 633 nsNodeUtils::UnlinkUserData(nsINode *aNode)
michael@0 634 {
michael@0 635 NS_ASSERTION(aNode->HasProperties(), "Call to UnlinkUserData not needed.");
michael@0 636
michael@0 637 // Strong reference to the document so that deleting properties can't
michael@0 638 // delete the document.
michael@0 639 nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
michael@0 640 document->PropertyTable(DOM_USER_DATA)->DeleteAllPropertiesFor(aNode);
michael@0 641 document->PropertyTable(DOM_USER_DATA_HANDLER)->DeleteAllPropertiesFor(aNode);
michael@0 642 }
michael@0 643
michael@0 644 bool
michael@0 645 nsNodeUtils::IsTemplateElement(const nsINode *aNode)
michael@0 646 {
michael@0 647 return aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::_template);
michael@0 648 }
michael@0 649
michael@0 650 nsIContent*
michael@0 651 nsNodeUtils::GetFirstChildOfTemplateOrNode(nsINode* aNode)
michael@0 652 {
michael@0 653 if (nsNodeUtils::IsTemplateElement(aNode)) {
michael@0 654 DocumentFragment* frag =
michael@0 655 static_cast<HTMLTemplateElement*>(aNode)->Content();
michael@0 656 return frag->GetFirstChild();
michael@0 657 }
michael@0 658
michael@0 659 return aNode->GetFirstChild();
michael@0 660 }
michael@0 661

mercurial