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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2006
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Jonas Sicking <jonas@sicking.cc> (Original Author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsNodeUtils.h"
40 : #include "nsContentUtils.h"
41 : #include "nsINode.h"
42 : #include "nsIContent.h"
43 : #include "mozilla/dom/Element.h"
44 : #include "nsIMutationObserver.h"
45 : #include "nsIMutationObserver2.h"
46 : #include "nsIDocument.h"
47 : #include "nsIDOMUserDataHandler.h"
48 : #include "nsEventListenerManager.h"
49 : #include "nsIAttribute.h"
50 : #include "nsIXPConnect.h"
51 : #include "nsGenericElement.h"
52 : #include "pldhash.h"
53 : #include "nsIDOMAttr.h"
54 : #include "nsCOMArray.h"
55 : #include "nsPIDOMWindow.h"
56 : #include "nsDocument.h"
57 : #ifdef MOZ_XUL
58 : #include "nsXULElement.h"
59 : #endif
60 : #include "nsBindingManager.h"
61 : #include "nsGenericHTMLElement.h"
62 : #ifdef MOZ_MEDIA
63 : #include "nsHTMLMediaElement.h"
64 : #endif // MOZ_MEDIA
65 : #include "nsImageLoadingContent.h"
66 : #include "jsgc.h"
67 : #include "nsWrapperCacheInlines.h"
68 : #include "nsObjectLoadingContent.h"
69 :
70 : using namespace mozilla::dom;
71 :
72 : // This macro expects the ownerDocument of content_ to be in scope as
73 : // |nsIDocument* doc|
74 : // NOTE: AttributeChildRemoved doesn't use this macro but has a very similar use.
75 : // If you change how this macro behave please update AttributeChildRemoved.
76 : #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_) \
77 : PR_BEGIN_MACRO \
78 : nsINode* node = content_; \
79 : NS_ASSERTION(node->OwnerDoc() == doc, "Bogus document"); \
80 : if (doc) { \
81 : static_cast<nsIMutationObserver*>(doc->BindingManager())-> \
82 : func_ params_; \
83 : } \
84 : do { \
85 : nsINode::nsSlots* slots = node->GetExistingSlots(); \
86 : if (slots && !slots->mMutationObservers.IsEmpty()) { \
87 : /* No need to explicitly notify the first observer first \
88 : since that'll happen anyway. */ \
89 : NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS( \
90 : slots->mMutationObservers, nsIMutationObserver, \
91 : func_, params_); \
92 : } \
93 : node = node->GetNodeParent(); \
94 : } while (node); \
95 : PR_END_MACRO
96 :
97 : void
98 32 : nsNodeUtils::CharacterDataWillChange(nsIContent* aContent,
99 : CharacterDataChangeInfo* aInfo)
100 : {
101 32 : nsIDocument* doc = aContent->OwnerDoc();
102 32 : IMPL_MUTATION_NOTIFICATION(CharacterDataWillChange, aContent,
103 : (doc, aContent, aInfo));
104 32 : }
105 :
106 : void
107 32 : nsNodeUtils::CharacterDataChanged(nsIContent* aContent,
108 : CharacterDataChangeInfo* aInfo)
109 : {
110 32 : nsIDocument* doc = aContent->OwnerDoc();
111 32 : IMPL_MUTATION_NOTIFICATION(CharacterDataChanged, aContent,
112 : (doc, aContent, aInfo));
113 32 : }
114 :
115 : void
116 1874 : nsNodeUtils::AttributeWillChange(Element* aElement,
117 : PRInt32 aNameSpaceID,
118 : nsIAtom* aAttribute,
119 : PRInt32 aModType)
120 : {
121 1874 : nsIDocument* doc = aElement->OwnerDoc();
122 1874 : IMPL_MUTATION_NOTIFICATION(AttributeWillChange, aElement,
123 : (doc, aElement, aNameSpaceID, aAttribute,
124 : aModType));
125 1874 : }
126 :
127 : void
128 1874 : nsNodeUtils::AttributeChanged(Element* aElement,
129 : PRInt32 aNameSpaceID,
130 : nsIAtom* aAttribute,
131 : PRInt32 aModType)
132 : {
133 1874 : nsIDocument* doc = aElement->OwnerDoc();
134 1874 : IMPL_MUTATION_NOTIFICATION(AttributeChanged, aElement,
135 : (doc, aElement, aNameSpaceID, aAttribute,
136 : aModType));
137 1874 : }
138 :
139 : void
140 2366 : nsNodeUtils::ContentAppended(nsIContent* aContainer,
141 : nsIContent* aFirstNewContent,
142 : PRInt32 aNewIndexInContainer)
143 : {
144 2366 : nsIDocument* doc = aContainer->OwnerDoc();
145 :
146 2366 : IMPL_MUTATION_NOTIFICATION(ContentAppended, aContainer,
147 : (doc, aContainer, aFirstNewContent,
148 : aNewIndexInContainer));
149 2366 : }
150 :
151 : void
152 1295 : nsNodeUtils::ContentInserted(nsINode* aContainer,
153 : nsIContent* aChild,
154 : PRInt32 aIndexInContainer)
155 : {
156 1295 : NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) ||
157 : aContainer->IsNodeOfType(nsINode::eDOCUMENT),
158 : "container must be an nsIContent or an nsIDocument");
159 : nsIContent* container;
160 1295 : nsIDocument* doc = aContainer->OwnerDoc();
161 : nsIDocument* document;
162 1295 : if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
163 12 : container = static_cast<nsIContent*>(aContainer);
164 12 : document = doc;
165 : }
166 : else {
167 1283 : container = nsnull;
168 1283 : document = static_cast<nsIDocument*>(aContainer);
169 : }
170 :
171 1295 : IMPL_MUTATION_NOTIFICATION(ContentInserted, aContainer,
172 : (document, container, aChild, aIndexInContainer));
173 1295 : }
174 :
175 : void
176 239 : nsNodeUtils::ContentRemoved(nsINode* aContainer,
177 : nsIContent* aChild,
178 : PRInt32 aIndexInContainer,
179 : nsIContent* aPreviousSibling)
180 : {
181 239 : NS_PRECONDITION(aContainer->IsNodeOfType(nsINode::eCONTENT) ||
182 : aContainer->IsNodeOfType(nsINode::eDOCUMENT),
183 : "container must be an nsIContent or an nsIDocument");
184 : nsIContent* container;
185 239 : nsIDocument* doc = aContainer->OwnerDoc();
186 : nsIDocument* document;
187 239 : if (aContainer->IsNodeOfType(nsINode::eCONTENT)) {
188 229 : container = static_cast<nsIContent*>(aContainer);
189 229 : document = doc;
190 : }
191 : else {
192 10 : container = nsnull;
193 10 : document = static_cast<nsIDocument*>(aContainer);
194 : }
195 :
196 239 : IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer,
197 : (document, container, aChild, aIndexInContainer,
198 : aPreviousSibling));
199 239 : }
200 :
201 : void
202 0 : nsNodeUtils::AttributeChildRemoved(nsINode* aAttribute,
203 : nsIContent* aChild)
204 : {
205 0 : NS_PRECONDITION(aAttribute->IsNodeOfType(nsINode::eATTRIBUTE),
206 : "container must be a nsIAttribute");
207 :
208 : // This is a variant of IMPL_MUTATION_NOTIFICATION.
209 0 : do {
210 0 : nsINode::nsSlots* slots = aAttribute->GetExistingSlots();
211 0 : if (slots && !slots->mMutationObservers.IsEmpty()) {
212 : // This is a variant of NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS.
213 : nsTObserverArray<nsIMutationObserver*>::ForwardIterator iter_ =
214 0 : slots->mMutationObservers;
215 0 : nsCOMPtr<nsIMutationObserver2> obs_;
216 0 : while (iter_.HasMore()) {
217 0 : obs_ = do_QueryInterface(iter_.GetNext());
218 0 : if (obs_) {
219 0 : obs_->AttributeChildRemoved(aAttribute, aChild);
220 : }
221 : }
222 : }
223 0 : aAttribute = aAttribute->GetNodeParent();
224 : } while (aAttribute);
225 0 : }
226 :
227 : void
228 481635 : nsNodeUtils::ParentChainChanged(nsIContent *aContent)
229 : {
230 : // No need to notify observers on the parents since their parent
231 : // chain must have been changed too and so their observers were
232 : // notified at that time.
233 :
234 481635 : nsINode::nsSlots* slots = aContent->GetExistingSlots();
235 481635 : if (slots && !slots->mMutationObservers.IsEmpty()) {
236 512 : NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(
237 : slots->mMutationObservers,
238 : nsIMutationObserver,
239 : ParentChainChanged,
240 : (aContent));
241 : }
242 481635 : }
243 :
244 : void
245 115281 : nsNodeUtils::LastRelease(nsINode* aNode)
246 : {
247 115281 : nsINode::nsSlots* slots = aNode->GetExistingSlots();
248 115281 : if (slots) {
249 4609 : if (!slots->mMutationObservers.IsEmpty()) {
250 1292 : NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers,
251 : nsIMutationObserver,
252 : NodeWillBeDestroyed, (aNode));
253 : }
254 :
255 4609 : delete slots;
256 4609 : aNode->mSlots = nsnull;
257 : }
258 :
259 : // Kill properties first since that may run external code, so we want to
260 : // be in as complete state as possible at that time.
261 115281 : if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
262 : // Delete all properties before tearing down the document. Some of the
263 : // properties are bound to nsINode objects and the destructor functions of
264 : // the properties may want to use the owner document of the nsINode.
265 1271 : static_cast<nsIDocument*>(aNode)->DeleteAllProperties();
266 : }
267 : else {
268 114010 : if (aNode->HasProperties()) {
269 : // Strong reference to the document so that deleting properties can't
270 : // delete the document.
271 0 : nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
272 0 : document->DeleteAllPropertiesFor(aNode);
273 : }
274 :
275 : // I wonder whether it's faster to do the HasFlag check first....
276 114011 : if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
277 1 : aNode->HasFlag(ADDED_TO_FORM)) {
278 : // Tell the form (if any) this node is going away. Don't
279 : // notify, since we're being destroyed in any case.
280 0 : static_cast<nsGenericHTMLFormElement*>(aNode)->ClearForm(true);
281 : }
282 : }
283 115281 : aNode->UnsetFlags(NODE_HAS_PROPERTIES);
284 :
285 229291 : if (aNode->NodeType() != nsIDOMNode::DOCUMENT_NODE &&
286 114010 : aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
287 : #ifdef DEBUG
288 0 : if (nsContentUtils::IsInitialized()) {
289 : nsEventListenerManager* manager =
290 0 : nsContentUtils::GetListenerManager(aNode, false);
291 0 : if (!manager) {
292 : NS_ERROR("Huh, our bit says we have a listener manager list, "
293 0 : "but there's nothing in the hash!?!!");
294 : }
295 : }
296 : #endif
297 :
298 0 : nsContentUtils::RemoveListenerManager(aNode);
299 0 : aNode->UnsetFlags(NODE_HAS_LISTENERMANAGER);
300 : }
301 :
302 115281 : if (aNode->IsElement()) {
303 37963 : nsIDocument* ownerDoc = aNode->OwnerDoc();
304 37963 : Element* elem = aNode->AsElement();
305 37963 : ownerDoc->ClearBoxObjectFor(elem);
306 :
307 37963 : NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
308 : !ownerDoc->BindingManager() ||
309 : !ownerDoc->BindingManager()->GetBinding(elem),
310 : "Non-forced node has binding on destruction");
311 :
312 : // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding
313 : // attached
314 37963 : if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) &&
315 0 : ownerDoc->BindingManager()) {
316 0 : ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc);
317 : }
318 : }
319 :
320 115281 : nsContentUtils::ReleaseWrapper(aNode, aNode);
321 :
322 115281 : delete aNode;
323 115281 : }
324 :
325 : struct NS_STACK_CLASS nsHandlerData
326 524 : {
327 : PRUint16 mOperation;
328 : nsCOMPtr<nsIDOMNode> mSource;
329 : nsCOMPtr<nsIDOMNode> mDest;
330 : nsCxPusher mPusher;
331 : };
332 :
333 : static void
334 0 : CallHandler(void *aObject, nsIAtom *aKey, void *aHandler, void *aData)
335 : {
336 0 : nsHandlerData *handlerData = static_cast<nsHandlerData*>(aData);
337 : nsCOMPtr<nsIDOMUserDataHandler> handler =
338 0 : static_cast<nsIDOMUserDataHandler*>(aHandler);
339 0 : nsINode *node = static_cast<nsINode*>(aObject);
340 : nsCOMPtr<nsIVariant> data =
341 0 : static_cast<nsIVariant*>(node->GetProperty(DOM_USER_DATA, aKey));
342 0 : NS_ASSERTION(data, "Handler without data?");
343 :
344 0 : if (!handlerData->mPusher.RePush(node)) {
345 : return;
346 : }
347 0 : nsAutoString key;
348 0 : aKey->ToString(key);
349 0 : handler->Handle(handlerData->mOperation, key, data, handlerData->mSource,
350 0 : handlerData->mDest);
351 : }
352 :
353 : /* static */
354 : nsresult
355 262 : nsNodeUtils::CallUserDataHandlers(nsCOMArray<nsINode> &aNodesWithProperties,
356 : nsIDocument *aOwnerDocument,
357 : PRUint16 aOperation, bool aCloned)
358 : {
359 262 : NS_PRECONDITION(!aCloned || (aNodesWithProperties.Count() % 2 == 0),
360 : "Expected aNodesWithProperties to contain original and "
361 : "cloned nodes.");
362 :
363 262 : if (!nsContentUtils::IsSafeToRunScript()) {
364 0 : if (nsContentUtils::IsChromeDoc(aOwnerDocument)) {
365 0 : NS_WARNING("Fix the caller! Userdata callback disabled.");
366 : } else {
367 0 : NS_ERROR("This is unsafe! Fix the caller! Userdata callback disabled.");
368 : }
369 :
370 0 : return NS_OK;
371 : }
372 :
373 262 : nsPropertyTable *table = aOwnerDocument->PropertyTable(DOM_USER_DATA_HANDLER);
374 :
375 : // Keep the document alive, just in case one of the handlers causes it to go
376 : // away.
377 524 : nsCOMPtr<nsIDocument> ownerDoc = aOwnerDocument;
378 :
379 524 : nsHandlerData handlerData;
380 262 : handlerData.mOperation = aOperation;
381 :
382 262 : PRUint32 i, count = aNodesWithProperties.Count();
383 262 : for (i = 0; i < count; ++i) {
384 0 : nsINode *nodeWithProperties = aNodesWithProperties[i];
385 :
386 : nsresult rv;
387 0 : handlerData.mSource = do_QueryInterface(nodeWithProperties, &rv);
388 0 : NS_ENSURE_SUCCESS(rv, rv);
389 :
390 0 : if (aCloned) {
391 0 : handlerData.mDest = do_QueryInterface(aNodesWithProperties[++i], &rv);
392 0 : NS_ENSURE_SUCCESS(rv, rv);
393 : }
394 :
395 0 : table->Enumerate(nodeWithProperties, CallHandler, &handlerData);
396 : }
397 :
398 262 : return NS_OK;
399 : }
400 :
401 : static void
402 0 : NoteUserData(void *aObject, nsIAtom *aKey, void *aXPCOMChild, void *aData)
403 : {
404 : nsCycleCollectionTraversalCallback* cb =
405 0 : static_cast<nsCycleCollectionTraversalCallback*>(aData);
406 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "[user data (or handler)]");
407 0 : cb->NoteXPCOMChild(static_cast<nsISupports*>(aXPCOMChild));
408 0 : }
409 :
410 : /* static */
411 : void
412 0 : nsNodeUtils::TraverseUserData(nsINode* aNode,
413 : nsCycleCollectionTraversalCallback &aCb)
414 : {
415 0 : nsIDocument* ownerDoc = aNode->OwnerDoc();
416 0 : ownerDoc->PropertyTable(DOM_USER_DATA)->Enumerate(aNode, NoteUserData, &aCb);
417 0 : ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->Enumerate(aNode, NoteUserData, &aCb);
418 0 : }
419 :
420 : /* static */
421 : nsresult
422 262 : nsNodeUtils::CloneNodeImpl(nsINode *aNode, bool aDeep,
423 : bool aCallUserDataHandlers,
424 : nsIDOMNode **aResult)
425 : {
426 262 : *aResult = nsnull;
427 :
428 524 : nsCOMPtr<nsIDOMNode> newNode;
429 524 : nsCOMArray<nsINode> nodesWithProperties;
430 : nsresult rv = Clone(aNode, aDeep, nsnull, nodesWithProperties,
431 262 : getter_AddRefs(newNode));
432 262 : NS_ENSURE_SUCCESS(rv, rv);
433 :
434 262 : if (aCallUserDataHandlers) {
435 : rv = CallUserDataHandlers(nodesWithProperties, aNode->OwnerDoc(),
436 262 : nsIDOMUserDataHandler::NODE_CLONED, true);
437 262 : NS_ENSURE_SUCCESS(rv, rv);
438 : }
439 :
440 262 : newNode.swap(*aResult);
441 :
442 262 : return NS_OK;
443 : }
444 :
445 : /* static */
446 : nsresult
447 823 : nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
448 : nsNodeInfoManager *aNewNodeInfoManager,
449 : JSContext *aCx, JSObject *aNewScope,
450 : nsCOMArray<nsINode> &aNodesWithProperties,
451 : nsINode *aParent, nsINode **aResult)
452 : {
453 823 : NS_PRECONDITION((!aClone && aNewNodeInfoManager) || !aCx,
454 : "If cloning or not getting a new nodeinfo we shouldn't "
455 : "rewrap");
456 823 : NS_PRECONDITION(!aCx || aNewScope, "Must have new scope");
457 823 : NS_PRECONDITION(!aParent || aNode->IsNodeOfType(nsINode::eCONTENT),
458 : "Can't insert document or attribute nodes into a parent");
459 :
460 823 : *aResult = nsnull;
461 :
462 : // First deal with aNode and walk its attributes (and their children). Then,
463 : // if aDeep is true, deal with aNode's children (and recurse into their
464 : // attributes and children).
465 :
466 : nsresult rv;
467 : JSObject *wrapper;
468 823 : if (aCx && (wrapper = aNode->GetWrapper())) {
469 0 : rv = xpc_MorphSlimWrapper(aCx, aNode);
470 0 : NS_ENSURE_SUCCESS(rv, rv);
471 : }
472 :
473 823 : nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
474 :
475 : // aNode.
476 823 : nsINodeInfo *nodeInfo = aNode->mNodeInfo;
477 1646 : nsCOMPtr<nsINodeInfo> newNodeInfo;
478 823 : if (nodeInfoManager) {
479 :
480 : // Don't allow importing/adopting nodes from non-privileged "scriptable"
481 : // documents to "non-scriptable" documents.
482 0 : nsIDocument* newDoc = nodeInfoManager->GetDocument();
483 0 : NS_ENSURE_STATE(newDoc);
484 0 : bool hasHadScriptHandlingObject = false;
485 0 : if (!newDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
486 0 : !hasHadScriptHandlingObject) {
487 0 : nsIDocument* currentDoc = aNode->OwnerDoc();
488 0 : NS_ENSURE_STATE((nsContentUtils::IsChromeDoc(currentDoc) ||
489 : (!currentDoc->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
490 : !hasHadScriptHandlingObject)));
491 : }
492 :
493 : newNodeInfo = nodeInfoManager->GetNodeInfo(nodeInfo->NameAtom(),
494 : nodeInfo->GetPrefixAtom(),
495 : nodeInfo->NamespaceID(),
496 0 : nodeInfo->NodeType(),
497 0 : nodeInfo->GetExtraName());
498 0 : NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
499 :
500 0 : nodeInfo = newNodeInfo;
501 : }
502 :
503 823 : nsGenericElement *elem = aNode->IsElement() ?
504 : static_cast<nsGenericElement*>(aNode) :
505 823 : nsnull;
506 :
507 1646 : nsCOMPtr<nsINode> clone;
508 823 : if (aClone) {
509 823 : rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
510 823 : NS_ENSURE_SUCCESS(rv, rv);
511 :
512 823 : if (aParent) {
513 : // If we're cloning we need to insert the cloned children into the cloned
514 : // parent.
515 561 : rv = aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
516 561 : false);
517 561 : NS_ENSURE_SUCCESS(rv, rv);
518 : }
519 262 : else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
520 : // After cloning the document itself, we want to clone the children into
521 : // the cloned document (somewhat like cloning and importing them into the
522 : // cloned document).
523 0 : nodeInfoManager = clone->mNodeInfo->NodeInfoManager();
524 : }
525 : }
526 0 : else if (nodeInfoManager) {
527 0 : nsIDocument* oldDoc = aNode->OwnerDoc();
528 0 : bool wasRegistered = false;
529 0 : if (aNode->IsElement()) {
530 0 : Element* element = aNode->AsElement();
531 0 : oldDoc->ClearBoxObjectFor(element);
532 0 : wasRegistered = oldDoc->UnregisterFreezableElement(element);
533 : }
534 :
535 0 : aNode->mNodeInfo.swap(newNodeInfo);
536 0 : if (elem) {
537 0 : elem->NodeInfoChanged(newNodeInfo);
538 : }
539 :
540 0 : nsIDocument* newDoc = aNode->OwnerDoc();
541 0 : if (newDoc) {
542 : // XXX what if oldDoc is null, we don't know if this should be
543 : // registered or not! Can that really happen?
544 0 : if (wasRegistered) {
545 0 : newDoc->RegisterFreezableElement(aNode->AsElement());
546 : }
547 :
548 0 : nsPIDOMWindow* window = newDoc->GetInnerWindow();
549 0 : if (window) {
550 0 : nsEventListenerManager* elm = aNode->GetListenerManager(false);
551 0 : if (elm) {
552 0 : window->SetMutationListeners(elm->MutationListenerBits());
553 0 : if (elm->MayHavePaintEventListener()) {
554 0 : window->SetHasPaintEventListeners();
555 : }
556 : #ifdef MOZ_MEDIA
557 0 : if (elm->MayHaveAudioAvailableEventListener()) {
558 0 : window->SetHasAudioAvailableEventListeners();
559 : }
560 : #endif
561 0 : if (elm->MayHaveTouchEventListener()) {
562 0 : window->SetHasTouchEventListeners();
563 : }
564 0 : if (elm->MayHaveMouseEnterLeaveEventListener()) {
565 0 : window->SetHasMouseEnterLeaveEventListeners();
566 : }
567 : }
568 : }
569 : }
570 :
571 0 : if (wasRegistered && oldDoc != newDoc) {
572 : #ifdef MOZ_MEDIA
573 0 : nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aNode));
574 0 : if (domMediaElem) {
575 0 : nsHTMLMediaElement* mediaElem = static_cast<nsHTMLMediaElement*>(aNode);
576 0 : mediaElem->NotifyOwnerDocumentActivityChanged();
577 : }
578 : #endif
579 0 : nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aNode));
580 0 : if (objectLoadingContent) {
581 0 : nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
582 0 : olc->NotifyOwnerDocumentActivityChanged();
583 : }
584 : }
585 :
586 : // nsImageLoadingContent needs to know when its document changes
587 0 : if (oldDoc != newDoc) {
588 0 : nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aNode));
589 0 : if (imageContent)
590 0 : imageContent->NotifyOwnerDocumentChanged(oldDoc);
591 : }
592 :
593 0 : if (elem) {
594 0 : elem->RecompileScriptEventListeners();
595 : }
596 :
597 0 : if (aCx && wrapper) {
598 0 : nsIXPConnect *xpc = nsContentUtils::XPConnect();
599 0 : if (xpc) {
600 0 : nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
601 : rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode,
602 0 : getter_AddRefs(oldWrapper));
603 :
604 0 : if (NS_FAILED(rv)) {
605 0 : aNode->mNodeInfo.swap(nodeInfo);
606 :
607 0 : return rv;
608 : }
609 : }
610 : }
611 : }
612 :
613 : // XXX If there are any attribute nodes on this element with UserDataHandlers
614 : // we should technically adopt/clone/import such attribute nodes and notify
615 : // those handlers. However we currently don't have code to do so without
616 : // also notifying when it's not safe so we're not doing that at this time.
617 :
618 : // The DOM spec says to always adopt/clone/import the children of attribute
619 : // nodes.
620 : // XXX The following block is here because our implementation of attribute
621 : // nodes is broken when it comes to inserting children. Instead of cloning
622 : // their children we force creation of the only child by calling
623 : // GetChildAt(0). We can remove this when
624 : // https://bugzilla.mozilla.org/show_bug.cgi?id=56758 is fixed.
625 823 : if (aClone && aNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
626 96 : nsCOMPtr<nsINode> attrChildNode = aNode->GetChildAt(0);
627 : // We only need to do this if the child node has properties (because we
628 : // might need to call a userdata handler).
629 48 : if (attrChildNode && attrChildNode->HasProperties()) {
630 0 : nsCOMPtr<nsINode> clonedAttrChildNode = clone->GetChildAt(0);
631 0 : if (clonedAttrChildNode) {
632 0 : bool ok = aNodesWithProperties.AppendObject(attrChildNode) &&
633 0 : aNodesWithProperties.AppendObject(clonedAttrChildNode);
634 0 : NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
635 : }
636 : }
637 : }
638 : // XXX End of workaround for broken attribute nodes.
639 775 : else if (aDeep || aNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
640 : // aNode's children.
641 1327 : for (nsIContent* cloneChild = aNode->GetFirstChild();
642 : cloneChild;
643 561 : cloneChild = cloneChild->GetNextSibling()) {
644 1122 : nsCOMPtr<nsINode> child;
645 : rv = CloneAndAdopt(cloneChild, aClone, true, nodeInfoManager,
646 : aCx, aNewScope, aNodesWithProperties, clone,
647 561 : getter_AddRefs(child));
648 561 : NS_ENSURE_SUCCESS(rv, rv);
649 : }
650 : }
651 :
652 : // XXX setting document on some nodes not in a document so XBL will bind
653 : // and chrome won't break. Make XBL bind to document-less nodes!
654 : // XXXbz Once this is fixed, fix up the asserts in all implementations of
655 : // BindToTree to assert what they would like to assert, and fix the
656 : // ChangeDocumentFor() call in nsXULElement::BindToTree as well. Also,
657 : // remove the UnbindFromTree call in ~nsXULElement, and add back in the
658 : // precondition in nsXULElement::UnbindFromTree and remove the line in
659 : // nsXULElement.h that makes nsNodeUtils a friend of nsXULElement.
660 : // Note: Make sure to do this witchery _after_ we've done any deep
661 : // cloning, so kids of the new node aren't confused about whether they're
662 : // in a document.
663 : #ifdef MOZ_XUL
664 939 : if (aClone && !aParent && aNode->IsElement() &&
665 116 : aNode->AsElement()->IsXUL()) {
666 0 : nsXULElement *xulElem = static_cast<nsXULElement*>(elem);
667 0 : if (!xulElem->mPrototype || xulElem->IsInDoc()) {
668 0 : clone->SetFlags(NODE_FORCE_XBL_BINDINGS);
669 : }
670 : }
671 : #endif
672 :
673 823 : if (aNode->HasProperties()) {
674 0 : bool ok = aNodesWithProperties.AppendObject(aNode);
675 0 : if (aClone) {
676 0 : ok = ok && aNodesWithProperties.AppendObject(clone);
677 : }
678 :
679 0 : NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
680 : }
681 :
682 823 : clone.forget(aResult);
683 :
684 823 : return NS_OK;
685 : }
686 :
687 :
688 : /* static */
689 : void
690 0 : nsNodeUtils::UnlinkUserData(nsINode *aNode)
691 : {
692 0 : NS_ASSERTION(aNode->HasProperties(), "Call to UnlinkUserData not needed.");
693 :
694 : // Strong reference to the document so that deleting properties can't
695 : // delete the document.
696 0 : nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
697 0 : document->PropertyTable(DOM_USER_DATA)->DeleteAllPropertiesFor(aNode);
698 0 : document->PropertyTable(DOM_USER_DATA_HANDLER)->DeleteAllPropertiesFor(aNode);
699 0 : }
|