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