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