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