|
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/. */ |
|
6 |
|
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" |
|
36 |
|
37 using namespace mozilla; |
|
38 using namespace mozilla::dom; |
|
39 using mozilla::AutoJSContext; |
|
40 |
|
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 |
|
74 |
|
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 } |
|
83 |
|
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 } |
|
92 |
|
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 } |
|
104 |
|
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 } |
|
116 |
|
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 } |
|
126 |
|
127 void |
|
128 nsNodeUtils::ContentAppended(nsIContent* aContainer, |
|
129 nsIContent* aFirstNewContent, |
|
130 int32_t aNewIndexInContainer) |
|
131 { |
|
132 nsIDocument* doc = aContainer->OwnerDoc(); |
|
133 |
|
134 IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer, |
|
135 (doc, aContainer, aFirstNewContent, |
|
136 aNewIndexInContainer)); |
|
137 } |
|
138 |
|
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 } |
|
158 |
|
159 IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer, |
|
160 (document, container, aChild, aIndexInContainer)); |
|
161 } |
|
162 |
|
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 } |
|
183 |
|
184 IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer, |
|
185 (document, container, aChild, aIndexInContainer, |
|
186 aPreviousSibling)); |
|
187 } |
|
188 |
|
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 } |
|
199 |
|
200 delete slots; |
|
201 aNode->mSlots = nullptr; |
|
202 } |
|
203 |
|
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 } |
|
219 |
|
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 } |
|
227 |
|
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); |
|
235 |
|
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 |
|
248 |
|
249 nsContentUtils::RemoveListenerManager(aNode); |
|
250 aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER); |
|
251 } |
|
252 |
|
253 if (aNode->IsElement()) { |
|
254 nsIDocument* ownerDoc = aNode->OwnerDoc(); |
|
255 Element* elem = aNode->AsElement(); |
|
256 ownerDoc->ClearBoxObjectFor(elem); |
|
257 |
|
258 NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) || |
|
259 !elem->GetXBLBinding(), |
|
260 "Non-forced node has binding on destruction"); |
|
261 |
|
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 } |
|
269 |
|
270 aNode->ReleaseWrapper(aNode); |
|
271 |
|
272 FragmentOrElement::RemoveBlackMarkedNode(aNode); |
|
273 } |
|
274 |
|
275 struct MOZ_STACK_CLASS nsHandlerData |
|
276 { |
|
277 uint16_t mOperation; |
|
278 nsCOMPtr<nsIDOMNode> mSource; |
|
279 nsCOMPtr<nsIDOMNode> mDest; |
|
280 nsCxPusher mPusher; |
|
281 }; |
|
282 |
|
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?"); |
|
293 |
|
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 } |
|
302 |
|
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."); |
|
312 |
|
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 } |
|
319 |
|
320 return NS_OK; |
|
321 } |
|
322 |
|
323 nsPropertyTable *table = aOwnerDocument->PropertyTable(DOM_USER_DATA_HANDLER); |
|
324 |
|
325 // Keep the document alive, just in case one of the handlers causes it to go |
|
326 // away. |
|
327 nsCOMPtr<nsIDocument> ownerDoc = aOwnerDocument; |
|
328 |
|
329 nsHandlerData handlerData; |
|
330 handlerData.mOperation = aOperation; |
|
331 |
|
332 uint32_t i, count = aNodesWithProperties.Count(); |
|
333 for (i = 0; i < count; ++i) { |
|
334 nsINode *nodeWithProperties = aNodesWithProperties[i]; |
|
335 |
|
336 nsresult rv; |
|
337 handlerData.mSource = do_QueryInterface(nodeWithProperties, &rv); |
|
338 NS_ENSURE_SUCCESS(rv, rv); |
|
339 |
|
340 if (aCloned) { |
|
341 handlerData.mDest = do_QueryInterface(aNodesWithProperties[++i], &rv); |
|
342 NS_ENSURE_SUCCESS(rv, rv); |
|
343 } |
|
344 |
|
345 table->Enumerate(nodeWithProperties, CallHandler, &handlerData); |
|
346 } |
|
347 |
|
348 return NS_OK; |
|
349 } |
|
350 |
|
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 } |
|
359 |
|
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 } |
|
369 |
|
370 /* static */ |
|
371 nsresult |
|
372 nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep, |
|
373 bool aCallUserDataHandlers, |
|
374 nsINode **aResult) |
|
375 { |
|
376 *aResult = nullptr; |
|
377 |
|
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); |
|
383 |
|
384 if (aCallUserDataHandlers) { |
|
385 rv = CallUserDataHandlers(nodesWithProperties, aNode->OwnerDoc(), |
|
386 nsIDOMUserDataHandler::NODE_CLONED, true); |
|
387 NS_ENSURE_SUCCESS(rv, rv); |
|
388 } |
|
389 |
|
390 newNode.swap(*aResult); |
|
391 |
|
392 return NS_OK; |
|
393 } |
|
394 |
|
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"); |
|
408 |
|
409 *aResult = nullptr; |
|
410 |
|
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). |
|
414 |
|
415 AutoJSContext cx; |
|
416 nsresult rv; |
|
417 |
|
418 nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager; |
|
419 |
|
420 // aNode. |
|
421 nsINodeInfo *nodeInfo = aNode->mNodeInfo; |
|
422 nsCOMPtr<nsINodeInfo> newNodeInfo; |
|
423 if (nodeInfoManager) { |
|
424 |
|
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 } |
|
437 |
|
438 newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(), |
|
439 nodeInfo->GetPrefixAtom(), |
|
440 nodeInfo->NamespaceID(), |
|
441 nodeInfo->NodeType(), |
|
442 nodeInfo->GetExtraName()); |
|
443 |
|
444 nodeInfo = newNodeInfo; |
|
445 } |
|
446 |
|
447 Element *elem = aNode->IsElement() ? aNode->AsElement() : nullptr; |
|
448 |
|
449 nsCOMPtr<nsINode> clone; |
|
450 if (aClone) { |
|
451 rv = aNode->Clone(nodeInfo, getter_AddRefs(clone)); |
|
452 NS_ENSURE_SUCCESS(rv, rv); |
|
453 |
|
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 } |
|
476 |
|
477 aNode->mNodeInfo.swap(newNodeInfo); |
|
478 if (elem) { |
|
479 elem->NodeInfoChanged(newNodeInfo); |
|
480 } |
|
481 |
|
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 } |
|
489 |
|
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 } |
|
510 |
|
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 } |
|
523 |
|
524 if (oldDoc != newDoc && oldDoc->MayHaveDOMMutationObservers()) { |
|
525 newDoc->SetMayHaveDOMMutationObservers(); |
|
526 } |
|
527 |
|
528 if (elem) { |
|
529 elem->RecompileScriptEventListeners(); |
|
530 } |
|
531 |
|
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); |
|
548 |
|
549 return rv; |
|
550 } |
|
551 } |
|
552 } |
|
553 } |
|
554 |
|
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. |
|
559 |
|
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 } |
|
572 |
|
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(); |
|
579 |
|
580 // Clone the children into the clone's template content owner |
|
581 // document's nodeinfo manager. |
|
582 nsNodeInfoManager* ownerNodeInfoManager = |
|
583 cloneContent->mNodeInfo->NodeInfoManager(); |
|
584 |
|
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 } |
|
595 |
|
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 |
|
615 |
|
616 if (aNode->HasProperties()) { |
|
617 bool ok = aNodesWithProperties.AppendObject(aNode); |
|
618 if (aClone) { |
|
619 ok = ok && aNodesWithProperties.AppendObject(clone); |
|
620 } |
|
621 |
|
622 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
|
623 } |
|
624 |
|
625 clone.forget(aResult); |
|
626 |
|
627 return NS_OK; |
|
628 } |
|
629 |
|
630 |
|
631 /* static */ |
|
632 void |
|
633 nsNodeUtils::UnlinkUserData(nsINode *aNode) |
|
634 { |
|
635 NS_ASSERTION(aNode->HasProperties(), "Call to UnlinkUserData not needed."); |
|
636 |
|
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 } |
|
643 |
|
644 bool |
|
645 nsNodeUtils::IsTemplateElement(const nsINode *aNode) |
|
646 { |
|
647 return aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::_template); |
|
648 } |
|
649 |
|
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 } |
|
658 |
|
659 return aNode->GetFirstChild(); |
|
660 } |
|
661 |