1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsDOMMutationObserver.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,848 @@ 1.4 +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */ 1.5 +/* vim: set sw=4 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsDOMMutationObserver.h" 1.11 + 1.12 +#include "mozilla/dom/OwningNonNull.h" 1.13 +#include "nsError.h" 1.14 +#include "nsIScriptGlobalObject.h" 1.15 +#include "nsContentUtils.h" 1.16 +#include "nsThreadUtils.h" 1.17 +#include "nsIDOMMutationEvent.h" 1.18 +#include "nsTextFragment.h" 1.19 +#include "nsServiceManagerUtils.h" 1.20 + 1.21 +nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* 1.22 + nsDOMMutationObserver::sScheduledMutationObservers = nullptr; 1.23 + 1.24 +nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr; 1.25 + 1.26 +uint32_t nsDOMMutationObserver::sMutationLevel = 0; 1.27 +uint64_t nsDOMMutationObserver::sCount = 0; 1.28 + 1.29 +nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>* 1.30 +nsDOMMutationObserver::sCurrentlyHandlingObservers = nullptr; 1.31 + 1.32 +nsINodeList* 1.33 +nsDOMMutationRecord::AddedNodes() 1.34 +{ 1.35 + if (!mAddedNodes) { 1.36 + mAddedNodes = new nsSimpleContentList(mTarget); 1.37 + } 1.38 + return mAddedNodes; 1.39 +} 1.40 + 1.41 +nsINodeList* 1.42 +nsDOMMutationRecord::RemovedNodes() 1.43 +{ 1.44 + if (!mRemovedNodes) { 1.45 + mRemovedNodes = new nsSimpleContentList(mTarget); 1.46 + } 1.47 + return mRemovedNodes; 1.48 +} 1.49 + 1.50 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationRecord) 1.51 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.52 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.53 +NS_INTERFACE_MAP_END 1.54 + 1.55 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord) 1.56 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord) 1.57 + 1.58 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_7(nsDOMMutationRecord, 1.59 + mTarget, 1.60 + mPreviousSibling, mNextSibling, 1.61 + mAddedNodes, mRemovedNodes, 1.62 + mNext, mOwner) 1.63 + 1.64 +// Observer 1.65 + 1.66 +NS_IMPL_ADDREF(nsMutationReceiver) 1.67 +NS_IMPL_RELEASE(nsMutationReceiver) 1.68 + 1.69 +NS_INTERFACE_MAP_BEGIN(nsMutationReceiver) 1.70 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.71 + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 1.72 +NS_INTERFACE_MAP_END 1.73 + 1.74 +nsMutationReceiver::nsMutationReceiver(nsINode* aTarget, 1.75 + nsDOMMutationObserver* aObserver) 1.76 +: nsMutationReceiverBase(aTarget, aObserver) 1.77 +{ 1.78 + mTarget->BindObject(aObserver); 1.79 +} 1.80 + 1.81 +void 1.82 +nsMutationReceiver::Disconnect(bool aRemoveFromObserver) 1.83 +{ 1.84 + if (mRegisterTarget) { 1.85 + mRegisterTarget->RemoveMutationObserver(this); 1.86 + mRegisterTarget = nullptr; 1.87 + } 1.88 + 1.89 + mParent = nullptr; 1.90 + nsINode* target = mTarget; 1.91 + mTarget = nullptr; 1.92 + nsDOMMutationObserver* observer = mObserver; 1.93 + mObserver = nullptr; 1.94 + RemoveClones(); 1.95 + 1.96 + if (target && observer) { 1.97 + if (aRemoveFromObserver) { 1.98 + static_cast<nsDOMMutationObserver*>(observer)->RemoveReceiver(this); 1.99 + } 1.100 + // UnbindObject may delete 'this'! 1.101 + target->UnbindObject(observer); 1.102 + } 1.103 +} 1.104 + 1.105 +void 1.106 +nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument, 1.107 + mozilla::dom::Element* aElement, 1.108 + int32_t aNameSpaceID, 1.109 + nsIAtom* aAttribute, 1.110 + int32_t aModType) 1.111 +{ 1.112 + if (nsAutoMutationBatch::IsBatching() || 1.113 + !ObservesAttr(aElement, aNameSpaceID, aAttribute) || 1.114 + aElement->ChromeOnlyAccess()) { 1.115 + return; 1.116 + } 1.117 + 1.118 + nsDOMMutationRecord* m = 1.119 + Observer()->CurrentRecord(nsGkAtoms::attributes); 1.120 + 1.121 + NS_ASSERTION(!m->mTarget || m->mTarget == aElement, 1.122 + "Wrong target!"); 1.123 + NS_ASSERTION(!m->mAttrName || m->mAttrName == aAttribute, 1.124 + "Wrong attribute!"); 1.125 + if (!m->mTarget) { 1.126 + m->mTarget = aElement; 1.127 + m->mAttrName = aAttribute; 1.128 + if (aNameSpaceID == kNameSpaceID_None) { 1.129 + m->mAttrNamespace.SetIsVoid(true); 1.130 + } else { 1.131 + nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, 1.132 + m->mAttrNamespace); 1.133 + } 1.134 + } 1.135 + 1.136 + if (AttributeOldValue() && m->mPrevValue.IsVoid()) { 1.137 + if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) { 1.138 + m->mPrevValue.SetIsVoid(true); 1.139 + } 1.140 + } 1.141 +} 1.142 + 1.143 +void 1.144 +nsMutationReceiver::CharacterDataWillChange(nsIDocument *aDocument, 1.145 + nsIContent* aContent, 1.146 + CharacterDataChangeInfo* aInfo) 1.147 +{ 1.148 + if (nsAutoMutationBatch::IsBatching() || 1.149 + !CharacterData() || !(Subtree() || aContent == Target()) || 1.150 + aContent->ChromeOnlyAccess()) { 1.151 + return; 1.152 + } 1.153 + 1.154 + nsDOMMutationRecord* m = 1.155 + Observer()->CurrentRecord(nsGkAtoms::characterData); 1.156 + 1.157 + NS_ASSERTION(!m->mTarget || m->mTarget == aContent, 1.158 + "Wrong target!"); 1.159 + 1.160 + if (!m->mTarget) { 1.161 + m->mTarget = aContent; 1.162 + } 1.163 + if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) { 1.164 + aContent->GetText()->AppendTo(m->mPrevValue); 1.165 + } 1.166 +} 1.167 + 1.168 +void 1.169 +nsMutationReceiver::ContentAppended(nsIDocument* aDocument, 1.170 + nsIContent* aContainer, 1.171 + nsIContent* aFirstNewContent, 1.172 + int32_t aNewIndexInContainer) 1.173 +{ 1.174 + nsINode* parent = NODE_FROM(aContainer, aDocument); 1.175 + bool wantsChildList = ChildList() && (Subtree() || parent == Target()); 1.176 + if (!wantsChildList || aFirstNewContent->ChromeOnlyAccess()) { 1.177 + return; 1.178 + } 1.179 + 1.180 + if (nsAutoMutationBatch::IsBatching()) { 1.181 + if (parent == nsAutoMutationBatch::GetBatchTarget()) { 1.182 + nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList); 1.183 + } 1.184 + return; 1.185 + } 1.186 + 1.187 + nsDOMMutationRecord* m = 1.188 + Observer()->CurrentRecord(nsGkAtoms::childList); 1.189 + NS_ASSERTION(!m->mTarget || m->mTarget == parent, 1.190 + "Wrong target!"); 1.191 + if (m->mTarget) { 1.192 + // Already handled case. 1.193 + return; 1.194 + } 1.195 + m->mTarget = parent; 1.196 + m->mAddedNodes = new nsSimpleContentList(parent); 1.197 + 1.198 + nsINode* n = aFirstNewContent; 1.199 + while (n) { 1.200 + m->mAddedNodes->AppendElement(static_cast<nsIContent*>(n)); 1.201 + n = n->GetNextSibling(); 1.202 + } 1.203 + m->mPreviousSibling = aFirstNewContent->GetPreviousSibling(); 1.204 +} 1.205 + 1.206 +void 1.207 +nsMutationReceiver::ContentInserted(nsIDocument* aDocument, 1.208 + nsIContent* aContainer, 1.209 + nsIContent* aChild, 1.210 + int32_t aIndexInContainer) 1.211 +{ 1.212 + nsINode* parent = NODE_FROM(aContainer, aDocument); 1.213 + bool wantsChildList = ChildList() && (Subtree() || parent == Target()); 1.214 + if (!wantsChildList || aChild->ChromeOnlyAccess()) { 1.215 + return; 1.216 + } 1.217 + 1.218 + if (nsAutoMutationBatch::IsBatching()) { 1.219 + if (parent == nsAutoMutationBatch::GetBatchTarget()) { 1.220 + nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList); 1.221 + } 1.222 + return; 1.223 + } 1.224 + 1.225 + nsDOMMutationRecord* m = 1.226 + Observer()->CurrentRecord(nsGkAtoms::childList); 1.227 + if (m->mTarget) { 1.228 + // Already handled case. 1.229 + return; 1.230 + } 1.231 + m->mTarget = parent; 1.232 + m->mAddedNodes = new nsSimpleContentList(parent); 1.233 + m->mAddedNodes->AppendElement(aChild); 1.234 + m->mPreviousSibling = aChild->GetPreviousSibling(); 1.235 + m->mNextSibling = aChild->GetNextSibling(); 1.236 +} 1.237 + 1.238 +void 1.239 +nsMutationReceiver::ContentRemoved(nsIDocument* aDocument, 1.240 + nsIContent* aContainer, 1.241 + nsIContent* aChild, 1.242 + int32_t aIndexInContainer, 1.243 + nsIContent* aPreviousSibling) 1.244 +{ 1.245 + if (aChild->ChromeOnlyAccess()) { 1.246 + return; 1.247 + } 1.248 + 1.249 + nsINode* parent = NODE_FROM(aContainer, aDocument); 1.250 + if (nsAutoMutationBatch::IsBatching()) { 1.251 + if (nsAutoMutationBatch::IsRemovalDone()) { 1.252 + // This can happen for example if HTML parser parses to 1.253 + // context node, but needs to move elements around. 1.254 + return; 1.255 + } 1.256 + if (nsAutoMutationBatch::GetBatchTarget() != parent) { 1.257 + return; 1.258 + } 1.259 + 1.260 + bool wantsChildList = ChildList() && (Subtree() || parent == Target()); 1.261 + if (wantsChildList || Subtree()) { 1.262 + nsAutoMutationBatch::NodeRemoved(aChild); 1.263 + nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList); 1.264 + } 1.265 + 1.266 + return; 1.267 + } 1.268 + 1.269 + if (Subtree()) { 1.270 + // Try to avoid creating transient observer if the node 1.271 + // already has an observer observing the same set of nodes. 1.272 + nsMutationReceiver* orig = GetParent() ? GetParent() : this; 1.273 + if (Observer()->GetReceiverFor(aChild, false) != orig) { 1.274 + bool transientExists = false; 1.275 + nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr; 1.276 + Observer()->mTransientReceivers.Get(aChild, &transientReceivers); 1.277 + if (!transientReceivers) { 1.278 + transientReceivers = new nsCOMArray<nsMutationReceiver>(); 1.279 + Observer()->mTransientReceivers.Put(aChild, transientReceivers); 1.280 + } else { 1.281 + for (int32_t i = 0; i < transientReceivers->Count(); ++i) { 1.282 + nsMutationReceiver* r = transientReceivers->ObjectAt(i); 1.283 + if (r->GetParent() == orig) { 1.284 + transientExists = true; 1.285 + } 1.286 + } 1.287 + } 1.288 + if (!transientExists) { 1.289 + // Make sure the elements which are removed from the 1.290 + // subtree are kept in the same observation set. 1.291 + transientReceivers->AppendObject(new nsMutationReceiver(aChild, orig)); 1.292 + } 1.293 + } 1.294 + } 1.295 + 1.296 + if (ChildList() && (Subtree() || parent == Target())) { 1.297 + nsDOMMutationRecord* m = 1.298 + Observer()->CurrentRecord(nsGkAtoms::childList); 1.299 + if (m->mTarget) { 1.300 + // Already handled case. 1.301 + return; 1.302 + } 1.303 + m->mTarget = parent; 1.304 + m->mRemovedNodes = new nsSimpleContentList(parent); 1.305 + m->mRemovedNodes->AppendElement(aChild); 1.306 + m->mPreviousSibling = aPreviousSibling; 1.307 + m->mNextSibling = parent->GetChildAt(aIndexInContainer); 1.308 + } 1.309 + // We need to schedule always, so that after microtask mTransientReceivers 1.310 + // can be cleared correctly. 1.311 + Observer()->ScheduleForRun(); 1.312 +} 1.313 + 1.314 +void nsMutationReceiver::NodeWillBeDestroyed(const nsINode *aNode) 1.315 +{ 1.316 + NS_ASSERTION(!mParent, "Shouldn't have mParent here!"); 1.317 + Disconnect(true); 1.318 +} 1.319 + 1.320 +// Observer 1.321 + 1.322 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver) 1.323 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.324 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.325 + NS_INTERFACE_MAP_ENTRY(nsDOMMutationObserver) 1.326 +NS_INTERFACE_MAP_END 1.327 + 1.328 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver) 1.329 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver) 1.330 + 1.331 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver) 1.332 + 1.333 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver) 1.334 + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 1.335 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.336 + 1.337 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver) 1.338 + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1.339 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) 1.340 + for (int32_t i = 0; i < tmp->mReceivers.Count(); ++i) { 1.341 + tmp->mReceivers[i]->Disconnect(false); 1.342 + } 1.343 + tmp->mReceivers.Clear(); 1.344 + tmp->ClearPendingRecords(); 1.345 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) 1.346 + // No need to handle mTransientReceivers 1.347 + NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.348 + 1.349 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver) 1.350 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.351 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) 1.352 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers) 1.353 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstPendingMutation) 1.354 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) 1.355 + // No need to handle mTransientReceivers 1.356 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.357 + 1.358 +nsMutationReceiver* 1.359 +nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate) 1.360 +{ 1.361 + if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) { 1.362 + return nullptr; 1.363 + } 1.364 + 1.365 + for (int32_t i = 0; i < mReceivers.Count(); ++i) { 1.366 + if (mReceivers[i]->Target() == aNode) { 1.367 + return mReceivers[i]; 1.368 + } 1.369 + } 1.370 + if (!aMayCreate) { 1.371 + return nullptr; 1.372 + } 1.373 + 1.374 + nsMutationReceiver* r = new nsMutationReceiver(aNode, this); 1.375 + mReceivers.AppendObject(r); 1.376 + return r; 1.377 +} 1.378 + 1.379 +void 1.380 +nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver* aReceiver) 1.381 +{ 1.382 + mReceivers.RemoveObject(aReceiver); 1.383 +} 1.384 + 1.385 +void 1.386 +nsDOMMutationObserver::GetAllSubtreeObserversFor(nsINode* aNode, 1.387 + nsTArray<nsMutationReceiver*>& 1.388 + aReceivers) 1.389 +{ 1.390 + nsINode* n = aNode; 1.391 + while (n) { 1.392 + if (n->MayHaveDOMMutationObserver()) { 1.393 + nsMutationReceiver* r = GetReceiverFor(n, false); 1.394 + if (r && r->Subtree() && !aReceivers.Contains(r)) { 1.395 + aReceivers.AppendElement(r); 1.396 + // If we've found all the receivers the observer has, 1.397 + // no need to search for more. 1.398 + if (mReceivers.Count() == int32_t(aReceivers.Length())) { 1.399 + return; 1.400 + } 1.401 + } 1.402 + nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr; 1.403 + if (mTransientReceivers.Get(n, &transientReceivers) && transientReceivers) { 1.404 + for (int32_t i = 0; i < transientReceivers->Count(); ++i) { 1.405 + nsMutationReceiver* r = transientReceivers->ObjectAt(i); 1.406 + nsMutationReceiver* parent = r->GetParent(); 1.407 + if (r->Subtree() && parent && !aReceivers.Contains(parent)) { 1.408 + aReceivers.AppendElement(parent); 1.409 + } 1.410 + } 1.411 + if (mReceivers.Count() == int32_t(aReceivers.Length())) { 1.412 + return; 1.413 + } 1.414 + } 1.415 + } 1.416 + n = n->GetParentNode(); 1.417 + } 1.418 +} 1.419 + 1.420 +void 1.421 +nsDOMMutationObserver::ScheduleForRun() 1.422 +{ 1.423 + nsDOMMutationObserver::AddCurrentlyHandlingObserver(this); 1.424 + 1.425 + if (mWaitingForRun) { 1.426 + return; 1.427 + } 1.428 + mWaitingForRun = true; 1.429 + RescheduleForRun(); 1.430 +} 1.431 + 1.432 +void 1.433 +nsDOMMutationObserver::RescheduleForRun() 1.434 +{ 1.435 + if (!sScheduledMutationObservers) { 1.436 + sScheduledMutationObservers = new nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>; 1.437 + } 1.438 + 1.439 + bool didInsert = false; 1.440 + for (uint32_t i = 0; i < sScheduledMutationObservers->Length(); ++i) { 1.441 + if (static_cast<nsDOMMutationObserver*>((*sScheduledMutationObservers)[i]) 1.442 + ->mId > mId) { 1.443 + sScheduledMutationObservers->InsertElementAt(i, this); 1.444 + didInsert = true; 1.445 + break; 1.446 + } 1.447 + } 1.448 + if (!didInsert) { 1.449 + sScheduledMutationObservers->AppendElement(this); 1.450 + } 1.451 +} 1.452 + 1.453 +void 1.454 +nsDOMMutationObserver::Observe(nsINode& aTarget, 1.455 + const mozilla::dom::MutationObserverInit& aOptions, 1.456 + mozilla::ErrorResult& aRv) 1.457 +{ 1.458 + 1.459 + if (!(aOptions.mChildList || aOptions.mAttributes || aOptions.mCharacterData)) { 1.460 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.461 + return; 1.462 + } 1.463 + if (aOptions.mAttributeOldValue && !aOptions.mAttributes) { 1.464 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.465 + return; 1.466 + } 1.467 + if (aOptions.mCharacterDataOldValue && !aOptions.mCharacterData) { 1.468 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.469 + return; 1.470 + } 1.471 + 1.472 + nsCOMArray<nsIAtom> filters; 1.473 + bool allAttrs = true; 1.474 + if (aOptions.mAttributeFilter.WasPassed()) { 1.475 + allAttrs = false; 1.476 + const mozilla::dom::Sequence<nsString>& filtersAsString = 1.477 + aOptions.mAttributeFilter.Value(); 1.478 + uint32_t len = filtersAsString.Length(); 1.479 + 1.480 + if (len != 0 && !aOptions.mAttributes) { 1.481 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.482 + return; 1.483 + } 1.484 + filters.SetCapacity(len); 1.485 + 1.486 + for (uint32_t i = 0; i < len; ++i) { 1.487 + nsCOMPtr<nsIAtom> a = do_GetAtom(filtersAsString[i]); 1.488 + filters.AppendObject(a); 1.489 + } 1.490 + } 1.491 + 1.492 + nsMutationReceiver* r = GetReceiverFor(&aTarget, true); 1.493 + r->SetChildList(aOptions.mChildList); 1.494 + r->SetAttributes(aOptions.mAttributes); 1.495 + r->SetCharacterData(aOptions.mCharacterData); 1.496 + r->SetSubtree(aOptions.mSubtree); 1.497 + r->SetAttributeOldValue(aOptions.mAttributeOldValue); 1.498 + r->SetCharacterDataOldValue(aOptions.mCharacterDataOldValue); 1.499 + r->SetAttributeFilter(filters); 1.500 + r->SetAllAttributes(allAttrs); 1.501 + r->RemoveClones(); 1.502 + 1.503 +#ifdef DEBUG 1.504 + for (int32_t i = 0; i < mReceivers.Count(); ++i) { 1.505 + NS_WARN_IF_FALSE(mReceivers[i]->Target(), 1.506 + "All the receivers should have a target!"); 1.507 + } 1.508 +#endif 1.509 +} 1.510 + 1.511 +void 1.512 +nsDOMMutationObserver::Disconnect() 1.513 +{ 1.514 + for (int32_t i = 0; i < mReceivers.Count(); ++i) { 1.515 + mReceivers[i]->Disconnect(false); 1.516 + } 1.517 + mReceivers.Clear(); 1.518 + mCurrentMutations.Clear(); 1.519 + ClearPendingRecords(); 1.520 +} 1.521 + 1.522 +void 1.523 +nsDOMMutationObserver::TakeRecords( 1.524 + nsTArray<nsRefPtr<nsDOMMutationRecord> >& aRetVal) 1.525 +{ 1.526 + aRetVal.Clear(); 1.527 + aRetVal.SetCapacity(mPendingMutationCount); 1.528 + nsRefPtr<nsDOMMutationRecord> current; 1.529 + current.swap(mFirstPendingMutation); 1.530 + for (uint32_t i = 0; i < mPendingMutationCount; ++i) { 1.531 + nsRefPtr<nsDOMMutationRecord> next; 1.532 + current->mNext.swap(next); 1.533 + *aRetVal.AppendElement() = current.forget(); 1.534 + current.swap(next); 1.535 + } 1.536 + ClearPendingRecords(); 1.537 +} 1.538 + 1.539 +void 1.540 +nsDOMMutationObserver::GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult) 1.541 +{ 1.542 + aResult.SetCapacity(mReceivers.Count()); 1.543 + for (int32_t i = 0; i < mReceivers.Count(); ++i) { 1.544 + MutationObservingInfo& info = aResult.AppendElement()->SetValue(); 1.545 + nsMutationReceiver* mr = mReceivers[i]; 1.546 + info.mChildList = mr->ChildList(); 1.547 + info.mAttributes = mr->Attributes(); 1.548 + info.mCharacterData = mr->CharacterData(); 1.549 + info.mSubtree = mr->Subtree(); 1.550 + info.mAttributeOldValue = mr->AttributeOldValue(); 1.551 + info.mCharacterDataOldValue = mr->CharacterDataOldValue(); 1.552 + nsCOMArray<nsIAtom>& filters = mr->AttributeFilter(); 1.553 + if (filters.Count()) { 1.554 + info.mAttributeFilter.Construct(); 1.555 + mozilla::dom::Sequence<nsString>& filtersAsStrings = 1.556 + info.mAttributeFilter.Value(); 1.557 + for (int32_t j = 0; j < filters.Count(); ++j) { 1.558 + filtersAsStrings.AppendElement(nsDependentAtomString(filters[j])); 1.559 + } 1.560 + } 1.561 + info.mObservedNode = mr->Target(); 1.562 + } 1.563 +} 1.564 + 1.565 +// static 1.566 +already_AddRefed<nsDOMMutationObserver> 1.567 +nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal, 1.568 + mozilla::dom::MutationCallback& aCb, 1.569 + mozilla::ErrorResult& aRv) 1.570 +{ 1.571 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); 1.572 + if (!window) { 1.573 + aRv.Throw(NS_ERROR_FAILURE); 1.574 + return nullptr; 1.575 + } 1.576 + MOZ_ASSERT(window->IsInnerWindow()); 1.577 + nsRefPtr<nsDOMMutationObserver> observer = 1.578 + new nsDOMMutationObserver(window.forget(), aCb); 1.579 + return observer.forget(); 1.580 +} 1.581 + 1.582 +void 1.583 +nsDOMMutationObserver::HandleMutation() 1.584 +{ 1.585 + NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!"); 1.586 + NS_ASSERTION(mCurrentMutations.IsEmpty(), 1.587 + "Still generating MutationRecords?"); 1.588 + 1.589 + mWaitingForRun = false; 1.590 + 1.591 + for (int32_t i = 0; i < mReceivers.Count(); ++i) { 1.592 + mReceivers[i]->RemoveClones(); 1.593 + } 1.594 + mTransientReceivers.Clear(); 1.595 + 1.596 + nsPIDOMWindow* outer = mOwner->GetOuterWindow(); 1.597 + if (!mPendingMutationCount || !outer || 1.598 + outer->GetCurrentInnerWindow() != mOwner) { 1.599 + ClearPendingRecords(); 1.600 + return; 1.601 + } 1.602 + 1.603 + mozilla::dom::Sequence<mozilla::dom::OwningNonNull<nsDOMMutationRecord> > 1.604 + mutations; 1.605 + if (mutations.SetCapacity(mPendingMutationCount)) { 1.606 + // We can't use TakeRecords easily here, because it deals with a 1.607 + // different type of array, and we want to optimize out any extra copying. 1.608 + nsRefPtr<nsDOMMutationRecord> current; 1.609 + current.swap(mFirstPendingMutation); 1.610 + for (uint32_t i = 0; i < mPendingMutationCount; ++i) { 1.611 + nsRefPtr<nsDOMMutationRecord> next; 1.612 + current->mNext.swap(next); 1.613 + *mutations.AppendElement() = current; 1.614 + current.swap(next); 1.615 + } 1.616 + } 1.617 + ClearPendingRecords(); 1.618 + 1.619 + mozilla::ErrorResult rv; 1.620 + mCallback->Call(this, mutations, *this, rv); 1.621 +} 1.622 + 1.623 +class AsyncMutationHandler : public nsRunnable 1.624 +{ 1.625 +public: 1.626 + NS_IMETHOD Run() 1.627 + { 1.628 + nsDOMMutationObserver::HandleMutations(); 1.629 + return NS_OK; 1.630 + } 1.631 +}; 1.632 + 1.633 +void 1.634 +nsDOMMutationObserver::HandleMutationsInternal() 1.635 +{ 1.636 + if (!nsContentUtils::IsSafeToRunScript()) { 1.637 + nsContentUtils::AddScriptRunner(new AsyncMutationHandler()); 1.638 + return; 1.639 + } 1.640 + static nsRefPtr<nsDOMMutationObserver> sCurrentObserver; 1.641 + if (sCurrentObserver && !sCurrentObserver->Suppressed()) { 1.642 + // In normal cases sScheduledMutationObservers will be handled 1.643 + // after previous mutations are handled. But in case some 1.644 + // callback calls a sync API, which spins the eventloop, we need to still 1.645 + // process other mutations happening during that sync call. 1.646 + // This does *not* catch all cases, but should work for stuff running 1.647 + // in separate tabs. 1.648 + return; 1.649 + } 1.650 + 1.651 + nsTArray<nsRefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr; 1.652 + 1.653 + while (sScheduledMutationObservers) { 1.654 + nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* observers = 1.655 + sScheduledMutationObservers; 1.656 + sScheduledMutationObservers = nullptr; 1.657 + for (uint32_t i = 0; i < observers->Length(); ++i) { 1.658 + sCurrentObserver = static_cast<nsDOMMutationObserver*>((*observers)[i]); 1.659 + if (!sCurrentObserver->Suppressed()) { 1.660 + sCurrentObserver->HandleMutation(); 1.661 + } else { 1.662 + if (!suppressedObservers) { 1.663 + suppressedObservers = new nsTArray<nsRefPtr<nsDOMMutationObserver> >; 1.664 + } 1.665 + if (!suppressedObservers->Contains(sCurrentObserver)) { 1.666 + suppressedObservers->AppendElement(sCurrentObserver); 1.667 + } 1.668 + } 1.669 + } 1.670 + delete observers; 1.671 + } 1.672 + 1.673 + if (suppressedObservers) { 1.674 + for (uint32_t i = 0; i < suppressedObservers->Length(); ++i) { 1.675 + static_cast<nsDOMMutationObserver*>(suppressedObservers->ElementAt(i))-> 1.676 + RescheduleForRun(); 1.677 + } 1.678 + delete suppressedObservers; 1.679 + suppressedObservers = nullptr; 1.680 + } 1.681 + sCurrentObserver = nullptr; 1.682 +} 1.683 + 1.684 +nsDOMMutationRecord* 1.685 +nsDOMMutationObserver::CurrentRecord(nsIAtom* aType) 1.686 +{ 1.687 + NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!"); 1.688 + 1.689 + while (mCurrentMutations.Length() < sMutationLevel) { 1.690 + mCurrentMutations.AppendElement(static_cast<nsDOMMutationRecord*>(nullptr)); 1.691 + } 1.692 + 1.693 + uint32_t last = sMutationLevel - 1; 1.694 + if (!mCurrentMutations[last]) { 1.695 + nsRefPtr<nsDOMMutationRecord> r = new nsDOMMutationRecord(aType, GetParentObject()); 1.696 + mCurrentMutations[last] = r; 1.697 + AppendMutationRecord(r.forget()); 1.698 + ScheduleForRun(); 1.699 + } 1.700 + 1.701 + NS_ASSERTION(mCurrentMutations[last]->mType == aType, 1.702 + "Unexpected MutationRecord type!"); 1.703 + 1.704 + return mCurrentMutations[last]; 1.705 +} 1.706 + 1.707 +nsDOMMutationObserver::~nsDOMMutationObserver() 1.708 +{ 1.709 + for (int32_t i = 0; i < mReceivers.Count(); ++i) { 1.710 + mReceivers[i]->RemoveClones(); 1.711 + } 1.712 +} 1.713 + 1.714 +void 1.715 +nsDOMMutationObserver::EnterMutationHandling() 1.716 +{ 1.717 + ++sMutationLevel; 1.718 +} 1.719 + 1.720 +// Leave the current mutation level (there can be several levels if in case 1.721 +// of nested calls to the nsIMutationObserver methods). 1.722 +// The most recent mutation record is removed from mCurrentMutations, so 1.723 +// that is doesn't get modified anymore by receivers. 1.724 +void 1.725 +nsDOMMutationObserver::LeaveMutationHandling() 1.726 +{ 1.727 + if (sCurrentlyHandlingObservers && 1.728 + sCurrentlyHandlingObservers->Length() == sMutationLevel) { 1.729 + nsTArray<nsRefPtr<nsDOMMutationObserver> >& obs = 1.730 + sCurrentlyHandlingObservers->ElementAt(sMutationLevel - 1); 1.731 + for (uint32_t i = 0; i < obs.Length(); ++i) { 1.732 + nsDOMMutationObserver* o = 1.733 + static_cast<nsDOMMutationObserver*>(obs[i]); 1.734 + if (o->mCurrentMutations.Length() == sMutationLevel) { 1.735 + // It is already in pending mutations. 1.736 + o->mCurrentMutations.RemoveElementAt(sMutationLevel - 1); 1.737 + } 1.738 + } 1.739 + sCurrentlyHandlingObservers->RemoveElementAt(sMutationLevel - 1); 1.740 + } 1.741 + --sMutationLevel; 1.742 +} 1.743 + 1.744 +void 1.745 +nsDOMMutationObserver::AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver) 1.746 +{ 1.747 + NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!"); 1.748 + 1.749 + if (!sCurrentlyHandlingObservers) { 1.750 + sCurrentlyHandlingObservers = 1.751 + new nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>; 1.752 + } 1.753 + 1.754 + while (sCurrentlyHandlingObservers->Length() < sMutationLevel) { 1.755 + sCurrentlyHandlingObservers->InsertElementAt( 1.756 + sCurrentlyHandlingObservers->Length()); 1.757 + } 1.758 + 1.759 + uint32_t last = sMutationLevel - 1; 1.760 + if (!sCurrentlyHandlingObservers->ElementAt(last).Contains(aObserver)) { 1.761 + sCurrentlyHandlingObservers->ElementAt(last).AppendElement(aObserver); 1.762 + } 1.763 +} 1.764 + 1.765 +void 1.766 +nsDOMMutationObserver::Shutdown() 1.767 +{ 1.768 + delete sCurrentlyHandlingObservers; 1.769 + sCurrentlyHandlingObservers = nullptr; 1.770 + delete sScheduledMutationObservers; 1.771 + sScheduledMutationObservers = nullptr; 1.772 +} 1.773 + 1.774 +nsAutoMutationBatch* 1.775 +nsAutoMutationBatch::sCurrentBatch = nullptr; 1.776 + 1.777 +void 1.778 +nsAutoMutationBatch::Done() 1.779 +{ 1.780 + if (sCurrentBatch != this) { 1.781 + return; 1.782 + } 1.783 + 1.784 + sCurrentBatch = mPreviousBatch; 1.785 + if (mObservers.IsEmpty()) { 1.786 + nsDOMMutationObserver::LeaveMutationHandling(); 1.787 + // Nothing to do. 1.788 + return; 1.789 + } 1.790 + 1.791 + uint32_t len = mObservers.Length(); 1.792 + for (uint32_t i = 0; i < len; ++i) { 1.793 + nsDOMMutationObserver* ob = mObservers[i].mObserver; 1.794 + bool wantsChildList = mObservers[i].mWantsChildList; 1.795 + 1.796 + nsRefPtr<nsSimpleContentList> removedList; 1.797 + if (wantsChildList) { 1.798 + removedList = new nsSimpleContentList(mBatchTarget); 1.799 + } 1.800 + 1.801 + nsTArray<nsMutationReceiver*> allObservers; 1.802 + ob->GetAllSubtreeObserversFor(mBatchTarget, allObservers); 1.803 + 1.804 + int32_t j = mFromFirstToLast ? 0 : mRemovedNodes.Length() - 1; 1.805 + int32_t end = mFromFirstToLast ? mRemovedNodes.Length() : -1; 1.806 + for (; j != end; mFromFirstToLast ? ++j : --j) { 1.807 + nsCOMPtr<nsIContent> removed = mRemovedNodes[j]; 1.808 + if (removedList) { 1.809 + removedList->AppendElement(removed); 1.810 + } 1.811 + 1.812 + if (allObservers.Length()) { 1.813 + nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr; 1.814 + ob->mTransientReceivers.Get(removed, &transientReceivers); 1.815 + if (!transientReceivers) { 1.816 + transientReceivers = new nsCOMArray<nsMutationReceiver>(); 1.817 + ob->mTransientReceivers.Put(removed, transientReceivers); 1.818 + } 1.819 + for (uint32_t k = 0; k < allObservers.Length(); ++k) { 1.820 + nsMutationReceiver* r = allObservers[k]; 1.821 + nsMutationReceiver* orig = r->GetParent() ? r->GetParent() : r; 1.822 + if (ob->GetReceiverFor(removed, false) != orig) { 1.823 + // Make sure the elements which are removed from the 1.824 + // subtree are kept in the same observation set. 1.825 + transientReceivers->AppendObject(new nsMutationReceiver(removed, orig)); 1.826 + } 1.827 + } 1.828 + } 1.829 + } 1.830 + if (wantsChildList && (mRemovedNodes.Length() || mAddedNodes.Length())) { 1.831 + nsRefPtr<nsSimpleContentList> addedList = 1.832 + new nsSimpleContentList(mBatchTarget); 1.833 + for (uint32_t i = 0; i < mAddedNodes.Length(); ++i) { 1.834 + addedList->AppendElement(mAddedNodes[i]); 1.835 + } 1.836 + nsRefPtr<nsDOMMutationRecord> m = 1.837 + new nsDOMMutationRecord(nsGkAtoms::childList, 1.838 + ob->GetParentObject()); 1.839 + m->mTarget = mBatchTarget; 1.840 + m->mRemovedNodes = removedList; 1.841 + m->mAddedNodes = addedList; 1.842 + m->mPreviousSibling = mPrevSibling; 1.843 + m->mNextSibling = mNextSibling; 1.844 + ob->AppendMutationRecord(m.forget()); 1.845 + } 1.846 + // Always schedule the observer so that transient receivers are 1.847 + // removed correctly. 1.848 + ob->ScheduleForRun(); 1.849 + } 1.850 + nsDOMMutationObserver::LeaveMutationHandling(); 1.851 +}