Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
2 /* vim: set sw=4 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDOMMutationObserver.h"
9 #include "mozilla/dom/OwningNonNull.h"
10 #include "nsError.h"
11 #include "nsIScriptGlobalObject.h"
12 #include "nsContentUtils.h"
13 #include "nsThreadUtils.h"
14 #include "nsIDOMMutationEvent.h"
15 #include "nsTextFragment.h"
16 #include "nsServiceManagerUtils.h"
18 nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>*
19 nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
21 nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
23 uint32_t nsDOMMutationObserver::sMutationLevel = 0;
24 uint64_t nsDOMMutationObserver::sCount = 0;
26 nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>*
27 nsDOMMutationObserver::sCurrentlyHandlingObservers = nullptr;
29 nsINodeList*
30 nsDOMMutationRecord::AddedNodes()
31 {
32 if (!mAddedNodes) {
33 mAddedNodes = new nsSimpleContentList(mTarget);
34 }
35 return mAddedNodes;
36 }
38 nsINodeList*
39 nsDOMMutationRecord::RemovedNodes()
40 {
41 if (!mRemovedNodes) {
42 mRemovedNodes = new nsSimpleContentList(mTarget);
43 }
44 return mRemovedNodes;
45 }
47 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationRecord)
48 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
49 NS_INTERFACE_MAP_ENTRY(nsISupports)
50 NS_INTERFACE_MAP_END
52 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord)
53 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord)
55 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_7(nsDOMMutationRecord,
56 mTarget,
57 mPreviousSibling, mNextSibling,
58 mAddedNodes, mRemovedNodes,
59 mNext, mOwner)
61 // Observer
63 NS_IMPL_ADDREF(nsMutationReceiver)
64 NS_IMPL_RELEASE(nsMutationReceiver)
66 NS_INTERFACE_MAP_BEGIN(nsMutationReceiver)
67 NS_INTERFACE_MAP_ENTRY(nsISupports)
68 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
69 NS_INTERFACE_MAP_END
71 nsMutationReceiver::nsMutationReceiver(nsINode* aTarget,
72 nsDOMMutationObserver* aObserver)
73 : nsMutationReceiverBase(aTarget, aObserver)
74 {
75 mTarget->BindObject(aObserver);
76 }
78 void
79 nsMutationReceiver::Disconnect(bool aRemoveFromObserver)
80 {
81 if (mRegisterTarget) {
82 mRegisterTarget->RemoveMutationObserver(this);
83 mRegisterTarget = nullptr;
84 }
86 mParent = nullptr;
87 nsINode* target = mTarget;
88 mTarget = nullptr;
89 nsDOMMutationObserver* observer = mObserver;
90 mObserver = nullptr;
91 RemoveClones();
93 if (target && observer) {
94 if (aRemoveFromObserver) {
95 static_cast<nsDOMMutationObserver*>(observer)->RemoveReceiver(this);
96 }
97 // UnbindObject may delete 'this'!
98 target->UnbindObject(observer);
99 }
100 }
102 void
103 nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
104 mozilla::dom::Element* aElement,
105 int32_t aNameSpaceID,
106 nsIAtom* aAttribute,
107 int32_t aModType)
108 {
109 if (nsAutoMutationBatch::IsBatching() ||
110 !ObservesAttr(aElement, aNameSpaceID, aAttribute) ||
111 aElement->ChromeOnlyAccess()) {
112 return;
113 }
115 nsDOMMutationRecord* m =
116 Observer()->CurrentRecord(nsGkAtoms::attributes);
118 NS_ASSERTION(!m->mTarget || m->mTarget == aElement,
119 "Wrong target!");
120 NS_ASSERTION(!m->mAttrName || m->mAttrName == aAttribute,
121 "Wrong attribute!");
122 if (!m->mTarget) {
123 m->mTarget = aElement;
124 m->mAttrName = aAttribute;
125 if (aNameSpaceID == kNameSpaceID_None) {
126 m->mAttrNamespace.SetIsVoid(true);
127 } else {
128 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID,
129 m->mAttrNamespace);
130 }
131 }
133 if (AttributeOldValue() && m->mPrevValue.IsVoid()) {
134 if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) {
135 m->mPrevValue.SetIsVoid(true);
136 }
137 }
138 }
140 void
141 nsMutationReceiver::CharacterDataWillChange(nsIDocument *aDocument,
142 nsIContent* aContent,
143 CharacterDataChangeInfo* aInfo)
144 {
145 if (nsAutoMutationBatch::IsBatching() ||
146 !CharacterData() || !(Subtree() || aContent == Target()) ||
147 aContent->ChromeOnlyAccess()) {
148 return;
149 }
151 nsDOMMutationRecord* m =
152 Observer()->CurrentRecord(nsGkAtoms::characterData);
154 NS_ASSERTION(!m->mTarget || m->mTarget == aContent,
155 "Wrong target!");
157 if (!m->mTarget) {
158 m->mTarget = aContent;
159 }
160 if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) {
161 aContent->GetText()->AppendTo(m->mPrevValue);
162 }
163 }
165 void
166 nsMutationReceiver::ContentAppended(nsIDocument* aDocument,
167 nsIContent* aContainer,
168 nsIContent* aFirstNewContent,
169 int32_t aNewIndexInContainer)
170 {
171 nsINode* parent = NODE_FROM(aContainer, aDocument);
172 bool wantsChildList = ChildList() && (Subtree() || parent == Target());
173 if (!wantsChildList || aFirstNewContent->ChromeOnlyAccess()) {
174 return;
175 }
177 if (nsAutoMutationBatch::IsBatching()) {
178 if (parent == nsAutoMutationBatch::GetBatchTarget()) {
179 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
180 }
181 return;
182 }
184 nsDOMMutationRecord* m =
185 Observer()->CurrentRecord(nsGkAtoms::childList);
186 NS_ASSERTION(!m->mTarget || m->mTarget == parent,
187 "Wrong target!");
188 if (m->mTarget) {
189 // Already handled case.
190 return;
191 }
192 m->mTarget = parent;
193 m->mAddedNodes = new nsSimpleContentList(parent);
195 nsINode* n = aFirstNewContent;
196 while (n) {
197 m->mAddedNodes->AppendElement(static_cast<nsIContent*>(n));
198 n = n->GetNextSibling();
199 }
200 m->mPreviousSibling = aFirstNewContent->GetPreviousSibling();
201 }
203 void
204 nsMutationReceiver::ContentInserted(nsIDocument* aDocument,
205 nsIContent* aContainer,
206 nsIContent* aChild,
207 int32_t aIndexInContainer)
208 {
209 nsINode* parent = NODE_FROM(aContainer, aDocument);
210 bool wantsChildList = ChildList() && (Subtree() || parent == Target());
211 if (!wantsChildList || aChild->ChromeOnlyAccess()) {
212 return;
213 }
215 if (nsAutoMutationBatch::IsBatching()) {
216 if (parent == nsAutoMutationBatch::GetBatchTarget()) {
217 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
218 }
219 return;
220 }
222 nsDOMMutationRecord* m =
223 Observer()->CurrentRecord(nsGkAtoms::childList);
224 if (m->mTarget) {
225 // Already handled case.
226 return;
227 }
228 m->mTarget = parent;
229 m->mAddedNodes = new nsSimpleContentList(parent);
230 m->mAddedNodes->AppendElement(aChild);
231 m->mPreviousSibling = aChild->GetPreviousSibling();
232 m->mNextSibling = aChild->GetNextSibling();
233 }
235 void
236 nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
237 nsIContent* aContainer,
238 nsIContent* aChild,
239 int32_t aIndexInContainer,
240 nsIContent* aPreviousSibling)
241 {
242 if (aChild->ChromeOnlyAccess()) {
243 return;
244 }
246 nsINode* parent = NODE_FROM(aContainer, aDocument);
247 if (nsAutoMutationBatch::IsBatching()) {
248 if (nsAutoMutationBatch::IsRemovalDone()) {
249 // This can happen for example if HTML parser parses to
250 // context node, but needs to move elements around.
251 return;
252 }
253 if (nsAutoMutationBatch::GetBatchTarget() != parent) {
254 return;
255 }
257 bool wantsChildList = ChildList() && (Subtree() || parent == Target());
258 if (wantsChildList || Subtree()) {
259 nsAutoMutationBatch::NodeRemoved(aChild);
260 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
261 }
263 return;
264 }
266 if (Subtree()) {
267 // Try to avoid creating transient observer if the node
268 // already has an observer observing the same set of nodes.
269 nsMutationReceiver* orig = GetParent() ? GetParent() : this;
270 if (Observer()->GetReceiverFor(aChild, false) != orig) {
271 bool transientExists = false;
272 nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr;
273 Observer()->mTransientReceivers.Get(aChild, &transientReceivers);
274 if (!transientReceivers) {
275 transientReceivers = new nsCOMArray<nsMutationReceiver>();
276 Observer()->mTransientReceivers.Put(aChild, transientReceivers);
277 } else {
278 for (int32_t i = 0; i < transientReceivers->Count(); ++i) {
279 nsMutationReceiver* r = transientReceivers->ObjectAt(i);
280 if (r->GetParent() == orig) {
281 transientExists = true;
282 }
283 }
284 }
285 if (!transientExists) {
286 // Make sure the elements which are removed from the
287 // subtree are kept in the same observation set.
288 transientReceivers->AppendObject(new nsMutationReceiver(aChild, orig));
289 }
290 }
291 }
293 if (ChildList() && (Subtree() || parent == Target())) {
294 nsDOMMutationRecord* m =
295 Observer()->CurrentRecord(nsGkAtoms::childList);
296 if (m->mTarget) {
297 // Already handled case.
298 return;
299 }
300 m->mTarget = parent;
301 m->mRemovedNodes = new nsSimpleContentList(parent);
302 m->mRemovedNodes->AppendElement(aChild);
303 m->mPreviousSibling = aPreviousSibling;
304 m->mNextSibling = parent->GetChildAt(aIndexInContainer);
305 }
306 // We need to schedule always, so that after microtask mTransientReceivers
307 // can be cleared correctly.
308 Observer()->ScheduleForRun();
309 }
311 void nsMutationReceiver::NodeWillBeDestroyed(const nsINode *aNode)
312 {
313 NS_ASSERTION(!mParent, "Shouldn't have mParent here!");
314 Disconnect(true);
315 }
317 // Observer
319 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver)
320 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
321 NS_INTERFACE_MAP_ENTRY(nsISupports)
322 NS_INTERFACE_MAP_ENTRY(nsDOMMutationObserver)
323 NS_INTERFACE_MAP_END
325 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver)
326 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver)
328 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver)
330 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver)
331 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
332 NS_IMPL_CYCLE_COLLECTION_TRACE_END
334 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver)
335 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
336 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
337 for (int32_t i = 0; i < tmp->mReceivers.Count(); ++i) {
338 tmp->mReceivers[i]->Disconnect(false);
339 }
340 tmp->mReceivers.Clear();
341 tmp->ClearPendingRecords();
342 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
343 // No need to handle mTransientReceivers
344 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
346 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver)
347 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
348 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
349 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers)
350 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstPendingMutation)
351 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
352 // No need to handle mTransientReceivers
353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
355 nsMutationReceiver*
356 nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate)
357 {
358 if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) {
359 return nullptr;
360 }
362 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
363 if (mReceivers[i]->Target() == aNode) {
364 return mReceivers[i];
365 }
366 }
367 if (!aMayCreate) {
368 return nullptr;
369 }
371 nsMutationReceiver* r = new nsMutationReceiver(aNode, this);
372 mReceivers.AppendObject(r);
373 return r;
374 }
376 void
377 nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver* aReceiver)
378 {
379 mReceivers.RemoveObject(aReceiver);
380 }
382 void
383 nsDOMMutationObserver::GetAllSubtreeObserversFor(nsINode* aNode,
384 nsTArray<nsMutationReceiver*>&
385 aReceivers)
386 {
387 nsINode* n = aNode;
388 while (n) {
389 if (n->MayHaveDOMMutationObserver()) {
390 nsMutationReceiver* r = GetReceiverFor(n, false);
391 if (r && r->Subtree() && !aReceivers.Contains(r)) {
392 aReceivers.AppendElement(r);
393 // If we've found all the receivers the observer has,
394 // no need to search for more.
395 if (mReceivers.Count() == int32_t(aReceivers.Length())) {
396 return;
397 }
398 }
399 nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr;
400 if (mTransientReceivers.Get(n, &transientReceivers) && transientReceivers) {
401 for (int32_t i = 0; i < transientReceivers->Count(); ++i) {
402 nsMutationReceiver* r = transientReceivers->ObjectAt(i);
403 nsMutationReceiver* parent = r->GetParent();
404 if (r->Subtree() && parent && !aReceivers.Contains(parent)) {
405 aReceivers.AppendElement(parent);
406 }
407 }
408 if (mReceivers.Count() == int32_t(aReceivers.Length())) {
409 return;
410 }
411 }
412 }
413 n = n->GetParentNode();
414 }
415 }
417 void
418 nsDOMMutationObserver::ScheduleForRun()
419 {
420 nsDOMMutationObserver::AddCurrentlyHandlingObserver(this);
422 if (mWaitingForRun) {
423 return;
424 }
425 mWaitingForRun = true;
426 RescheduleForRun();
427 }
429 void
430 nsDOMMutationObserver::RescheduleForRun()
431 {
432 if (!sScheduledMutationObservers) {
433 sScheduledMutationObservers = new nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>;
434 }
436 bool didInsert = false;
437 for (uint32_t i = 0; i < sScheduledMutationObservers->Length(); ++i) {
438 if (static_cast<nsDOMMutationObserver*>((*sScheduledMutationObservers)[i])
439 ->mId > mId) {
440 sScheduledMutationObservers->InsertElementAt(i, this);
441 didInsert = true;
442 break;
443 }
444 }
445 if (!didInsert) {
446 sScheduledMutationObservers->AppendElement(this);
447 }
448 }
450 void
451 nsDOMMutationObserver::Observe(nsINode& aTarget,
452 const mozilla::dom::MutationObserverInit& aOptions,
453 mozilla::ErrorResult& aRv)
454 {
456 if (!(aOptions.mChildList || aOptions.mAttributes || aOptions.mCharacterData)) {
457 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
458 return;
459 }
460 if (aOptions.mAttributeOldValue && !aOptions.mAttributes) {
461 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
462 return;
463 }
464 if (aOptions.mCharacterDataOldValue && !aOptions.mCharacterData) {
465 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
466 return;
467 }
469 nsCOMArray<nsIAtom> filters;
470 bool allAttrs = true;
471 if (aOptions.mAttributeFilter.WasPassed()) {
472 allAttrs = false;
473 const mozilla::dom::Sequence<nsString>& filtersAsString =
474 aOptions.mAttributeFilter.Value();
475 uint32_t len = filtersAsString.Length();
477 if (len != 0 && !aOptions.mAttributes) {
478 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
479 return;
480 }
481 filters.SetCapacity(len);
483 for (uint32_t i = 0; i < len; ++i) {
484 nsCOMPtr<nsIAtom> a = do_GetAtom(filtersAsString[i]);
485 filters.AppendObject(a);
486 }
487 }
489 nsMutationReceiver* r = GetReceiverFor(&aTarget, true);
490 r->SetChildList(aOptions.mChildList);
491 r->SetAttributes(aOptions.mAttributes);
492 r->SetCharacterData(aOptions.mCharacterData);
493 r->SetSubtree(aOptions.mSubtree);
494 r->SetAttributeOldValue(aOptions.mAttributeOldValue);
495 r->SetCharacterDataOldValue(aOptions.mCharacterDataOldValue);
496 r->SetAttributeFilter(filters);
497 r->SetAllAttributes(allAttrs);
498 r->RemoveClones();
500 #ifdef DEBUG
501 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
502 NS_WARN_IF_FALSE(mReceivers[i]->Target(),
503 "All the receivers should have a target!");
504 }
505 #endif
506 }
508 void
509 nsDOMMutationObserver::Disconnect()
510 {
511 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
512 mReceivers[i]->Disconnect(false);
513 }
514 mReceivers.Clear();
515 mCurrentMutations.Clear();
516 ClearPendingRecords();
517 }
519 void
520 nsDOMMutationObserver::TakeRecords(
521 nsTArray<nsRefPtr<nsDOMMutationRecord> >& aRetVal)
522 {
523 aRetVal.Clear();
524 aRetVal.SetCapacity(mPendingMutationCount);
525 nsRefPtr<nsDOMMutationRecord> current;
526 current.swap(mFirstPendingMutation);
527 for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
528 nsRefPtr<nsDOMMutationRecord> next;
529 current->mNext.swap(next);
530 *aRetVal.AppendElement() = current.forget();
531 current.swap(next);
532 }
533 ClearPendingRecords();
534 }
536 void
537 nsDOMMutationObserver::GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult)
538 {
539 aResult.SetCapacity(mReceivers.Count());
540 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
541 MutationObservingInfo& info = aResult.AppendElement()->SetValue();
542 nsMutationReceiver* mr = mReceivers[i];
543 info.mChildList = mr->ChildList();
544 info.mAttributes = mr->Attributes();
545 info.mCharacterData = mr->CharacterData();
546 info.mSubtree = mr->Subtree();
547 info.mAttributeOldValue = mr->AttributeOldValue();
548 info.mCharacterDataOldValue = mr->CharacterDataOldValue();
549 nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
550 if (filters.Count()) {
551 info.mAttributeFilter.Construct();
552 mozilla::dom::Sequence<nsString>& filtersAsStrings =
553 info.mAttributeFilter.Value();
554 for (int32_t j = 0; j < filters.Count(); ++j) {
555 filtersAsStrings.AppendElement(nsDependentAtomString(filters[j]));
556 }
557 }
558 info.mObservedNode = mr->Target();
559 }
560 }
562 // static
563 already_AddRefed<nsDOMMutationObserver>
564 nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
565 mozilla::dom::MutationCallback& aCb,
566 mozilla::ErrorResult& aRv)
567 {
568 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
569 if (!window) {
570 aRv.Throw(NS_ERROR_FAILURE);
571 return nullptr;
572 }
573 MOZ_ASSERT(window->IsInnerWindow());
574 nsRefPtr<nsDOMMutationObserver> observer =
575 new nsDOMMutationObserver(window.forget(), aCb);
576 return observer.forget();
577 }
579 void
580 nsDOMMutationObserver::HandleMutation()
581 {
582 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
583 NS_ASSERTION(mCurrentMutations.IsEmpty(),
584 "Still generating MutationRecords?");
586 mWaitingForRun = false;
588 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
589 mReceivers[i]->RemoveClones();
590 }
591 mTransientReceivers.Clear();
593 nsPIDOMWindow* outer = mOwner->GetOuterWindow();
594 if (!mPendingMutationCount || !outer ||
595 outer->GetCurrentInnerWindow() != mOwner) {
596 ClearPendingRecords();
597 return;
598 }
600 mozilla::dom::Sequence<mozilla::dom::OwningNonNull<nsDOMMutationRecord> >
601 mutations;
602 if (mutations.SetCapacity(mPendingMutationCount)) {
603 // We can't use TakeRecords easily here, because it deals with a
604 // different type of array, and we want to optimize out any extra copying.
605 nsRefPtr<nsDOMMutationRecord> current;
606 current.swap(mFirstPendingMutation);
607 for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
608 nsRefPtr<nsDOMMutationRecord> next;
609 current->mNext.swap(next);
610 *mutations.AppendElement() = current;
611 current.swap(next);
612 }
613 }
614 ClearPendingRecords();
616 mozilla::ErrorResult rv;
617 mCallback->Call(this, mutations, *this, rv);
618 }
620 class AsyncMutationHandler : public nsRunnable
621 {
622 public:
623 NS_IMETHOD Run()
624 {
625 nsDOMMutationObserver::HandleMutations();
626 return NS_OK;
627 }
628 };
630 void
631 nsDOMMutationObserver::HandleMutationsInternal()
632 {
633 if (!nsContentUtils::IsSafeToRunScript()) {
634 nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
635 return;
636 }
637 static nsRefPtr<nsDOMMutationObserver> sCurrentObserver;
638 if (sCurrentObserver && !sCurrentObserver->Suppressed()) {
639 // In normal cases sScheduledMutationObservers will be handled
640 // after previous mutations are handled. But in case some
641 // callback calls a sync API, which spins the eventloop, we need to still
642 // process other mutations happening during that sync call.
643 // This does *not* catch all cases, but should work for stuff running
644 // in separate tabs.
645 return;
646 }
648 nsTArray<nsRefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr;
650 while (sScheduledMutationObservers) {
651 nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* observers =
652 sScheduledMutationObservers;
653 sScheduledMutationObservers = nullptr;
654 for (uint32_t i = 0; i < observers->Length(); ++i) {
655 sCurrentObserver = static_cast<nsDOMMutationObserver*>((*observers)[i]);
656 if (!sCurrentObserver->Suppressed()) {
657 sCurrentObserver->HandleMutation();
658 } else {
659 if (!suppressedObservers) {
660 suppressedObservers = new nsTArray<nsRefPtr<nsDOMMutationObserver> >;
661 }
662 if (!suppressedObservers->Contains(sCurrentObserver)) {
663 suppressedObservers->AppendElement(sCurrentObserver);
664 }
665 }
666 }
667 delete observers;
668 }
670 if (suppressedObservers) {
671 for (uint32_t i = 0; i < suppressedObservers->Length(); ++i) {
672 static_cast<nsDOMMutationObserver*>(suppressedObservers->ElementAt(i))->
673 RescheduleForRun();
674 }
675 delete suppressedObservers;
676 suppressedObservers = nullptr;
677 }
678 sCurrentObserver = nullptr;
679 }
681 nsDOMMutationRecord*
682 nsDOMMutationObserver::CurrentRecord(nsIAtom* aType)
683 {
684 NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
686 while (mCurrentMutations.Length() < sMutationLevel) {
687 mCurrentMutations.AppendElement(static_cast<nsDOMMutationRecord*>(nullptr));
688 }
690 uint32_t last = sMutationLevel - 1;
691 if (!mCurrentMutations[last]) {
692 nsRefPtr<nsDOMMutationRecord> r = new nsDOMMutationRecord(aType, GetParentObject());
693 mCurrentMutations[last] = r;
694 AppendMutationRecord(r.forget());
695 ScheduleForRun();
696 }
698 NS_ASSERTION(mCurrentMutations[last]->mType == aType,
699 "Unexpected MutationRecord type!");
701 return mCurrentMutations[last];
702 }
704 nsDOMMutationObserver::~nsDOMMutationObserver()
705 {
706 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
707 mReceivers[i]->RemoveClones();
708 }
709 }
711 void
712 nsDOMMutationObserver::EnterMutationHandling()
713 {
714 ++sMutationLevel;
715 }
717 // Leave the current mutation level (there can be several levels if in case
718 // of nested calls to the nsIMutationObserver methods).
719 // The most recent mutation record is removed from mCurrentMutations, so
720 // that is doesn't get modified anymore by receivers.
721 void
722 nsDOMMutationObserver::LeaveMutationHandling()
723 {
724 if (sCurrentlyHandlingObservers &&
725 sCurrentlyHandlingObservers->Length() == sMutationLevel) {
726 nsTArray<nsRefPtr<nsDOMMutationObserver> >& obs =
727 sCurrentlyHandlingObservers->ElementAt(sMutationLevel - 1);
728 for (uint32_t i = 0; i < obs.Length(); ++i) {
729 nsDOMMutationObserver* o =
730 static_cast<nsDOMMutationObserver*>(obs[i]);
731 if (o->mCurrentMutations.Length() == sMutationLevel) {
732 // It is already in pending mutations.
733 o->mCurrentMutations.RemoveElementAt(sMutationLevel - 1);
734 }
735 }
736 sCurrentlyHandlingObservers->RemoveElementAt(sMutationLevel - 1);
737 }
738 --sMutationLevel;
739 }
741 void
742 nsDOMMutationObserver::AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver)
743 {
744 NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
746 if (!sCurrentlyHandlingObservers) {
747 sCurrentlyHandlingObservers =
748 new nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>;
749 }
751 while (sCurrentlyHandlingObservers->Length() < sMutationLevel) {
752 sCurrentlyHandlingObservers->InsertElementAt(
753 sCurrentlyHandlingObservers->Length());
754 }
756 uint32_t last = sMutationLevel - 1;
757 if (!sCurrentlyHandlingObservers->ElementAt(last).Contains(aObserver)) {
758 sCurrentlyHandlingObservers->ElementAt(last).AppendElement(aObserver);
759 }
760 }
762 void
763 nsDOMMutationObserver::Shutdown()
764 {
765 delete sCurrentlyHandlingObservers;
766 sCurrentlyHandlingObservers = nullptr;
767 delete sScheduledMutationObservers;
768 sScheduledMutationObservers = nullptr;
769 }
771 nsAutoMutationBatch*
772 nsAutoMutationBatch::sCurrentBatch = nullptr;
774 void
775 nsAutoMutationBatch::Done()
776 {
777 if (sCurrentBatch != this) {
778 return;
779 }
781 sCurrentBatch = mPreviousBatch;
782 if (mObservers.IsEmpty()) {
783 nsDOMMutationObserver::LeaveMutationHandling();
784 // Nothing to do.
785 return;
786 }
788 uint32_t len = mObservers.Length();
789 for (uint32_t i = 0; i < len; ++i) {
790 nsDOMMutationObserver* ob = mObservers[i].mObserver;
791 bool wantsChildList = mObservers[i].mWantsChildList;
793 nsRefPtr<nsSimpleContentList> removedList;
794 if (wantsChildList) {
795 removedList = new nsSimpleContentList(mBatchTarget);
796 }
798 nsTArray<nsMutationReceiver*> allObservers;
799 ob->GetAllSubtreeObserversFor(mBatchTarget, allObservers);
801 int32_t j = mFromFirstToLast ? 0 : mRemovedNodes.Length() - 1;
802 int32_t end = mFromFirstToLast ? mRemovedNodes.Length() : -1;
803 for (; j != end; mFromFirstToLast ? ++j : --j) {
804 nsCOMPtr<nsIContent> removed = mRemovedNodes[j];
805 if (removedList) {
806 removedList->AppendElement(removed);
807 }
809 if (allObservers.Length()) {
810 nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr;
811 ob->mTransientReceivers.Get(removed, &transientReceivers);
812 if (!transientReceivers) {
813 transientReceivers = new nsCOMArray<nsMutationReceiver>();
814 ob->mTransientReceivers.Put(removed, transientReceivers);
815 }
816 for (uint32_t k = 0; k < allObservers.Length(); ++k) {
817 nsMutationReceiver* r = allObservers[k];
818 nsMutationReceiver* orig = r->GetParent() ? r->GetParent() : r;
819 if (ob->GetReceiverFor(removed, false) != orig) {
820 // Make sure the elements which are removed from the
821 // subtree are kept in the same observation set.
822 transientReceivers->AppendObject(new nsMutationReceiver(removed, orig));
823 }
824 }
825 }
826 }
827 if (wantsChildList && (mRemovedNodes.Length() || mAddedNodes.Length())) {
828 nsRefPtr<nsSimpleContentList> addedList =
829 new nsSimpleContentList(mBatchTarget);
830 for (uint32_t i = 0; i < mAddedNodes.Length(); ++i) {
831 addedList->AppendElement(mAddedNodes[i]);
832 }
833 nsRefPtr<nsDOMMutationRecord> m =
834 new nsDOMMutationRecord(nsGkAtoms::childList,
835 ob->GetParentObject());
836 m->mTarget = mBatchTarget;
837 m->mRemovedNodes = removedList;
838 m->mAddedNodes = addedList;
839 m->mPreviousSibling = mPrevSibling;
840 m->mNextSibling = mNextSibling;
841 ob->AppendMutationRecord(m.forget());
842 }
843 // Always schedule the observer so that transient receivers are
844 // removed correctly.
845 ob->ScheduleForRun();
846 }
847 nsDOMMutationObserver::LeaveMutationHandling();
848 }